Skip to content

Commit 4398625

Browse files
committed
* always show top N snippets and conditionally show action for all surroundable snippets
* remove `canExecute` which isn't needed - the "framework" makes sure we only called when it makes sense * tweak styles to my preference and lipstick, try to contain things over loose functions and objects,
1 parent c9ea023 commit 4398625

File tree

1 file changed

+95
-108
lines changed

1 file changed

+95
-108
lines changed

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

Lines changed: 95 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -14,159 +14,146 @@ 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';
17-
import { CancellationToken } from 'vs/base/common/cancellation';
18-
import { Disposable } from 'vs/base/common/lifecycle';
17+
import { IDisposable } from 'vs/base/common/lifecycle';
1918
import { ITextModel } from 'vs/editor/common/model';
20-
import { CodeAction, CodeActionProvider, CodeActionContext, CodeActionList } from 'vs/editor/common/languages';
19+
import { CodeAction, CodeActionProvider, CodeActionList } from 'vs/editor/common/languages';
2120
import { CodeActionKind } from 'vs/editor/contrib/codeAction/browser/types';
2221
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
2322
import { Range, IRange } from 'vs/editor/common/core/range';
24-
import { URI } from 'vs/base/common/uri';
2523
import { Selection } from 'vs/editor/common/core/selection';
2624
import { Snippet } from 'vs/workbench/contrib/snippets/browser/snippetsFile';
2725
import { Registry } from 'vs/platform/registry/common/platform';
2826
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions';
2927
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
3028
import { Position } from 'vs/editor/common/core/position';
31-
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
32-
import { EditorInputCapabilities } from 'vs/workbench/common/editor';
33-
34-
const options = {
35-
id: 'editor.action.surroundWithSnippet',
36-
title: {
37-
value: localize('label', 'Surround With Snippet...'),
38-
original: 'Surround With Snippet...'
39-
},
40-
precondition: ContextKeyExpr.and(
41-
EditorContextKeys.writable,
42-
EditorContextKeys.hasNonEmptySelection
43-
),
44-
f1: true,
45-
};
46-
47-
const MAX_SNIPPETS_ON_CODE_ACTIONS_MENU = 6;
48-
49-
function makeCodeActionForSnippet(snippet: Snippet, resource: URI, range: IRange): CodeAction {
50-
const title = localize('codeAction', "Surround With Snippet: {0}", snippet.name);
51-
return {
52-
title,
53-
edit: {
54-
edits: [
55-
{
56-
versionId: undefined,
57-
resource: resource,
58-
textEdit: {
59-
insertAsSnippet: true,
60-
text: snippet.body,
61-
range: range
62-
}
63-
}
64-
]
65-
}
66-
};
67-
}
68-
69-
async function getSurroundableSnippets(accessor: ServicesAccessor, model: ITextModel | null, position: Position | null): Promise<Snippet[]> {
70-
if (!model) {
71-
return [];
72-
}
7329

74-
const snippetsService = accessor.get(ISnippetsService);
30+
async function getSurroundableSnippets(snippetsService: ISnippetsService, model: ITextModel, position: Position): Promise<Snippet[]> {
7531

76-
let languageId: string;
77-
if (position) {
78-
const { lineNumber, column } = position;
79-
model.tokenization.tokenizeIfCheap(lineNumber);
80-
languageId = model.getLanguageIdAtPosition(lineNumber, column);
81-
} else {
82-
languageId = model.getLanguageId();
83-
}
32+
const { lineNumber, column } = position;
33+
model.tokenization.tokenizeIfCheap(lineNumber);
34+
const languageId = model.getLanguageIdAtPosition(lineNumber, column);
8435

8536
const allSnippets = await snippetsService.getSnippets(languageId, { includeNoPrefixSnippets: true, includeDisabledSnippets: true });
8637
return allSnippets.filter(snippet => snippet.usesSelection);
8738
}
8839

89-
function canExecute(accessor: ServicesAccessor): boolean {
90-
const editorService = accessor.get(IEditorService);
40+
class SurroundWithSnippetEditorAction extends EditorAction2 {
9141

92-
const editor = editorService.activeEditor;
93-
if (!editor || editor.hasCapability(EditorInputCapabilities.Readonly)) {
94-
return false;
42+
static readonly options = {
43+
id: 'editor.action.surroundWithSnippet',
44+
title: {
45+
value: localize('label', 'Surround With Snippet...'),
46+
original: 'Surround With Snippet...'
47+
}
48+
};
49+
50+
constructor() {
51+
super({
52+
...SurroundWithSnippetEditorAction.options,
53+
precondition: ContextKeyExpr.and(
54+
EditorContextKeys.writable,
55+
EditorContextKeys.hasNonEmptySelection
56+
),
57+
f1: true,
58+
});
9559
}
96-
const selections = editorService.activeTextEditorControl?.getSelections();
97-
return !!selections && selections.length > 0;
98-
}
9960

100-
async function surroundWithSnippet(accessor: ServicesAccessor, editor: ICodeEditor) {
101-
const instaService = accessor.get(IInstantiationService);
102-
const clipboardService = accessor.get(IClipboardService);
61+
async runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor) {
62+
if (!editor.hasModel()) {
63+
return;
64+
}
10365

104-
if (!canExecute(accessor)) {
105-
return;
106-
}
66+
const instaService = accessor.get(IInstantiationService);
67+
const snippetsService = accessor.get(ISnippetsService);
68+
const clipboardService = accessor.get(IClipboardService);
10769

108-
const snippets = await getSurroundableSnippets(accessor, editor.getModel(), editor.getPosition());
109-
if (!snippets.length) {
110-
return;
111-
}
70+
const snippets = await getSurroundableSnippets(snippetsService, editor.getModel(), editor.getPosition());
71+
if (!snippets.length) {
72+
return;
73+
}
11274

113-
const snippet = await instaService.invokeFunction(pickSnippet, snippets);
114-
if (!snippet) {
115-
return;
116-
}
75+
const snippet = await instaService.invokeFunction(pickSnippet, snippets);
76+
if (!snippet) {
77+
return;
78+
}
11779

118-
let clipboardText: string | undefined;
119-
if (snippet.needsClipboard) {
120-
clipboardText = await clipboardService.readText();
121-
}
80+
let clipboardText: string | undefined;
81+
if (snippet.needsClipboard) {
82+
clipboardText = await clipboardService.readText();
83+
}
12284

123-
SnippetController2.get(editor)?.insert(snippet.codeSnippet, { clipboardText });
85+
SnippetController2.get(editor)?.insert(snippet.codeSnippet, { clipboardText });
86+
}
12487
}
12588

12689

127-
registerAction2(class SurroundWithSnippetEditorAction extends EditorAction2 {
128-
constructor() {
129-
super(options);
130-
}
131-
async runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ...args: any[]) {
132-
await surroundWithSnippet(accessor, editor);
133-
}
134-
});
90+
class SurroundWithSnippetCodeActionProvider implements CodeActionProvider, IWorkbenchContribution {
91+
92+
private static readonly _MAX_CODE_ACTIONS = 4;
13593

136-
export class SurroundWithSnippetCodeActionProvider extends Disposable implements CodeActionProvider, IWorkbenchContribution {
137-
private static readonly overflowCodeAction: CodeAction = {
94+
private static readonly _overflowCommandCodeAction: CodeAction = {
13895
kind: CodeActionKind.Refactor.value,
139-
title: options.title.value,
96+
title: SurroundWithSnippetEditorAction.options.title.value,
14097
command: {
141-
id: options.id,
142-
title: options.title.value,
98+
id: SurroundWithSnippetEditorAction.options.id,
99+
title: SurroundWithSnippetEditorAction.options.title.value,
143100
},
144101
};
145102

103+
private readonly _registration: IDisposable;
104+
146105
constructor(
106+
@ISnippetsService private readonly _snippetService: ISnippetsService,
147107
@ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService,
148-
@IInstantiationService private readonly instaService: IInstantiationService,
149108
) {
150-
super();
151-
this._register(languageFeaturesService.codeActionProvider.register('*', this));
109+
this._registration = languageFeaturesService.codeActionProvider.register('*', this);
152110
}
153111

154-
async provideCodeActions(model: ITextModel, range: Range | Selection, context: CodeActionContext, token: CancellationToken): Promise<CodeActionList> {
155-
if (!this.instaService.invokeFunction(canExecute)) {
156-
return { actions: [], dispose: () => { } };
157-
}
112+
dispose(): void {
113+
this._registration.dispose();
114+
}
158115

159-
const snippets = await this.instaService.invokeFunction(accessor => getSurroundableSnippets(accessor, model, range.getEndPosition()));
116+
async provideCodeActions(model: ITextModel, range: Range | Selection): Promise<CodeActionList | undefined> {
117+
118+
const snippets = await getSurroundableSnippets(this._snippetService, model, range.getEndPosition());
160119
if (!snippets.length) {
161-
return { actions: [], dispose: () => { } };
120+
return undefined;
121+
}
122+
123+
const actions: CodeAction[] = [];
124+
const hasMore = snippets.length > SurroundWithSnippetCodeActionProvider._MAX_CODE_ACTIONS;
125+
const len = Math.min(snippets.length, SurroundWithSnippetCodeActionProvider._MAX_CODE_ACTIONS);
126+
127+
for (let i = 0; i < len; i++) {
128+
actions.push(this._makeCodeActionForSnippet(snippets[i], model, range));
129+
}
130+
if (hasMore) {
131+
actions.push(SurroundWithSnippetCodeActionProvider._overflowCommandCodeAction);
162132
}
163133
return {
164-
actions: snippets.length <= MAX_SNIPPETS_ON_CODE_ACTIONS_MENU
165-
? snippets.map(x => makeCodeActionForSnippet(x, model.uri, range))
166-
: [SurroundWithSnippetCodeActionProvider.overflowCodeAction],
167-
dispose: () => { }
134+
actions,
135+
dispose() { }
136+
};
137+
}
138+
139+
private _makeCodeActionForSnippet(snippet: Snippet, model: ITextModel, range: IRange): CodeAction {
140+
return {
141+
title: localize('codeAction', "Surround With: {0}", snippet.name),
142+
kind: CodeActionKind.Refactor.value,
143+
edit: {
144+
edits: [{
145+
versionId: model.getVersionId(),
146+
resource: model.uri,
147+
textEdit: {
148+
range,
149+
text: snippet.body,
150+
insertAsSnippet: true,
151+
}
152+
}]
153+
}
168154
};
169155
}
170156
}
171157

158+
registerAction2(SurroundWithSnippetEditorAction);
172159
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(SurroundWithSnippetCodeActionProvider, LifecyclePhase.Restored);

0 commit comments

Comments
 (0)