Allow for a pylance middleware that does exclusions based on isDocumentAllowed but doesn't change anything

This commit is contained in:
Rich Chiodo 2021-11-02 12:06:52 -07:00
Родитель 883ee82068
Коммит 0d6523198b
11 изменённых файлов: 826 добавлений и 45 удалений

59
.vscode/settings.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,59 @@
// Place your settings in this file to overwrite default and user settings.
{
"files.exclude": {
"out": true, // set this to true to hide the "out" folder with the compiled JS files
"**/*.pyc": true,
".nyc_output": true,
"obj": true,
"bin": true,
"**/__pycache__": true,
"**/node_modules": false,
".vscode-test": true,
".vscode test": true,
".venv": true,
"**/.pytest_cache/**": true,
"languageServer.*/**": true,
"**/.mypy_cache/**": true,
"**/.ropeproject/**": true
},
"search.exclude": {
"out": true, // set this to false to include "out" folder in search results
"**/node_modules": true,
"coverage": true,
"languageServer*/**": true,
".vscode-test": true,
".vscode test": true
},
"[python]": {
"editor.formatOnSave": true
},
"[typescript]": {
"editor.formatOnSave": true
},
"[javascript]": {
"editor.formatOnSave": true
},
"typescript.tsdk": "./node_modules/typescript/lib", // we want to use the TS server from our node_modules folder to control its version
"python.linting.enabled": false,
"python.testing.promptToConfigure": false,
"python.workspaceSymbols.enabled": false,
"python.formatting.provider": "black",
"typescript.preferences.quoteStyle": "single",
"javascript.preferences.quoteStyle": "single",
"typescriptHero.imports.stringQuoteStyle": "'",
"prettier.printWidth": 120,
"prettier.singleQuote": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
"source.fixAll.tslint": true
},
"python.languageServer": "Pylance",
"python.analysis.logLevel": "Trace",
"python.linting.pylintEnabled": false,
"python.linting.flake8Enabled": true,
"python.linting.flake8Args": [
// Match what black does.
"--max-line-length=88"
],
"typescript.preferences.importModuleSpecifier": "relative"
}

2
package-lock.json сгенерированный
Просмотреть файл

@ -1,6 +1,6 @@
{
"name": "@vscode/jupyter-lsp-middleware",
"version": "0.2.17",
"version": "0.2.18",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

Просмотреть файл

@ -1,6 +1,6 @@
{
"name": "@vscode/jupyter-lsp-middleware",
"version": "0.2.17",
"version": "0.2.18",
"description": "VS Code Python Language Server Middleware for Jupyter Notebook",
"main": "dist/index.js",
"types": "dist/index.d.ts",

Просмотреть файл

@ -6,6 +6,7 @@ import { LanguageClient, Middleware } from 'vscode-languageclient/node';
import { IVSCodeNotebook } from './common/types';
import { HidingMiddlewareAddon } from './hidingMiddlewareAddon';
import { NotebookMiddlewareAddon } from './notebookMiddlewareAddon';
import { PylanceMiddlewareAddon } from './pylanceMiddlewareAddon';
export type NotebookMiddleware = Middleware & Disposable & {
stopWatching(notebook: NotebookDocument): void;
@ -40,3 +41,17 @@ export function createNotebookMiddleware(
isDocumentAllowed
);
}
export function createPylanceMiddleware(
getClient: () => LanguageClient | undefined,
pythonPath: string,
isDocumentAllowed: (uri: Uri) => boolean
): NotebookMiddleware {
// LanguageClients are created per interpreter (as they start) with a selector for all notebooks
// Middleware swallows all requests for notebooks that don't match itself (isDocumentAllowed returns false)
return new PylanceMiddlewareAddon(
getClient,
pythonPath,
isDocumentAllowed
);
}

Просмотреть файл

@ -0,0 +1,577 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import {
CallHierarchyIncomingCall,
CallHierarchyItem,
CallHierarchyOutgoingCall,
CancellationToken,
CodeAction,
CodeActionContext,
CodeLens,
Color,
ColorInformation,
ColorPresentation,
Command,
CompletionContext,
CompletionItem,
Declaration,
Definition,
DefinitionLink,
Diagnostic,
Disposable,
DocumentHighlight,
DocumentLink,
DocumentSymbol,
FoldingContext,
FoldingRange,
FormattingOptions,
LinkedEditingRanges,
Location,
NotebookDocument,
Position,
Position as VPosition,
ProviderResult,
Range,
SelectionRange,
SemanticTokens,
SemanticTokensEdits,
SignatureHelp,
SignatureHelpContext,
SymbolInformation,
TextDocument,
TextEdit,
Uri,
WorkspaceEdit
} from 'vscode';
import {
ConfigurationParams,
ConfigurationRequest,
DidCloseTextDocumentNotification,
DidOpenTextDocumentNotification,
HandleDiagnosticsSignature,
LanguageClient,
Middleware,
PrepareRenameSignature,
ProvideCodeActionsSignature,
ProvideCodeLensesSignature,
ProvideCompletionItemsSignature,
ProvideDefinitionSignature,
ProvideDocumentFormattingEditsSignature,
ProvideDocumentHighlightsSignature,
ProvideDocumentLinksSignature,
ProvideDocumentRangeFormattingEditsSignature,
ProvideDocumentSymbolsSignature,
ProvideHoverSignature,
ProvideOnTypeFormattingEditsSignature,
ProvideReferencesSignature,
ProvideRenameEditsSignature,
ProvideSignatureHelpSignature,
ProvideWorkspaceSymbolsSignature,
ResolveCodeLensSignature,
ResolveCompletionItemSignature,
ResolveDocumentLinkSignature,
ResponseError
} from 'vscode-languageclient/node';
import { ProvideDeclarationSignature } from 'vscode-languageclient/lib/common/declaration';
import { isThenable } from './common/utils';
import { ProvideTypeDefinitionSignature } from 'vscode-languageclient/lib/common/typeDefinition';
import { ProvideImplementationSignature } from 'vscode-languageclient/lib/common/implementation';
import {
ProvideDocumentColorsSignature,
ProvideColorPresentationSignature
} from 'vscode-languageclient/lib/common/colorProvider';
import { ProvideFoldingRangeSignature } from 'vscode-languageclient/lib/common/foldingRange';
import { ProvideSelectionRangeSignature } from 'vscode-languageclient/lib/common/selectionRange';
import {
PrepareCallHierarchySignature,
CallHierarchyIncomingCallsSignature,
CallHierarchyOutgoingCallsSignature
} from 'vscode-languageclient/lib/common/callHierarchy';
import {
DocumentRangeSemanticTokensSignature,
DocumentSemanticsTokensEditsSignature,
DocumentSemanticsTokensSignature
} from 'vscode-languageclient/lib/common/semanticTokens';
import { ProvideLinkedEditingRangeSignature } from 'vscode-languageclient/lib/common/linkedEditingRange';
/**
* This class is a temporary solution to handling intellisense and diagnostics in python based notebooks.
*
* It is responsible for sending requests to pylance if they are allowed.
*/
export class PylanceMiddlewareAddon implements Middleware, Disposable {
constructor(
private readonly getClient: () => LanguageClient | undefined,
private readonly pythonPath: string,
private readonly isDocumentAllowed: (uri: Uri) => boolean
) {
// Make sure a bunch of functions are bound to this. VS code can call them without a this context
this.handleDiagnostics = this.handleDiagnostics.bind(this);
this.didOpen = this.didOpen.bind(this);
}
public workspace = {
configuration: async (
params: ConfigurationParams,
token: CancellationToken,
next: ConfigurationRequest.HandlerSignature
) => {
// Handle workspace/configuration requests.
let settings = next(params, token);
if (isThenable(settings)) {
settings = await settings;
}
if (settings instanceof ResponseError) {
return settings;
}
for (const [i, item] of params.items.entries()) {
if (item.section === 'python') {
settings[i].pythonPath = this.pythonPath;
}
}
return settings;
}
};
public dispose(): void {
// Nothing to dispose at the moment
}
public stopWatching(notebook: NotebookDocument): void {
// Close all of the cells. This should cause diags and other things to be cleared
const client = this.getClient();
if (client && notebook.cellCount > 0) {
notebook.getCells().forEach((c) => {
const params = client.code2ProtocolConverter.asCloseTextDocumentParams(c.document);
client.sendNotification(DidCloseTextDocumentNotification.type, params);
});
// Set the diagnostics to nothing for all the cells
if (client.diagnostics) {
notebook.getCells().forEach((c) => {
client.diagnostics?.set(c.document.uri, []);
});
}
}
}
public startWatching(notebook: NotebookDocument): void {
// We need to talk directly to the language client here.
const client = this.getClient();
// Mimic a document open for all cells
if (client && notebook.cellCount > 0) {
notebook.getCells().forEach((c) => {
this.didOpen(c.document, (ev) => {
const params = client.code2ProtocolConverter.asOpenTextDocumentParams(ev);
client.sendNotification(DidOpenTextDocumentNotification.type, params);
});
});
}
}
public didOpen(document: TextDocument, next: (ev: TextDocument) => void) {
next(document);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
public provideCompletionItem(
document: TextDocument,
position: Position,
context: CompletionContext,
token: CancellationToken,
next: ProvideCompletionItemsSignature
) {
if (this.shouldProvideIntellisense(document.uri)) {
return next(document, position, context, token);
}
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
public provideHover(
document: TextDocument,
position: Position,
token: CancellationToken,
next: ProvideHoverSignature
) {
if (this.shouldProvideIntellisense(document.uri)) {
return next(document, position, token);
}
}
// eslint-disable-next-line class-methods-use-this
public resolveCompletionItem(
item: CompletionItem,
token: CancellationToken,
next: ResolveCompletionItemSignature
): ProviderResult<CompletionItem> {
// Range should have already been remapped.
// TODO: What if the LS needs to read the range? It won't make sense. This might mean
// doing this at the extension level is not possible.
return next(item, token);
}
public provideSignatureHelp(
document: TextDocument,
position: Position,
context: SignatureHelpContext,
token: CancellationToken,
next: ProvideSignatureHelpSignature
): ProviderResult<SignatureHelp> {
if (this.shouldProvideIntellisense(document.uri)) {
return next(document, position, context, token);
}
}
public provideDefinition(
document: TextDocument,
position: Position,
token: CancellationToken,
next: ProvideDefinitionSignature
): ProviderResult<Definition | DefinitionLink[]> {
if (this.shouldProvideIntellisense(document.uri)) {
return next(document, position, token);
}
}
public provideReferences(
document: TextDocument,
position: Position,
options: {
includeDeclaration: boolean;
},
token: CancellationToken,
next: ProvideReferencesSignature
): ProviderResult<Location[]> {
if (this.shouldProvideIntellisense(document.uri)) {
return next(document, position, options, token);
}
}
public provideDocumentHighlights(
document: TextDocument,
position: Position,
token: CancellationToken,
next: ProvideDocumentHighlightsSignature
): ProviderResult<DocumentHighlight[]> {
if (this.shouldProvideIntellisense(document.uri)) {
return next(document, position, token);
}
}
public provideDocumentSymbols(
document: TextDocument,
token: CancellationToken,
next: ProvideDocumentSymbolsSignature
): ProviderResult<SymbolInformation[] | DocumentSymbol[]> {
if (this.shouldProvideIntellisense(document.uri)) {
return next(document, token);
}
}
public provideWorkspaceSymbols(
query: string,
token: CancellationToken,
next: ProvideWorkspaceSymbolsSignature
): ProviderResult<SymbolInformation[]> {
// Is this one possible to check?
return next(query, token);
}
// eslint-disable-next-line class-methods-use-this
public provideCodeActions(
document: TextDocument,
range: Range,
context: CodeActionContext,
token: CancellationToken,
next: ProvideCodeActionsSignature
): ProviderResult<(Command | CodeAction)[]> {
if (this.shouldProvideIntellisense(document.uri)) {
return next(document, range, context, token);
}
}
// eslint-disable-next-line class-methods-use-this
public provideCodeLenses(
document: TextDocument,
token: CancellationToken,
next: ProvideCodeLensesSignature
): ProviderResult<CodeLens[]> {
if (this.shouldProvideIntellisense(document.uri)) {
return next(document, token);
}
}
// eslint-disable-next-line class-methods-use-this
public resolveCodeLens(
codeLens: CodeLens,
token: CancellationToken,
next: ResolveCodeLensSignature
): ProviderResult<CodeLens> {
// Range should have already been remapped.
// TODO: What if the LS needs to read the range? It won't make sense. This might mean
// doing this at the extension level is not possible.
return next(codeLens, token);
}
// eslint-disable-next-line class-methods-use-this
public provideDocumentFormattingEdits(
document: TextDocument,
options: FormattingOptions,
token: CancellationToken,
next: ProvideDocumentFormattingEditsSignature
): ProviderResult<TextEdit[]> {
if (this.shouldProvideIntellisense(document.uri)) {
return next(document, options, token);
}
}
// eslint-disable-next-line class-methods-use-this
public provideDocumentRangeFormattingEdits(
document: TextDocument,
range: Range,
options: FormattingOptions,
token: CancellationToken,
next: ProvideDocumentRangeFormattingEditsSignature
): ProviderResult<TextEdit[]> {
if (this.shouldProvideIntellisense(document.uri)) {
return next(document, range, options, token);
}
}
// eslint-disable-next-line class-methods-use-this
public provideOnTypeFormattingEdits(
document: TextDocument,
position: Position,
ch: string,
options: FormattingOptions,
token: CancellationToken,
next: ProvideOnTypeFormattingEditsSignature
): ProviderResult<TextEdit[]> {
if (this.shouldProvideIntellisense(document.uri)) {
return next(document, position, ch, options, token);
}
}
// eslint-disable-next-line class-methods-use-this
public provideRenameEdits(
document: TextDocument,
position: Position,
newName: string,
token: CancellationToken,
next: ProvideRenameEditsSignature
): ProviderResult<WorkspaceEdit> {
if (this.shouldProvideIntellisense(document.uri)) {
return next(document, position, newName, token);
}
}
// eslint-disable-next-line class-methods-use-this
public prepareRename(
document: TextDocument,
position: Position,
token: CancellationToken,
next: PrepareRenameSignature
): ProviderResult<
| Range
| {
range: Range;
placeholder: string;
}
> {
if (this.shouldProvideIntellisense(document.uri)) {
return next(document, position, token);
}
}
// eslint-disable-next-line class-methods-use-this
public provideDocumentLinks(
document: TextDocument,
token: CancellationToken,
next: ProvideDocumentLinksSignature
): ProviderResult<DocumentLink[]> {
if (this.shouldProvideIntellisense(document.uri)) {
return next(document, token);
}
}
// eslint-disable-next-line class-methods-use-this
public resolveDocumentLink(
link: DocumentLink,
token: CancellationToken,
next: ResolveDocumentLinkSignature
): ProviderResult<DocumentLink> {
// Range should have already been remapped.
// TODO: What if the LS needs to read the range? It won't make sense. This might mean
// doing this at the extension level is not possible.
return next(link, token);
}
public handleDiagnostics(uri: Uri, diagnostics: Diagnostic[], next: HandleDiagnosticsSignature): void {
if (this.shouldProvideIntellisense(uri)) {
return next(uri, diagnostics);
} else {
// Swallow all other diagnostics
next(uri, []);
}
}
public provideTypeDefinition(
document: TextDocument,
position: Position,
token: CancellationToken,
next: ProvideTypeDefinitionSignature
) {
if (this.shouldProvideIntellisense(document.uri)) {
return next(document, position, token);
}
}
public provideImplementation(
document: TextDocument,
position: VPosition,
token: CancellationToken,
next: ProvideImplementationSignature
): ProviderResult<Definition | DefinitionLink[]> {
if (this.shouldProvideIntellisense(document.uri)) {
return next(document, position, token);
}
}
public provideDocumentColors(
document: TextDocument,
token: CancellationToken,
next: ProvideDocumentColorsSignature
): ProviderResult<ColorInformation[]> {
if (this.shouldProvideIntellisense(document.uri)) {
return next(document, token);
}
}
public provideColorPresentations(
color: Color,
context: {
document: TextDocument;
range: Range;
},
token: CancellationToken,
next: ProvideColorPresentationSignature
): ProviderResult<ColorPresentation[]> {
if (this.shouldProvideIntellisense(context.document.uri)) {
return next(color, context, token);
}
}
public provideFoldingRanges(
document: TextDocument,
context: FoldingContext,
token: CancellationToken,
next: ProvideFoldingRangeSignature
): ProviderResult<FoldingRange[]> {
if (this.shouldProvideIntellisense(document.uri)) {
return next(document, context, token);
}
}
public provideDeclaration(
document: TextDocument,
position: Position,
token: CancellationToken,
next: ProvideDeclarationSignature
): ProviderResult<Declaration> {
if (this.shouldProvideIntellisense(document.uri)) {
return next(document, position, token);
}
}
public provideSelectionRanges(
document: TextDocument,
positions: Position[],
token: CancellationToken,
next: ProvideSelectionRangeSignature
): ProviderResult<SelectionRange[]> {
if (this.shouldProvideIntellisense(document.uri)) {
return next(document, positions, token);
}
}
public prepareCallHierarchy(
document: TextDocument,
positions: Position,
token: CancellationToken,
next: PrepareCallHierarchySignature
): ProviderResult<CallHierarchyItem | CallHierarchyItem[]> {
if (this.shouldProvideIntellisense(document.uri)) {
return next(document, positions, token);
}
}
public provideCallHierarchyIncomingCalls(
item: CallHierarchyItem,
token: CancellationToken,
next: CallHierarchyIncomingCallsSignature
): ProviderResult<CallHierarchyIncomingCall[]> {
if (this.shouldProvideIntellisense(item.uri)) {
return next(item, token);
}
}
public provideCallHierarchyOutgoingCalls(
item: CallHierarchyItem,
token: CancellationToken,
next: CallHierarchyOutgoingCallsSignature
): ProviderResult<CallHierarchyOutgoingCall[]> {
if (this.shouldProvideIntellisense(item.uri)) {
return next(item, token);
}
}
public provideDocumentSemanticTokens(
document: TextDocument,
token: CancellationToken,
next: DocumentSemanticsTokensSignature
): ProviderResult<SemanticTokens> {
if (this.shouldProvideIntellisense(document.uri)) {
return next(document, token);
}
}
public provideDocumentSemanticTokensEdits(
document: TextDocument,
previousResultId: string,
token: CancellationToken,
next: DocumentSemanticsTokensEditsSignature
): ProviderResult<SemanticTokensEdits | SemanticTokens> {
if (this.shouldProvideIntellisense(document.uri)) {
return next(document, previousResultId, token);
}
}
public provideDocumentRangeSemanticTokens(
document: TextDocument,
range: Range,
token: CancellationToken,
next: DocumentRangeSemanticTokensSignature
): ProviderResult<SemanticTokens> {
if (this.shouldProvideIntellisense(document.uri)) {
return next(document, range, token);
}
}
public provideLinkedEditingRange(
document: TextDocument,
position: Position,
token: CancellationToken,
next: ProvideLinkedEditingRangeSignature
): ProviderResult<LinkedEditingRanges> {
if (this.shouldProvideIntellisense(document.uri)) {
return next(document, position, token);
}
}
private shouldProvideIntellisense(uri: Uri): boolean {
// Make sure document is allowed
return this.isDocumentAllowed(uri);
}
}

Просмотреть файл

@ -23,7 +23,7 @@ import {
ServerCapabilities,
StaticFeature
} from 'vscode-languageclient/node';
import { createNotebookMiddleware, createHidingMiddleware } from '../..';
import { createNotebookMiddleware, createHidingMiddleware, createPylanceMiddleware } from '../..';
import { FileBasedCancellationStrategy } from '../../fileBasedCancellationStrategy';
import * as uuid from 'uuid/v4';
@ -870,10 +870,40 @@ export class LanguageServer implements vscode.Disposable {
}
}
export type MiddlewareType = 'pylance' | 'hiding' | 'notebook';
function createMiddleware(
middlewareType: MiddlewareType,
notebookApi: IVSCodeNotebook,
getClient: () => LanguageClient | undefined,
traceInfo: (...args: any[]) => void,
cellSelector: DocumentSelector,
notebookFileRegex: RegExp,
pythonPath: string,
isDocumentAllowed: (uri: vscode.Uri) => boolean
) {
switch (middlewareType) {
case 'hiding':
return createHidingMiddleware();
case 'pylance':
return createPylanceMiddleware(getClient, pythonPath, isDocumentAllowed);
case 'notebook':
return createNotebookMiddleware(
notebookApi,
getClient,
traceInfo,
cellSelector,
notebookFileRegex,
pythonPath,
isDocumentAllowed
);
}
}
async function startLanguageServer(
outputChannel: string,
languageServerFolder: string,
hidingMiddleware: boolean,
middlewareType: MiddlewareType,
pythonPath: string,
selector: DocumentSelector,
shouldProvideIntellisense: (uri: vscode.Uri) => boolean
@ -902,7 +932,8 @@ async function startLanguageServer(
}
};
const middleware = hidingMiddleware ? createHidingMiddleware() : createNotebookMiddleware(
const middleware = createMiddleware(
middlewareType,
notebookApi,
() => languageClient,
traceInfo,
@ -952,7 +983,7 @@ async function startLanguageServer(
export async function createLanguageServer(
outputChannel: string,
selector: DocumentSelector,
hidingMiddleware: boolean,
middlewareType: MiddlewareType,
shouldProvideIntellisense: (uri: vscode.Uri) => boolean
): Promise<LanguageServer | undefined> {
// Python should be installed too.
@ -972,7 +1003,7 @@ export async function createLanguageServer(
return startLanguageServer(
outputChannel,
path.join(pylance.extensionPath, 'dist'),
hidingMiddleware,
middlewareType,
pythonPath,
selector,
shouldProvideIntellisense

Просмотреть файл

@ -3,15 +3,7 @@
/* eslint-disable @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires */
import { assert } from 'chai';
import {
Position,
Disposable,
languages,
Range,
WorkspaceEdit,
workspace,
Uri
} from 'vscode';
import { Position, Disposable, languages, Range, WorkspaceEdit, workspace, Uri } from 'vscode';
import { DocumentFilter } from 'vscode-languageserver-protocol';
import {
canRunNotebookTests,
@ -56,7 +48,7 @@ suite('Hiding tests', function () {
languageServer = await createLanguageServer(
'lsp-middleware-test',
NOTEBOOK_SELECTOR,
true,
'hiding',
shouldProvideIntellisense
);
});

Просмотреть файл

@ -63,7 +63,7 @@ suite('Notebook tests', function () {
languageServer = await createLanguageServer(
'lsp-middleware-test',
NOTEBOOK_SELECTOR,
false,
'notebook',
shouldProvideIntellisense
);
});
@ -230,7 +230,7 @@ suite('Notebook tests', function () {
);
});
test('Make sure diags are skipped when not allowing', async function () {
this.skip(); // Skip for now. Requires jupyter to not be providing intellisense too
this.skip(); // Skip for now. Requires jupyter extension to not be providing intellisense too
allowIntellisense = false;
await insertCodeCell('import sys\nprint(sys.executable)');
await insertCodeCell('import sys\nprint(sys.executable)');

Просмотреть файл

@ -0,0 +1,91 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
/* eslint-disable @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires */
import { assert } from 'chai';
import { Position, Disposable, languages, Range, WorkspaceEdit, workspace, Uri } from 'vscode';
import { DocumentFilter } from 'vscode-languageserver-protocol';
import {
canRunNotebookTests,
closeNotebooksAndCleanUpAfterTests,
insertCodeCell,
createEmptyPythonNotebook,
traceInfo,
createLanguageServer,
focusCell,
captureScreenShot,
captureOutputMessages,
LanguageServer,
waitForDiagnostics
} from './helper';
export const PYTHON_LANGUAGE = 'python';
export const NotebookCellScheme = 'vscode-notebook-cell';
export const InteractiveInputScheme = 'vscode-interactive-input';
export const NOTEBOOK_SELECTOR: DocumentFilter[] = [
{ scheme: NotebookCellScheme, language: PYTHON_LANGUAGE },
{ scheme: InteractiveInputScheme, language: PYTHON_LANGUAGE }
];
/* eslint-disable @typescript-eslint/no-explicit-any, no-invalid-this */
suite('Pylance tests', function () {
const disposables: Disposable[] = [];
let languageServer: LanguageServer | undefined = undefined;
let allowIntellisense = true;
let emptyNotebookUri: Uri | undefined;
const shouldProvideIntellisense = (uri: Uri) => {
if (emptyNotebookUri?.fsPath === uri.fsPath) {
return allowIntellisense;
}
return false;
};
this.timeout(120_000);
suiteSetup(async function () {
this.timeout(120_000);
if (!canRunNotebookTests()) {
return this.skip();
}
languageServer = await createLanguageServer(
'lsp-middleware-test',
NOTEBOOK_SELECTOR,
'pylance',
shouldProvideIntellisense
);
});
// Use same notebook without starting kernel in every single test (use one for whole suite).
setup(async function () {
traceInfo(`Start Test ${this.currentTest?.title}`);
allowIntellisense = true;
emptyNotebookUri = await createEmptyPythonNotebook(disposables);
traceInfo(`Start Test (completed) ${this.currentTest?.title}`);
});
teardown(async function () {
traceInfo(`Ended Test ${this.currentTest?.title}`);
if (this.currentTest && this.currentTest.state === 'failed') {
await captureScreenShot(this.currentTest.title);
await captureOutputMessages();
}
await closeNotebooksAndCleanUpAfterTests(disposables);
traceInfo(`Ended Test (completed) ${this.currentTest?.title}`);
});
suiteTeardown(async () => {
closeNotebooksAndCleanUpAfterTests(disposables);
await languageServer?.dispose();
});
test('Edit a cell and make sure diagnostics do show up', async () => {
// Pylance should definitely be able to handle a single cell
const cell = await insertCodeCell('import sys\nprint(sys.executable)\na = 1');
// Should be no diagnostics yet
let diagnostics = languages.getDiagnostics(cell.document.uri);
assert.isEmpty(diagnostics, 'No diagnostics should be found in the first cell');
// Edit the cell
await focusCell(cell);
const edit = new WorkspaceEdit();
edit.replace(cell.document.uri, new Range(new Position(0, 7), new Position(0, 10)), 'system');
await workspace.applyEdit(edit);
// There should be diagnostics now
await waitForDiagnostics(cell.document.uri);
});
});

63
vscode.d.ts поставляемый
Просмотреть файл

@ -766,8 +766,9 @@ declare module 'vscode' {
preserveFocus?: boolean;
/**
* An optional flag that controls if an {@link TextEditor editor}-tab will be replaced
* with the next editor or if it will be kept.
* An optional flag that controls if an {@link TextEditor editor}-tab shows as preview. Preview tabs will
* be replaced and reused until set to stay - either explicitly or through editing. The default behaviour depends
* on the `workbench.editor.enablePreview`-setting.
*/
preview?: boolean;
@ -2566,11 +2567,12 @@ declare module 'vscode' {
}
/**
* The MarkdownString represents human-readable text that supports formatting via the
* markdown syntax. Standard markdown is supported, also tables, but no embedded html.
* Human-readable text that supports formatting via the [markdown syntax](https://commonmark.org).
*
* Rendering of {@link ThemeIcon theme icons} via the `$(<name>)`-syntax is supported
* when the {@linkcode MarkdownString.supportThemeIcons supportThemeIcons} is set to `true`.
* when the {@linkcode supportThemeIcons} is set to `true`.
*
* Rendering of embedded html is supported when {@linkcode supportHtml} is set to `true`.
*/
export class MarkdownString {
@ -2591,7 +2593,7 @@ declare module 'vscode' {
supportThemeIcons?: boolean;
/**
* Indicates that this markdown string can contain raw html tags. Defaults to false.
* Indicates that this markdown string can contain raw html tags. Defaults to `false`.
*
* When `supportHtml` is false, the markdown renderer will strip out any raw html tags
* that appear in the markdown text. This means you can only use markdown syntax for rendering.
@ -3550,7 +3552,7 @@ declare module 'vscode' {
*
* This is the id that will be passed to `DocumentSemanticTokensProvider.provideDocumentSemanticTokensEdits` (if implemented).
*/
readonly resultId?: string;
readonly resultId: string | undefined;
/**
* The actual tokens data.
* @see {@link DocumentSemanticTokensProvider.provideDocumentSemanticTokens provideDocumentSemanticTokens} for an explanation of the format.
@ -3570,7 +3572,7 @@ declare module 'vscode' {
*
* This is the id that will be passed to `DocumentSemanticTokensProvider.provideDocumentSemanticTokensEdits` (if implemented).
*/
readonly resultId?: string;
readonly resultId: string | undefined;
/**
* The edits to the tokens data.
* All edits refer to the initial data state.
@ -3596,7 +3598,7 @@ declare module 'vscode' {
/**
* The elements to insert.
*/
readonly data?: Uint32Array;
readonly data: Uint32Array | undefined;
constructor(start: number, deleteCount: number, data?: Uint32Array);
}
@ -5745,7 +5747,7 @@ declare module 'vscode' {
* The priority of this item. Higher value means the item should
* be shown more to the left.
*/
readonly priority?: number;
readonly priority: number | undefined;
/**
* The name of the entry, like 'Python Language Indicator', 'Git Status' etc.
@ -5801,7 +5803,7 @@ declare module 'vscode' {
/**
* Accessibility information used when a screen reader interacts with this StatusBar item
*/
accessibilityInformation?: AccessibilityInformation;
accessibilityInformation: AccessibilityInformation | undefined;
/**
* Shows the entry in the status bar.
@ -8421,7 +8423,7 @@ declare module 'vscode' {
/**
* The detected default shell for the extension host, this is overridden by the
* `terminal.integrated.shell` setting for the extension host's platform. Note that in
* `terminal.integrated.defaultProfile` setting for the extension host's platform. Note that in
* environments that do not support a shell the value is the empty string.
*/
export const shell: string;
@ -9723,6 +9725,8 @@ declare module 'vscode' {
* Note writing `\n` will just move the cursor down 1 row, you need to write `\r` as well
* to move the cursor to the left-most cell.
*
* Events fired before {@link Pseudoterminal.open} is called will be be ignored.
*
* **Example:** Write red text to the terminal
* ```typescript
* const writeEmitter = new vscode.EventEmitter<string>();
@ -9748,6 +9752,8 @@ declare module 'vscode' {
* bar). Set to `undefined` for the terminal to go back to the regular dimensions (fit to
* the size of the panel).
*
* Events fired before {@link Pseudoterminal.open} is called will be be ignored.
*
* **Example:** Override the dimensions of a terminal to 20 columns and 10 rows
* ```typescript
* const dimensionsEmitter = new vscode.EventEmitter<vscode.TerminalDimensions>();
@ -9770,6 +9776,8 @@ declare module 'vscode' {
/**
* An event that when fired will signal that the pty is closed and dispose of the terminal.
*
* Events fired before {@link Pseudoterminal.open} is called will be be ignored.
*
* A number can be used to provide an exit code for the terminal. Exit codes must be
* positive and a non-zero exit codes signals failure which shows a notification for a
* regular terminal and allows dependent tasks to proceed when used with the
@ -9799,6 +9807,8 @@ declare module 'vscode' {
/**
* An event that when fired allows changing the name of the terminal.
*
* Events fired before {@link Pseudoterminal.open} is called will be be ignored.
*
* **Example:** Change the terminal name to "My new terminal".
* ```typescript
* const writeEmitter = new vscode.EventEmitter<string>();
@ -10835,8 +10845,8 @@ declare module 'vscode' {
* will be matched against the file paths of resulting matches relative to their workspace. Use a {@link RelativePattern relative pattern}
* to restrict the search results to a {@link WorkspaceFolder workspace folder}.
* @param exclude A {@link GlobPattern glob pattern} that defines files and folders to exclude. The glob pattern
* will be matched against the file paths of resulting matches relative to their workspace. When `undefined`, default excludes and the user's
* configured excludes will apply. When `null`, no excludes will apply.
* will be matched against the file paths of resulting matches relative to their workspace. When `undefined`, default file-excludes (e.g. the `files.exclude`-setting
* but not `search.exclude`) will apply. When `null`, no excludes will apply.
* @param maxResults An upper-bound for the result.
* @param token A token that can be used to signal cancellation to the underlying search engine.
* @return A thenable that resolves to an array of resource identifiers. Will return no results if no
@ -12733,8 +12743,9 @@ declare module 'vscode' {
* The UI-visible count of {@link SourceControlResourceState resource states} of
* this source control.
*
* Equals to the total number of {@link SourceControlResourceState resource state}
* of this source control, if undefined.
* If undefined, this source control will
* - display its UI-visible count as zero, and
* - contribute the count of its {@link SourceControlResourceState resource states} to the UI-visible aggregated count for all source controls
*/
count?: number;
@ -14101,7 +14112,7 @@ declare module 'vscode' {
* Associated tag for the profile. If this is set, only {@link TestItem}
* instances with the same tag will be eligible to execute in this profile.
*/
tag?: TestTag;
tag: TestTag | undefined;
/**
* If this method is present, a configuration gear will be present in the
@ -14109,7 +14120,7 @@ declare module 'vscode' {
* you can take other editor actions, such as showing a quick pick or
* opening a configuration file.
*/
configureHandler?: () => void;
configureHandler: (() => void) | undefined;
/**
* Handler called to start a test run. When invoked, the function should call
@ -14258,7 +14269,7 @@ declare module 'vscode' {
* The process of running tests should resolve the children of any test
* items who have not yet been resolved.
*/
readonly include?: TestItem[];
readonly include: TestItem[] | undefined;
/**
* An array of tests the user has marked as excluded from the test included
@ -14267,14 +14278,14 @@ declare module 'vscode' {
* May be omitted if no exclusions were requested. Test controllers should
* not run excluded tests or any children of excluded tests.
*/
readonly exclude?: TestItem[];
readonly exclude: TestItem[] | undefined;
/**
* The profile used for this request. This will always be defined
* for requests issued from the editor UI, though extensions may
* programmatically create requests not associated with any profile.
*/
readonly profile?: TestRunProfile;
readonly profile: TestRunProfile | undefined;
/**
* @param tests Array of specific tests to run, or undefined to run all tests
@ -14293,7 +14304,7 @@ declare module 'vscode' {
* disambiguate multiple sets of results in a test run. It is useful if
* tests are run across multiple platforms, for example.
*/
readonly name?: string;
readonly name: string | undefined;
/**
* A cancellation token which will be triggered when the test run is
@ -14433,7 +14444,7 @@ declare module 'vscode' {
/**
* URI this `TestItem` is associated with. May be a file or directory.
*/
readonly uri?: Uri;
readonly uri: Uri | undefined;
/**
* The children of this test item. For a test suite, this may contain the
@ -14446,7 +14457,7 @@ declare module 'vscode' {
* top-level items in the {@link TestController.items} and for items that
* aren't yet included in another item's {@link children}.
*/
readonly parent?: TestItem;
readonly parent: TestItem | undefined;
/**
* Tags associated with this test item. May be used in combination with
@ -14488,7 +14499,7 @@ declare module 'vscode' {
*
* This is only meaningful if the `uri` points to a file.
*/
range?: Range;
range: Range | undefined;
/**
* Optional error encountered while loading the test.
@ -14496,7 +14507,7 @@ declare module 'vscode' {
* Note that this is not a test result and should only be used to represent errors in
* test discovery, such as syntax errors.
*/
error?: string | MarkdownString;
error: string | MarkdownString | undefined;
}
/**

5
vscode.proposed.d.ts поставляемый
Просмотреть файл

@ -1575,6 +1575,10 @@ declare module 'vscode' {
}
export interface NotebookController {
/**
* The human-readable label used to categorise controllers.
*/
kind?: string;
// todo@API allow add, not remove
readonly rendererScripts: NotebookRendererScript[];
@ -1822,6 +1826,7 @@ declare module 'vscode' {
/**
* The text of the hint.
*/
// todo@API label?
text: string;
/**
* The position of this hint.