Add setting for kernel completions (#14763)
* Update types to reflect version of Node & browsers * Remove completion exp, add setting to enable it
This commit is contained in:
Родитель
52a880d3fa
Коммит
3af4d05a84
|
@ -113,7 +113,7 @@
|
||||||
"@types/memoize-one": "^4.1.1",
|
"@types/memoize-one": "^4.1.1",
|
||||||
"@types/mocha": "^9.1.0",
|
"@types/mocha": "^9.1.0",
|
||||||
"@types/nock": "^10.0.3",
|
"@types/nock": "^10.0.3",
|
||||||
"@types/node": "^16.18.6",
|
"@types/node": "^18.15.0",
|
||||||
"@types/node-fetch": "^2.5.7",
|
"@types/node-fetch": "^2.5.7",
|
||||||
"@types/pdfkit": "^0.11.0",
|
"@types/pdfkit": "^0.11.0",
|
||||||
"@types/promisify-node": "^0.4.0",
|
"@types/promisify-node": "^0.4.0",
|
||||||
|
@ -3573,9 +3573,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "16.18.6",
|
"version": "18.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.6.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.0.tgz",
|
||||||
"integrity": "sha512-vmYJF0REqDyyU0gviezF/KHq/fYaUbFhkcNbQCuPGFQj6VTbXuHZoxs/Y7mutWe73C8AC6l9fFu8mSYiBAqkGA==",
|
"integrity": "sha512-z6nr0TTEOBGkzLGmbypWOGnpSpSIBorEhC4L+4HeQ2iezKCi4f77kyslRwvHeNitymGQ+oFyIWGP96l/DPSV9w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/node-fetch": {
|
"node_modules/@types/node-fetch": {
|
||||||
|
@ -25095,9 +25095,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "16.18.6",
|
"version": "18.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.6.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.0.tgz",
|
||||||
"integrity": "sha512-vmYJF0REqDyyU0gviezF/KHq/fYaUbFhkcNbQCuPGFQj6VTbXuHZoxs/Y7mutWe73C8AC6l9fFu8mSYiBAqkGA==",
|
"integrity": "sha512-z6nr0TTEOBGkzLGmbypWOGnpSpSIBorEhC4L+4HeQ2iezKCi4f77kyslRwvHeNitymGQ+oFyIWGP96l/DPSV9w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/node-fetch": {
|
"@types/node-fetch": {
|
||||||
|
|
17
package.json
17
package.json
|
@ -1497,9 +1497,7 @@
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"default": [],
|
"default": [],
|
||||||
"items": {
|
"items": {
|
||||||
"enum": [
|
"enum": []
|
||||||
"KernelCompletions"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"markdownDescription": "%jupyter.configuration.jupyter.experiments.optInto.markdownDescription%",
|
"markdownDescription": "%jupyter.configuration.jupyter.experiments.optInto.markdownDescription%",
|
||||||
"scope": "application"
|
"scope": "application"
|
||||||
|
@ -1508,9 +1506,7 @@
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"default": [],
|
"default": [],
|
||||||
"items": {
|
"items": {
|
||||||
"enum": [
|
"enum": []
|
||||||
"KernelCompletions"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"markdownDescription": "%jupyter.configuration.jupyter.experiments.optOutFrom.markdownDescription%",
|
"markdownDescription": "%jupyter.configuration.jupyter.experiments.optOutFrom.markdownDescription%",
|
||||||
"scope": "application"
|
"scope": "application"
|
||||||
|
@ -1973,6 +1969,13 @@
|
||||||
"description": "Experimental feature to enable execution analysis in notebooks",
|
"description": "Experimental feature to enable execution analysis in notebooks",
|
||||||
"scope": "application"
|
"scope": "application"
|
||||||
},
|
},
|
||||||
|
"jupyter.enableKernelCompletions": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "Enable Non-Python Kernel Completions",
|
||||||
|
"markdownDescription": "%jupyter.configuration.jupyter.enableKernelCompletions.markdownDescription%",
|
||||||
|
"scope": "application"
|
||||||
|
},
|
||||||
"jupyter.completionTriggerCharacters": {
|
"jupyter.completionTriggerCharacters": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"patternProperties": {
|
"patternProperties": {
|
||||||
|
@ -2308,7 +2311,7 @@
|
||||||
"@types/memoize-one": "^4.1.1",
|
"@types/memoize-one": "^4.1.1",
|
||||||
"@types/mocha": "^9.1.0",
|
"@types/mocha": "^9.1.0",
|
||||||
"@types/nock": "^10.0.3",
|
"@types/nock": "^10.0.3",
|
||||||
"@types/node": "^16.18.6",
|
"@types/node": "^18.15.0",
|
||||||
"@types/node-fetch": "^2.5.7",
|
"@types/node-fetch": "^2.5.7",
|
||||||
"@types/pdfkit": "^0.11.0",
|
"@types/pdfkit": "^0.11.0",
|
||||||
"@types/promisify-node": "^0.4.0",
|
"@types/promisify-node": "^0.4.0",
|
||||||
|
|
|
@ -236,7 +236,8 @@
|
||||||
"message": "Add PYTHONNOUSERSITE to kernels before starting. This prevents global/user site-packages from being used in the PYTHONPATH of the kernel.",
|
"message": "Add PYTHONNOUSERSITE to kernels before starting. This prevents global/user site-packages from being used in the PYTHONPATH of the kernel.",
|
||||||
"comment": ["{Locked='PYTHONNOUSERSITE'}", "{Locked='PYTHONPATH'}"]
|
"comment": ["{Locked='PYTHONNOUSERSITE'}", "{Locked='PYTHONPATH'}"]
|
||||||
},
|
},
|
||||||
"jupyter.configuration.jupyter.enableExtendedKernelCompletions.markdownDescription": "Enables Jedi support for extended IntelliSense completions in running Jupyter kernels (see this [setting](https://ipython.readthedocs.io/en/stable/config/options/terminal.html?highlight=use_jedi#configtrait-Completer.use_jedi)). This can greatly impact notebook cell execution performance. Use with caution.",
|
"jupyter.configuration.jupyter.enableExtendedKernelCompletions.markdownDescription": "Enables Jedi support for extended IntelliSense completions in running Python Jupyter kernels for Python (see this [setting](https://ipython.readthedocs.io/en/stable/config/options/terminal.html?highlight=use_jedi#configtrait-Completer.use_jedi)). This can greatly impact notebook cell execution performance. Use with caution.",
|
||||||
|
"jupyter.configuration.jupyter.enableKernelCompletions.markdownDescription": "Enables support for auto completion in non-Python Notebooks and Interactive Windows using the associated Jupyter Kernel. \n\nWarning: This can greatly impact cell execution performance. Use with caution.",
|
||||||
"DataScience.exportDialogTitle": {
|
"DataScience.exportDialogTitle": {
|
||||||
"message": "Export to Jupyter Notebook",
|
"message": "Export to Jupyter Notebook",
|
||||||
"comment": ["{Locked='Notebook'}"]
|
"comment": ["{Locked='Notebook'}"]
|
||||||
|
|
14
src/gdpr.ts
14
src/gdpr.ts
|
@ -403,6 +403,20 @@
|
||||||
"kernelLanguage": {"classification":"PublicNonPersonalData","purpose":"FeatureInsight","comment":"Language of the kernel spec.","owner":"donjayamanne"},
|
"kernelLanguage": {"classification":"PublicNonPersonalData","purpose":"FeatureInsight","comment":"Language of the kernel spec.","owner":"donjayamanne"},
|
||||||
"kernelId": {"classification":"PublicNonPersonalData","purpose":"FeatureInsight","comment":"Hash of the Kernel Connection id.","owner":"donjayamanne"},
|
"kernelId": {"classification":"PublicNonPersonalData","purpose":"FeatureInsight","comment":"Hash of the Kernel Connection id.","owner":"donjayamanne"},
|
||||||
"cancelled": {"classification":"PublicNonPersonalData","purpose":"FeatureInsight","comment":"Whether the completion request was cancelled or not.","owner":"donjayamanne"},
|
"cancelled": {"classification":"PublicNonPersonalData","purpose":"FeatureInsight","comment":"Whether the completion request was cancelled or not.","owner":"donjayamanne"},
|
||||||
|
"resolved": {"classification":"PublicNonPersonalData","purpose":"FeatureInsight","comment":"Whether we resolved the documentation or not.","owner":"donjayamanne"},
|
||||||
|
"resolveDuration": {"classification":"SystemMetaData","purpose":"PerformanceAndHealth","comment":"Time taken to resolve the documentation.","owner":"donjayamanne","isMeasurement":true},
|
||||||
|
"${include}": [
|
||||||
|
"${F1}"
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
//Telemetry.KernelCodeCompletionCannotResolve
|
||||||
|
/* __GDPR__
|
||||||
|
"DATASCIENCE.JUPYTER_KERNEL_CODE_COMPLETION_CANNOT_RESOLVE" : {
|
||||||
|
"kernelConnectionType": {"classification":"PublicNonPersonalData","purpose":"FeatureInsight","comment":"What kind of kernel spec did we fail to create.","owner":"donjayamanne"},
|
||||||
|
"kernelLanguage": {"classification":"PublicNonPersonalData","purpose":"FeatureInsight","comment":"Language of the kernel spec.","owner":"donjayamanne"},
|
||||||
|
"kernelId": {"classification":"PublicNonPersonalData","purpose":"FeatureInsight","comment":"Hash of the Kernel Connection id.","owner":"donjayamanne"},
|
||||||
"${include}": [
|
"${include}": [
|
||||||
"${F1}"
|
"${F1}"
|
||||||
|
|
||||||
|
|
|
@ -386,6 +386,7 @@ export enum Telemetry {
|
||||||
CellOutputMimeType = 'DS_INTERNAL.CELL_OUTPUT_MIME_TYPE',
|
CellOutputMimeType = 'DS_INTERNAL.CELL_OUTPUT_MIME_TYPE',
|
||||||
JupyterApiUsage = 'DATASCIENCE.JUPYTER_API_USAGE',
|
JupyterApiUsage = 'DATASCIENCE.JUPYTER_API_USAGE',
|
||||||
KernelCodeCompletion = 'DATASCIENCE.JUPYTER_KERNEL_CODE_COMPLETION',
|
KernelCodeCompletion = 'DATASCIENCE.JUPYTER_KERNEL_CODE_COMPLETION',
|
||||||
|
KernelCodeCompletionCannotResolve = 'DATASCIENCE.JUPYTER_KERNEL_CODE_COMPLETION_CANNOT_RESOLVE',
|
||||||
JupyterKernelApiUsage = 'DATASCIENCE.JUPYTER_KERNEL_API_USAGE',
|
JupyterKernelApiUsage = 'DATASCIENCE.JUPYTER_KERNEL_API_USAGE',
|
||||||
NewJupyterKernelApiUsage = 'DATASCIENCE.JUPYTER_NEW_KERNEL_API_USAGE',
|
NewJupyterKernelApiUsage = 'DATASCIENCE.JUPYTER_NEW_KERNEL_API_USAGE',
|
||||||
NewJupyterKernelsApiUsage = 'DATASCIENCE.JUPYTER_NEW_KERNELS_API_USAGE',
|
NewJupyterKernelsApiUsage = 'DATASCIENCE.JUPYTER_NEW_KERNELS_API_USAGE',
|
||||||
|
|
|
@ -296,8 +296,7 @@ export interface IAsyncDisposableRegistry extends IAsyncDisposable {
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum Experiments {
|
export enum Experiments {
|
||||||
DataViewerContribution = 'DataViewerContribution',
|
DataViewerContribution = 'DataViewerContribution'
|
||||||
KernelCompletions = 'KernelCompletions'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -17,7 +17,7 @@ import {
|
||||||
} from 'vscode';
|
} from 'vscode';
|
||||||
import { raceCancellation } from '../../platform/common/cancellation';
|
import { raceCancellation } from '../../platform/common/cancellation';
|
||||||
import { traceInfo, traceVerbose, traceWarning } from '../../platform/logging';
|
import { traceInfo, traceVerbose, traceWarning } from '../../platform/logging';
|
||||||
import { Experiments, IDisposable, IDisposableRegistry, IExperimentService } from '../../platform/common/types';
|
import { IDisposable, IDisposableRegistry, Resource } from '../../platform/common/types';
|
||||||
import { StopWatch } from '../../platform/common/utils/stopWatch';
|
import { StopWatch } from '../../platform/common/utils/stopWatch';
|
||||||
import { IKernelProvider, IKernel } from '../../kernels/types';
|
import { IKernelProvider, IKernel } from '../../kernels/types';
|
||||||
import { INotebookEditorProvider } from '../../notebooks/types';
|
import { INotebookEditorProvider } from '../../notebooks/types';
|
||||||
|
@ -29,13 +29,34 @@ import { IExtensionSyncActivationService } from '../../platform/activation/types
|
||||||
import { ServiceContainer } from '../../platform/ioc/container';
|
import { ServiceContainer } from '../../platform/ioc/container';
|
||||||
import { DisposableBase } from '../../platform/common/utils/lifecycle';
|
import { DisposableBase } from '../../platform/common/utils/lifecycle';
|
||||||
import { generateSortString } from './pythonKernelCompletionProvider';
|
import { generateSortString } from './pythonKernelCompletionProvider';
|
||||||
import { sleep } from '../../platform/common/utils/async';
|
import { raceTimeout, sleep } from '../../platform/common/utils/async';
|
||||||
import { sendTelemetryEvent } from '../../telemetry';
|
import { sendTelemetryEvent } from '../../telemetry';
|
||||||
import { getTelemetrySafeHashedString } from '../../platform/telemetry/helpers';
|
import { getTelemetrySafeHashedString } from '../../platform/telemetry/helpers';
|
||||||
import { getDisplayNameOrNameOfKernelConnection } from '../../kernels/helpers';
|
import { getDisplayNameOrNameOfKernelConnection } from '../../kernels/helpers';
|
||||||
|
import type { KernelMessage } from '@jupyterlab/services';
|
||||||
|
import { stripAnsi } from '../../platform/common/utils/regexp';
|
||||||
|
|
||||||
|
// Not all kernels support requestInspect method.
|
||||||
|
// E.g. deno does not support this, hence waiting for this to complete is poinless.
|
||||||
|
// As that results in a `loading...` method to appear against the completion item.
|
||||||
|
// If we have n consecutive attempts where the response never comes back in 1s,
|
||||||
|
// then we'll always ignore `requestInspect` method for this kernel.
|
||||||
|
const MAX_ATTEMPTS_BEFORE_IGNORING_RESOLVE_COMPLETION = 5;
|
||||||
|
const MAX_TIMEOUT_WAITING_FOR_RESOLVE_COMPLETION = 1_000;
|
||||||
|
|
||||||
|
const kernelIdsThatToNotSupportCompletionResolve = new Set<string>();
|
||||||
|
|
||||||
class NotebookCellSpecificKernelCompletionProvider implements CompletionItemProvider {
|
class NotebookCellSpecificKernelCompletionProvider implements CompletionItemProvider {
|
||||||
|
private totalNumberOfTimeoutsWaitingForResolveCompletion = 0;
|
||||||
constructor(private readonly kernel: IKernel) {}
|
constructor(private readonly kernel: IKernel) {}
|
||||||
|
public get canResolveCompletionItem() {
|
||||||
|
return this.totalNumberOfTimeoutsWaitingForResolveCompletion < MAX_ATTEMPTS_BEFORE_IGNORING_RESOLVE_COMPLETION;
|
||||||
|
}
|
||||||
|
|
||||||
|
private previousCompletionItems = new WeakMap<
|
||||||
|
CompletionItem,
|
||||||
|
{ code: string; cursor: { start: number; end: number } }
|
||||||
|
>();
|
||||||
async provideCompletionItems(
|
async provideCompletionItems(
|
||||||
document: TextDocument,
|
document: TextDocument,
|
||||||
position: Position,
|
position: Position,
|
||||||
|
@ -65,11 +86,13 @@ class NotebookCellSpecificKernelCompletionProvider implements CompletionItemProv
|
||||||
if (!this.kernel.session?.kernel) {
|
if (!this.kernel.session?.kernel) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
const code = document.getText();
|
||||||
|
const cursor_pos = document.offsetAt(position);
|
||||||
const kernelCompletions = await raceCancellation(
|
const kernelCompletions = await raceCancellation(
|
||||||
token,
|
token,
|
||||||
this.kernel.session.kernel.requestComplete({
|
this.kernel.session.kernel.requestComplete({
|
||||||
code: document.getText(),
|
code,
|
||||||
cursor_pos: document.offsetAt(position)
|
cursor_pos
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
traceVerbose(`Jupyter completion time: ${stopWatch.elapsedTime}`);
|
traceVerbose(`Jupyter completion time: ${stopWatch.elapsedTime}`);
|
||||||
|
@ -97,6 +120,7 @@ class NotebookCellSpecificKernelCompletionProvider implements CompletionItemProv
|
||||||
// Check if we have more information about the completion items & whether its valid.
|
// Check if we have more information about the completion items & whether its valid.
|
||||||
// This will ensure that we don't regress (as long as all items are valid & we have the same number of completions items
|
// This will ensure that we don't regress (as long as all items are valid & we have the same number of completions items
|
||||||
// then we should be able to use the experiment matches value)
|
// then we should be able to use the experiment matches value)
|
||||||
|
const dataToStore = { code, cursor: result.cursor };
|
||||||
if (
|
if (
|
||||||
Array.isArray(experimentMatches) &&
|
Array.isArray(experimentMatches) &&
|
||||||
experimentMatches.length >= result.matches.length &&
|
experimentMatches.length >= result.matches.length &&
|
||||||
|
@ -108,27 +132,84 @@ class NotebookCellSpecificKernelCompletionProvider implements CompletionItemProv
|
||||||
typeof item.text === 'string'
|
typeof item.text === 'string'
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
return experimentMatches.map((item, index) => {
|
return kernelCompletions.content.matches.map((label, index) => {
|
||||||
return {
|
const item = experimentMatches[index];
|
||||||
label: item.text,
|
const type = item.type ? mapJupyterKind.get(item.type) : CompletionItemKind.Field;
|
||||||
range: new Range(document.positionAt(item.start), document.positionAt(item.end)),
|
|
||||||
kind: item.type ? mapJupyterKind.get(item.type) : CompletionItemKind.Field,
|
const completionItem = new CompletionItem(label, type);
|
||||||
sortText: generateSortString(index)
|
|
||||||
};
|
completionItem.range = new Range(document.positionAt(item.start), document.positionAt(item.end));
|
||||||
|
completionItem.insertText = item.text;
|
||||||
|
completionItem.sortText = generateSortString(index);
|
||||||
|
this.previousCompletionItems.set(completionItem, dataToStore);
|
||||||
|
return completionItem;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return result.matches.map((item, index) => {
|
return result.matches.map((label, index) => {
|
||||||
return {
|
const completionItem = new CompletionItem(label);
|
||||||
label: item,
|
|
||||||
sortText: generateSortString(index)
|
completionItem.range = new Range(
|
||||||
};
|
document.positionAt(result.cursor.start),
|
||||||
|
document.positionAt(result.cursor.end)
|
||||||
|
);
|
||||||
|
completionItem.sortText = generateSortString(index);
|
||||||
|
this.previousCompletionItems.set(completionItem, dataToStore);
|
||||||
|
return completionItem;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Kernel provider will use the inspect request to lazy-load the content
|
||||||
|
* for document panel.
|
||||||
|
*/
|
||||||
|
async resolveCompletionItem(item: CompletionItem, token: CancellationToken): Promise<CompletionItem> {
|
||||||
|
if (!item.range || !this.kernel.session?.kernel) {
|
||||||
|
// We always set a range in the completion item we send.
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
const info = this.previousCompletionItems.get(item);
|
||||||
|
if (!info) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
const { code, cursor } = info;
|
||||||
|
const newCode = code.substring(0, cursor.start) + (item.insertText || item.label);
|
||||||
|
const cursor_pos =
|
||||||
|
cursor.start +
|
||||||
|
(
|
||||||
|
(typeof item.insertText === 'string' ? item.insertText : item.insertText?.value) ||
|
||||||
|
(typeof item.label === 'string' ? item.label : item.label.label) ||
|
||||||
|
''
|
||||||
|
).length;
|
||||||
|
const contents: KernelMessage.IInspectRequestMsg['content'] = {
|
||||||
|
code: newCode,
|
||||||
|
cursor_pos,
|
||||||
|
detail_level: 0
|
||||||
|
};
|
||||||
|
const stopWatch = new StopWatch();
|
||||||
|
const msg = await raceTimeout(
|
||||||
|
MAX_TIMEOUT_WAITING_FOR_RESOLVE_COMPLETION,
|
||||||
|
raceCancellation(token, this.kernel.session.kernel.requestInspect(contents))
|
||||||
|
);
|
||||||
|
if (token.isCancellationRequested) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
if (!msg || msg.content.status !== 'ok' || !msg.content.found) {
|
||||||
|
if (stopWatch.elapsedTime > MAX_TIMEOUT_WAITING_FOR_RESOLVE_COMPLETION) {
|
||||||
|
this.totalNumberOfTimeoutsWaitingForResolveCompletion += 1;
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
item.documentation = stripAnsi(msg.content.data['text/plain'] as string);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class KernelSpecificCompletionProvider extends DisposableBase implements CompletionItemProvider {
|
class KernelSpecificCompletionProvider extends DisposableBase implements CompletionItemProvider {
|
||||||
private cellCompletionProviders = new WeakMap<TextDocument, NotebookCellSpecificKernelCompletionProvider>();
|
private cellCompletionProviders = new WeakMap<TextDocument, NotebookCellSpecificKernelCompletionProvider>();
|
||||||
|
private completionItemsSent = new WeakMap<
|
||||||
|
CompletionItem,
|
||||||
|
{ duration: number; provider: NotebookCellSpecificKernelCompletionProvider }
|
||||||
|
>();
|
||||||
private readonly monacoLanguage: string;
|
private readonly monacoLanguage: string;
|
||||||
private readonly kernelLanguage: string;
|
private readonly kernelLanguage: string;
|
||||||
private completionProvider?: IDisposable;
|
private completionProvider?: IDisposable;
|
||||||
|
@ -143,6 +224,12 @@ class KernelSpecificCompletionProvider extends DisposableBase implements Complet
|
||||||
this.registerCompletionProvider();
|
this.registerCompletionProvider();
|
||||||
this._register(
|
this._register(
|
||||||
workspace.onDidChangeConfiguration((e) => {
|
workspace.onDidChangeConfiguration((e) => {
|
||||||
|
if (e.affectsConfiguration('jupyter.enableKernelCompletions')) {
|
||||||
|
if (!isKernelCompletionEnabled(this.kernel.notebook.uri)) {
|
||||||
|
this.completionProvider?.dispose();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!e.affectsConfiguration('jupyter.completionTriggerCharacters')) {
|
if (!e.affectsConfiguration('jupyter.completionTriggerCharacters')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -152,6 +239,10 @@ class KernelSpecificCompletionProvider extends DisposableBase implements Complet
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
private registerCompletionProvider() {
|
private registerCompletionProvider() {
|
||||||
|
if (!isKernelCompletionEnabled(this.kernel.notebook.uri)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const triggerCharacters = this.getCompletionTriggerCharacter();
|
const triggerCharacters = this.getCompletionTriggerCharacter();
|
||||||
if (triggerCharacters.length === 0) {
|
if (triggerCharacters.length === 0) {
|
||||||
if (this.kernelLanguage.toLowerCase() === this.monacoLanguage.toLowerCase()) {
|
if (this.kernelLanguage.toLowerCase() === this.monacoLanguage.toLowerCase()) {
|
||||||
|
@ -228,15 +319,50 @@ class KernelSpecificCompletionProvider extends DisposableBase implements Complet
|
||||||
this.cellCompletionProviders.set(document, provider);
|
this.cellCompletionProviders.set(document, provider);
|
||||||
}
|
}
|
||||||
const stopWatch = new StopWatch();
|
const stopWatch = new StopWatch();
|
||||||
return provider.provideCompletionItems(document, position, token, _context).finally(() => {
|
const items = await provider.provideCompletionItems(document, position, token, _context);
|
||||||
|
const duration = stopWatch.elapsedTime;
|
||||||
|
sendTelemetryEvent(
|
||||||
|
Telemetry.KernelCodeCompletion,
|
||||||
|
{ duration, resolveDuration: 0 },
|
||||||
|
{
|
||||||
|
kernelId: this.kernelId,
|
||||||
|
kernelConnectionType: this.kernel.kernelConnectionMetadata.kind,
|
||||||
|
kernelLanguage: this.monacoLanguage,
|
||||||
|
cancelled: token.isCancellationRequested
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const data = { duration, provider };
|
||||||
|
items.forEach((item) => this.completionItemsSent.set(item, data));
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
async resolveCompletionItem(item: CompletionItem, token: CancellationToken): Promise<CompletionItem> {
|
||||||
|
const info = this.completionItemsSent.get(item);
|
||||||
|
if (!info || kernelIdsThatToNotSupportCompletionResolve.has(this.kernelId)) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
const { duration, provider } = info;
|
||||||
|
if (!provider.canResolveCompletionItem) {
|
||||||
|
// Never send the telemetry again and do not try in this session.
|
||||||
|
kernelIdsThatToNotSupportCompletionResolve.add(this.kernelId);
|
||||||
|
sendTelemetryEvent(Telemetry.KernelCodeCompletionCannotResolve, undefined, {
|
||||||
|
kernelId: this.kernelId,
|
||||||
|
kernelConnectionType: this.kernel.kernelConnectionMetadata.kind,
|
||||||
|
kernelLanguage: this.monacoLanguage
|
||||||
|
});
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
const stopWatch = new StopWatch();
|
||||||
|
return provider.resolveCompletionItem(item, token).finally(() => {
|
||||||
sendTelemetryEvent(
|
sendTelemetryEvent(
|
||||||
Telemetry.KernelCodeCompletion,
|
Telemetry.KernelCodeCompletion,
|
||||||
{ duration: stopWatch.elapsedTime },
|
{ duration, resolveDuration: stopWatch.elapsedTime },
|
||||||
{
|
{
|
||||||
kernelId: this.kernelId,
|
kernelId: this.kernelId,
|
||||||
kernelConnectionType: this.kernel.kernelConnectionMetadata.kind,
|
kernelConnectionType: this.kernel.kernelConnectionMetadata.kind,
|
||||||
kernelLanguage: this.monacoLanguage,
|
kernelLanguage: this.monacoLanguage,
|
||||||
cancelled: token.isCancellationRequested
|
cancelled: token.isCancellationRequested,
|
||||||
|
resolved: true
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -255,12 +381,11 @@ export class NonPythonKernelCompletionProvider extends DisposableBase implements
|
||||||
}
|
}
|
||||||
public activate(): void {
|
public activate(): void {
|
||||||
const kernelProvider = ServiceContainer.instance.get<IKernelProvider>(IKernelProvider);
|
const kernelProvider = ServiceContainer.instance.get<IKernelProvider>(IKernelProvider);
|
||||||
const experimentService = ServiceContainer.instance.get<IExperimentService>(IExperimentService);
|
|
||||||
if (!experimentService.inExperiment(Experiments.KernelCompletions)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._register(
|
this._register(
|
||||||
kernelProvider.onDidStartKernel(async (e) => {
|
kernelProvider.onDidStartKernel(async (e) => {
|
||||||
|
if (!isKernelCompletionEnabled(e.notebook.uri)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const kernelId = await getTelemetrySafeHashedString(e.kernelConnectionMetadata.id);
|
const kernelId = await getTelemetrySafeHashedString(e.kernelConnectionMetadata.id);
|
||||||
const language = getKernelLanguageAsMonacoLanguage(e);
|
const language = getKernelLanguageAsMonacoLanguage(e);
|
||||||
if (!language || language.toLowerCase() === PYTHON_LANGUAGE.toLowerCase()) {
|
if (!language || language.toLowerCase() === PYTHON_LANGUAGE.toLowerCase()) {
|
||||||
|
@ -304,9 +429,10 @@ function getKernelLanguage(kernel: IKernel) {
|
||||||
kernelSpecLanguage = kernel.kernelConnectionMetadata.kernelSpec.language;
|
kernelSpecLanguage = kernel.kernelConnectionMetadata.kernelSpec.language;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!kernelSpecLanguage || (kernelSpecLanguage || '').toLowerCase() === PYTHON_LANGUAGE.toLowerCase()) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
return kernelSpecLanguage.toLowerCase();
|
return (kernelSpecLanguage || '').toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
function isKernelCompletionEnabled(resource: Resource) {
|
||||||
|
return workspace.getConfiguration('jupyter', resource).get<boolean>('enableKernelCompletions', false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3254,13 +3254,26 @@ export class IEventNamePropertyMapping {
|
||||||
* Whether the completion request was cancelled or not.
|
* Whether the completion request was cancelled or not.
|
||||||
*/
|
*/
|
||||||
cancelled: boolean;
|
cancelled: boolean;
|
||||||
|
/**
|
||||||
|
* Time taken to resolve the documentation.
|
||||||
|
*/
|
||||||
|
resolveDuration: number;
|
||||||
|
/**
|
||||||
|
* Whether we resolved the documentation or not.
|
||||||
|
*/
|
||||||
|
resolved?: boolean;
|
||||||
}
|
}
|
||||||
> = {
|
> = {
|
||||||
owner: 'donjayamanne',
|
owner: 'donjayamanne',
|
||||||
feature: 'N/A',
|
feature: 'N/A',
|
||||||
source: 'N/A',
|
source: 'N/A',
|
||||||
measures: {
|
measures: {
|
||||||
...commonClassificationForDurationProperties()
|
...commonClassificationForDurationProperties(),
|
||||||
|
resolveDuration: {
|
||||||
|
classification: 'SystemMetaData',
|
||||||
|
purpose: 'PerformanceAndHealth',
|
||||||
|
isMeasurement: true
|
||||||
|
}
|
||||||
},
|
},
|
||||||
properties: {
|
properties: {
|
||||||
kernelConnectionType: {
|
kernelConnectionType: {
|
||||||
|
@ -3278,6 +3291,51 @@ export class IEventNamePropertyMapping {
|
||||||
cancelled: {
|
cancelled: {
|
||||||
classification: 'PublicNonPersonalData',
|
classification: 'PublicNonPersonalData',
|
||||||
purpose: 'FeatureInsight'
|
purpose: 'FeatureInsight'
|
||||||
|
},
|
||||||
|
resolved: {
|
||||||
|
classification: 'PublicNonPersonalData',
|
||||||
|
purpose: 'FeatureInsight'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Telemetry sent when we the kernel does not reply back with a response for requestInspect message.
|
||||||
|
* The requestInspect request is used to resolve completion items in auto complete lists.
|
||||||
|
*/
|
||||||
|
[Telemetry.KernelCodeCompletionCannotResolve]: TelemetryEventInfo<{
|
||||||
|
/**
|
||||||
|
* Hash of the Kernel Connection id.
|
||||||
|
*/
|
||||||
|
kernelId: string;
|
||||||
|
/**
|
||||||
|
* What kind of kernel spec did we fail to create.
|
||||||
|
*/
|
||||||
|
kernelConnectionType:
|
||||||
|
| 'startUsingPythonInterpreter'
|
||||||
|
| 'startUsingLocalKernelSpec'
|
||||||
|
| 'startUsingRemoteKernelSpec'
|
||||||
|
| 'connectToLiveRemoteKernel';
|
||||||
|
/**
|
||||||
|
* Language of the kernel spec.
|
||||||
|
*/
|
||||||
|
kernelLanguage: string | undefined;
|
||||||
|
}> = {
|
||||||
|
owner: 'donjayamanne',
|
||||||
|
feature: 'N/A',
|
||||||
|
source: 'N/A',
|
||||||
|
properties: {
|
||||||
|
kernelConnectionType: {
|
||||||
|
classification: 'PublicNonPersonalData',
|
||||||
|
purpose: 'FeatureInsight'
|
||||||
|
},
|
||||||
|
kernelLanguage: {
|
||||||
|
classification: 'PublicNonPersonalData',
|
||||||
|
purpose: 'FeatureInsight'
|
||||||
|
},
|
||||||
|
kernelId: {
|
||||||
|
classification: 'PublicNonPersonalData',
|
||||||
|
purpose: 'FeatureInsight'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
"lib": ["es6", "es2018", "ES2019", "ES2020"],
|
"lib": ["es6", "es2018", "ES2019", "ES2020", "ES2021", "ES2022"],
|
||||||
"types": []
|
"types": []
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
"outDir": "out",
|
"outDir": "out",
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
"lib": ["es6", "es2018", "dom", "ES2019", "ES2020"],
|
"lib": ["es6", "es2018", "dom", "ES2019", "ES2020", "ES2021", "ES2022"],
|
||||||
"paths": {
|
"paths": {
|
||||||
"*": ["types/*"]
|
"*": ["types/*"]
|
||||||
},
|
},
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
"outDir": "out",
|
"outDir": "out",
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
"lib": ["es6", "es2018", "dom", "ES2019", "ES2020"],
|
"lib": ["es6", "es2018", "dom", "ES2019", "ES2020", "ES2021", "ES2022"],
|
||||||
"paths": {
|
"paths": {
|
||||||
"*": ["types/*"]
|
"*": ["types/*"]
|
||||||
},
|
},
|
||||||
|
|
Загрузка…
Ссылка в новой задаче