Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions docs/customize/deep-dives/autocomplete.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,16 @@ In VS Code, if you don't want to be shown suggestions automatically you can:
4. Press the key combination you want to use to trigger suggestions (e.g. <kbd>cmd/ctrl</kbd> + <kbd>space</kbd>)
5. Now whenever you want to see a suggestion, you can press your key binding (e.g. <kbd>cmd/ctrl</kbd> + <kbd>space</kbd>) to trigger suggestions manually

The setting is evaluated for the active document, so you can also configure it for specific languages with VS Code's language-scoped settings. For example:

```json
"[python]": {
"editor.inlineSuggest.enabled": false
}
```

Continue honors the manual trigger while automatic inline suggestions are disabled.

### Shortcut for Accepting One Line at a Time in Autocomplete

This is a built-in feature of VS Code, but it's just a bit hidden. Follow these settings to reassign the keyboard shortcuts in VS Code:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,140 @@ beforeEach(() => {
);

(vscode.window as any).activeTextEditor = null;
setInlineSuggestEnabled(true);
});

describe("ContinueCompletionProvider triggering logic", () => {
it("does not request automatic completions when inline suggestions are disabled", async () => {
const document = createDocument();
setActiveEditor(document);
setInlineSuggestEnabled(false);

const provider = buildProvider();

const result = await provider.provideInlineCompletionItems(
document,
createPosition(),
createContext(),
createToken(),
);

expect(result).toBeNull();
expect(vscode.workspace.getConfiguration).toHaveBeenCalledWith(
"editor.inlineSuggest",
document,
);
expect(mockNextEditProvider.startChain).not.toHaveBeenCalled();
expect(
mockNextEditProvider.provideInlineCompletionItems,
).not.toHaveBeenCalled();
});

it("keeps manual completions available when inline suggestions are disabled", async () => {
const document = createDocument();
setActiveEditor(document);
setInlineSuggestEnabled(false);

const provider = buildProvider();

const manualResult = await provider.provideInlineCompletionItems(
document,
createPosition(),
createContext(vscode.InlineCompletionTriggerKind.Invoke),
createToken(),
);

const automaticResult = await provider.provideInlineCompletionItems(
document,
createPosition(),
createContext(),
createToken(),
);

expect(manualResult).not.toBeNull();
expect(automaticResult).toBeNull();
expect(mockNextEditProvider.startChain).toHaveBeenCalledTimes(1);
expect(
mockNextEditProvider.provideInlineCompletionItems,
).toHaveBeenCalledTimes(1);
});

it("uses a language override when it disables inline suggestions", async () => {
const document = createDocument(undefined, "python");
setActiveEditor(document);
setInlineSuggestConfiguration(true, { python: false });

const provider = buildProvider();
const result = await provider.provideInlineCompletionItems(
document,
createPosition(),
createContext(),
createToken(),
);

expect(result).toBeNull();
expect(vscode.workspace.getConfiguration).toHaveBeenCalledWith(
"editor.inlineSuggest",
document,
);
expect(mockNextEditProvider.startChain).not.toHaveBeenCalled();
});

it("uses a language override when it enables inline suggestions", async () => {
const document = createDocument(undefined, "python");
setActiveEditor(document);
setInlineSuggestConfiguration(false, { python: true });

const provider = buildProvider();
const result = await provider.provideInlineCompletionItems(
document,
createPosition(),
createContext(),
createToken(),
);

expect(result).not.toBeNull();
expect(vscode.workspace.getConfiguration).toHaveBeenCalledWith(
"editor.inlineSuggest",
document,
);
expect(mockNextEditProvider.startChain).toHaveBeenCalledTimes(1);
});

it("passes trigger kind to regular autocomplete and allows enabled automatic requests", async () => {
const document = createDocument();
setActiveEditor(document);
setInlineSuggestEnabled(true);

const provider = buildProvider({ activateNextEdit: false });
const completionProvider = (provider as any).completionProvider;
completionProvider.provideInlineCompletionItems.mockResolvedValue(
mockOutcome,
);

const manualResult = await provider.provideInlineCompletionItems(
document,
createPosition(),
createContext(vscode.InlineCompletionTriggerKind.Invoke),
createToken(),
);
const automaticResult = await provider.provideInlineCompletionItems(
document,
createPosition(),
createContext(),
createToken(),
);

expect(manualResult).not.toBeNull();
expect(automaticResult).not.toBeNull();
expect(
completionProvider.provideInlineCompletionItems,
).toHaveBeenNthCalledWith(1, expect.anything(), expect.anything(), true);
expect(
completionProvider.provideInlineCompletionItems,
).toHaveBeenNthCalledWith(2, expect.anything(), expect.anything(), false);
});

it("starts a new chain when none exists", async () => {
const document = createDocument();
setActiveEditor(document);
Expand Down Expand Up @@ -158,7 +289,9 @@ describe("ContinueCompletionProvider triggering logic", () => {
});
});

function buildProvider(options: { usingFullFileDiff?: boolean } = {}) {
function buildProvider(
options: { usingFullFileDiff?: boolean; activateNextEdit?: boolean } = {},
) {
const usingFullFileDiff = options.usingFullFileDiff ?? true;
const configHandler = {
loadConfig: vi.fn(async () => ({
Expand All @@ -175,16 +308,20 @@ function buildProvider(options: { usingFullFileDiff?: boolean } = {}) {
webviewProtocol,
usingFullFileDiff,
);
provider.activateNextEdit();
if (options.activateNextEdit ?? true) {
provider.activateNextEdit();
}
return provider;
}

function createDocument(
text = "function example() {\n return true;\n}",
languageId = "typescript",
): vscode.TextDocument {
const lines = text.split("\n");
return {
uri: vscode.Uri.parse("file:///test"),
languageId,
isUntitled: false,
getText: (range?: any) => {
if (!range) {
Expand Down Expand Up @@ -220,9 +357,11 @@ function createDocument(
} as unknown as vscode.TextDocument;
}

function createContext(): any {
function createContext(
triggerKind = vscode.InlineCompletionTriggerKind.Automatic,
): any {
return {
triggerKind: (vscode.InlineCompletionTriggerKind as any).Automatic,
triggerKind,
selectedCompletionInfo: undefined,
};
}
Expand All @@ -238,6 +377,21 @@ function createToken(): any {
};
}

function setInlineSuggestEnabled(enabled: boolean) {
setInlineSuggestConfiguration(enabled);
}

function setInlineSuggestConfiguration(
globalEnabled: boolean,
languageOverrides: Record<string, boolean> = {},
) {
(vscode.workspace.getConfiguration as any).mockImplementation(
(_section: string, scope: any) => ({
get: vi.fn(() => languageOverrides[scope?.languageId] ?? globalEnabled),
}),
);
}

function setActiveEditor(document: any, cursor = createPosition()) {
const selection = { active: cursor, anchor: cursor };
(vscode.window as any).activeTextEditor = {
Expand Down
14 changes: 14 additions & 0 deletions extensions/vscode/src/autocomplete/completionProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,20 @@ export class ContinueCompletionProvider
return null;
}

// VS Code still asks providers for automatic completions after an explicit
// invocation, even when inline suggestions are disabled. Only honor those
// explicit invocations in that mode. Passing the document as the scope
// ensures that language-scoped editor settings are respected.
const inlineSuggestEnabled = vscode.workspace
.getConfiguration("editor.inlineSuggest", document)
.get<boolean>("enabled", true);
if (
inlineSuggestEnabled === false &&
context.triggerKind === vscode.InlineCompletionTriggerKind.Automatic
) {
return null;
}

if (document.uri.scheme === "vscode-scm") {
return null;
}
Expand Down
Loading