-
Notifications
You must be signed in to change notification settings - Fork 293
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
track code lenses and related context keys in dedicated class (#16204)
* track code lenses and related context keys in dedicated class * fix test * move tests * make tests pass * always track code cell ranges
- Loading branch information
Showing
11 changed files
with
235 additions
and
105 deletions.
There are no files selected for viewing
104 changes: 104 additions & 0 deletions
104
src/interactive-window/editor-integration/cellRangeCache.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
import * as vscode from 'vscode'; | ||
import { inject, injectable } from 'inversify'; | ||
import { ICellRange, IConfigurationService } from '../../platform/common/types'; | ||
import { IDisposable } from '../../platform/common/types'; | ||
import { generateCellRangesFromDocument } from './cellFactory'; | ||
import { ConfigurationChangeEvent } from 'vscode'; | ||
import { ContextKey } from '../../platform/common/contextKey'; | ||
import { | ||
EditorContexts, | ||
InteractiveInputScheme, | ||
NotebookCellScheme, | ||
PYTHON_LANGUAGE | ||
} from '../../platform/common/constants'; | ||
import { noop } from '../../platform/common/utils/misc'; | ||
import { logger } from '../../platform/logging'; | ||
import { ICellRangeCache } from './types'; | ||
|
||
@injectable() | ||
export class CellRangeCache implements ICellRangeCache { | ||
private cachedOwnsSetting: boolean; | ||
private cache = new Map<vscode.Uri, { version: number; ranges: ICellRange[] }>(); | ||
private disposables: IDisposable[] = []; | ||
|
||
constructor(@inject(IConfigurationService) private readonly configService: IConfigurationService) { | ||
this.cachedOwnsSetting = this.configService.getSettings(undefined).sendSelectionToInteractiveWindow; | ||
vscode.workspace.onDidChangeConfiguration(this.onSettingChanged, this, this.disposables); | ||
vscode.window.onDidChangeActiveTextEditor(this.onChangedActiveTextEditor, this, this.disposables); | ||
this.onChangedActiveTextEditor(); | ||
vscode.workspace.onDidCloseTextDocument(this.onClosedDocument, this, this.disposables); | ||
} | ||
|
||
public getCellRanges(document: vscode.TextDocument): ICellRange[] { | ||
const cached = this.cache.get(document.uri); | ||
if (cached && cached.version === document.version) { | ||
return cached.ranges; | ||
} | ||
|
||
const settings = this.configService.getSettings(document.uri); | ||
const ranges = generateCellRangesFromDocument(document, settings); | ||
this.cache.set(document.uri, { version: document.version, ranges }); | ||
|
||
this.updateContextKeys(document); | ||
|
||
return ranges; | ||
} | ||
|
||
public clear(): void { | ||
this.cache.clear(); | ||
} | ||
|
||
private onChangedActiveTextEditor() { | ||
const activeEditor = vscode.window.activeTextEditor; | ||
|
||
if ( | ||
!activeEditor || | ||
activeEditor.document.languageId != PYTHON_LANGUAGE || | ||
[NotebookCellScheme, InteractiveInputScheme].includes(activeEditor.document.uri.scheme) | ||
) { | ||
// set the context to false so our command doesn't run for other files | ||
const hasCellsContext = new ContextKey(EditorContexts.HasCodeCells); | ||
hasCellsContext.set(false).catch((ex) => logger.warn('Failed to set jupyter.HasCodeCells context', ex)); | ||
this.updateContextKeys(false); | ||
} else { | ||
this.updateContextKeys(activeEditor.document); | ||
} | ||
} | ||
|
||
private onSettingChanged(e: ConfigurationChangeEvent) { | ||
this.cache.clear(); | ||
|
||
if (e.affectsConfiguration('jupyter.interactiveWindow.textEditor.executeSelection')) { | ||
const settings = this.configService.getSettings(undefined); | ||
this.cachedOwnsSetting = settings.sendSelectionToInteractiveWindow; | ||
this.updateContextKeys(); | ||
} | ||
} | ||
|
||
private updateContextKeys(documentOrOverride?: vscode.TextDocument | boolean) { | ||
let hasCodeCells = false; | ||
if (typeof documentOrOverride == 'boolean') { | ||
hasCodeCells = documentOrOverride; | ||
} else { | ||
const document = documentOrOverride ?? vscode.window.activeTextEditor?.document; | ||
hasCodeCells = document ? this.getCellRanges(document).length > 0 : false; | ||
} | ||
|
||
new ContextKey(EditorContexts.OwnsSelection).set(this.cachedOwnsSetting || hasCodeCells).catch(noop); | ||
new ContextKey(EditorContexts.HasCodeCells).set(hasCodeCells).catch(noop); | ||
} | ||
|
||
private onClosedDocument(doc: vscode.TextDocument) { | ||
this.cache.delete(doc.uri); | ||
|
||
// Don't delete the document execution count, we need to keep track | ||
// of it past the closing of a doc if the notebook or interactive window is still open. | ||
} | ||
|
||
public dispose() { | ||
this.disposables.forEach((d) => d.dispose()); | ||
} | ||
} |
89 changes: 89 additions & 0 deletions
89
src/interactive-window/editor-integration/cellRangeCache.unit.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
import { Uri, Disposable } from 'vscode'; | ||
import { createDocument } from '../../test/datascience/editor-integration/helpers'; | ||
import * as TypeMoq from 'typemoq'; | ||
import { CodeLensFactory } from './codeLensFactory'; | ||
import { IConfigurationService } from '../../platform/common/types'; | ||
import { CellRangeCache } from './cellRangeCache'; | ||
import { IKernelProvider } from '../../kernels/types'; | ||
import { mockedVSCodeNamespaces } from '../../test/vscode-mock'; | ||
import { IGeneratedCodeStorageFactory } from './types'; | ||
import { IReplNotebookTrackerService } from '../../platform/notebooks/replNotebookTrackerService'; | ||
import { when, anything, verify } from 'ts-mockito'; | ||
import { MockJupyterSettings } from '../../test/datascience/mockJupyterSettings'; | ||
import { SystemVariables } from '../../platform/common/variables/systemVariables.node'; | ||
|
||
suite('DataScienceCodeLensProvider Unit Tests', () => { | ||
let configService: TypeMoq.IMock<IConfigurationService>; | ||
let jupyterSettings: MockJupyterSettings; | ||
|
||
const storageFactory = TypeMoq.Mock.ofType<IGeneratedCodeStorageFactory>(); | ||
const kernelProvider = TypeMoq.Mock.ofType<IKernelProvider>(); | ||
const replTracker = TypeMoq.Mock.ofType<IReplNotebookTrackerService>(); | ||
const disposables: Disposable[] = []; | ||
|
||
setup(() => { | ||
configService = TypeMoq.Mock.ofType<IConfigurationService>(); | ||
jupyterSettings = new MockJupyterSettings(undefined, SystemVariables, 'node'); | ||
configService.setup((c) => c.getSettings(TypeMoq.It.isAny())).returns(() => jupyterSettings); | ||
when(mockedVSCodeNamespaces.commands.executeCommand(anything(), anything(), anything())).thenResolve(); | ||
}); | ||
|
||
function createCodeLensFactory() { | ||
return new CodeLensFactory( | ||
configService.object, | ||
disposables, | ||
storageFactory.object, | ||
kernelProvider.object, | ||
replTracker.object, | ||
new CellRangeCache(configService.object) | ||
); | ||
} | ||
|
||
test('Having code lenses will update context keys to true', async () => { | ||
jupyterSettings.sendSelectionToInteractiveWindow = false; | ||
|
||
const fileName = Uri.file('test.py').fsPath; | ||
const version = 1; | ||
const inputText = `# %%\nprint(1)`; | ||
const document = createDocument(inputText, fileName, version, TypeMoq.Times.atLeastOnce(), true); | ||
|
||
createCodeLensFactory().getCellRanges(document.object); | ||
|
||
// verify context keys set | ||
verify(mockedVSCodeNamespaces.commands.executeCommand('setContext', 'jupyter.ownsSelection', true)).atLeast(1); | ||
verify(mockedVSCodeNamespaces.commands.executeCommand('setContext', 'jupyter.hascodecells', true)).atLeast(1); | ||
}); | ||
|
||
test('Having no code lenses will set context keys to false', async () => { | ||
jupyterSettings.sendSelectionToInteractiveWindow = false; | ||
|
||
const fileName = Uri.file('test.py').fsPath; | ||
const version = 1; | ||
const inputText = `print(1)`; | ||
const document = createDocument(inputText, fileName, version, TypeMoq.Times.atLeastOnce(), true); | ||
|
||
createCodeLensFactory().getCellRanges(document.object); | ||
|
||
// verify context keys set | ||
verify(mockedVSCodeNamespaces.commands.executeCommand('setContext', 'jupyter.ownsSelection', true)).atLeast(1); | ||
verify(mockedVSCodeNamespaces.commands.executeCommand('setContext', 'jupyter.hascodecells', true)).atLeast(1); | ||
}); | ||
|
||
test('Having no code lenses but ownership setting true will set context keys correctly', async () => { | ||
jupyterSettings.sendSelectionToInteractiveWindow = true; | ||
|
||
const fileName = Uri.file('test.py').fsPath; | ||
const version = 1; | ||
const inputText = `print(1)`; | ||
const document = createDocument(inputText, fileName, version, TypeMoq.Times.atLeastOnce(), true); | ||
|
||
createCodeLensFactory().getCellRanges(document.object); | ||
|
||
// verify context keys set | ||
verify(mockedVSCodeNamespaces.commands.executeCommand('setContext', 'jupyter.ownsSelection', true)).atLeast(1); | ||
verify(mockedVSCodeNamespaces.commands.executeCommand('setContext', 'jupyter.hascodecells', true)).atLeast(1); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.