Notebook Tokenization Fixes (#7375)

* Fix don't like; unclear if grammar necessssary too

* Cleanup and sanity check

* Cleanup and sanity check

* Add test

* Call onBeforeAttached for 3 types of editor models
This commit is contained in:
Chris LaFreniere
2019-09-27 10:04:29 -07:00
committed by GitHub
parent ba8ba9f68d
commit 6ef415d0e6
5 changed files with 305 additions and 9 deletions

View File

@@ -341,10 +341,17 @@ export class NotebookInput extends EditorInput {
} else {
let textOrUntitledEditorModel: UntitledEditorModel | IEditorModel;
if (this.resource.scheme === Schemas.untitled) {
textOrUntitledEditorModel = this._untitledEditorModel ? this._untitledEditorModel : await this._textInput.resolve();
}
else {
if (this._untitledEditorModel) {
this._untitledEditorModel.textEditorModel.onBeforeAttached();
textOrUntitledEditorModel = this._untitledEditorModel;
} else {
let resolvedInput = await this._textInput.resolve();
resolvedInput.textEditorModel.onBeforeAttached();
textOrUntitledEditorModel = resolvedInput;
}
} else {
const textEditorModelReference = await this.textModelService.createModelReference(this.resource);
textEditorModelReference.object.textEditorModel.onBeforeAttached();
textOrUntitledEditorModel = await textEditorModelReference.object.load();
}
this._model = this.instantiationService.createInstance(NotebookEditorModel, this.resource, textOrUntitledEditorModel);
@@ -385,6 +392,9 @@ export class NotebookInput extends EditorInput {
}
public dispose(): void {
if (this._model) {
this._model.editorModel.textEditorModel.onBeforeDetached();
}
this._disposeContainer();
super.dispose();
}

View File

@@ -69,17 +69,23 @@ export class NotebookTextFileModel {
} else {
newOutput = '\n'.concat(newOutput).concat('\n');
}
let range = this.getEndOfOutputs(textEditorModel, contentChange.cells[0].cellGuid);
if (range) {
// Execution count will always be after the end of the outputs in JSON. This is a sanity mechanism.
let executionCountMatch = this.getExecutionCountRange(textEditorModel, contentChange.cells[0].cellGuid);
if (!executionCountMatch || !executionCountMatch.range) {
return false;
}
let endOutputsRange = this.getEndOfOutputs(textEditorModel, contentChange.cells[0].cellGuid);
if (endOutputsRange && endOutputsRange.startLineNumber < executionCountMatch.range.startLineNumber) {
textEditorModel.textEditorModel.applyEdits([{
range: new Range(range.startLineNumber, range.startColumn, range.startLineNumber, range.startColumn),
range: new Range(endOutputsRange.startLineNumber, endOutputsRange.startColumn, endOutputsRange.startLineNumber, endOutputsRange.startColumn),
text: newOutput
}]);
return true;
}
} else {
return false;
}
return true;
return false;
}
public transformAndApplyEditForCellUpdated(contentChange: NotebookContentChange, textEditorModel: TextFileEditorModel | UntitledEditorModel): boolean {

View File

@@ -589,6 +589,66 @@ suite('Notebook Editor Model', function (): void {
should(notebookEditorModel.lastEditFullReplacement).equal(false);
});
test('should not insert update at incorrect location', async function (): Promise<void> {
await createNewNotebookModel();
let notebookEditorModel = await createTextEditorModel(this);
notebookEditorModel.replaceEntireTextEditorModel(notebookModel, undefined);
let newCell = notebookModel.addCell(CellTypes.Code);
let contentChange: NotebookContentChange = {
changeType: NotebookChangeType.CellsModified,
cells: [newCell],
cellIndex: 0
};
notebookEditorModel.updateModel(contentChange, NotebookChangeType.CellsModified);
should(notebookEditorModel.lastEditFullReplacement).equal(true);
should(notebookEditorModel.editorModel.textEditorModel.getLineContent(14)).equal(' "outputs": [');
// First update the model with unmatched brackets
let newUnmatchedBracketOutput: nb.IStreamResult = { output_type: 'stream', name: 'stdout', text: '[0em' };
newCell[<any>'_outputs'] = newCell.outputs.concat(newUnmatchedBracketOutput);
contentChange = {
changeType: NotebookChangeType.CellOutputUpdated,
cells: [newCell]
};
notebookEditorModel.updateModel(contentChange, NotebookChangeType.CellOutputUpdated);
should(notebookEditorModel.editorModel.textEditorModel.getLineContent(8)).equal(' "source": [');
should(notebookEditorModel.editorModel.textEditorModel.getLineContent(12)).equal(' "azdata_cell_guid": "' + newCell.cellGuid + '"');
should(notebookEditorModel.editorModel.textEditorModel.getLineContent(14)).equal(' "outputs": [');
should(notebookEditorModel.editorModel.textEditorModel.getLineContent(26)).equal(' "text": "[0em"');
should(notebookEditorModel.editorModel.textEditorModel.getLineContent(27)).equal('}');
should(notebookEditorModel.editorModel.textEditorModel.getLineContent(28)).equal(' ],');
should(notebookEditorModel.editorModel.textEditorModel.getLineContent(29)).equal(' "execution_count": 0');
should(notebookEditorModel.editorModel.textEditorModel.getLineContent(30)).equal(' }');
should(notebookEditorModel.lastEditFullReplacement).equal(false);
// Now test updating the model after an unmatched bracket was previously output
let newBracketlessOutput: nb.IStreamResult = { output_type: 'stream', name: 'stdout', text: 'test test test' };
newCell[<any>'_outputs'] = newCell[<any>'_outputs'].concat(newBracketlessOutput);
contentChange = {
changeType: NotebookChangeType.CellOutputUpdated,
cells: [newCell]
};
notebookEditorModel.updateModel(contentChange, NotebookChangeType.CellOutputUpdated);
should(notebookEditorModel.editorModel.textEditorModel.getLineContent(32)).equal(' "text": "test test test"');
should(notebookEditorModel.editorModel.textEditorModel.getLineContent(33)).equal(' }');
should(notebookEditorModel.editorModel.textEditorModel.getLineContent(34)).equal(' ],');
should(notebookEditorModel.editorModel.textEditorModel.getLineContent(35)).equal(' "execution_count": 0');
should(notebookEditorModel.editorModel.textEditorModel.getLineContent(36)).equal(' }');
should(notebookEditorModel.editorModel.textEditorModel.getLineContent(37)).equal(' ]');
should(notebookEditorModel.editorModel.textEditorModel.getLineContent(38)).equal('}');
should(notebookEditorModel.lastEditFullReplacement).equal(true);
});
test('should not replace entire text model for output changes (1st update)', async function (): Promise<void> {
await createNewNotebookModel();