-
Notifications
You must be signed in to change notification settings - Fork 196
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix OmniSharp formatting issue #8669
Changes from all commits
7cd3723
310c835
bc66573
c9f5677
af17392
2c25a1b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,32 +5,28 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.Common; | |
|
||
internal static class RazorLanguageServerCustomMessageTargets | ||
{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Decided to re-org this file, so we can track how we're going on the path to single server everywhere. Further along than I thought! |
||
public const string RazorUpdateCSharpBufferEndpoint = "razor/updateCSharpBuffer"; | ||
// VS Internal | ||
public const string RazorInlineCompletionEndpoint = "razor/inlineCompletion"; | ||
public const string RazorValidateBreakpointRangeName = "razor/validateBreakpointRange"; | ||
public const string RazorOnAutoInsertEndpointName = "razor/onAutoInsert"; | ||
public const string RazorSemanticTokensRefreshEndpoint = "razor/semanticTokensRefresh"; | ||
public const string RazorTextPresentationEndpoint = "razor/textPresentation"; | ||
public const string RazorUriPresentationEndpoint = "razor/uriPresentation"; | ||
|
||
// Cross platform | ||
public const string RazorUpdateCSharpBufferEndpoint = "razor/updateCSharpBuffer"; | ||
public const string RazorUpdateHtmlBufferEndpoint = "razor/updateHtmlBuffer"; | ||
|
||
public const string RazorRangeFormattingEndpoint = "razor/rangeFormatting"; | ||
|
||
public const string RazorProvideCodeActionsEndpoint = "razor/provideCodeActions"; | ||
|
||
public const string RazorResolveCodeActionsEndpoint = "razor/resolveCodeActions"; | ||
|
||
public const string RazorProvideSemanticTokensRangeEndpoint = "razor/provideSemanticTokensRange"; | ||
|
||
public const string RazorProvideHtmlDocumentColorEndpoint = "razor/provideHtmlDocumentColor"; | ||
|
||
public const string RazorProvideHtmlColorPresentationEndpoint = "razor/provideHtmlColorPresentation"; | ||
|
||
public const string RazorInlineCompletionEndpoint = "razor/inlineCompletion"; | ||
|
||
public const string RazorProvideHtmlDocumentColorEndpoint = "razor/provideHtmlDocumentColor"; | ||
public const string RazorPullDiagnosticEndpointName = "razor/pullDiagnostics"; | ||
public const string RazorProvideSemanticTokensRangeEndpoint = "razor/provideSemanticTokensRange"; | ||
public const string RazorFoldingRangeEndpoint = "razor/foldingRange"; | ||
public const string RazorHtmlFormattingEndpoint = "razor/htmlFormatting"; | ||
public const string RazorHtmlOnTypeFormattingEndpoint = "razor/htmlOnTypeFormatting"; | ||
|
||
public const string RazorSemanticTokensRefreshEndpoint = "razor/semanticTokensRefresh"; | ||
|
||
public const string RazorTextPresentationEndpoint = "razor/textPresentation"; | ||
|
||
public const string RazorUriPresentationEndpoint = "razor/uriPresentation"; | ||
|
||
// Still to migrate | ||
public const string RazorRenameEndpointName = "razor/rename"; | ||
|
||
public const string RazorHoverEndpointName = "razor/hover"; | ||
|
@@ -43,11 +39,5 @@ internal static class RazorLanguageServerCustomMessageTargets | |
|
||
public const string RazorImplementationEndpointName = "razor/implementation"; | ||
|
||
public const string RazorOnAutoInsertEndpointName = "razor/onAutoInsert"; | ||
|
||
public const string RazorValidateBreakpointRangeName = "razor/validateBreakpointRange"; | ||
|
||
public const string RazorPullDiagnosticEndpointName = "razor/pullDiagnostics"; | ||
|
||
public const string RazorReferencesEndpointName = "razor/references"; | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,27 +5,36 @@ | |
|
||
import * as vscode from 'vscode'; | ||
import { RequestType } from 'vscode-languageclient'; | ||
import { IRazorDocument } from '../Document/IRazorDocument'; | ||
import { RazorDocumentManager } from '../Document/RazorDocumentManager'; | ||
import { RazorDocumentSynchronizer } from '../Document/RazorDocumentSynchronizer'; | ||
import { RazorLanguageServerClient } from '../RazorLanguageServerClient'; | ||
import { RazorLogger } from '../RazorLogger'; | ||
import { convertTextEditToSerializable, SerializableTextEdit } from '../RPC/SerializableTextEdit'; | ||
import { SerializableFormattingParams } from './SerializableFormattingParams'; | ||
import { SerializableFormattingResponse } from './SerializableFormattingResponse'; | ||
import { SerializableOnTypeFormattingParams } from './SerializableOnTypeFormattingParams'; | ||
|
||
export class FormattingHandler { | ||
private static readonly provideFormattingEndpoint = 'textDocument/formatting'; | ||
private static readonly provideFormattingEndpoint = 'razor/htmlFormatting'; | ||
private static readonly provideOnTypeFormattingEndpoint = 'razor/htmlOnTypeFormatting'; | ||
private formattingRequestType: RequestType<SerializableFormattingParams, SerializableFormattingResponse, any> = new RequestType(FormattingHandler.provideFormattingEndpoint); | ||
private onTypeFormattingRequestType: RequestType<SerializableOnTypeFormattingParams, SerializableFormattingResponse, any> = new RequestType(FormattingHandler.provideOnTypeFormattingEndpoint); | ||
private emptyFormattingResponse = new SerializableFormattingResponse(); | ||
|
||
constructor( | ||
private readonly documentManager: RazorDocumentManager, | ||
private readonly documentSynchronizer: RazorDocumentSynchronizer, | ||
private readonly serverClient: RazorLanguageServerClient, | ||
private readonly logger: RazorLogger) { } | ||
|
||
public register() { | ||
return this.serverClient.onRequestWithParams<SerializableFormattingParams, SerializableFormattingResponse, any>( | ||
public async register() { | ||
await this.serverClient.onRequestWithParams<SerializableFormattingParams, SerializableFormattingResponse, any>( | ||
this.formattingRequestType, | ||
async (request, token) => this.provideFormatting(request, token)); | ||
await this.serverClient.onRequestWithParams<SerializableOnTypeFormattingParams, SerializableFormattingResponse, any>( | ||
this.onTypeFormattingRequestType, | ||
async (request, token) => this.provideOnTypeFormatting(request, token)); | ||
} | ||
|
||
private async provideFormatting( | ||
|
@@ -38,6 +47,12 @@ export class FormattingHandler { | |
return this.emptyFormattingResponse; | ||
} | ||
|
||
const textDocument = await vscode.workspace.openTextDocument(razorDocumentUri); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, is this line needed? I think There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not actually needing the document opening part of this, but this was the only API I could find that gave me a |
||
const synchronized = await this.documentSynchronizer.trySynchronizeProjectedDocument(textDocument, razorDocument.csharpDocument, formattingParams.hostDocumentVersion, cancellationToken); | ||
if (!synchronized) { | ||
return this.emptyFormattingResponse; | ||
} | ||
|
||
const virtualHtmlUri = razorDocument.htmlDocument.uri; | ||
|
||
const textEdits = await vscode.commands.executeCommand<vscode.TextEdit[]>( | ||
|
@@ -49,24 +64,47 @@ export class FormattingHandler { | |
return this.emptyFormattingResponse; | ||
} | ||
|
||
const htmlDocText = razorDocument.htmlDocument.getContent(); | ||
const zeroBasedLineCount = this.countLines(htmlDocText); | ||
const serializableTextEdits = Array<SerializableTextEdit>(); | ||
for (let textEdit of textEdits) { | ||
// The below workaround is needed due to a bug on the HTML side where | ||
// they'll sometimes send us an end position that exceeds the length | ||
// of the document. Tracked by https://github.com/microsoft/vscode/issues/175298. | ||
if (textEdit.range.end.line > zeroBasedLineCount) { | ||
const lastLineLength = this.getLastLineLength(htmlDocText); | ||
const updatedEndPosition = new vscode.Position(zeroBasedLineCount, lastLineLength); | ||
const updatedRange = new vscode.Range(textEdit.range.start, updatedEndPosition); | ||
textEdit = new vscode.TextEdit(updatedRange, textEdit.newText); | ||
} | ||
const serializableTextEdits = this.sanitizeTextEdits(razorDocument, textEdits); | ||
|
||
return new SerializableFormattingResponse(serializableTextEdits); | ||
} catch (error) { | ||
this.logger.logWarning(`${FormattingHandler.provideFormattingEndpoint} failed with ${error}`); | ||
} | ||
|
||
return this.emptyFormattingResponse; | ||
} | ||
|
||
const serializableTextEdit = convertTextEditToSerializable(textEdit); | ||
serializableTextEdits.push(serializableTextEdit); | ||
private async provideOnTypeFormatting( | ||
formattingParams: SerializableOnTypeFormattingParams, | ||
cancellationToken: vscode.CancellationToken) { | ||
try { | ||
const razorDocumentUri = vscode.Uri.parse(formattingParams.textDocument.uri); | ||
const razorDocument = await this.documentManager.getDocument(razorDocumentUri); | ||
if (razorDocument === undefined) { | ||
return this.emptyFormattingResponse; | ||
} | ||
|
||
const textDocument = await vscode.workspace.openTextDocument(razorDocumentUri); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (same question here as above) |
||
const synchronized = await this.documentSynchronizer.trySynchronizeProjectedDocument(textDocument, razorDocument.csharpDocument, formattingParams.hostDocumentVersion, cancellationToken); | ||
if (!synchronized) { | ||
return this.emptyFormattingResponse; | ||
} | ||
|
||
const virtualHtmlUri = razorDocument.htmlDocument.uri; | ||
|
||
const textEdits = await vscode.commands.executeCommand<vscode.TextEdit[]>( | ||
'vscode.executeFormatOnTypeProvider', | ||
virtualHtmlUri, | ||
formattingParams.position, | ||
formattingParams.ch, | ||
formattingParams.options); | ||
|
||
if (textEdits === undefined) { | ||
return this.emptyFormattingResponse; | ||
} | ||
|
||
const serializableTextEdits = this.sanitizeTextEdits(razorDocument, textEdits); | ||
|
||
return new SerializableFormattingResponse(serializableTextEdits); | ||
} catch (error) { | ||
this.logger.logWarning(`${FormattingHandler.provideFormattingEndpoint} failed with ${error}`); | ||
|
@@ -75,6 +113,39 @@ export class FormattingHandler { | |
return this.emptyFormattingResponse; | ||
} | ||
|
||
private sanitizeTextEdits(razorDocument: IRazorDocument, textEdits: vscode.TextEdit[]) { | ||
const htmlDocText = razorDocument.htmlDocument.getContent(); | ||
const zeroBasedLineCount = this.countLines(htmlDocText); | ||
const serializableTextEdits = Array<SerializableTextEdit>(); | ||
for (let textEdit of textEdits) { | ||
// The below workaround is needed due to a bug on the HTML side where | ||
// they'll sometimes send us an end position that exceeds the length | ||
// of the document. Tracked by https://github.com/microsoft/vscode/issues/175298. | ||
if (textEdit.range.end.line > zeroBasedLineCount || | ||
textEdit.range.start.line > zeroBasedLineCount) { | ||
const lastLineLength = this.getLastLineLength(htmlDocText); | ||
const updatedPosition = new vscode.Position(zeroBasedLineCount, lastLineLength); | ||
|
||
let start = textEdit.range.start; | ||
let end = textEdit.range.end; | ||
if (textEdit.range.start.line > zeroBasedLineCount) { | ||
start = updatedPosition; | ||
davidwengier marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
if (textEdit.range.end.line > zeroBasedLineCount) { | ||
end = updatedPosition; | ||
} | ||
davidwengier marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
const updatedRange = new vscode.Range(start, end); | ||
textEdit = new vscode.TextEdit(updatedRange, textEdit.newText); | ||
} | ||
|
||
const serializableTextEdit = convertTextEditToSerializable(textEdit); | ||
serializableTextEdits.push(serializableTextEdit); | ||
} | ||
return serializableTextEdits; | ||
} | ||
|
||
private countLines(text: string) { | ||
let lineCount = 0; | ||
for (const i of text) { | ||
|
This file was deleted.
This file was deleted.
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These were using the per-spec LSP methods, but for internal comms between our language servers. Bad mojo. This is where I started because I assumed this would be the issue, but I was totally wrong, but figured it was still good to fix this. Most of the changes are due to this clean up :)