Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions news/1 Enhancements/9821.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add undo/redo support to notebooks.
13 changes: 1 addition & 12 deletions src/client/common/application/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@
import { CancellationToken, Position, TextDocument, Uri } from 'vscode';
import { Commands as LSCommands } from '../../activation/languageServer/constants';
import { Commands as DSCommands } from '../../datascience/constants';
import { IEditCell, IInsertCell, ISwapCells } from '../../datascience/interactive-common/interactiveWindowTypes';
import { LiveKernelModel } from '../../datascience/jupyter/kernels/types';
import { ICell, IJupyterKernelSpec, INotebook } from '../../datascience/types';
import { PythonInterpreter } from '../../interpreter/contracts';
import { INotebook } from '../../datascience/types';
import { CommandSource } from '../../testing/common/constants';
import { TestFunction, TestsToRun } from '../../testing/common/types';
import { TestDataItem, TestWorkspaceFolder } from '../../testing/types';
Expand Down Expand Up @@ -148,12 +145,4 @@ export interface ICommandNameArgumentTypeMapping extends ICommandNameWithoutArgu
[DSCommands.ScrollToCell]: [string, string];
[DSCommands.ViewJupyterOutput]: [];
[DSCommands.SwitchJupyterKernel]: [INotebook | undefined];
[DSCommands.NotebookStorage_DeleteAllCells]: [Uri];
[DSCommands.NotebookStorage_ModifyCells]: [Uri, ICell[]];
[DSCommands.NotebookStorage_EditCell]: [Uri, IEditCell];
[DSCommands.NotebookStorage_InsertCell]: [Uri, IInsertCell];
[DSCommands.NotebookStorage_RemoveCell]: [Uri, string];
[DSCommands.NotebookStorage_SwapCells]: [Uri, ISwapCells];
[DSCommands.NotebookStorage_ClearCellOutputs]: [Uri];
[DSCommands.NotebookStorage_UpdateVersion]: [Uri, PythonInterpreter | undefined, IJupyterKernelSpec | LiveKernelModel | undefined];
}
10 changes: 0 additions & 10 deletions src/client/datascience/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,6 @@ export namespace Commands {
export const ScrollToCell = 'python.datascience.scrolltocell';
export const CreateNewNotebook = 'python.datascience.createnewnotebook';
export const ViewJupyterOutput = 'python.datascience.viewJupyterOutput';

// Make sure to put these into the package .json
export const NotebookStorage_DeleteAllCells = 'python.datascience.notebook.deleteall';
export const NotebookStorage_ModifyCells = 'python.datascience.notebook.modifycells';
export const NotebookStorage_EditCell = 'python.datascience.notebook.editcell';
export const NotebookStorage_InsertCell = 'python.datascience.notebook.insertcell';
export const NotebookStorage_RemoveCell = 'python.datascience.notebook.removecell';
export const NotebookStorage_SwapCells = 'python.datascience.notebook.swapcells';
export const NotebookStorage_ClearCellOutputs = 'python.datascience.notebook.clearoutputs';
export const NotebookStorage_UpdateVersion = 'python.datascience.notebook.updateversion';
}

export namespace CodeLensCommands {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
'use strict';
import '../../../common/extensions';

import * as monacoEditor from 'monaco-editor/esm/vs/editor/editor.api';
import { EndOfLine, Position, Range, TextDocument, TextDocumentContentChangeEvent, TextLine, Uri } from 'vscode';
import * as vscodeLanguageClient from 'vscode-languageclient';

import { PYTHON_LANGUAGE } from '../../../common/constants';
import { Identifiers } from '../../constants';
import { IEditorContentChange } from '../interactiveWindowTypes';
import { DefaultWordPattern, ensureValidWordDefinition, getWordAtText, regExpLeadsToEndlessLoop } from './wordHelper';

class IntellisenseLine implements TextLine {
Expand Down Expand Up @@ -201,54 +201,57 @@ export class IntellisenseDocument implements TextDocument {
}

public loadAllCells(cells: { code: string; id: string }[]): TextDocumentContentChangeEvent[] {
let changes: TextDocumentContentChangeEvent[] = [];
if (!this.inEditMode) {
this.inEditMode = true;
this._version += 1;
return this.reloadCells(cells);
}
return [];
}

// Normalize all of the cells, removing \r and separating each
// with a newline
const normalized = cells.map(c => {
return {
id: c.id,
code: `${c.code.replace(/\r/g, '')}\n`
};
});
public reloadCells(cells: { code: string; id: string }[]): TextDocumentContentChangeEvent[] {
this._version += 1;

// Normalize all of the cells, removing \r and separating each
// with a newline
const normalized = cells.map(c => {
return {
id: c.id,
code: `${c.code.replace(/\r/g, '')}\n`
};
});

// Contents are easy, just load all of the code in a row
this._contents = normalized
.map(c => c.code)
.reduce((p, c) => {
return `${p}${c}`;
});

// Cell ranges are slightly more complicated
let prev: number = 0;
this._cellRanges = normalized.map(c => {
const result = {
id: c.id,
start: prev,
fullEnd: prev + c.code.length,
currentEnd: prev + c.code.length
};
prev += c.code.length;
return result;
// Contents are easy, just load all of the code in a row
this._contents = normalized
.map(c => c.code)
.reduce((p, c) => {
return `${p}${c}`;
});

// Then create the lines.
this._lines = this.createLines();
// Cell ranges are slightly more complicated
let prev: number = 0;
this._cellRanges = normalized.map(c => {
const result = {
id: c.id,
start: prev,
fullEnd: prev + c.code.length,
currentEnd: prev + c.code.length
};
prev += c.code.length;
return result;
});

// Return our changes
changes = [
{
range: this.createSerializableRange(new Position(0, 0), new Position(0, 0)),
rangeOffset: 0,
rangeLength: 0, // Adds are always zero
text: this._contents
}
];
}
return changes;
// Then create the lines.
this._lines = this.createLines();

// Return our changes
return [
{
range: this.createSerializableRange(new Position(0, 0), new Position(0, 0)),
rangeOffset: 0,
rangeLength: 0, // Adds are always zero
text: this._contents
}
];
}

public addCell(fullCode: string, currentCode: string, id: string): TextDocumentContentChangeEvent[] {
Expand Down Expand Up @@ -293,16 +296,33 @@ export class IntellisenseDocument implements TextDocument {
];
}

public insertCell(id: string, code: string, codeCellAbove: string | undefined): TextDocumentContentChangeEvent[] {
public reloadCell(id: string, code: string): TextDocumentContentChangeEvent[] {
this._version += 1;

// Make sure to put a newline between this code and the next code
const newCode = `${code.replace(/\r/g, '')}\n`;

// Figure where this goes
const index = this._cellRanges.findIndex(r => r.id === id);
if (index >= 0) {
const start = this.positionAt(this._cellRanges[index].start);
const end = this.positionAt(this._cellRanges[index].currentEnd);
return this.removeRange(newCode, start, end, index);
}

return [];
}

public insertCell(id: string, code: string, codeCellAboveOrIndex: string | undefined | number): TextDocumentContentChangeEvent[] {
// This should only happen once for each cell.
this._version += 1;

// Make sure to put a newline between this code and the next code
const newCode = `${code.replace(/\r/g, '')}\n`;

// Figure where this goes
const aboveIndex = this._cellRanges.findIndex(r => r.id === codeCellAbove);
const insertIndex = aboveIndex + 1;
const aboveIndex = this._cellRanges.findIndex(r => r.id === codeCellAboveOrIndex);
const insertIndex = typeof codeCellAboveOrIndex === 'number' ? codeCellAboveOrIndex : aboveIndex + 1;

// Compute where we start from.
const fromOffset = insertIndex < this._cellRanges.length ? this._cellRanges[insertIndex].start : this._contents.length;
Expand Down Expand Up @@ -356,7 +376,7 @@ export class IntellisenseDocument implements TextDocument {
return [];
}

public edit(editorChanges: monacoEditor.editor.IModelContentChange[], id: string): TextDocumentContentChangeEvent[] {
public editCell(editorChanges: IEditorContentChange[], id: string): TextDocumentContentChangeEvent[] {
this._version += 1;

// Convert the range to local (and remove 1 based)
Expand Down
Loading