-
Notifications
You must be signed in to change notification settings - Fork 298
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
Allow clients to redefine untitled files protocol during their creation #4841
Changes from all commits
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 |
---|---|---|
|
@@ -487,7 +487,7 @@ export class Agent extends MessageHandler implements ExtensionClient { | |
|
||
this.registerNotification('textDocument/didSave', async params => { | ||
const uri = vscode.Uri.parse(params.uri) | ||
const document = await vscode.workspace.openTextDocument(uri) | ||
const document = await this.workspace.openTextDocument(uri) | ||
vscode_shim.onDidSaveTextDocument.fire(document) | ||
}) | ||
|
||
|
@@ -1319,6 +1319,27 @@ export class Agent extends MessageHandler implements ExtensionClient { | |
return this.fixups | ||
} | ||
|
||
public openNewDocument = async ( | ||
_: typeof vscode.workspace, | ||
uri: vscode.Uri | ||
): Promise<vscode.TextDocument | undefined> => { | ||
if (uri.scheme !== 'untitled') { | ||
return vscode_shim.workspace.openTextDocument(uri) | ||
} | ||
|
||
if (this.clientInfo?.capabilities?.untitledDocuments !== 'enabled') { | ||
const errorMessage = | ||
'Client does not support untitled documents. To fix this problem, set `untitledDocuments: "enabled"` in client capabilities' | ||
logError('Agent', 'unsupported operation', errorMessage) | ||
throw new Error(errorMessage) | ||
} | ||
|
||
const result = await this.request('textDocument/openUntitledDocument', { | ||
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. Previously this call was in the |
||
uri: uri.toString(), | ||
}) | ||
return result ? vscode_shim.workspace.openTextDocument(result.uri) : undefined | ||
} | ||
|
||
private maybeExtension: ExtensionObjects | undefined | ||
|
||
public async provide(extension: ExtensionObjects): Promise<vscode.Disposable> { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -301,16 +301,14 @@ const _workspace: typeof vscode.workspace = { | |
throw new Error('workspaceDocuments is uninitialized') | ||
} | ||
|
||
const result = toUri(uriOrString) | ||
if (result) { | ||
if (result.uri.scheme === 'untitled' && result.shouldOpenInClient) { | ||
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. That was source of some of our problems, as VSC does not expect any side effects from this function, but we do not control client implementation. |
||
await openUntitledDocument(result.uri) | ||
} | ||
return workspaceDocuments.openTextDocument(result.uri) | ||
} | ||
return Promise.reject( | ||
new Error(`workspace.openTextDocument:unsupported argument ${JSON.stringify(uriOrString)}`) | ||
) | ||
const uri = toUri(uriOrString) | ||
return uri | ||
? workspaceDocuments.openTextDocument(uri) | ||
: Promise.reject( | ||
new Error( | ||
`workspace.openTextDocument: unsupported argument ${JSON.stringify(uriOrString)}` | ||
) | ||
) | ||
}, | ||
workspaceFolders, | ||
getWorkspaceFolder: () => { | ||
|
@@ -463,56 +461,27 @@ const defaultTreeView: vscode.TreeView<any> = { | |
title: undefined, | ||
} | ||
|
||
/** | ||
* @returns An object with a URI and a boolean indicating whether the URI should be opened in the client. | ||
* This object with UUID path is used only when we want to create in-memory temp files, and those we do not want to send to the clients. | ||
*/ | ||
function toUri( | ||
uriOrString: string | vscode.Uri | { language?: string; content?: string } | undefined | ||
): { uri: Uri; shouldOpenInClient: boolean } | undefined { | ||
): Uri | undefined { | ||
if (typeof uriOrString === 'string') { | ||
return { uri: Uri.file(uriOrString), shouldOpenInClient: true } | ||
return Uri.parse(uriOrString) | ||
} | ||
if (uriOrString instanceof Uri) { | ||
return { uri: uriOrString, shouldOpenInClient: true } | ||
return uriOrString | ||
} | ||
if ( | ||
typeof uriOrString === 'object' && | ||
((uriOrString as any)?.language || (uriOrString as any)?.content) | ||
) { | ||
const language = (uriOrString as any)?.language ?? '' | ||
const extension = extensionForLanguage(language) ?? language | ||
return { | ||
uri: Uri.from({ | ||
scheme: 'untitled', | ||
path: `${uuid.v4()}.${extension}`, | ||
}), | ||
shouldOpenInClient: false, | ||
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. nice 🚀 |
||
} | ||
} | ||
return | ||
} | ||
|
||
async function openUntitledDocument(uri: Uri, content?: string, language?: string) { | ||
if (clientInfo?.capabilities?.untitledDocuments !== 'enabled') { | ||
const errorMessage = | ||
'Client does not support untitled documents. To fix this problem, set `untitledDocuments: "enabled"` in client capabilities' | ||
logError('vscode.workspace.openTextDocument', 'unsupported operation', errorMessage) | ||
throw new Error(errorMessage) | ||
} | ||
if (agent) { | ||
const result = await agent.request('textDocument/openUntitledDocument', { | ||
uri: uri.toString(), | ||
content, | ||
language, | ||
return Uri.from({ | ||
scheme: 'untitled', | ||
path: `${uuid.v4()}.${extension}`, | ||
}) | ||
|
||
if (!result) { | ||
throw new Error( | ||
`client returned false from textDocument/openUntitledDocument: ${uri.toString()}` | ||
) | ||
} | ||
} | ||
return | ||
} | ||
|
||
function outputChannel(name: string): vscode.LogOutputChannel { | ||
|
@@ -705,6 +674,7 @@ const _window: typeof vscode.window = { | |
selection.end.character | ||
) | ||
: undefined | ||
|
||
const result = await agent.request('textDocument/show', { | ||
uri, | ||
options: { | ||
|
@@ -715,6 +685,7 @@ const _window: typeof vscode.window = { | |
if (!result) { | ||
throw new Error(`showTextDocument: client returned false when trying to show URI ${uri}`) | ||
} | ||
|
||
if (!workspaceDocuments) { | ||
throw new Error('workspaceDocuments is undefined') | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -311,7 +311,7 @@ export type ServerRequests = { | |
'window/showMessage': [ShowWindowMessageParams, string | null] | ||
|
||
'textDocument/edit': [TextDocumentEditParams, boolean] | ||
'textDocument/openUntitledDocument': [UntitledTextDocument, boolean] | ||
'textDocument/openUntitledDocument': [UntitledTextDocument, ProtocolTextDocument | undefined | 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. is it something normal to have both undefined and null as a possible return type? for me it looks weird 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. It's typical declaration in our protocol, please look at the rest of the file. |
||
'textDocument/show': [ | ||
{ | ||
uri: string | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -71,7 +71,7 @@ export class FixupController | |
|
||
constructor( | ||
private readonly authProvider: AuthProvider, | ||
client: ExtensionClient | ||
private readonly client: ExtensionClient | ||
) { | ||
this.controlApplicator = client.createFixupControlApplicator(this) | ||
// Observe file renaming and deletion | ||
|
@@ -985,16 +985,20 @@ export class FixupController | |
} | ||
|
||
// append response to new file | ||
const doc = await vscode.workspace.openTextDocument(newFileUri) | ||
const doc = await this.client.openNewDocument(vscode.workspace, newFileUri) | ||
if (!doc) { | ||
throw new Error(`Cannot create file for the fixup: ${newFileUri.toString()}`) | ||
} | ||
Comment on lines
+988
to
+991
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. Can this not just be My worry is that it won't be clear to everyone that we need to use this 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. We do not need to use it for all Problem is fundamental: I'm very open to discussion though. If you would like to jump on the call @umpox I can explain all corner cases. |
||
|
||
const pos = new vscode.Position(Math.max(doc.lineCount - 1, 0), 0) | ||
const range = new vscode.Range(pos, pos) | ||
task.selectionRange = range | ||
task.insertionPoint = range.start | ||
task.fixupFile = this.files.replaceFile(task.fixupFile.uri, newFileUri) | ||
task.fixupFile = this.files.replaceFile(task.fixupFile.uri, doc.uri) | ||
|
||
// Set original text to empty as we are not replacing original text but appending to file | ||
task.original = '' | ||
task.destinationFile = newFileUri | ||
task.destinationFile = doc.uri | ||
|
||
// Show the new document before streaming start | ||
await vscode.window.showTextDocument(doc, { | ||
|
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.
Unrelated change but it was looking like bug to me.