From b5cb9ff4ba3c68486d082ee321024f4db057f490 Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Mon, 12 Apr 2021 14:19:16 -0700 Subject: [PATCH 1/3] add safe notebook document --- .../languageserver/safeNotebookDocument.ts | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 src/client/jupyter/languageserver/safeNotebookDocument.ts diff --git a/src/client/jupyter/languageserver/safeNotebookDocument.ts b/src/client/jupyter/languageserver/safeNotebookDocument.ts new file mode 100644 index 000000000000..4df10b837d4c --- /dev/null +++ b/src/client/jupyter/languageserver/safeNotebookDocument.ts @@ -0,0 +1,80 @@ +import { NotebookCell, NotebookCellRange, NotebookDocumentMetadata, Uri } from 'vscode'; +import { NotebookDocument } from 'vscode-proposed'; + +export interface ISafeNotebookDocument extends NotebookDocument {} + +// The old API for NotebookDocument +interface IOldNotebookDocument { + readonly cells: ReadonlyArray; +} + +// In the Python extension we often need to support different changing +// VS Code api versions. This class adds a layer of indirection so that we +// can handle changes to the NotebookDocument class in the API +export class SafeNotebookDocument implements ISafeNotebookDocument { + constructor(private notebook: NotebookDocument) {} + + // Functions changed to handle multiple APIs + public getCells(range?: NotebookCellRange): ReadonlyArray { + if ('getCells' in this.notebook) { + return this.notebook.getCells(range); + } + // Old API with .cells + return (this.notebook as IOldNotebookDocument).cells; + } + + public cellAt(index: number): NotebookCell { + if ('cellAt' in this.notebook) { + return this.notebook.cellAt(index); + } + + // Old API with .cells + return (this.notebook as IOldNotebookDocument).cells[index]; + } + + public get cellCount(): number { + if ('cellCount' in this.notebook) { + return this.notebook.cellCount; + } + + // Old API with .cells + return (this.notebook as IOldNotebookDocument).cells.length; + } + + // Functions directly implemented by NotebookDocument + public get uri(): Uri { + return this.notebook.uri; + } + + public get version(): number { + return this.notebook.version; + } + + public get fileName(): string { + return this.notebook.fileName; + } + + public get isDirty(): boolean { + return this.notebook.isDirty; + } + + public get isUntitled(): boolean { + return this.notebook.isUntitled; + } + + public get isClosed(): boolean { + return this.notebook.isClosed; + } + + public get metadata(): NotebookDocumentMetadata { + return this.notebook.metadata; + } + + public get viewType(): string { + return this.notebook.viewType; + } + + public save(): Thenable { + return this.notebook.save(); + } +} From af3644847b071f5f5828f6168ab48b9c5d4a8db2 Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Mon, 12 Apr 2021 14:47:46 -0700 Subject: [PATCH 2/3] use wrapper --- .../jupyter/languageserver/notebookConcatDocument.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/client/jupyter/languageserver/notebookConcatDocument.ts b/src/client/jupyter/languageserver/notebookConcatDocument.ts index 0659d02230dc..4039e71f9434 100644 --- a/src/client/jupyter/languageserver/notebookConcatDocument.ts +++ b/src/client/jupyter/languageserver/notebookConcatDocument.ts @@ -21,6 +21,7 @@ import { NotebookConcatTextDocument, NotebookCell, NotebookDocument } from 'vsco import { IVSCodeNotebook } from '../../common/application/types'; import { IDisposable } from '../../common/types'; import { PYTHON_LANGUAGE } from '../../common/constants'; +import { SafeNotebookDocument } from './safeNotebookDocument'; const NotebookConcatPrefix = '_NotebookConcat_'; @@ -28,6 +29,10 @@ const NotebookConcatPrefix = '_NotebookConcat_'; * This helper class is used to present a converted document to an LS */ export class NotebookConcatDocument implements TextDocument, IDisposable { + public get notebook(): SafeNotebookDocument { + return this._notebook; + } + public get notebookUri(): Uri { return this.notebook.uri; } @@ -113,10 +118,14 @@ export class NotebookConcatDocument implements TextDocument, IDisposable { private onCellsChangedEmitter = new EventEmitter(); - constructor(public notebook: NotebookDocument, notebookApi: IVSCodeNotebook, selector: DocumentSelector) { + private _notebook: SafeNotebookDocument; + + // constructor(public notebook: NotebookDocument, notebookApi: IVSCodeNotebook, selector: DocumentSelector) { + constructor(notebook: NotebookDocument, notebookApi: IVSCodeNotebook, selector: DocumentSelector) { const dir = path.dirname(notebook.uri.fsPath); // Note: Has to be different than the prefix for old notebook editor (HiddenFileFormat) so // that the caller doesn't remove diagnostics for this document. + this._notebook = new SafeNotebookDocument(notebook); this.dummyFilePath = path.join(dir, `${NotebookConcatPrefix}${uuid().replace(/-/g, '')}.py`); this.dummyUri = Uri.file(this.dummyFilePath); this.concatDocument = notebookApi.createConcatTextDocument(notebook, selector); From 0bb50b584677c5818fed15545b8e858557c744cb Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Mon, 12 Apr 2021 16:10:02 -0700 Subject: [PATCH 3/3] add comments --- src/client/jupyter/languageserver/notebookConcatDocument.ts | 6 ++++-- src/client/jupyter/languageserver/safeNotebookDocument.ts | 6 +++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/client/jupyter/languageserver/notebookConcatDocument.ts b/src/client/jupyter/languageserver/notebookConcatDocument.ts index 4039e71f9434..edae4464753e 100644 --- a/src/client/jupyter/languageserver/notebookConcatDocument.ts +++ b/src/client/jupyter/languageserver/notebookConcatDocument.ts @@ -120,12 +120,14 @@ export class NotebookConcatDocument implements TextDocument, IDisposable { private _notebook: SafeNotebookDocument; - // constructor(public notebook: NotebookDocument, notebookApi: IVSCodeNotebook, selector: DocumentSelector) { constructor(notebook: NotebookDocument, notebookApi: IVSCodeNotebook, selector: DocumentSelector) { const dir = path.dirname(notebook.uri.fsPath); + // Create a safe notebook document so that we can handle both >= 1.56 vscode API and < 1.56 + // when vscode stable is 1.56 and both Python release and insiders can update to that engine version we + // can remove this and just use NotebookDocument directly + this._notebook = new SafeNotebookDocument(notebook); // Note: Has to be different than the prefix for old notebook editor (HiddenFileFormat) so // that the caller doesn't remove diagnostics for this document. - this._notebook = new SafeNotebookDocument(notebook); this.dummyFilePath = path.join(dir, `${NotebookConcatPrefix}${uuid().replace(/-/g, '')}.py`); this.dummyUri = Uri.file(this.dummyFilePath); this.concatDocument = notebookApi.createConcatTextDocument(notebook, selector); diff --git a/src/client/jupyter/languageserver/safeNotebookDocument.ts b/src/client/jupyter/languageserver/safeNotebookDocument.ts index 4df10b837d4c..6ae056dd0532 100644 --- a/src/client/jupyter/languageserver/safeNotebookDocument.ts +++ b/src/client/jupyter/languageserver/safeNotebookDocument.ts @@ -1,9 +1,11 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. import { NotebookCell, NotebookCellRange, NotebookDocumentMetadata, Uri } from 'vscode'; import { NotebookDocument } from 'vscode-proposed'; export interface ISafeNotebookDocument extends NotebookDocument {} -// The old API for NotebookDocument +// The old API for NotebookDocument for vscode engine version < 1.56 interface IOldNotebookDocument { readonly cells: ReadonlyArray; } @@ -11,6 +13,8 @@ interface IOldNotebookDocument { // In the Python extension we often need to support different changing // VS Code api versions. This class adds a layer of indirection so that we // can handle changes to the NotebookDocument class in the API +// When python extension ships with engine version 1.56 for stable and insiders we can remove +// this class and just use NotebookDocument directly export class SafeNotebookDocument implements ISafeNotebookDocument { constructor(private notebook: NotebookDocument) {}