@@ -13,48 +13,147 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService
13
13
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey' ;
14
14
import { IInstantiationService , ServicesAccessor } from 'vs/platform/instantiation/common/instantiation' ;
15
15
import { pickSnippet } from 'vs/workbench/contrib/snippets/browser/snippetPicker' ;
16
- import { ISnippetsService } from 'vs/workbench/contrib/snippets/browser/snippets.contribution' ;
16
+ import { ISnippetsService } from './snippets.contribution' ;
17
+ import { IDisposable } from 'vs/base/common/lifecycle' ;
18
+ import { ITextModel } from 'vs/editor/common/model' ;
19
+ import { CodeAction , CodeActionProvider , CodeActionList } from 'vs/editor/common/languages' ;
20
+ import { CodeActionKind } from 'vs/editor/contrib/codeAction/browser/types' ;
21
+ import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures' ;
22
+ import { Range , IRange } from 'vs/editor/common/core/range' ;
23
+ import { Selection } from 'vs/editor/common/core/selection' ;
24
+ import { Snippet } from 'vs/workbench/contrib/snippets/browser/snippetsFile' ;
25
+ import { Registry } from 'vs/platform/registry/common/platform' ;
26
+ import { IWorkbenchContributionsRegistry , Extensions as WorkbenchExtensions , IWorkbenchContribution } from 'vs/workbench/common/contributions' ;
27
+ import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle' ;
28
+ import { Position } from 'vs/editor/common/core/position' ;
17
29
30
+ async function getSurroundableSnippets ( snippetsService : ISnippetsService , model : ITextModel , position : Position ) : Promise < Snippet [ ] > {
18
31
19
- registerAction2 ( class SurroundWithAction extends EditorAction2 {
32
+ const { lineNumber, column } = position ;
33
+ model . tokenization . tokenizeIfCheap ( lineNumber ) ;
34
+ const languageId = model . getLanguageIdAtPosition ( lineNumber , column ) ;
35
+
36
+ const allSnippets = await snippetsService . getSnippets ( languageId , { includeNoPrefixSnippets : true , includeDisabledSnippets : true } ) ;
37
+ return allSnippets . filter ( snippet => snippet . usesSelection ) ;
38
+ }
39
+
40
+ class SurroundWithSnippetEditorAction extends EditorAction2 {
41
+
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
+ } ;
20
49
21
50
constructor ( ) {
22
51
super ( {
23
- id : 'editor.action.surroundWithSnippet' ,
24
- title : { value : localize ( 'label' , 'Surround With Snippet...' ) , original : 'Surround With Snippet...' } ,
25
- precondition : ContextKeyExpr . and ( EditorContextKeys . writable , EditorContextKeys . hasNonEmptySelection ) ,
26
- f1 : true
52
+ ...SurroundWithSnippetEditorAction . options ,
53
+ precondition : ContextKeyExpr . and (
54
+ EditorContextKeys . writable ,
55
+ EditorContextKeys . hasNonEmptySelection
56
+ ) ,
57
+ f1 : true ,
27
58
} ) ;
28
59
}
29
60
30
- async runEditorCommand ( accessor : ServicesAccessor , editor : ICodeEditor , ...args : any [ ] ) {
31
-
32
- const snippetService = accessor . get ( ISnippetsService ) ;
33
- const clipboardService = accessor . get ( IClipboardService ) ;
34
- const instaService = accessor . get ( IInstantiationService ) ;
35
-
61
+ async runEditorCommand ( accessor : ServicesAccessor , editor : ICodeEditor ) {
36
62
if ( ! editor . hasModel ( ) ) {
37
63
return ;
38
64
}
39
65
40
- const { lineNumber , column } = editor . getPosition ( ) ;
41
- editor . getModel ( ) . tokenization . tokenizeIfCheap ( lineNumber ) ;
42
- const languageId = editor . getModel ( ) . getLanguageIdAtPosition ( lineNumber , column ) ;
66
+ const instaService = accessor . get ( IInstantiationService ) ;
67
+ const snippetsService = accessor . get ( ISnippetsService ) ;
68
+ const clipboardService = accessor . get ( IClipboardService ) ;
43
69
44
- const allSnippets = await snippetService . getSnippets ( languageId , { includeNoPrefixSnippets : true , includeDisabledSnippets : true } ) ;
45
- const surroundSnippets = allSnippets . filter ( snippet => snippet . usesSelection ) ;
46
- const snippet = await instaService . invokeFunction ( pickSnippet , surroundSnippets ) ;
70
+ const snippets = await getSurroundableSnippets ( snippetsService , editor . getModel ( ) , editor . getPosition ( ) ) ;
71
+ if ( ! snippets . length ) {
72
+ return ;
73
+ }
47
74
75
+ const snippet = await instaService . invokeFunction ( pickSnippet , snippets ) ;
48
76
if ( ! snippet ) {
49
77
return ;
50
78
}
51
79
52
-
53
80
let clipboardText : string | undefined ;
54
81
if ( snippet . needsClipboard ) {
55
82
clipboardText = await clipboardService . readText ( ) ;
56
83
}
57
84
58
85
SnippetController2 . get ( editor ) ?. insert ( snippet . codeSnippet , { clipboardText } ) ;
59
86
}
60
- } ) ;
87
+ }
88
+
89
+
90
+ class SurroundWithSnippetCodeActionProvider implements CodeActionProvider , IWorkbenchContribution {
91
+
92
+ private static readonly _MAX_CODE_ACTIONS = 4 ;
93
+
94
+ private static readonly _overflowCommandCodeAction : CodeAction = {
95
+ kind : CodeActionKind . Refactor . value ,
96
+ title : SurroundWithSnippetEditorAction . options . title . value ,
97
+ command : {
98
+ id : SurroundWithSnippetEditorAction . options . id ,
99
+ title : SurroundWithSnippetEditorAction . options . title . value ,
100
+ } ,
101
+ } ;
102
+
103
+ private readonly _registration : IDisposable ;
104
+
105
+ constructor (
106
+ @ISnippetsService private readonly _snippetService : ISnippetsService ,
107
+ @ILanguageFeaturesService languageFeaturesService : ILanguageFeaturesService ,
108
+ ) {
109
+ this . _registration = languageFeaturesService . codeActionProvider . register ( '*' , this ) ;
110
+ }
111
+
112
+ dispose ( ) : void {
113
+ this . _registration . dispose ( ) ;
114
+ }
115
+
116
+ async provideCodeActions ( model : ITextModel , range : Range | Selection ) : Promise < CodeActionList | undefined > {
117
+
118
+ const snippets = await getSurroundableSnippets ( this . _snippetService , model , range . getEndPosition ( ) ) ;
119
+ if ( ! snippets . length ) {
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 ) ;
132
+ }
133
+ return {
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
+ }
154
+ } ;
155
+ }
156
+ }
157
+
158
+ registerAction2 ( SurroundWithSnippetEditorAction ) ;
159
+ Registry . as < IWorkbenchContributionsRegistry > ( WorkbenchExtensions . Workbench ) . registerWorkbenchContribution ( SurroundWithSnippetCodeActionProvider , LifecyclePhase . Restored ) ;
0 commit comments