-
Notifications
You must be signed in to change notification settings - Fork 676
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
Don't open closed documents #7826
base: main
Are you sure you want to change the base?
Changes from all commits
2f4b51c
de1837d
212e710
d23f7df
0e170b1
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 |
---|---|---|
@@ -0,0 +1,32 @@ | ||
/*--------------------------------------------------------------------------------------------- | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. See License.txt in the project root for license information. | ||
*--------------------------------------------------------------------------------------------*/ | ||
|
||
import * as vscode from 'vscode'; | ||
import { CSharpProjectedDocument } from '../csharp/csharpProjectedDocument'; | ||
import { HtmlProjectedDocument } from '../html/htmlProjectedDocument'; | ||
import { getUriPath } from '../uriPaths'; | ||
import { IRazorDocument } from './IRazorDocument'; | ||
|
||
export class RazorDocument implements IRazorDocument { | ||
public readonly path: string; | ||
|
||
constructor( | ||
readonly uri: vscode.Uri, | ||
readonly csharpDocument: CSharpProjectedDocument, | ||
readonly htmlDocument: HtmlProjectedDocument | ||
) { | ||
this.path = getUriPath(uri); | ||
} | ||
|
||
public get isOpen(): boolean { | ||
for (const textDocument of vscode.workspace.textDocuments) { | ||
if (textDocument.uri.fsPath == this.uri.fsPath) { | ||
return true; | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,19 +4,23 @@ | |
*--------------------------------------------------------------------------------------------*/ | ||
|
||
import * as vscode from 'vscode'; | ||
import { DocumentUri } from 'vscode-languageclient/node'; | ||
import { UriConverter } from '../../../lsptoolshost/uriConverter'; | ||
import { RazorDocumentManager } from '../document/razorDocumentManager'; | ||
import { RazorLogger } from '../razorLogger'; | ||
import { ProvideDynamicFileParams } from './provideDynamicFileParams'; | ||
import { ProvideDynamicFileResponse } from './provideDynamicFileResponse'; | ||
import { RemoveDynamicFileParams } from './removeDynamicFileParams'; | ||
import { CSharpProjectedDocument } from '../csharp/csharpProjectedDocument'; | ||
import { RazorDocumentChangeKind } from '../document/razorDocumentChangeKind'; | ||
import { RazorDynamicFileChangedParams } from './dynamicFileUpdatedParams'; | ||
import { TextDocumentIdentifier } from 'vscode-languageserver-protocol'; | ||
|
||
// Handles Razor generated doc communication between the Roslyn workspace and Razor. | ||
// didChange behavior for Razor generated docs is handled in the RazorDocumentManager. | ||
export class DynamicFileInfoHandler { | ||
public static readonly provideDynamicFileInfoCommand = 'razor.provideDynamicFileInfo'; | ||
public static readonly removeDynamicFileInfoCommand = 'razor.removeDynamicFileInfo'; | ||
public static readonly dynamicFileUpdatedCommand = 'razor.dynamicFileUpdated'; | ||
|
||
constructor(private readonly documentManager: RazorDocumentManager, private readonly logger: RazorLogger) {} | ||
|
||
|
@@ -33,39 +37,57 @@ export class DynamicFileInfoHandler { | |
await this.removeDynamicFileInfo(request); | ||
} | ||
); | ||
this.documentManager.onChange(async (e) => { | ||
if (e.kind == RazorDocumentChangeKind.csharpChanged && !e.document.isOpen) { | ||
const uriString = UriConverter.serialize(e.document.uri); | ||
const identifier = TextDocumentIdentifier.create(uriString); | ||
await vscode.commands.executeCommand( | ||
DynamicFileInfoHandler.dynamicFileUpdatedCommand, | ||
new RazorDynamicFileChangedParams(identifier) | ||
); | ||
} | ||
}); | ||
} | ||
|
||
// Given Razor document URIs, returns associated generated doc URIs | ||
private async provideDynamicFileInfo( | ||
request: ProvideDynamicFileParams | ||
): Promise<ProvideDynamicFileResponse | null> { | ||
let virtualUri: DocumentUri | null = null; | ||
this.documentManager.roslynActivated = true; | ||
const vscodeUri = vscode.Uri.parse(request.razorDocument.uri, true); | ||
|
||
// Normally we start receiving dynamic info after Razor is initialized, but if the user had a .razor file open | ||
// when they started VS Code, the order is the other way around. This no-ops if Razor is already initialized. | ||
await this.documentManager.ensureRazorInitialized(); | ||
|
||
const razorDocument = await this.documentManager.getDocument(vscodeUri); | ||
try { | ||
const vscodeUri = vscode.Uri.parse(request.razorDocument.uri, true); | ||
const razorDocument = await this.documentManager.getDocument(vscodeUri); | ||
if (razorDocument === undefined) { | ||
this.logger.logWarning( | ||
`Could not find Razor document ${vscodeUri.fsPath}; adding null as a placeholder in URI array.` | ||
); | ||
} else { | ||
// Retrieve generated doc URIs for each Razor URI we are given | ||
const virtualCsharpUri = UriConverter.serialize(razorDocument.csharpDocument.uri); | ||
virtualUri = virtualCsharpUri; | ||
|
||
return null; | ||
} | ||
|
||
this.documentManager.roslynActivated = true; | ||
const virtualCsharpUri = UriConverter.serialize(razorDocument.csharpDocument.uri); | ||
|
||
// Normally we start receiving dynamic info after Razor is initialized, but if the user had a .razor file open | ||
// when they started VS Code, the order is the other way around. This no-ops if Razor is already initialized. | ||
await this.documentManager.ensureRazorInitialized(); | ||
if (this.documentManager.isRazorDocumentOpenInCSharpWorkspace(vscodeUri)) { | ||
// Open documents have didOpen/didChange to update the csharp buffer. Razor | ||
// does not send edits and instead lets vscode handle them. | ||
return new ProvideDynamicFileResponse({ uri: virtualCsharpUri }, null); | ||
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. Should this reset any stored edits, so that when a document is closed, things have a clean slate? ie, when a document is opened, LSP will send a didOpen with the full contents, then any future edits, and then when its closed, are we at risk of sending some edits that were made before the open? |
||
} else { | ||
// Closed documents provide edits since the last time they were requested since | ||
// there is no open buffer in vscode corresponding to the csharp content. | ||
const csharpDocument = razorDocument.csharpDocument as CSharpProjectedDocument; | ||
const edits = csharpDocument.getAndApplyEdits(); | ||
|
||
return new ProvideDynamicFileResponse({ uri: virtualCsharpUri }, edits ?? null); | ||
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. Given this can send null, and open docs always do, and thinking about the above comment, I wonder if these two branches can be combined. ie, on open, clear the edits, then provide the dynamic file response with |
||
} | ||
} catch (error) { | ||
this.logger.logWarning(`${DynamicFileInfoHandler.provideDynamicFileInfoCommand} failed with ${error}`); | ||
} | ||
|
||
if (virtualUri) { | ||
return new ProvideDynamicFileResponse({ uri: virtualUri }); | ||
} | ||
|
||
return null; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
/*--------------------------------------------------------------------------------------------- | ||
* 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-protocol'; | ||
|
||
export class RazorDynamicFileChangedParams { | ||
constructor(public readonly razorDocument: TextDocumentIdentifier) {} | ||
} |
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.
I'm surprised this works, given elsewhere the edits are concated together. Wouldn't we need to insert subsequent edits at position 0?
Though that could be at odds with the Roslyn algorithm that processes the same edits?
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.
that's a good point. I don't think I've actually hit a scenario where they got concat together in testing so probably missed this. Idk why reverse was used initially. Let me look...
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.
Reverse makes sense for a single batch of ordered changes, it means you don't need to track forward when applying edits earlier in the file, and adjust positions later in the file. But the batches would need to be applied in order.
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.
I didn't realize they were guaranteed to be ordered. Or that it was guaranteed to not overlap.
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.
I think I can just update to applying stored edits first instead of doing a concat
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.
This should be better now. I updated to keep track of
Updates
as individual units sent from Razor. How they get applied remains consistent and the edits being returned on ordered to be sent back to Roslyn