Skip to content

Commit afdf9a6

Browse files
committed
Initial support for provisional completion
1 parent 88914f3 commit afdf9a6

File tree

2 files changed

+86
-20
lines changed

2 files changed

+86
-20
lines changed

src/razor/src/completion/completionHandler.ts

Lines changed: 85 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ import { SerializableDelegatedCompletionParams } from './serializableDelegatedCo
2626
import { SerializableDelegatedCompletionItemResolveParams } from './serializableDelegatedCompletionItemResolveParams';
2727
import { LanguageKind } from '../rpc/languageKind';
2828
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';
2933

3034
export 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;

src/razor/src/extension.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ export async function activate(
146146
documentManager,
147147
documentSynchronizer,
148148
languageServerClient,
149+
csharpFeature.projectionProvider,
149150
logger
150151
);
151152

0 commit comments

Comments
 (0)