@@ -26,6 +26,10 @@ import { SerializableDelegatedCompletionParams } from './serializableDelegatedCo
26
26
import { SerializableDelegatedCompletionItemResolveParams } from './serializableDelegatedCompletionItemResolveParams' ;
27
27
import { LanguageKind } from '../rpc/languageKind' ;
28
28
import { UriConverter } from '../../../lsptoolshost/uriConverter' ;
29
+ import { SerializableTextEdit } from '../rpc/serializableTextEdit' ;
30
+ import { CSharpProjectedDocument } from '../csharp/csharpProjectedDocument' ;
31
+ import { IProjectedDocument } from '../projection/IProjectedDocument' ;
32
+ import { CSharpProjectedDocumentContentProvider } from '../csharp/csharpProjectedDocumentContentProvider' ;
29
33
30
34
export class CompletionHandler {
31
35
private static readonly completionEndpoint = 'razor/completion' ;
@@ -46,6 +50,7 @@ export class CompletionHandler {
46
50
private readonly documentManager : RazorDocumentManager ,
47
51
private readonly documentSynchronizer : RazorDocumentSynchronizer ,
48
52
private readonly serverClient : RazorLanguageServerClient ,
53
+ private readonly projectedCSharpProvider : CSharpProjectedDocumentContentProvider ,
49
54
private readonly logger : RazorLogger
50
55
) { }
51
56
@@ -124,26 +129,14 @@ export class CompletionHandler {
124
129
125
130
// Roslyn/C# completion
126
131
if ( delegatedCompletionParams . projectedKind === LanguageKind . CSharp ) {
127
- const params : CompletionParams = {
128
- context : {
129
- triggerKind : triggerKind ,
130
- triggerCharacter : modifiedTriggerCharacter ,
131
- } ,
132
- textDocument : {
133
- uri : virtualDocumentUri ,
134
- } ,
135
- position : delegatedCompletionParams . projectedPosition ,
136
- } ;
137
-
138
- const roslynCompletions = await vscode . commands . executeCommand < CompletionList > (
139
- provideCompletionsCommand ,
140
- params
132
+ return this . provideCSharpCompletions (
133
+ triggerKind ,
134
+ modifiedTriggerCharacter ,
135
+ virtualDocument as CSharpProjectedDocument ,
136
+ virtualDocumentUri ,
137
+ delegatedCompletionParams . projectedPosition ,
138
+ delegatedCompletionParams . provisionalTextEdit
141
139
) ;
142
- CompletionHandler . AdjustRoslynCompletionList (
143
- roslynCompletions ,
144
- delegatedCompletionParams . context . triggerCharacter
145
- ) ;
146
- return roslynCompletions ;
147
140
}
148
141
149
142
// HTML completion
@@ -222,7 +215,7 @@ export class CompletionHandler {
222
215
return CompletionHandler . emptyCompletionItem ;
223
216
}
224
217
225
- private static AdjustRoslynCompletionList ( completionList : CompletionList , triggerCharacter : string | undefined ) {
218
+ private static AdjustCSharpCompletionList ( completionList : CompletionList , triggerCharacter : string | undefined ) {
226
219
const data = completionList . itemDefaults ?. data ;
227
220
for ( const completionItem of completionList . items ) {
228
221
// textEdit is deprecated in favor of .range. Clear out its value to avoid any unexpected behavior.
@@ -249,6 +242,78 @@ export class CompletionHandler {
249
242
}
250
243
}
251
244
245
+ private async provideCSharpCompletions (
246
+ triggerKind : CompletionTriggerKind ,
247
+ triggerCharacter : string | undefined ,
248
+ virtualDocument : CSharpProjectedDocument ,
249
+ virtualDocumentUri : string ,
250
+ projectedPosition : Position ,
251
+ provisionalTextEdit ?: SerializableTextEdit
252
+ ) {
253
+ const params : CompletionParams = {
254
+ context : {
255
+ triggerKind : triggerKind ,
256
+ triggerCharacter : triggerCharacter ,
257
+ } ,
258
+ textDocument : {
259
+ uri : virtualDocumentUri ,
260
+ } ,
261
+ position : projectedPosition ,
262
+ } ;
263
+
264
+ if ( provisionalTextEdit ) {
265
+ const absoluteIndex = CompletionHandler . getIndexOfPosition ( virtualDocument , projectedPosition ) ;
266
+ if ( absoluteIndex === - 1 ) {
267
+ return CompletionHandler . emptyCompletionList ;
268
+ }
269
+ virtualDocument . addProvisionalDotAt ( absoluteIndex ) ;
270
+ this . projectedCSharpProvider . ensureDocumentContent ( virtualDocument . uri ) ;
271
+
272
+ // We open and then re-save because we're adding content to the text document within an event.
273
+ // We need to allow the system to propogate this text document change.
274
+ const newDocument = await vscode . workspace . openTextDocument ( virtualDocument . uri ) ;
275
+ await newDocument . save ( ) ;
276
+
277
+ params . position = < Position > {
278
+ line : projectedPosition . line ,
279
+ character : projectedPosition . character + 1 ,
280
+ } ;
281
+ }
282
+ const csharpCompletions = await vscode . commands . executeCommand < CompletionList > (
283
+ provideCompletionsCommand ,
284
+ params
285
+ ) ;
286
+ CompletionHandler . AdjustCSharpCompletionList ( csharpCompletions , triggerCharacter ) ;
287
+ return csharpCompletions ;
288
+ }
289
+
290
+ private static getIndexOfPosition ( document : IProjectedDocument , position : Position ) {
291
+ const content : string = document . getContent ( ) ;
292
+ let lineNumber = 0 ;
293
+ let index = 0 ;
294
+ while ( lineNumber < position . line && index < content . length ) {
295
+ const ch = content [ index ] ;
296
+ if ( ch === '\r' ) {
297
+ lineNumber ++ ;
298
+ if ( index + 1 < content . length && content [ index + 1 ] === '\n' ) {
299
+ index ++ ;
300
+ }
301
+ } else if ( ch === '\n' ) {
302
+ lineNumber ++ ;
303
+ }
304
+
305
+ index ++ ;
306
+ }
307
+
308
+ if ( lineNumber !== position . line ) {
309
+ return - 1 ;
310
+ }
311
+
312
+ const positionIndex = index + position . character - 1 ;
313
+
314
+ return positionIndex ;
315
+ }
316
+
252
317
// converts completion item documentation from vscode format to LSP format
253
318
private static ToMarkupContent ( documentation ?: string | vscode . MarkdownString ) : string | MarkupContent | undefined {
254
319
const markdownString = documentation as vscode . MarkdownString ;
0 commit comments