Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,21 @@

# 2.77.x
* Bump xamlTools to 17.14.36106.43 (PR: [#8260](https://github.com/dotnet/vscode-csharp/pull/8260))
* Bump Razor to 10.0.0-preview.25255.4 (PR: [#8249](https://github.com/dotnet/vscode-csharp/pull/8249))
* Bump Razor to 10.0.0-preview.25256.6 (PR: [#8259](https://github.com/dotnet/vscode-csharp/pull/8259))
* Improve document handling in VS Code and cohosting (#11825) (PR: [#11825](https://github.com/dotnet/razor/pull/11825))
* Use new select and order method (#11826) (PR: [#11826](https://github.com/dotnet/razor/pull/11826))
* Make it so validation happens after filtering and normalization (#11811) (PR: [#11811](https://github.com/dotnet/razor/pull/11811))
* Port remaining cohosting endpoints to VS Code (#11815) (PR: [#11815](https://github.com/dotnet/razor/pull/11815))
* Fix cohost semantic tokens in VS Code (#11816) (PR: [#11816](https://github.com/dotnet/razor/pull/11816))
* Fix some hardcoded values in cohosting (#11817) (PR: [#11817](https://github.com/dotnet/razor/pull/11817))
* Add new shared SelectXXXAsArray helpers (#11796) (PR: [#11796](https://github.com/dotnet/razor/pull/11796))
* Bump Roslyn to 5.0.0-1.25255.4 (PR: [#8249](https://github.com/dotnet/vscode-csharp/pull/8249))
* Ensure hover markdown for supported platforms uses non-breaking spaces for indentation (#78405) (PR: [#78405](https://github.com/dotnet/roslyn/pull/78405))
* Change O(n) + O(lg n) search in SolutionState.SortedProjectStates to just O(lg n) (#78427) (PR: [#78427](https://github.com/dotnet/roslyn/pull/78427))
* Fix syntax tree creation when modifying source generated documents (#78343) (PR: [#78343](https://github.com/dotnet/roslyn/pull/78343))

# 2.76.x
* Bump Roslyn to 5.0.0-1.25256.5 (PR: [#8254](https://github.com/dotnet/vscode-csharp/pull/8254))
* Do not parse URIs during LSP serialization/deserialization(PR: [#76691](https://github.com/dotnet/roslyn/pull/76691))
* Ensure hover markdown for supported platforms uses non-breaking spaces for indentation(PR: [#78405](https://github.com/dotnet/roslyn/pull/78405))
* Change O(n) + O(lg n) search in SolutionState.SortedProjectStates to just O(lg n) (#78427) (PR: [#78427](https://github.com/dotnet/roslyn/pull/78427))
* Fix syntax tree creation when modifying source generated documents (#78343) (PR: [#78343](https://github.com/dotnet/roslyn/pull/78343))
* Bump Razor to 10.0.0-preview.25252.1 (PR: [#8239](https://github.com/dotnet/vscode-csharp/pull/8239))
* If SupportDiagnostics is false then dynamic files don't report diagnostics... (PR: #11807)
* Client settings tweaks, and implement VS Code configuration monitoring (PR: #11800)
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"defaults": {
"roslyn": "5.0.0-1.25256.5",
"omniSharp": "1.39.12",
"razor": "10.0.0-preview.25255.4",
"razor": "10.0.0-preview.25256.6",
"razorOmnisharp": "7.0.0-preview.23363.1",
"xamlTools": "17.14.36106.43"
},
Expand Down
11 changes: 9 additions & 2 deletions src/lsptoolshost/razor/htmlDocument.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,23 @@ import { getUriPath } from '../../razor/src/uriPaths';
export class HtmlDocument {
public readonly path: string;
private content = '';
private checksum = '';

public constructor(public readonly uri: vscode.Uri) {
public constructor(public readonly uri: vscode.Uri, checksum: string) {
this.path = getUriPath(uri);
this.checksum = checksum;
}

public getContent() {
return this.content;
}

public setContent(content: string) {
public getChecksum() {
return this.checksum;
}

public setContent(checksum: string, content: string) {
this.checksum = checksum;
this.content = content;
}
}
64 changes: 48 additions & 16 deletions src/lsptoolshost/razor/htmlDocumentManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,22 @@ import { getUriPath } from '../../razor/src/uriPaths';
import { virtualHtmlSuffix } from '../../razor/src/razorConventions';
import { HtmlDocumentContentProvider } from './htmlDocumentContentProvider';
import { HtmlDocument } from './htmlDocument';
import { RoslynLanguageServer } from '../server/roslynLanguageServer';
import { RequestType, TextDocumentIdentifier } from 'vscode-languageserver-protocol';

export class HtmlDocumentManager {
private readonly htmlDocuments: { [hostDocumentPath: string]: HtmlDocument } = {};
private readonly contentProvider: HtmlDocumentContentProvider;

constructor(private readonly platformInfo: PlatformInformation, private readonly logger: RazorLogger) {
private readonly razorDocumentClosedRequest: RequestType<TextDocumentIdentifier, void, Error> = new RequestType(
'razor/documentClosed'
);

constructor(
private readonly platformInfo: PlatformInformation,
private readonly roslynLanguageServer: RoslynLanguageServer,
private readonly logger: RazorLogger
) {
this.contentProvider = new HtmlDocumentContentProvider(this, this.logger);
}

Expand All @@ -37,7 +47,13 @@ export class HtmlDocumentManager {

await this.closeDocument(document.uri);

// TODO: Send a notification back to the server so it can cancel any pending sync requests and clear its cache.
// We don't care about the response, but Razor cohosting can't currently do notifications with documents
// so making it a request means the logs end up in the right place.
await this.roslynLanguageServer.sendRequest(
this.razorDocumentClosedRequest,
TextDocumentIdentifier.create(getUriPath(document.uri)),
new vscode.CancellationTokenSource().token
);
}
});

Expand All @@ -49,12 +65,22 @@ export class HtmlDocumentManager {
return vscode.Disposable.from(didCloseRegistration, providerRegistration);
}

public async updateDocumentText(uri: vscode.Uri, text: string) {
const document = await this.getDocument(uri);
public async updateDocumentText(uri: vscode.Uri, checksum: string, text: string) {
// We don't pass the checksum in here, because we'd be comparing the new one against the old one.
let document = await this.findDocument(uri);

if (!document) {
this.logger.logTrace(
`File '${uri}' didn't exist in the Razor document list, so adding it with checksum '${checksum}'.`
);
document = this.addDocument(uri, checksum);
}

this.logger.logTrace(`New content for '${uri}', updating '${document.path}', checksum '${checksum}'.`);

this.logger.logTrace(`New content for '${uri}', updating '${document.path}'.`);
await vscode.workspace.openTextDocument(document.uri);

document.setContent(text);
document.setContent(checksum, text);

this.contentProvider.fireDidChange(document.uri);
}
Expand All @@ -69,30 +95,36 @@ export class HtmlDocumentManager {
}
}

public async getDocument(uri: vscode.Uri): Promise<HtmlDocument> {
let document = this.findDocument(uri);
public async getDocument(uri: vscode.Uri, checksum?: string): Promise<HtmlDocument | undefined> {
const document = this.findDocument(uri);

// This might happen in the case that a file is opened outside the workspace
if (!document) {
this.logger.logTrace(`File '${uri}' didn't exist in the Razor document list. Doing nothing.`);
return undefined;
}

if (checksum && document.getChecksum() !== checksum) {
this.logger.logInfo(
`File '${uri}' didn't exist in the Razor document list. This is likely because it's from outside the workspace.`
`Found '${uri}' in the Razor document list, but the checksum '${document.getChecksum()}' doesn't match '${checksum}'.`
);
document = this.addDocument(uri);
return undefined;
}

// No checksum, just give them the latest document and hope they know what to do with it.

await vscode.workspace.openTextDocument(document.uri);

return document!;
return document;
}

private addDocument(uri: vscode.Uri): HtmlDocument {
private addDocument(uri: vscode.Uri, checksum: string): HtmlDocument {
let document = this.findDocument(uri);
if (document) {
this.logger.logInfo(`Skipping document creation for '${document.path}' because it already exists.`);
return document;
}

document = this.createDocument(uri);
document = this.createDocument(uri, checksum);
this.htmlDocuments[document.path] = document;

return document;
Expand All @@ -115,12 +147,12 @@ export class HtmlDocumentManager {
);
}

private createDocument(uri: vscode.Uri) {
private createDocument(uri: vscode.Uri, checksum: string) {
uri = uri.with({
scheme: HtmlDocumentContentProvider.scheme,
path: `${uri.path}${virtualHtmlSuffix}`,
});
const projectedDocument = new HtmlDocument(uri);
const projectedDocument = new HtmlDocument(uri, checksum);

return projectedDocument;
}
Expand Down
13 changes: 13 additions & 0 deletions src/lsptoolshost/razor/htmlForwardedRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { TextDocumentIdentifier } from 'vscode-languageserver-types';

export class HtmlForwardedRequest<Params> {
constructor(
public readonly textDocument: TextDocumentIdentifier,
public readonly checksum: string,
public readonly request: Params
) {}
}
6 changes: 5 additions & 1 deletion src/lsptoolshost/razor/htmlUpdateParameters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,9 @@
import { TextDocumentIdentifier } from 'vscode-languageserver-protocol';

export class HtmlUpdateParameters {
constructor(public readonly textDocument: TextDocumentIdentifier, public readonly text: string) {}
constructor(
public readonly textDocument: TextDocumentIdentifier,
public readonly checksum: string,
public readonly text: string
) {}
}
82 changes: 29 additions & 53 deletions src/lsptoolshost/razor/razorEndpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ import { RazorMapTextChangesParams } from '../../razor/src/mapping/razorMapTextC
import { RazorMapTextChangesResponse } from '../../razor/src/mapping/razorMapTextChangesResponse';
import { FormattingHandler } from '../../razor/src/formatting/formattingHandler';
import { ReportIssueCommand } from '../../razor/src/diagnostics/reportIssueCommand';
import { HtmlDocument } from './htmlDocument';
import { HtmlForwardedRequest } from './htmlForwardedRequest';

export function registerRazorEndpoints(
context: vscode.ExtensionContext,
Expand All @@ -76,34 +78,25 @@ export function registerRazorEndpoints(
// Local Functions
//
function registerCohostingEndpoints() {
const documentManager = new HtmlDocumentManager(platformInfo, razorLogger);
const documentManager = new HtmlDocumentManager(platformInfo, roslynLanguageServer, razorLogger);
const reportIssueCommand = new ReportIssueCommand(vscode, undefined, documentManager, razorLogger);
context.subscriptions.push(documentManager.register());
context.subscriptions.push(reportIssueCommand.register());

registerMethodHandler<HtmlUpdateParameters, void>('razor/updateHtml', async (params) => {
const uri = UriConverter.deserialize(params.textDocument.uri);
await documentManager.updateDocumentText(uri, params.text);
await documentManager.updateDocumentText(uri, params.checksum, params.text);
});

registerRequestHandler(DocumentColorRequest.type, async (params) => {
const uri = UriConverter.deserialize(params.textDocument.uri);
const document = await documentManager.getDocument(uri);

registerCohostHandler(DocumentColorRequest.type, documentManager, async (document) => {
return await DocumentColorHandler.doDocumentColorRequest(document.uri);
});

registerRequestHandler(ColorPresentationRequest.type, async (params) => {
const uri = UriConverter.deserialize(params.textDocument.uri);
const document = await documentManager.getDocument(uri);

registerCohostHandler(ColorPresentationRequest.type, documentManager, async (document, params) => {
return await ColorPresentationHandler.doColorPresentationRequest(document.uri, params);
});

registerRequestHandler(FoldingRangeRequest.type, async (params) => {
const uri = UriConverter.deserialize(params.textDocument.uri);
const document = await documentManager.getDocument(uri);

registerCohostHandler(FoldingRangeRequest.type, documentManager, async (document) => {
const results = await vscode.commands.executeCommand<vscode.FoldingRange[]>(
'vscode.executeFoldingRangeProvider',
document.uri
Expand All @@ -112,10 +105,7 @@ export function registerRazorEndpoints(
return FoldingRangeHandler.convertFoldingRanges(results, razorLogger);
});

registerRequestHandler(HoverRequest.type, async (params) => {
const uri = UriConverter.deserialize(params.textDocument.uri);
const document = await documentManager.getDocument(uri);

registerCohostHandler(HoverRequest.type, documentManager, async (document, params) => {
const results = await vscode.commands.executeCommand<vscode.Hover[]>(
'vscode.executeHoverProvider',
document.uri,
Expand All @@ -126,10 +116,7 @@ export function registerRazorEndpoints(
return rewriteHover(applicableHover);
});

registerRequestHandler(DocumentHighlightRequest.type, async (params) => {
const uri = UriConverter.deserialize(params.textDocument.uri);
const document = await documentManager.getDocument(uri);

registerCohostHandler(DocumentHighlightRequest.type, documentManager, async (document, params) => {
const results = await vscode.commands.executeCommand<vscode.DocumentHighlight[]>(
'vscode.executeDocumentHighlights',
document.uri,
Expand All @@ -139,21 +126,15 @@ export function registerRazorEndpoints(
return rewriteHighlight(results);
});

registerRequestHandler(CompletionRequest.type, async (params) => {
const uri = UriConverter.deserialize(params.textDocument.uri);
const document = await documentManager.getDocument(uri);

registerCohostHandler(CompletionRequest.type, documentManager, async (document, params) => {
return CompletionHandler.provideVscodeCompletions(
document.uri,
params.position,
params.context?.triggerCharacter
);
});

registerRequestHandler(ReferencesRequest.type, async (params) => {
const uri = UriConverter.deserialize(params.textDocument.uri);
const document = await documentManager.getDocument(uri);

registerCohostHandler(ReferencesRequest.type, documentManager, async (document, params) => {
const results = await vscode.commands.executeCommand<vscode.Location[]>(
'vscode.executeReferenceProvider',
document.uri,
Expand All @@ -163,10 +144,7 @@ export function registerRazorEndpoints(
return rewriteLocations(results);
});

registerRequestHandler(ImplementationRequest.type, async (params) => {
const uri = UriConverter.deserialize(params.textDocument.uri);
const document = await documentManager.getDocument(uri);

registerCohostHandler(ImplementationRequest.type, documentManager, async (document, params) => {
const results = await vscode.commands.executeCommand<vscode.Location[]>(
'vscode.executeImplementationProvider',
document.uri,
Expand All @@ -176,10 +154,7 @@ export function registerRazorEndpoints(
return rewriteLocations(results);
});

registerRequestHandler(DefinitionRequest.type, async (params) => {
const uri = UriConverter.deserialize(params.textDocument.uri);
const document = await documentManager.getDocument(uri);

registerCohostHandler(DefinitionRequest.type, documentManager, async (document, params) => {
const results = await vscode.commands.executeCommand<vscode.Location[]>(
'vscode.executeDefinitionProvider',
document.uri,
Expand All @@ -189,10 +164,7 @@ export function registerRazorEndpoints(
return rewriteLocations(results);
});

registerRequestHandler(SignatureHelpRequest.type, async (params) => {
const uri = UriConverter.deserialize(params.textDocument.uri);
const document = await documentManager.getDocument(uri);

registerCohostHandler(SignatureHelpRequest.type, documentManager, async (document, params) => {
const results = await vscode.commands.executeCommand<vscode.SignatureHelp>(
'vscode.executeSignatureHelpProvider',
document.uri,
Expand All @@ -206,21 +178,15 @@ export function registerRazorEndpoints(
return rewriteSignatureHelp(results);
});

registerRequestHandler(DocumentFormattingRequest.type, async (params) => {
const uri = UriConverter.deserialize(params.textDocument.uri);
const document = await documentManager.getDocument(uri);

registerCohostHandler(DocumentFormattingRequest.type, documentManager, async (document, params) => {
const content = document.getContent();
const options = <vscode.FormattingOptions>params.options;

const response = await FormattingHandler.getHtmlFormattingResult(document.uri, content, options);
return response?.edits;
});

registerRequestHandler(DocumentOnTypeFormattingRequest.type, async (params) => {
const uri = UriConverter.deserialize(params.textDocument.uri);
const document = await documentManager.getDocument(uri);

registerCohostHandler(DocumentOnTypeFormattingRequest.type, documentManager, async (document, params) => {
const content = document.getContent();
const options = <vscode.FormattingOptions>params.options;

Expand Down Expand Up @@ -262,11 +228,21 @@ export function registerRazorEndpoints(
}

// Helper method that registers a request handler, and logs errors to the Razor logger.
function registerRequestHandler<Params, Result, Error>(
function registerCohostHandler<Params, Result, Error>(
type: RequestType<Params, Result, Error>,
invocation: (params: Params) => Promise<Result>
documentManager: HtmlDocumentManager,
invocation: (document: HtmlDocument, request: Params) => Promise<Result>
) {
return registerMethodHandler<Params, Result>(type.method, invocation);
return registerMethodHandler<HtmlForwardedRequest<Params>, Result | undefined>(type.method, async (params) => {
const uri = UriConverter.deserialize(params.textDocument.uri);
const document = await documentManager.getDocument(uri, params.checksum);

if (!document) {
return undefined;
}

return invocation(document, params.request);
});
}

function registerMethodHandler<Params, Result>(method: string, invocation: (params: Params) => Promise<Result>) {
Expand Down
Loading
Loading