Skip to content

Commit

Permalink
Add Razor C# semantic tokens support in VS Code (#6489)
Browse files Browse the repository at this point in the history
  • Loading branch information
allisonchou authored Oct 5, 2023
1 parent d443538 commit c118328
Show file tree
Hide file tree
Showing 5 changed files with 237 additions and 18 deletions.
169 changes: 168 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5175,6 +5175,168 @@
"markupCommentPunctuation": [
"punctuation.definition.comment.html",
"comment.block.html"
],
"keyword": [
"keyword.cs"
],
"excludedCode": [
"support.other.excluded.cs"
],
"controlKeyword": [
"keyword.control.cs"
],
"operatorOverloaded": [
"entity.name.function.member.overload.cs"
],
"preprocessorText": [
"meta.preprocessor.string.cs"
],
"punctuation": [
"punctuation.cs"
],
"stringVerbatim": [
"string.verbatim.cs"
],
"stringEscapeCharacter": [
"constant.character.escape.cs"
],
"delegate": [
"entity.name.type.delegate.cs"
],
"module": [
"entity.name.type.module.cs"
],
"field": [
"entity.name.variable.field.cs"
],
"constant": [
"variable.other.constant"
],
"extensionMethod": [
"entity.name.function.extension.cs"
],
"xmlDocCommentAttributeName": [
"comment.documentation.attribute.name.cs"
],
"xmlDocCommentAttributeQuotes": [
"comment.documentation.attribute.quotes.cs"
],
"xmlDocCommentAttributeValue": [
"comment.documentation.attribute.value.cs"
],
"xmlDocCommentCDataSection": [
"comment.documentation.cdata.cs"
],
"xmlDocCommentComment": [
"comment.documentation.comment.cs"
],
"xmlDocCommentDelimiter": [
"comment.documentation.delimiter.cs"
],
"xmlDocCommentEntityReference": [
"comment.documentation.entityReference.cs"
],
"xmlDocCommentName": [
"comment.documentation.name.cs"
],
"xmlDocCommentProcessingInstruction": [
"comment.documentation.processingInstruction.cs"
],
"xmlDocCommentText": [
"comment.documentation.cs"
],
"xmlLiteralAttributeName": [
"entity.other.attribute-name.localname.xml"
],
"xmlLiteralAttributeQuotes": [
"string.quoted.double.xml"
],
"xmlLiteralAttributeValue": [
"meta.tag.xml"
],
"xmlLiteralCDataSection": [
"string.quoted.double.xml"
],
"xmlLiteralComment": [
"comment.block.xml"
],
"xmlLiteralDelimiter": [
"text.xml"
],
"xmlLiteralEmbeddedExpression": [
"meta.tag.xml"
],
"xmlLiteralEntityReference": [
"meta.tag.xml"
],
"xmlLiteralName": [
"entity.name.tag.localname.xml"
],
"xmlLiteralProcessingInstruction": [
"meta.tag.xml"
],
"xmlLiteralText": [
"text.xml"
],
"regexComment": [
"string.regexp.comment.cs"
],
"regexCharacterClass": [
"constant.character.character-class.regexp.cs"
],
"regexAnchor": [
"keyword.control.anchor.regexp.cs"
],
"regexQuantifier": [
"keyword.operator.quantifier.regexp.cs"
],
"regexGrouping": [
"punctuation.definition.group.regexp.cs"
],
"regexAlternation": [
"keyword.operator.or.regexp.cs"
],
"regexText": [
"string.regexp"
],
"regexSelfEscapedCharacter": [
"string.regexp.self-escaped-character.cs"
],
"regexOtherEscape": [
"string.regexp.other-escape.cs"
],
"jsonComment": [
"comment.line.double-slash.js"
],
"jsonNumber": [
"constant.numeric.json"
],
"jsonString": [
"string.quoted.double.json"
],
"jsonKeyword": [
"constant.language.json"
],
"jsonText": [
"string.quoted.double.json"
],
"jsonOperator": [
"string.quoted.double.json"
],
"jsonPunctuation": [
"punctuation.separator.dictionary.key-value.json"
],
"jsonArray": [
"punctuation.definition.array.begin.json"
],
"jsonObject": [
"punctuation.definition.dictionary.begin.json"
],
"jsonPropertyName": [
"support.type.property-name.json"
],
"jsonConstructorName": [
"support.type.property-name.json"
]
}
},
Expand Down Expand Up @@ -5369,7 +5531,12 @@
{
"language": "aspnetcorerazor",
"scopeName": "text.aspnetcorerazor",
"path": "./src/razor/syntaxes/aspnetcorerazor.tmLanguage.json"
"path": "./src/razor/syntaxes/aspnetcorerazor.tmLanguage.json",
"unbalancedBracketScopes": [
"keyword.operator.arrow.cs",
"keyword.operator.bitwise.shift.cs",
"keyword.operator.relational.cs"
]
}
],
"menus": {
Expand Down
10 changes: 5 additions & 5 deletions src/lsptoolshost/razorCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export const provideCodeActionsCommand = 'roslyn.provideCodeActions';
export const resolveCodeActionCommand = 'roslyn.resolveCodeAction';
export const provideCompletionsCommand = 'roslyn.provideCompletions';
export const resolveCompletionsCommand = 'roslyn.resolveCompletion';
export const provideSemanticTokensRangeCommand = 'roslyn.provideSemanticTokensRange';
export const roslynSimplifyMethodCommand = 'roslyn.simplifyMethod';
export const roslynFormatNewFileCommand = 'roslyn.formatNewFile';
export const razorInitializeCommand = 'razor.initialize';
Expand Down Expand Up @@ -72,15 +73,14 @@ export function registerRazorCommands(context: vscode.ExtensionContext, language
}
)
);

const formatNewFileRequestType = new RequestType<SerializableFormatNewFileParams, string | undefined, any>(
'roslyn/formatNewFile'
);
context.subscriptions.push(
vscode.commands.registerCommand(
roslynFormatNewFileCommand,
async (request: SerializableFormatNewFileParams) => {
const formatNewFileRequestType = new RequestType<
SerializableFormatNewFileParams,
string | undefined,
any
>('roslyn/formatNewFile');
return await languageServer.sendRequest(formatNewFileRequestType, request, CancellationToken.None);
}
)
Expand Down
7 changes: 6 additions & 1 deletion src/razor/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,12 @@ export async function activate(
languageServiceClient,
logger
);
const semanticTokenHandler = new SemanticTokensRangeHandler(languageServerClient);
const semanticTokenHandler = new SemanticTokensRangeHandler(
documentManager,
documentSynchronizer,
languageServerClient,
logger
);
const colorPresentationHandler = new ColorPresentationHandler(
documentManager,
languageServerClient,
Expand Down
2 changes: 1 addition & 1 deletion src/razor/src/semantic/provideSemanticTokensResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@

export class ProvideSemanticTokensResponse {
// tslint:disable-next-line: variable-name
constructor(public Tokens: Array<Array<number>>, public HostDocumentSyncVersion: number) {}
constructor(public Tokens: number[], public HostDocumentSyncVersion: number) {}
}
67 changes: 57 additions & 10 deletions src/razor/src/semantic/semanticTokensRangeHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import { RequestType } from 'vscode-languageclient';
import { RazorLanguageServerClient } from '../razorLanguageServerClient';
import { ProvideSemanticTokensResponse } from './provideSemanticTokensResponse';
import { SerializableSemanticTokensParams } from './serializableSemanticTokensParams';
import { RazorDocumentManager } from '../document/razorDocumentManager';
import { RazorDocumentSynchronizer } from '../document/razorDocumentSynchronizer';
import { RazorLogger } from '../razorLogger';

export class SemanticTokensRangeHandler {
private static readonly getSemanticTokensRangeEndpoint = 'razor/provideSemanticTokensRange';
Expand All @@ -16,9 +19,14 @@ export class SemanticTokensRangeHandler {
ProvideSemanticTokensResponse,
any
> = new RequestType(SemanticTokensRangeHandler.getSemanticTokensRangeEndpoint);
private emptyTokensInResponse: Array<Array<number>> = new Array<Array<number>>();
private emptyTokensResponse: number[] = new Array<number>();

constructor(private readonly serverClient: RazorLanguageServerClient) {}
constructor(
private readonly documentManager: RazorDocumentManager,
private readonly documentSynchronizer: RazorDocumentSynchronizer,
private readonly serverClient: RazorLanguageServerClient,
private readonly logger: RazorLogger
) {}

public async register() {
await this.serverClient.onRequestWithParams<
Expand All @@ -33,16 +41,55 @@ export class SemanticTokensRangeHandler {
}

private async getSemanticTokens(
_semanticTokensParams: SerializableSemanticTokensParams,
_cancellationToken: vscode.CancellationToken
semanticTokensParams: SerializableSemanticTokensParams,
cancellationToken: vscode.CancellationToken
): Promise<ProvideSemanticTokensResponse> {
// This is currently a no-op since (1) the default C# semantic tokens experience is already powerful and
// (2) there seems to be an issue with the semantic tokens execute command - possibly either O# not
// returning tokens, or an issue with the command itself:
// https://github.com/dotnet/razor/issues/6922
try {
const razorDocumentUri = vscode.Uri.parse(semanticTokensParams.textDocument.uri, true);
const razorDocument = await this.documentManager.getDocument(razorDocumentUri);
if (razorDocument === undefined) {
this.logger.logWarning(
`Could not find Razor document ${razorDocumentUri}; returning semantic tokens information.`
);

return new ProvideSemanticTokensResponse(
this.emptyTokensResponse,
semanticTokensParams.requiredHostDocumentVersion
);
}

const textDocument = await vscode.workspace.openTextDocument(razorDocumentUri);
const synchronized = await this.documentSynchronizer.trySynchronizeProjectedDocument(
textDocument,
razorDocument.csharpDocument,
semanticTokensParams.requiredHostDocumentVersion,
cancellationToken
);

if (!synchronized) {
return new ProvideSemanticTokensResponse(
this.emptyTokensResponse,
semanticTokensParams.requiredHostDocumentVersion
);
}

const tokens = await vscode.commands.executeCommand<vscode.SemanticTokens>(
'vscode.provideDocumentRangeSemanticTokens',
razorDocument.csharpDocument.uri,
semanticTokensParams.ranges[0]
);

return new ProvideSemanticTokensResponse(
Array.from(tokens.data),
semanticTokensParams.requiredHostDocumentVersion
);
} catch (error) {
this.logger.logWarning(`${SemanticTokensRangeHandler.getSemanticTokensRangeEndpoint} failed with ${error}`);
}

return new ProvideSemanticTokensResponse(
this.emptyTokensInResponse,
_semanticTokensParams.requiredHostDocumentVersion
this.emptyTokensResponse,
semanticTokensParams.requiredHostDocumentVersion
);
}
}

0 comments on commit c118328

Please sign in to comment.