@@ -26,6 +26,10 @@ import { SerializableDelegatedCompletionParams } from './serializableDelegatedCo
2626import { SerializableDelegatedCompletionItemResolveParams } from './serializableDelegatedCompletionItemResolveParams' ;
2727import { LanguageKind } from '../rpc/languageKind' ;
2828import { 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' ;
2933
3034export class CompletionHandler {
3135 private static readonly completionEndpoint = 'razor/completion' ;
@@ -46,6 +50,7 @@ export class CompletionHandler {
4650 private readonly documentManager : RazorDocumentManager ,
4751 private readonly documentSynchronizer : RazorDocumentSynchronizer ,
4852 private readonly serverClient : RazorLanguageServerClient ,
53+ private readonly projectedCSharpProvider : CSharpProjectedDocumentContentProvider ,
4954 private readonly logger : RazorLogger
5055 ) { }
5156
@@ -124,26 +129,14 @@ export class CompletionHandler {
124129
125130 // Roslyn/C# completion
126131 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
141139 ) ;
142- CompletionHandler . AdjustRoslynCompletionList (
143- roslynCompletions ,
144- delegatedCompletionParams . context . triggerCharacter
145- ) ;
146- return roslynCompletions ;
147140 }
148141
149142 // HTML completion
@@ -222,7 +215,7 @@ export class CompletionHandler {
222215 return CompletionHandler . emptyCompletionItem ;
223216 }
224217
225- private static AdjustRoslynCompletionList ( completionList : CompletionList , triggerCharacter : string | undefined ) {
218+ private static AdjustCSharpCompletionList ( completionList : CompletionList , triggerCharacter : string | undefined ) {
226219 const data = completionList . itemDefaults ?. data ;
227220 for ( const completionItem of completionList . items ) {
228221 // textEdit is deprecated in favor of .range. Clear out its value to avoid any unexpected behavior.
@@ -249,6 +242,78 @@ export class CompletionHandler {
249242 }
250243 }
251244
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+
252317 // converts completion item documentation from vscode format to LSP format
253318 private static ToMarkupContent ( documentation ?: string | vscode . MarkdownString ) : string | MarkupContent | undefined {
254319 const markdownString = documentation as vscode . MarkdownString ;
0 commit comments