未验证 提交 b41213c5 编写于 作者: R Rob Lourens 提交者: GitHub

Enable setTextDocumentLanguage (#119429)

Change cell TextModel handling, fix #117936
上级 0631bbc8
......@@ -1025,6 +1025,27 @@ suite('Notebook API tests', function () {
await saveFileAndCloseAll(resource);
});
test('change cell language when notebook editor is not open', async function () {
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
const firstCell = vscode.window.activeNotebookEditor!.document.cellAt(0);
const cellUri = firstCell.document.uri;
await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
let cellDoc = await vscode.workspace.openTextDocument(cellUri);
cellDoc = await vscode.languages.setTextDocumentLanguage(cellDoc, 'css');
assert.strictEqual(cellDoc.languageId, 'css');
});
test('change cell language when notebook editor is open', async function () {
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
const firstCell = vscode.window.activeNotebookEditor!.document.cellAt(0);
const cellDoc = await vscode.languages.setTextDocumentLanguage(firstCell.document, 'css');
assert.strictEqual(cellDoc.languageId, 'css');
});
test('multiple tabs: dirty + clean', async function () {
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
......
......@@ -8,7 +8,6 @@ import { raceCancellation } from 'vs/base/common/async';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { IDimension } from 'vs/editor/common/editorCommon';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { EDITOR_BOTTOM_PADDING } from 'vs/workbench/contrib/notebook/browser/constants';
import { CellFocusMode, CodeCellRenderTemplate, getEditorTopPadding, IActiveNotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
......@@ -33,8 +32,7 @@ export class CodeCell extends Disposable {
@IInstantiationService private readonly instantiationService: IInstantiationService,
@INotebookCellStatusBarService readonly notebookCellStatusBarService: INotebookCellStatusBarService,
@IOpenerService readonly openerService: IOpenerService,
@ITextFileService readonly textFileService: ITextFileService,
@IModeService private readonly _modeService: IModeService,
@ITextFileService readonly textFileService: ITextFileService
// @IKeybindingService private readonly _keybindingService: IKeybindingService,
) {
......@@ -108,13 +106,6 @@ export class CodeCell extends Disposable {
}
}));
this._register(viewCell.onDidChangeState((e) => {
if (e.languageChanged) {
const mode = this._modeService.create(viewCell.language);
templateData.editor?.getModel()?.setMode(mode.languageIdentifier);
}
}));
this._register(viewCell.onDidChangeLayout((e) => {
if (e.outerWidth !== undefined) {
const layoutInfo = templateData.editor.getLayoutInfo();
......
......@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { Disposable, IDisposable, IReference } from 'vs/base/common/lifecycle';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
......@@ -17,6 +17,7 @@ import { CellEditState, CellFocusMode, CursorAtBoundary, CellViewModelStateChang
import { CellKind, NotebookCellMetadata, NotebookDocumentMetadata, INotebookSearchOptions, ShowCellStatusBarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService';
export abstract class BaseCellViewModel extends Disposable {
......@@ -86,17 +87,12 @@ export abstract class BaseCellViewModel extends Disposable {
}>();
private _lastDecorationId: number = 0;
private _textModel: model.ITextModel | undefined = undefined;
get textModel(): model.ITextModel | undefined {
return this._textModel;
}
set textModel(m: model.ITextModel | undefined) {
this._textModel = m;
return this.model.textModel;
}
hasModel(): this is IEditableCellViewModel {
return !!this._textModel;
return !!this.textModel;
}
private _dragging: boolean = false;
......@@ -108,6 +104,8 @@ export abstract class BaseCellViewModel extends Disposable {
this._dragging = v;
}
protected _textModelRef: IReference<IResolvedTextEditorModel> | undefined;
constructor(
readonly viewType: string,
readonly model: NotebookCellTextModel,
......@@ -116,10 +114,6 @@ export abstract class BaseCellViewModel extends Disposable {
) {
super();
this._register(model.onDidChangeLanguage(() => {
this._onDidChangeState.fire({ languageChanged: true });
}));
this._register(model.onDidChangeMetadata(e => {
this._onDidChangeState.fire({ metadataChanged: true, runStateChanged: e.runStateChanged });
}));
......@@ -164,7 +158,6 @@ export abstract class BaseCellViewModel extends Disposable {
}
this._textEditor = editor;
this.textModel = this._textEditor.getModel() || undefined;
if (this._editorViewStates) {
this._restoreViewState(this._editorViewStates);
......@@ -199,10 +192,15 @@ export abstract class BaseCellViewModel extends Disposable {
});
this._textEditor = undefined;
this.textModel = undefined;
this.model.textModel = undefined;
this._cursorChangeListener?.dispose();
this._cursorChangeListener = null;
this._onDidChangeEditorAttachState.fire();
if (this._textModelRef) {
this._textModelRef.dispose();
this._textModelRef = undefined;
}
}
getText(): string {
......@@ -373,7 +371,7 @@ export abstract class BaseCellViewModel extends Disposable {
return true;
}
if (selection.startLineNumber === this._textModel?.getLineCount() && selection.startColumn === this._textModel?.getLineMaxColumn(selection.startLineNumber)) {
if (selection.startLineNumber === this.textModel?.getLineCount() && selection.startColumn === this.textModel?.getLineMaxColumn(selection.startLineNumber)) {
return true;
}
......@@ -467,6 +465,10 @@ export abstract class BaseCellViewModel extends Disposable {
dispose() {
super.dispose();
if (this._textModelRef) {
this._textModelRef.dispose();
}
}
toJSON(): object {
......
......@@ -260,11 +260,9 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
* Text model is used for editing.
*/
async resolveTextModel(): Promise<model.ITextModel> {
if (!this.textModel) {
const ref = await this.model.resolveTextModelRef();
this.textModel = ref.object.textEditorModel;
this._register(ref);
this._register(this.textModel.onDidChangeContent(() => {
if (!this._textModelRef || !this.textModel) {
this._textModelRef = await this.model.resolveTextModelRef();
this._register(this.textModel!.onDidChangeContent(() => {
if (this.editState !== CellEditState.Editing) {
this.editState = CellEditState.Editing;
this._onDidChangeState.fire({ contentChanged: true });
......@@ -272,7 +270,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
}));
}
return this.textModel;
return this.textModel!;
}
onDeselect() {
......
......@@ -103,7 +103,7 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie
readonly foldingDelegate: EditorFoldingStateDelegate,
readonly eventDispatcher: NotebookEventDispatcher,
private readonly _mdRenderer: MarkdownRenderer,
@IConfigurationService configurationService: IConfigurationService
@IConfigurationService configurationService: IConfigurationService,
) {
super(viewType, model, UUID.generateUuid(), configurationService);
......@@ -225,13 +225,10 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie
}
async resolveTextModel(): Promise<model.ITextModel> {
if (!this.textModel) {
const ref = await this.model.resolveTextModelRef();
this.textModel = ref.object.textEditorModel;
this._version = this.textModel.getVersionId();
this._register(ref);
this._register(this.textModel.onDidChangeContent(() => {
if (!this._textModelRef || !this.textModel) {
this._textModelRef = await this.model.resolveTextModelRef();
this._version = this.textModel!.getVersionId();
this._register(this.textModel!.onDidChangeContent(() => {
this._html = null;
if (this.textModel) {
this._version = this.textModel.getVersionId();
......@@ -239,7 +236,7 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie
this._onDidChangeState.fire({ contentChanged: true });
}));
}
return this.textModel;
return this.textModel!;
}
onDeselect() {
......
......@@ -33,6 +33,7 @@ import { IPosition, Position } from 'vs/editor/common/core/position';
import { MultiModelEditStackElement, SingleModelEditStackElement } from 'vs/editor/common/model/editStack';
import { ResourceNotebookCellEdit } from 'vs/workbench/contrib/bulkEdit/browser/bulkCellEdits';
import { NotebookCellSelectionCollection } from 'vs/workbench/contrib/notebook/browser/viewModel/cellSelectionCollection';
import { IModelService } from 'vs/editor/common/services/modelService';
export interface INotebookEditorViewState {
editingCells: { [key: number]: boolean };
......@@ -228,7 +229,8 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD
private _layoutInfo: NotebookLayoutInfo | null,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@IBulkEditService private readonly _bulkEditService: IBulkEditService,
@IUndoRedoService private readonly _undoService: IUndoRedoService
@IUndoRedoService private readonly _undoService: IUndoRedoService,
@IModelService modelService: IModelService,
) {
super();
......@@ -236,6 +238,8 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD
this.id = '$notebookViewModel' + MODEL_ID;
this._instanceId = strings.singleLetterHash(MODEL_ID);
const compute = (changes: NotebookCellTextModelSplice<ICell>[], synchronous: boolean) => {
const diffs = changes.map(splice => {
return [splice[0], splice[1], splice[2].map(cell => {
......
......@@ -10,7 +10,7 @@ import { URI } from 'vs/base/common/uri';
import * as UUID from 'vs/base/common/uuid';
import * as model from 'vs/editor/common/model';
import { Range } from 'vs/editor/common/core/range';
import { Disposable } from 'vs/base/common/lifecycle';
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { hash } from 'vs/base/common/hash';
import { PieceTreeTextBuffer } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer';
......@@ -53,6 +53,10 @@ export class NotebookCellTextModel extends Disposable implements ICell {
}
set language(newLanguage: string) {
if (this._language === newLanguage) {
return;
}
this._language = newLanguage;
this._hash = null;
this._onDidChangeLanguage.fire(newLanguage);
......@@ -82,6 +86,31 @@ export class NotebookCellTextModel extends Disposable implements ICell {
private _hash: number | null = null;
private _textModelDisposables = new DisposableStore();
private _textModel: model.ITextModel | undefined = undefined;
get textModel(): model.ITextModel | undefined {
return this._textModel;
}
set textModel(m: model.ITextModel | undefined) {
if (this._textModel === m) {
return;
}
this._textModelDisposables.clear();
this._textModel = m;
if (this._textModel) {
// Init language from text model
this.language = this._textModel.getLanguageIdentifier().language;
// Listen to language changes on the model
this._textModelDisposables.add(this._textModel.onDidChangeLanguage(e => {
this.language = e.newLanguage;
this._onDidChangeContent.fire();
}));
this._textModelDisposables.add(this._textModel.onWillDispose(() => this.textModel = undefined));
}
}
constructor(
readonly uri: URI,
......@@ -170,6 +199,7 @@ export class NotebookCellTextModel extends Disposable implements ICell {
async resolveTextModelRef() {
const ref = await this._modelService.createModelReference(this.uri);
this.textModel = ref.object.textEditorModel;
return ref;
}
......
......@@ -14,6 +14,9 @@ import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { ISequence, LcsDiff } from 'vs/base/common/diff/diff';
import { hash } from 'vs/base/common/hash';
import { NotebookCellOutputTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellOutputTextModel';
import { IModelService } from 'vs/editor/common/services/modelService';
import { Schemas } from 'vs/base/common/network';
import { isEqual } from 'vs/base/common/resources';
class StackOperation implements IWorkspaceUndoRedoElement {
......@@ -222,13 +225,29 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
metadata: NotebookDocumentMetadata,
options: TransientOptions,
@IUndoRedoService private _undoService: IUndoRedoService,
@ITextModelService private _modelService: ITextModelService,
@ITextModelService private _textModelService: ITextModelService,
@IModelService _modelService: IModelService,
) {
super();
this.transientOptions = options;
this.metadata = metadata;
this._initialize(cells);
this._register(_modelService.onModelAdded(e => {
if (e.uri.scheme === Schemas.vscodeNotebookCell) {
const cellUri = CellUri.parse(e.uri);
if (cellUri && isEqual(cellUri.notebook, this.uri)) {
const cellIdx = this._getCellIndexByHandle(cellUri.handle);
if (cellIdx >= 0) {
const cell = this.cells[cellIdx];
if (cell) {
cell.textModel = e;
}
}
}
}
}));
this._eventEmitter = new DelayedEmitter(
this._onDidChangeContent,
this
......@@ -252,7 +271,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
const mainCells = cells.map(cell => {
const cellHandle = this._cellhandlePool++;
const cellUri = CellUri.generate(this.uri, cellHandle);
return new NotebookCellTextModel(cellUri, cellHandle, cell.source, cell.language, cell.cellKind, cell.outputs || [], cell.metadata, this.transientOptions, this._modelService);
return new NotebookCellTextModel(cellUri, cellHandle, cell.source, cell.language, cell.cellKind, cell.outputs || [], cell.metadata, this.transientOptions, this._textModelService);
});
for (let i = 0; i < mainCells.length; i++) {
......@@ -412,7 +431,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
const cell = new NotebookCellTextModel(
cellUri, cellHandle,
cellDto.source, cellDto.language, cellDto.cellKind, cellDto.outputs || [], cellDto.metadata, this.transientOptions,
this._modelService
this._textModelService
);
const dirtyStateListener = cell.onDidChangeContent(() => {
this._increaseVersionId();
......
......@@ -5,28 +5,37 @@
import * as assert from 'assert';
import { URI } from 'vs/base/common/uri';
import { NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
import { CellKind, NotebookCellMetadata, diff, ICellRange, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { withTestNotebook, NotebookEditorTestModel, setupInstantiationService } from 'vs/workbench/contrib/notebook/test/testNotebookEditor';
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher';
import { TrackedRangeStickiness } from 'vs/editor/common/model';
import { reduceCellRanges } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { IModelService } from 'vs/editor/common/services/modelService';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService';
import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo';
import { reduceCellRanges } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher';
import { NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
import { CellKind, diff, ICellRange, NotebookCellMetadata, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { NotebookEditorTestModel, setupInstantiationService, withTestNotebook } from 'vs/workbench/contrib/notebook/test/testNotebookEditor';
suite('NotebookViewModel', () => {
const instantiationService = setupInstantiationService();
const textModelService = instantiationService.get(ITextModelService);
const bulkEditService = instantiationService.get(IBulkEditService);
const undoRedoService = instantiationService.get(IUndoRedoService);
const modelService = instantiationService.get(IModelService);
instantiationService.stub(IConfigurationService, new TestConfigurationService());
instantiationService.stub(IThemeService, new TestThemeService());
test('ctor', function () {
const notebook = new NotebookTextModel('notebook', URI.parse('test'), [], notebookDocumentMetadataDefaults, { transientMetadata: {}, transientOutputs: false }, undoRedoService, textModelService);
const notebook = new NotebookTextModel('notebook', URI.parse('test'), [], notebookDocumentMetadataDefaults, { transientMetadata: {}, transientOutputs: false }, undoRedoService, textModelService, modelService);
const model = new NotebookEditorTestModel(notebook);
const eventDispatcher = new NotebookEventDispatcher();
const viewModel = new NotebookViewModel('notebook', model.notebook, eventDispatcher, null, instantiationService, bulkEditService, undoRedoService);
const viewModel = new NotebookViewModel('notebook', model.notebook, eventDispatcher, null, instantiationService, bulkEditService, undoRedoService, modelService);
assert.strictEqual(viewModel.viewType, 'notebook');
});
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册