Skip to content

Commit 6eab2fe

Browse files
author
David Kutugata
authored
Davidkutu/port intellisense fix (#10531)
* Jupyter autocompletion will only show up on empty lines, instead of appearing in functions. * filter out magic commands instead of ignoring all the jupyter intellisense. * moved the new code to a function and created tests * removed pressCtrlSpace function * added comments * update changelog and delete news file
1 parent 8e2603a commit 6eab2fe

File tree

4 files changed

+91
-8
lines changed

4 files changed

+91
-8
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525

2626
### Fixes
2727

28+
1. Jupyter autocompletion will only show magic commands on empty lines, preventing them of appearing in functions.
29+
([#10023](https://github.com/Microsoft/vscode-python/issues/10023))
2830
1. Remove extra lines at the end of the file when formatting with Black.
2931
([#1877](https://github.com/Microsoft/vscode-python/issues/1877))
3032
1. Capitalize `Activate.ps1` in code for PowerShell Core on Linux.

src/client/datascience/interactive-common/intellisense/intellisenseProvider.ts

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ import {
3636
IInteractiveWindowListener,
3737
IInteractiveWindowProvider,
3838
IJupyterExecution,
39-
INotebook
39+
INotebook,
40+
INotebookCompletion
4041
} from '../../types';
4142
import {
4243
ICancelIntellisenseRequest,
@@ -377,6 +378,7 @@ export class IntellisenseProvider implements IInteractiveWindowListener {
377378
request.cellId,
378379
cancelSource.token
379380
);
381+
380382
const jupyterCompletions = this.provideJupyterCompletionItems(
381383
request.position,
382384
request.context,
@@ -469,6 +471,8 @@ export class IntellisenseProvider implements IInteractiveWindowListener {
469471

470472
const jupyterResults = await activeNotebook.getCompletion(data.text, offsetInCode, cancelToken);
471473
if (jupyterResults && jupyterResults.matches) {
474+
const filteredMatches = this.filterJupyterMatches(document, jupyterResults, cellId, position);
475+
472476
const baseOffset = data.offset;
473477
const basePosition = document.positionAt(baseOffset);
474478
const startPosition = document.positionAt(jupyterResults.cursor.start + baseOffset);
@@ -480,11 +484,7 @@ export class IntellisenseProvider implements IInteractiveWindowListener {
480484
endColumn: endPosition.character + 1
481485
};
482486
return {
483-
suggestions: convertStringsToSuggestions(
484-
jupyterResults.matches,
485-
range,
486-
jupyterResults.metadata
487-
),
487+
suggestions: convertStringsToSuggestions(filteredMatches, range, jupyterResults.metadata),
488488
incomplete: false
489489
};
490490
}
@@ -502,6 +502,23 @@ export class IntellisenseProvider implements IInteractiveWindowListener {
502502
};
503503
}
504504

505+
// The suggestions that the kernel is giving always include magic commands. That is confusing to the user.
506+
// This function is called by provideJupyterCompletionItems to filter those magic commands when not in an empty line of code.
507+
private filterJupyterMatches(
508+
document: IntellisenseDocument,
509+
jupyterResults: INotebookCompletion,
510+
cellId: string,
511+
position: monacoEditor.Position
512+
) {
513+
// If the line we're analyzing is empty or a whitespace, we filter out the magic commands
514+
// as its confusing to see them appear after a . or inside ().
515+
const pos = document.convertToDocumentPosition(cellId, position.lineNumber, position.column);
516+
const line = document.lineAt(pos);
517+
return line.isEmptyOrWhitespace
518+
? jupyterResults.matches
519+
: jupyterResults.matches.filter(match => !match.startsWith('%'));
520+
}
521+
505522
private postTimedResponse<R, M extends IInteractiveWindowMapping, T extends keyof M>(
506523
promises: Promise<R>[],
507524
message: T,

src/test/datascience/intellisense.functional.test.tsx

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { MonacoEditor } from '../../datascience-ui/react-common/monacoEditor';
1212
import { noop } from '../core';
1313
import { DataScienceIocContainer } from './dataScienceIocContainer';
1414
import { getOrCreateInteractiveWindow, runMountedTest } from './interactiveWindowTestHelpers';
15-
import { getInteractiveEditor, typeCode } from './testHelpers';
15+
import { enterEditorKey, getInteractiveEditor, typeCode } from './testHelpers';
1616

1717
// tslint:disable:max-func-body-length trailing-comma no-any no-multiline-string
1818
suite('DataScience Intellisense tests', () => {
@@ -69,6 +69,14 @@ suite('DataScience Intellisense tests', () => {
6969
assert.ok(innerTexts.includes(expectedSpan), 'Intellisense row not matching');
7070
}
7171

72+
function verifyIntellisenseNotVisible(
73+
wrapper: ReactWrapper<any, Readonly<{}>, React.Component>,
74+
expectedSpan: string
75+
) {
76+
const innerTexts = getIntellisenseTextLines(wrapper);
77+
assert.ok(!innerTexts.includes(expectedSpan), 'Intellisense row is showing');
78+
}
79+
7280
function waitForSuggestion(
7381
wrapper: ReactWrapper<any, Readonly<{}>, React.Component>
7482
): { disposable: IDisposable; promise: Promise<void> } {
@@ -230,4 +238,60 @@ suite('DataScience Intellisense tests', () => {
230238
return ioc;
231239
}
232240
);
241+
242+
runMountedTest(
243+
'Filtered Jupyter autocomplete, verify magic commands appear',
244+
async wrapper => {
245+
if (ioc.mockJupyter) {
246+
// This test only works when mocking.
247+
248+
// Create an interactive window so that it listens to the results.
249+
const interactiveWindow = await getOrCreateInteractiveWindow(ioc);
250+
await interactiveWindow.show();
251+
252+
// Then enter some code. Don't submit, we're just testing that autocomplete appears
253+
const suggestion = waitForSuggestion(wrapper);
254+
typeCode(getInteractiveEditor(wrapper), 'print');
255+
enterEditorKey(wrapper, { code: ' ', ctrlKey: true });
256+
await suggestion.promise;
257+
suggestion.disposable.dispose();
258+
verifyIntellisenseNotVisible(wrapper, '%%bash');
259+
260+
// Force suggestion box to disappear so that shutdown doesn't try to generate suggestions
261+
// while we're destroying the editor.
262+
clearEditor(wrapper);
263+
}
264+
},
265+
() => {
266+
return ioc;
267+
}
268+
);
269+
270+
runMountedTest(
271+
'Filtered Jupyter autocomplete, verify magic commands are filtered',
272+
async wrapper => {
273+
if (ioc.mockJupyter) {
274+
// This test only works when mocking.
275+
276+
// Create an interactive window so that it listens to the results.
277+
const interactiveWindow = await getOrCreateInteractiveWindow(ioc);
278+
await interactiveWindow.show();
279+
280+
// Then enter some code. Don't submit, we're just testing that autocomplete appears
281+
const suggestion = waitForSuggestion(wrapper);
282+
typeCode(getInteractiveEditor(wrapper), ' ');
283+
enterEditorKey(wrapper, { code: ' ', ctrlKey: true });
284+
await suggestion.promise;
285+
suggestion.disposable.dispose();
286+
verifyIntellisenseVisible(wrapper, '%%bash');
287+
288+
// Force suggestion box to disappear so that shutdown doesn't try to generate suggestions
289+
// while we're destroying the editor.
290+
clearEditor(wrapper);
291+
}
292+
},
293+
() => {
294+
return ioc;
295+
}
296+
);
233297
});

src/test/datascience/mockJupyterSession.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ export class MockJupyterSession implements IJupyterSession {
153153

154154
return {
155155
content: {
156-
matches: ['printly'], // This keeps this in the intellisense when the editor pairs down results
156+
matches: ['printly', '%%bash'], // This keeps this in the intellisense when the editor pairs down results
157157
cursor_start: 0,
158158
cursor_end: 7,
159159
status: 'ok',

0 commit comments

Comments
 (0)