src/vs/editor/browser/widget/codeEditorWidget.ts/CodeEditorWidget/_attachModel
class CodeEditorWidget:	protected _attachModel(model: ITextModel | null): void {
		if (!model) {
			this._modelData = null;
			return;
		}

		const listenersToRemove: IDisposable[] = [];

		this._domElement.setAttribute('data-mode-id', model.getLanguageId());
		this._configuration.setIsDominatedByLongLines(model.isDominatedByLongLines());
		this._configuration.setModelLineCount(model.getLineCount());

		model.onBeforeAttached();

		const viewModel = new ViewModel(
			this._id,
			this._configuration,
			model,
			DOMLineBreaksComputerFactory.create(),
			MonospaceLineBreaksComputerFactory.create(this._configuration.options),
			(callback) => dom.scheduleAtNextAnimationFrame(callback),
			this.languageConfigurationService,
			this._themeService
		);

		// Someone might destroy the model from under the editor, so prevent any exceptions by setting a null model
		listenersToRemove.push(model.onWillDispose(() => this.setModel(null)));

		listenersToRemove.push(viewModel.onEvent((e) => {
			switch (e.kind) {
				case OutgoingViewModelEventKind.ContentSizeChanged:
					this._onDidContentSizeChange.fire(e);
					break;
				case OutgoingViewModelEventKind.FocusChanged:
					this._editorTextFocus.setValue(e.hasFocus);
					break;
				case OutgoingViewModelEventKind.ScrollChanged:
					this._onDidScrollChange.fire(e);
					break;
				case OutgoingViewModelEventKind.ViewZonesChanged:
					this._onDidChangeViewZones.fire();
					break;
				case OutgoingViewModelEventKind.HiddenAreasChanged:
					this._onDidChangeHiddenAreas.fire();
					break;
				case OutgoingViewModelEventKind.ReadOnlyEditAttempt:
					this._onDidAttemptReadOnlyEdit.fire();
					break;
				case OutgoingViewModelEventKind.CursorStateChanged: {
					if (e.reachedMaxCursorCount) {

						const multiCursorLimit = this.getOption(EditorOption.multiCursorLimit);
						const message = nls.localize('cursors.maximum', "The number of cursors has been limited to {0}. Consider using [find and replace](https://code.visualstudio.com/docs/editor/codebasics#_find-and-replace) for larger changes or increase the editor multi cursor limit setting.", multiCursorLimit);
						this._notificationService.prompt(Severity.Warning, message, [
							{
								label: 'Find and Replace',
								run: () => {
									this._commandService.executeCommand('editor.action.startFindReplaceAction');
								}
							},
							{
								label: nls.localize('goToSetting', 'Increase Multi Cursor Limit'),
								run: () => {
									this._commandService.executeCommand('workbench.action.openSettings2', {
										query: 'editor.multiCursorLimit'
									});
								}
							}
						]);
					}

					const positions: Position[] = [];
					for (let i = 0, len = e.selections.length; i < len; i++) {
						positions[i] = e.selections[i].getPosition();
					}

					const e1: ICursorPositionChangedEvent = {
						position: positions[0],
						secondaryPositions: positions.slice(1),
						reason: e.reason,
						source: e.source
					};
					this._onDidChangeCursorPosition.fire(e1);

					const e2: ICursorSelectionChangedEvent = {
						selection: e.selections[0],
						secondarySelections: e.selections.slice(1),
						modelVersionId: e.modelVersionId,
						oldSelections: e.oldSelections,
						oldModelVersionId: e.oldModelVersionId,
						source: e.source,
						reason: e.reason
					};
					this._onDidChangeCursorSelection.fire(e2);

					break;
				}
				case OutgoingViewModelEventKind.ModelDecorationsChanged:
					this._onDidChangeModelDecorations.fire(e.event);
					break;
				case OutgoingViewModelEventKind.ModelLanguageChanged:
					this._domElement.setAttribute('data-mode-id', model.getLanguageId());
					this._onDidChangeModelLanguage.fire(e.event);
					break;
				case OutgoingViewModelEventKind.ModelLanguageConfigurationChanged:
					this._onDidChangeModelLanguageConfiguration.fire(e.event);
					break;
				case OutgoingViewModelEventKind.ModelContentChanged:
					this._onDidChangeModelContent.fire(e.event);
					break;
				case OutgoingViewModelEventKind.ModelOptionsChanged:
					this._onDidChangeModelOptions.fire(e.event);
					break;
				case OutgoingViewModelEventKind.ModelTokensChanged:
					this._onDidChangeModelTokens.fire(e.event);
					break;

			}
		}));

		const [view, hasRealView] = this._createView(viewModel);
		if (hasRealView) {
			this._domElement.appendChild(view.domNode.domNode);

			let keys = Object.keys(this._contentWidgets);
			for (let i = 0, len = keys.length; i < len; i++) {
				const widgetId = keys[i];
				view.addContentWidget(this._contentWidgets[widgetId]);
			}

			keys = Object.keys(this._overlayWidgets);
			for (let i = 0, len = keys.length; i < len; i++) {
				const widgetId = keys[i];
				view.addOverlayWidget(this._overlayWidgets[widgetId]);
			}

			view.render(false, true);
			view.domNode.domNode.setAttribute('data-uri', model.uri.toString());
		}

		this._modelData = new ModelData(model, viewModel, view, hasRealView, listenersToRemove);
	}