Skip to content

Commit d0f9637

Browse files
committed
🔨 Refactor "Surround With Snippet" editor contribution into workbench contribution
Signed-off-by: Babak K. Shandiz <[email protected]>
1 parent 746bc9f commit d0f9637

File tree

1 file changed

+82
-58
lines changed

1 file changed

+82
-58
lines changed

‎src/vs/workbench/contrib/snippets/browser/surroundWithSnippet.ts

Lines changed: 82 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
7-
import { EditorAction2, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
7+
import { EditorAction2 } from 'vs/editor/browser/editorExtensions';
88
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
99
import { SnippetController2 } from 'vs/editor/contrib/snippet/browser/snippetController2';
1010
import { localize } from 'vs/nls';
1111
import { registerAction2 } from 'vs/platform/actions/common/actions';
1212
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
13-
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
13+
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
1414
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
1515
import { pickSnippet } from 'vs/workbench/contrib/snippets/browser/snippetPicker';
1616
import { ISnippetsService } from './snippets.contribution';
@@ -23,6 +23,12 @@ import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeat
2323
import { Range } from 'vs/editor/common/core/range';
2424
import { Selection } from 'vs/editor/common/core/selection';
2525
import { Snippet } from 'vs/workbench/contrib/snippets/browser/snippetsFile';
26+
import { Registry } from 'vs/platform/registry/common/platform';
27+
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions';
28+
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
29+
import { Position } from 'vs/editor/common/core/position';
30+
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
31+
import { EditorInputCapabilities } from 'vs/workbench/common/editor';
2632

2733
const options = {
2834
id: 'editor.action.surroundWithSnippet',
@@ -37,72 +43,88 @@ const options = {
3743
f1: true,
3844
};
3945

40-
class SurroundWithSnippet {
41-
constructor(
42-
private readonly _editor: ICodeEditor,
43-
@ISnippetsService private readonly _snippetService: ISnippetsService,
44-
@IClipboardService private readonly _clipboardService: IClipboardService,
45-
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
46-
@IInstantiationService private readonly _instaService: IInstantiationService,
47-
) { }
48-
49-
async getSurroundableSnippets(): Promise<Snippet[]> {
50-
if (!this._editor.hasModel()) {
51-
return [];
52-
}
46+
const MAX_SNIPPETS_ON_CODE_ACTIONS_MENU = 6;
5347

54-
const model = this._editor.getModel();
55-
const { lineNumber, column } = this._editor.getPosition();
56-
model.tokenization.tokenizeIfCheap(lineNumber);
57-
const languageId = model.getLanguageIdAtPosition(lineNumber, column);
48+
function makeCodeActionForSnippet(snippet: Snippet): CodeAction {
49+
const title = localize('codeAction', "Surround With Snippet: {0}", snippet.name);
50+
return {
51+
title,
52+
command: {
53+
id: 'editor.action.insertSnippet',
54+
title,
55+
arguments: [{ name: snippet.name }]
56+
},
57+
};
58+
}
5859

59-
const allSnippets = await this._snippetService.getSnippets(languageId, { includeNoPrefixSnippets: true, includeDisabledSnippets: true });
60-
return allSnippets.filter(snippet => snippet.usesSelection);
60+
async function getSurroundableSnippets(accessor: ServicesAccessor, model: ITextModel | null, position: Position | null): Promise<Snippet[]> {
61+
if (!model) {
62+
return [];
6163
}
6264

63-
canExecute(): boolean {
64-
return this._contextKeyService.contextMatchesRules(options.precondition);
65+
const snippetsService = accessor.get(ISnippetsService);
66+
67+
let languageId: string;
68+
if (position) {
69+
const { lineNumber, column } = position;
70+
model.tokenization.tokenizeIfCheap(lineNumber);
71+
languageId = model.getLanguageIdAtPosition(lineNumber, column);
72+
} else {
73+
languageId = model.getLanguageId();
6574
}
6675

67-
async run() {
68-
if (!this.canExecute()) {
69-
return;
70-
}
76+
const allSnippets = await snippetsService.getSnippets(languageId, { includeNoPrefixSnippets: true, includeDisabledSnippets: true });
77+
return allSnippets.filter(snippet => snippet.usesSelection);
78+
}
7179

72-
const snippets = await this.getSurroundableSnippets();
73-
if (!snippets.length) {
74-
return;
75-
}
80+
function canExecute(accessor: ServicesAccessor): boolean {
81+
const editorService = accessor.get(IEditorService);
7682

77-
const snippet = await this._instaService.invokeFunction(pickSnippet, snippets);
78-
if (!snippet) {
79-
return;
80-
}
83+
const editor = editorService.activeEditor;
84+
if (!editor || editor.hasCapability(EditorInputCapabilities.Readonly)) {
85+
return false;
86+
}
87+
const selections = editorService.activeTextEditorControl?.getSelections();
88+
return !!selections && selections.length > 0;
89+
}
8190

82-
let clipboardText: string | undefined;
83-
if (snippet.needsClipboard) {
84-
clipboardText = await this._clipboardService.readText();
85-
}
91+
async function surroundWithSnippet(accessor: ServicesAccessor, editor: ICodeEditor) {
92+
const instaService = accessor.get(IInstantiationService);
93+
const clipboardService = accessor.get(IClipboardService);
8694

87-
SnippetController2.get(this._editor)?.insert(snippet.codeSnippet, { clipboardText });
95+
if (!canExecute(accessor)) {
96+
return;
8897
}
98+
99+
const snippets = await getSurroundableSnippets(accessor, editor.getModel(), editor.getPosition());
100+
if (!snippets.length) {
101+
return;
102+
}
103+
104+
const snippet = await instaService.invokeFunction(pickSnippet, snippets);
105+
if (!snippet) {
106+
return;
107+
}
108+
109+
let clipboardText: string | undefined;
110+
if (snippet.needsClipboard) {
111+
clipboardText = await clipboardService.readText();
112+
}
113+
114+
SnippetController2.get(editor)?.insert(snippet.codeSnippet, { clipboardText });
89115
}
90116

91-
class SurroundWithSnippetEditorAction extends EditorAction2 {
117+
118+
registerAction2(class SurroundWithSnippetEditorAction extends EditorAction2 {
92119
constructor() {
93120
super(options);
94121
}
95122
async runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ...args: any[]) {
96-
const instaService = accessor.get(IInstantiationService);
97-
const core = instaService.createInstance(SurroundWithSnippet, editor);
98-
await core.run();
123+
await surroundWithSnippet(accessor, editor);
99124
}
100-
}
101-
102-
registerAction2(SurroundWithSnippetEditorAction);
125+
});
103126

104-
105-
export class SurroundWithSnippetCodeActionProvider extends Disposable implements CodeActionProvider {
127+
export class SurroundWithSnippetCodeActionProvider extends Disposable implements CodeActionProvider, IWorkbenchContribution {
106128
private static readonly codeAction: CodeAction = {
107129
kind: CodeActionKind.Refactor.value,
108130
title: options.title.value,
@@ -112,28 +134,30 @@ export class SurroundWithSnippetCodeActionProvider extends Disposable implements
112134
},
113135
};
114136

115-
private core: SurroundWithSnippet;
116-
117137
constructor(
118-
editor: ICodeEditor,
119138
@ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService,
120-
@IInstantiationService instaService: IInstantiationService,
139+
@IInstantiationService private readonly instaService: IInstantiationService,
121140
) {
122141
super();
123-
this.core = instaService.createInstance(SurroundWithSnippet, editor);
124142
this._register(languageFeaturesService.codeActionProvider.register('*', this));
125143
}
126144

127145
async provideCodeActions(model: ITextModel, range: Range | Selection, context: CodeActionContext, token: CancellationToken): Promise<CodeActionList> {
128-
if (!this.core.canExecute()) {
146+
if (!this.instaService.invokeFunction(canExecute)) {
147+
return { actions: [], dispose: () => { } };
148+
}
149+
150+
const snippets = await this.instaService.invokeFunction(accessor => getSurroundableSnippets(accessor, model, range.getEndPosition()));
151+
if (!snippets.length) {
129152
return { actions: [], dispose: () => { } };
130153
}
131-
const snippets = await this.core.getSurroundableSnippets();
132154
return {
133-
actions: snippets.length ? [SurroundWithSnippetCodeActionProvider.codeAction] : [],
155+
actions: snippets.length <= MAX_SNIPPETS_ON_CODE_ACTIONS_MENU
156+
? snippets.map(x => makeCodeActionForSnippet(x))
157+
: [SurroundWithSnippetCodeActionProvider.codeAction],
134158
dispose: () => { }
135159
};
136160
}
137161
}
138162

139-
registerEditorContribution(options.id, SurroundWithSnippetCodeActionProvider);
163+
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(SurroundWithSnippetCodeActionProvider, LifecyclePhase.Restored);

0 commit comments

Comments
 (0)