Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/contrib/inlineCompletions/browser/controller/commands.ts
5283 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { KeyCode, KeyMod } from '../../../../../base/common/keyCodes.js';
7
import { asyncTransaction, transaction } from '../../../../../base/common/observable.js';
8
import { splitLines } from '../../../../../base/common/strings.js';
9
import { vBoolean, vObj, vOptionalProp, vString, vUnchecked, vUndefined, vUnion, vWithJsonSchemaRef } from '../../../../../base/common/validation.js';
10
import * as nls from '../../../../../nls.js';
11
import { CONTEXT_ACCESSIBILITY_MODE_ENABLED } from '../../../../../platform/accessibility/common/accessibility.js';
12
import { Action2, MenuId } from '../../../../../platform/actions/common/actions.js';
13
import { IClipboardService } from '../../../../../platform/clipboard/common/clipboardService.js';
14
import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js';
15
import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js';
16
import { KeybindingsRegistry, KeybindingWeight } from '../../../../../platform/keybinding/common/keybindingsRegistry.js';
17
import { INotificationService, Severity } from '../../../../../platform/notification/common/notification.js';
18
import { ICodeEditor } from '../../../../browser/editorBrowser.js';
19
import { EditorAction, ServicesAccessor } from '../../../../browser/editorExtensions.js';
20
import { EditorContextKeys } from '../../../../common/editorContextKeys.js';
21
import { InlineCompletionsProvider } from '../../../../common/languages.js';
22
import { ILanguageFeaturesService } from '../../../../common/services/languageFeatures.js';
23
import { Context as SuggestContext } from '../../../suggest/browser/suggest.js';
24
import { hideInlineCompletionId, inlineSuggestCommitAlternativeActionId, inlineSuggestCommitId, jumpToNextInlineEditId, showNextInlineSuggestionActionId, showPreviousInlineSuggestionActionId, toggleShowCollapsedId } from './commandIds.js';
25
import { InlineCompletionContextKeys } from './inlineCompletionContextKeys.js';
26
import { InlineCompletionsController } from './inlineCompletionsController.js';
27
28
export class ShowNextInlineSuggestionAction extends EditorAction {
29
public static ID = showNextInlineSuggestionActionId;
30
constructor() {
31
super({
32
id: ShowNextInlineSuggestionAction.ID,
33
label: nls.localize2('action.inlineSuggest.showNext', "Show Next Inline Suggestion"),
34
precondition: ContextKeyExpr.and(EditorContextKeys.writable, InlineCompletionContextKeys.inlineSuggestionVisible),
35
kbOpts: {
36
weight: 100,
37
primary: KeyMod.Alt | KeyCode.BracketRight,
38
},
39
});
40
}
41
42
public async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
43
const controller = InlineCompletionsController.get(editor);
44
controller?.model.get()?.next();
45
}
46
}
47
48
export class ShowPreviousInlineSuggestionAction extends EditorAction {
49
public static ID = showPreviousInlineSuggestionActionId;
50
constructor() {
51
super({
52
id: ShowPreviousInlineSuggestionAction.ID,
53
label: nls.localize2('action.inlineSuggest.showPrevious', "Show Previous Inline Suggestion"),
54
precondition: ContextKeyExpr.and(EditorContextKeys.writable, InlineCompletionContextKeys.inlineSuggestionVisible),
55
kbOpts: {
56
weight: 100,
57
primary: KeyMod.Alt | KeyCode.BracketLeft,
58
},
59
});
60
}
61
62
public async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
63
const controller = InlineCompletionsController.get(editor);
64
controller?.model.get()?.previous();
65
}
66
}
67
68
export const providerIdSchemaUri = 'vscode://schemas/inlineCompletionProviderIdArgs';
69
70
export function inlineCompletionProviderGetMatcher(provider: InlineCompletionsProvider): string[] {
71
const result: string[] = [];
72
if (provider.providerId) {
73
result.push(provider.providerId.toStringWithoutVersion());
74
result.push(provider.providerId.extensionId + ':*');
75
}
76
return result;
77
}
78
79
const argsValidator = vUnion(vObj({
80
showNoResultNotification: vOptionalProp(vBoolean()),
81
providerId: vOptionalProp(vWithJsonSchemaRef(providerIdSchemaUri, vString())),
82
explicit: vOptionalProp(vBoolean()),
83
changeHintData: vOptionalProp(vUnchecked()),
84
}), vUndefined());
85
86
export class TriggerInlineSuggestionAction extends EditorAction {
87
constructor() {
88
super({
89
id: 'editor.action.inlineSuggest.trigger',
90
label: nls.localize2('action.inlineSuggest.trigger', "Trigger Inline Suggestion"),
91
precondition: EditorContextKeys.writable,
92
metadata: {
93
description: nls.localize('inlineSuggest.trigger.description', "Triggers an inline suggestion in the editor."),
94
args: [{
95
name: 'args',
96
description: nls.localize('inlineSuggest.trigger.args', "Options for triggering inline suggestions."),
97
isOptional: true,
98
schema: argsValidator.getJSONSchema(),
99
}]
100
}
101
});
102
}
103
104
public override async run(accessor: ServicesAccessor, editor: ICodeEditor, args: unknown): Promise<void> {
105
const notificationService = accessor.get(INotificationService);
106
const languageFeaturesService = accessor.get(ILanguageFeaturesService);
107
108
const controller = InlineCompletionsController.get(editor);
109
110
const validatedArgs = argsValidator.validateOrThrow(args);
111
112
const provider = validatedArgs?.providerId ?
113
languageFeaturesService.inlineCompletionsProvider.all(editor.getModel()!)
114
.find(p => inlineCompletionProviderGetMatcher(p).some(m => m === validatedArgs.providerId))
115
: undefined;
116
117
await asyncTransaction(async tx => {
118
/** @description triggerExplicitly from command */
119
await controller?.model.get()?.trigger(tx, {
120
provider: provider,
121
explicit: validatedArgs?.explicit ?? true,
122
changeHint: validatedArgs?.changeHintData ? { data: validatedArgs.changeHintData } : undefined,
123
});
124
controller?.playAccessibilitySignal(tx);
125
});
126
127
if (validatedArgs?.showNoResultNotification) {
128
if (!controller?.model.get()?.state.get()) {
129
notificationService.notify({
130
severity: Severity.Info,
131
message: nls.localize('noInlineSuggestionAvailable', "No inline suggestion is available.")
132
});
133
}
134
}
135
}
136
}
137
138
export class AcceptNextWordOfInlineCompletion extends EditorAction {
139
constructor() {
140
super({
141
id: 'editor.action.inlineSuggest.acceptNextWord',
142
label: nls.localize2('action.inlineSuggest.acceptNextWord', "Accept Next Word Of Inline Suggestion"),
143
precondition: ContextKeyExpr.and(EditorContextKeys.writable, InlineCompletionContextKeys.inlineSuggestionVisible),
144
kbOpts: {
145
weight: KeybindingWeight.EditorContrib + 1,
146
primary: KeyMod.CtrlCmd | KeyCode.RightArrow,
147
kbExpr: ContextKeyExpr.and(EditorContextKeys.writable, InlineCompletionContextKeys.inlineSuggestionVisible, InlineCompletionContextKeys.cursorBeforeGhostText, CONTEXT_ACCESSIBILITY_MODE_ENABLED.negate()),
148
},
149
menuOpts: [{
150
menuId: MenuId.InlineSuggestionToolbar,
151
title: nls.localize('acceptWord', 'Accept Word'),
152
group: 'primary',
153
order: 2,
154
}],
155
});
156
}
157
158
public async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
159
const controller = InlineCompletionsController.get(editor);
160
await controller?.model.get()?.acceptNextWord();
161
}
162
}
163
164
export class AcceptNextLineOfInlineCompletion extends EditorAction {
165
constructor() {
166
super({
167
id: 'editor.action.inlineSuggest.acceptNextLine',
168
label: nls.localize2('action.inlineSuggest.acceptNextLine', "Accept Next Line Of Inline Suggestion"),
169
precondition: ContextKeyExpr.and(EditorContextKeys.writable, InlineCompletionContextKeys.inlineSuggestionVisible),
170
kbOpts: {
171
weight: KeybindingWeight.EditorContrib + 1,
172
},
173
menuOpts: [{
174
menuId: MenuId.InlineSuggestionToolbar,
175
title: nls.localize('acceptLine', 'Accept Line'),
176
group: 'secondary',
177
order: 2,
178
}],
179
});
180
}
181
182
public async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
183
const controller = InlineCompletionsController.get(editor);
184
await controller?.model.get()?.acceptNextLine();
185
}
186
}
187
188
export class AcceptInlineCompletion extends EditorAction {
189
constructor() {
190
super({
191
id: inlineSuggestCommitId,
192
label: nls.localize2('action.inlineSuggest.accept', "Accept Inline Suggestion"),
193
precondition: ContextKeyExpr.or(InlineCompletionContextKeys.inlineSuggestionVisible, InlineCompletionContextKeys.inlineEditVisible),
194
menuOpts: [{
195
menuId: MenuId.InlineSuggestionToolbar,
196
title: nls.localize('accept', "Accept"),
197
group: 'primary',
198
order: 2,
199
}, {
200
menuId: MenuId.InlineEditsActions,
201
title: nls.localize('accept', "Accept"),
202
group: 'primary',
203
order: 2,
204
}],
205
kbOpts: [
206
{
207
primary: KeyCode.Tab,
208
weight: 200,
209
kbExpr: ContextKeyExpr.or(
210
ContextKeyExpr.and(
211
InlineCompletionContextKeys.inlineSuggestionVisible,
212
EditorContextKeys.tabMovesFocus.toNegated(),
213
SuggestContext.Visible.toNegated(),
214
EditorContextKeys.hoverFocused.toNegated(),
215
InlineCompletionContextKeys.hasSelection.toNegated(),
216
217
InlineCompletionContextKeys.inlineSuggestionHasIndentationLessThanTabSize,
218
),
219
ContextKeyExpr.and(
220
InlineCompletionContextKeys.inlineEditVisible,
221
EditorContextKeys.tabMovesFocus.toNegated(),
222
SuggestContext.Visible.toNegated(),
223
EditorContextKeys.hoverFocused.toNegated(),
224
225
InlineCompletionContextKeys.tabShouldAcceptInlineEdit,
226
)
227
),
228
}
229
],
230
});
231
}
232
233
public async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
234
const controller = InlineCompletionsController.getInFocusedEditorOrParent(accessor);
235
if (controller) {
236
controller.model.get()?.accept(controller.editor);
237
controller.editor.focus();
238
}
239
}
240
}
241
KeybindingsRegistry.registerKeybindingRule({
242
id: inlineSuggestCommitId,
243
weight: 202, // greater than jump
244
primary: KeyCode.Tab,
245
when: ContextKeyExpr.and(InlineCompletionContextKeys.inInlineEditsPreviewEditor)
246
});
247
248
export class AcceptInlineCompletionAlternativeAction extends EditorAction {
249
constructor() {
250
super({
251
id: inlineSuggestCommitAlternativeActionId,
252
label: nls.localize2('action.inlineSuggest.acceptAlternativeAction', "Accept Inline Suggestion Alternative Action"),
253
precondition: ContextKeyExpr.and(InlineCompletionContextKeys.inlineSuggestionAlternativeActionVisible, InlineCompletionContextKeys.inlineEditVisible),
254
menuOpts: [],
255
kbOpts: [
256
{
257
primary: KeyMod.Shift | KeyCode.Tab,
258
weight: 203,
259
}
260
],
261
});
262
}
263
264
public async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
265
const controller = InlineCompletionsController.getInFocusedEditorOrParent(accessor);
266
if (controller) {
267
controller.model.get()?.accept(controller.editor, true);
268
controller.editor.focus();
269
}
270
}
271
}
272
KeybindingsRegistry.registerKeybindingRule({
273
id: inlineSuggestCommitAlternativeActionId,
274
weight: 203,
275
primary: KeyMod.Shift | KeyCode.Tab,
276
when: ContextKeyExpr.and(InlineCompletionContextKeys.inInlineEditsPreviewEditor)
277
});
278
279
export class JumpToNextInlineEdit extends EditorAction {
280
constructor() {
281
super({
282
id: jumpToNextInlineEditId,
283
label: nls.localize2('action.inlineSuggest.jump', "Jump to next inline edit"),
284
precondition: InlineCompletionContextKeys.inlineEditVisible,
285
menuOpts: [{
286
menuId: MenuId.InlineEditsActions,
287
title: nls.localize('jump', "Jump"),
288
group: 'primary',
289
order: 1,
290
when: InlineCompletionContextKeys.cursorAtInlineEdit.toNegated(),
291
}],
292
kbOpts: {
293
primary: KeyCode.Tab,
294
weight: 201,
295
kbExpr: ContextKeyExpr.and(
296
InlineCompletionContextKeys.inlineEditVisible,
297
EditorContextKeys.tabMovesFocus.toNegated(),
298
SuggestContext.Visible.toNegated(),
299
EditorContextKeys.hoverFocused.toNegated(),
300
InlineCompletionContextKeys.tabShouldJumpToInlineEdit,
301
),
302
}
303
});
304
}
305
306
public async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
307
const controller = InlineCompletionsController.get(editor);
308
if (controller) {
309
controller.jump();
310
}
311
}
312
}
313
314
export class HideInlineCompletion extends EditorAction {
315
public static ID = hideInlineCompletionId;
316
317
constructor() {
318
super({
319
id: HideInlineCompletion.ID,
320
label: nls.localize2('action.inlineSuggest.hide', "Hide Inline Suggestion"),
321
precondition: ContextKeyExpr.or(InlineCompletionContextKeys.inlineSuggestionVisible, InlineCompletionContextKeys.inlineEditVisible),
322
kbOpts: {
323
weight: KeybindingWeight.EditorContrib + 90, // same as hiding the suggest widget
324
primary: KeyCode.Escape,
325
},
326
menuOpts: [{
327
menuId: MenuId.InlineEditsActions,
328
title: nls.localize('reject', "Reject"),
329
group: 'primary',
330
order: 3,
331
}]
332
});
333
}
334
335
public async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
336
const controller = InlineCompletionsController.getInFocusedEditorOrParent(accessor);
337
transaction(tx => {
338
controller?.model.get()?.stop('explicitCancel', tx);
339
});
340
controller?.editor.focus();
341
}
342
}
343
344
export class ToggleInlineCompletionShowCollapsed extends EditorAction {
345
public static ID = toggleShowCollapsedId;
346
347
constructor() {
348
super({
349
id: ToggleInlineCompletionShowCollapsed.ID,
350
label: nls.localize2('action.inlineSuggest.toggleShowCollapsed', "Toggle Inline Suggestions Show Collapsed"),
351
precondition: ContextKeyExpr.true(),
352
});
353
}
354
355
public async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
356
const configurationService = accessor.get(IConfigurationService);
357
const showCollapsed = configurationService.getValue<boolean>('editor.inlineSuggest.edits.showCollapsed');
358
configurationService.updateValue('editor.inlineSuggest.edits.showCollapsed', !showCollapsed);
359
}
360
}
361
362
KeybindingsRegistry.registerKeybindingRule({
363
id: HideInlineCompletion.ID,
364
weight: -1, // very weak
365
primary: KeyCode.Escape,
366
secondary: [KeyMod.Shift | KeyCode.Escape],
367
when: ContextKeyExpr.and(InlineCompletionContextKeys.inInlineEditsPreviewEditor)
368
});
369
370
export class ToggleAlwaysShowInlineSuggestionToolbar extends Action2 {
371
public static ID = 'editor.action.inlineSuggest.toggleAlwaysShowToolbar';
372
373
constructor() {
374
super({
375
id: ToggleAlwaysShowInlineSuggestionToolbar.ID,
376
title: nls.localize('action.inlineSuggest.alwaysShowToolbar', "Always Show Toolbar"),
377
f1: false,
378
precondition: undefined,
379
menu: [{
380
id: MenuId.InlineSuggestionToolbar,
381
group: 'secondary',
382
order: 10,
383
}],
384
toggled: ContextKeyExpr.equals('config.editor.inlineSuggest.showToolbar', 'always')
385
});
386
}
387
388
public async run(accessor: ServicesAccessor): Promise<void> {
389
const configService = accessor.get(IConfigurationService);
390
const currentValue = configService.getValue<'always' | 'onHover'>('editor.inlineSuggest.showToolbar');
391
const newValue = currentValue === 'always' ? 'onHover' : 'always';
392
configService.updateValue('editor.inlineSuggest.showToolbar', newValue);
393
}
394
}
395
396
export class DevExtractReproSample extends EditorAction {
397
constructor() {
398
super({
399
id: 'editor.action.inlineSuggest.dev.extractRepro',
400
label: nls.localize('action.inlineSuggest.dev.extractRepro', "Developer: Extract Inline Suggest State"),
401
alias: 'Developer: Inline Suggest Extract Repro',
402
precondition: ContextKeyExpr.or(InlineCompletionContextKeys.inlineEditVisible, InlineCompletionContextKeys.inlineSuggestionVisible),
403
});
404
}
405
406
public override async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<any> {
407
const clipboardService = accessor.get(IClipboardService);
408
409
const controller = InlineCompletionsController.get(editor);
410
const m = controller?.model.get();
411
if (!m) { return; }
412
const repro = m.extractReproSample();
413
414
const inlineCompletionLines = splitLines(JSON.stringify({ inlineCompletion: repro.inlineCompletion }, null, 4));
415
416
const json = inlineCompletionLines.map(l => '// ' + l).join('\n');
417
418
const reproStr = `${repro.documentValue}\n\n// <json>\n${json}\n// </json>\n`;
419
420
await clipboardService.writeText(reproStr);
421
422
return { reproCase: reproStr };
423
}
424
}
425
426