如何通过语言服务器扩展在工作区中创建和编辑新文件
How to create and edit a new file in the workspace via Language Server Extension
如何获取语言服务器扩展来触发新文件的创建、编辑它并在附加的客户端工作区中显示它?
我在 node.js 中有一个使用 vscode-languageserver 编写的 LSP 扩展,它通过 onExecuteCommand 在服务器 上执行命令 。我想要这个服务器端命令触发客户端创建一个新的文本文件,用一些文本填充它,所以它出现在客户端的打开文件的工作区列表中。
查看 https://github.com/microsoft/vscode-languageserver-node/blob/master/client-tests/src/helpers.test.ts 我相信我需要做的是创建一个 WorkspaceChange 对象,运行 createFile(),应用一些更改 (.insert),然后告诉客户通过connection.workspace.applyEdit() 但这不起作用 - 没有文件被创建并且调试器中没有错误被抛出。
这是我在服务器的 onExecuteCommand 中的代码:
//add some text
const textToAdd: string = "test string";
//create new WorkspaceChange obj
let workspaceChange = new WorkspaceChange();
//uri of the file we want to create
let newuri = 'file:///c:/temp/create.txt';
//make a TextEditChange obj. Fails if you do not supply version
let change = workspaceChange.getTextEditChange({ uri: newuri, version: 10 });
// give it some text
change.insert(Position.create(0, 1), textToAdd);
// add a createFile documentChange to the workspaceChange
workspaceChange.createFile(newuri);
// pass these edits to the client to apply:
let reply = connection.workspace.applyEdit(workspaceChange.edit);
console.log(reply); //always <Pending>
如果我提供了一个不存在的文件名,那么该过程将失败 - 工作区中没有创建或打开任何文件。
但是,如果我提供现有文件名,则会应用编辑并按预期在工作区中打开文件。
我认为这是因为我在 createFile 之前提供了编辑,但是如果我在 createFile() 之前 运行 getTextEditChange() 然后该过程失败并出现错误“工作区编辑未配置文档更改”
谢谢你的鼓励,是的,我在一段时间后开始工作
这里实现了:
https://github.com/proclaimforum/vscode-proclaimscript-language/blob/master/server/src/server.ts
第 476 行以后是值得注意的,其中:
- 构造一个CreateFile变量,保存要创建的文件的路径(uri),组成一个CreateFiles数组(第476-482行)
- 创建了一个 WorkspaceEdit 变量,为 documentChanges 指定了我们上面的 CreateFile 数组 属性 (485)
- 我们将其传递给客户以通过 workspace.applyEdit (488) 进行申请。这实际上创建了文件。
- 要添加到新文档中的文本首先形成一个 TextEdit 对象数组,由范围 (range:) 和内容 (newText:) (491-498) 组成
- 构造了一个TextDocumentEdit数组,包含我们的TextEdit(500-503)
- 我们更新 workspaceEdit 变量 属性 以引用此 TextDocumentEdits 数组 (506)
- 最后要求我们连接的客户端应用编辑 (510)
我复制下面的相关代码部分以供参考,但请查看 github link 以了解完整实现,包括设置客户端连接等、设置功能等
//uri of new file
let currentPath :string = (thisdoc.uri).substr(0,thisdoc.uri.lastIndexOf('/'));
let newuri = currentPath+'/syntaxcheck.txt';
//construct a CreateFile variable
let createFile: CreateFile = { kind: 'create', uri: newuri };
//and make into array
let createFiles: CreateFile[] = [];
createFiles.push(createFile);
//make a new workspaceEdit variable, specifying a createFile document change
var workspaceEdit: WorkspaceEdit = { documentChanges: createFiles };
//pass to client to apply this edit
await connection.workspace.applyEdit(workspaceEdit);
//To insert the text (and pop up the window), create array of TextEdit
let textEdit: TextEdit[] = [];
//let document = documents.get(newuri);
let documentRange: Range = Range.create(0, 0, Number.MAX_VALUE, Number.MAX_VALUE);
//populate with the text, and where to insert (surely this is what workspaceChange.insert is for?)
let textEdits: TextEdit = { range: documentRange, newText: syntaxmessage };
textEdit.push(textEdits);
//make a new array of textDocumentEdits, containing our TextEdit (range and text)
let textDocumentEdit = TextDocumentEdit.create({ uri: newuri, version: 1 }, textEdit);
let textDocumentEdits: TextDocumentEdit[] = [];
textDocumentEdits.push(textDocumentEdit);
//set our workspaceEdit variable to this new TextDocumentEdit
workspaceEdit = { documentChanges: textDocumentEdits };
//and finally apply this to our workspace.
// we can probably do this some more elegant way
connection.workspace.applyEdit(workspaceEdit);
我相信此功能已在 LSP 规范 3.15 中引入:https://microsoft.github.io/language-server-protocol/specifications/specification-3-15
我创建了以下 class 来帮助更新、创建和删除文件:
import { DocumentUri, OptionalVersionedTextDocumentIdentifier, Position, Range } from 'vscode-languageserver';
import { WorkspaceChange, ApplyWorkspaceEditResponse} from 'vscode-languageserver-protocol';
import { connection } from "../server";
export class WSUpdates{
private _wschanges: WorkspaceChange;
constructor(){
this._wschanges = new WorkspaceChange();
}
hasChanges(): boolean{
return (this._wschanges.edit.changes != undefined || this._wschanges.edit.documentChanges != undefined);
}
createFile(uri: DocumentUri, contents: string, overwrite: boolean){
this._wschanges.createFile(uri, {overwrite: overwrite});
const edit = this._wschanges.edit;
const change = this._wschanges.getTextEditChange(OptionalVersionedTextDocumentIdentifier.create(uri,null));
this.insertText(uri,contents,0,0);
}
renameFile(uri:DocumentUri, newUri: DocumentUri, overwrite: boolean){
this._wschanges.renameFile(uri, newUri, { overwrite: overwrite});
}
deleteFileFolder(uri:DocumentUri, recursive: boolean, ignoreIfNotExists: boolean){
this._wschanges.deleteFile(uri, { recursive: recursive, ignoreIfNotExists: ignoreIfNotExists});
}
insertText(uri: DocumentUri, contents: string, line: number, column: number){
const change = this._wschanges.getTextEditChange(OptionalVersionedTextDocumentIdentifier.create(uri, null));
change.insert(Position.create(line,column),contents);
}
replaceText(uri: DocumentUri, contents: string, startLine: number, startColumn: number, endLine: number, endColumn: number){
const change = this._wschanges.getTextEditChange(OptionalVersionedTextDocumentIdentifier.create(uri,null));
change.replace(
Range.create(Position.create(startLine, startColumn), Position.create(endLine, endColumn)),
contents
);
}
replaceAllText(uri: DocumentUri, contents: string){
this.replaceText(uri, contents, 0,0,Number.MAX_VALUE, Number.MAX_VALUE);
}
deleteText(uri: DocumentUri, contents: string, startLine: number, startColumn: number, endLine: number, endColumn: number){
const change = this._wschanges.getTextEditChange(OptionalVersionedTextDocumentIdentifier.create(uri,null));
change.delete(
Range.create(Position.create(startLine, startColumn), Position.create(endLine, endColumn))
);
}
async applyChanges(): Promise<ApplyWorkspaceEditResponse>{
return connection.workspace.applyEdit(this._wschanges.edit);
}
}
您可以从您的服务器代码中调用此 class。例如
const wsu = new WSUpdates();
wsu.createFile('file.txt','Creating a new file\n');
const resp = await wsu.applyChanges();
if (!resp.applied){
console.log('Handle failure');
}
要记住的一件事是更改不能重叠在同一个文件上。
如何获取语言服务器扩展来触发新文件的创建、编辑它并在附加的客户端工作区中显示它?
我在 node.js 中有一个使用 vscode-languageserver 编写的 LSP 扩展,它通过 onExecuteCommand 在服务器 上执行命令 。我想要这个服务器端命令触发客户端创建一个新的文本文件,用一些文本填充它,所以它出现在客户端的打开文件的工作区列表中。
查看 https://github.com/microsoft/vscode-languageserver-node/blob/master/client-tests/src/helpers.test.ts 我相信我需要做的是创建一个 WorkspaceChange 对象,运行 createFile(),应用一些更改 (.insert),然后告诉客户通过connection.workspace.applyEdit() 但这不起作用 - 没有文件被创建并且调试器中没有错误被抛出。
这是我在服务器的 onExecuteCommand 中的代码:
//add some text
const textToAdd: string = "test string";
//create new WorkspaceChange obj
let workspaceChange = new WorkspaceChange();
//uri of the file we want to create
let newuri = 'file:///c:/temp/create.txt';
//make a TextEditChange obj. Fails if you do not supply version
let change = workspaceChange.getTextEditChange({ uri: newuri, version: 10 });
// give it some text
change.insert(Position.create(0, 1), textToAdd);
// add a createFile documentChange to the workspaceChange
workspaceChange.createFile(newuri);
// pass these edits to the client to apply:
let reply = connection.workspace.applyEdit(workspaceChange.edit);
console.log(reply); //always <Pending>
如果我提供了一个不存在的文件名,那么该过程将失败 - 工作区中没有创建或打开任何文件。
但是,如果我提供现有文件名,则会应用编辑并按预期在工作区中打开文件。
我认为这是因为我在 createFile 之前提供了编辑,但是如果我在 createFile() 之前 运行 getTextEditChange() 然后该过程失败并出现错误“工作区编辑未配置文档更改”
谢谢你的鼓励,是的,我在一段时间后开始工作
这里实现了: https://github.com/proclaimforum/vscode-proclaimscript-language/blob/master/server/src/server.ts
第 476 行以后是值得注意的,其中:
- 构造一个CreateFile变量,保存要创建的文件的路径(uri),组成一个CreateFiles数组(第476-482行)
- 创建了一个 WorkspaceEdit 变量,为 documentChanges 指定了我们上面的 CreateFile 数组 属性 (485)
- 我们将其传递给客户以通过 workspace.applyEdit (488) 进行申请。这实际上创建了文件。
- 要添加到新文档中的文本首先形成一个 TextEdit 对象数组,由范围 (range:) 和内容 (newText:) (491-498) 组成
- 构造了一个TextDocumentEdit数组,包含我们的TextEdit(500-503)
- 我们更新 workspaceEdit 变量 属性 以引用此 TextDocumentEdits 数组 (506)
- 最后要求我们连接的客户端应用编辑 (510)
我复制下面的相关代码部分以供参考,但请查看 github link 以了解完整实现,包括设置客户端连接等、设置功能等
//uri of new file
let currentPath :string = (thisdoc.uri).substr(0,thisdoc.uri.lastIndexOf('/'));
let newuri = currentPath+'/syntaxcheck.txt';
//construct a CreateFile variable
let createFile: CreateFile = { kind: 'create', uri: newuri };
//and make into array
let createFiles: CreateFile[] = [];
createFiles.push(createFile);
//make a new workspaceEdit variable, specifying a createFile document change
var workspaceEdit: WorkspaceEdit = { documentChanges: createFiles };
//pass to client to apply this edit
await connection.workspace.applyEdit(workspaceEdit);
//To insert the text (and pop up the window), create array of TextEdit
let textEdit: TextEdit[] = [];
//let document = documents.get(newuri);
let documentRange: Range = Range.create(0, 0, Number.MAX_VALUE, Number.MAX_VALUE);
//populate with the text, and where to insert (surely this is what workspaceChange.insert is for?)
let textEdits: TextEdit = { range: documentRange, newText: syntaxmessage };
textEdit.push(textEdits);
//make a new array of textDocumentEdits, containing our TextEdit (range and text)
let textDocumentEdit = TextDocumentEdit.create({ uri: newuri, version: 1 }, textEdit);
let textDocumentEdits: TextDocumentEdit[] = [];
textDocumentEdits.push(textDocumentEdit);
//set our workspaceEdit variable to this new TextDocumentEdit
workspaceEdit = { documentChanges: textDocumentEdits };
//and finally apply this to our workspace.
// we can probably do this some more elegant way
connection.workspace.applyEdit(workspaceEdit);
我相信此功能已在 LSP 规范 3.15 中引入:https://microsoft.github.io/language-server-protocol/specifications/specification-3-15
我创建了以下 class 来帮助更新、创建和删除文件:
import { DocumentUri, OptionalVersionedTextDocumentIdentifier, Position, Range } from 'vscode-languageserver';
import { WorkspaceChange, ApplyWorkspaceEditResponse} from 'vscode-languageserver-protocol';
import { connection } from "../server";
export class WSUpdates{
private _wschanges: WorkspaceChange;
constructor(){
this._wschanges = new WorkspaceChange();
}
hasChanges(): boolean{
return (this._wschanges.edit.changes != undefined || this._wschanges.edit.documentChanges != undefined);
}
createFile(uri: DocumentUri, contents: string, overwrite: boolean){
this._wschanges.createFile(uri, {overwrite: overwrite});
const edit = this._wschanges.edit;
const change = this._wschanges.getTextEditChange(OptionalVersionedTextDocumentIdentifier.create(uri,null));
this.insertText(uri,contents,0,0);
}
renameFile(uri:DocumentUri, newUri: DocumentUri, overwrite: boolean){
this._wschanges.renameFile(uri, newUri, { overwrite: overwrite});
}
deleteFileFolder(uri:DocumentUri, recursive: boolean, ignoreIfNotExists: boolean){
this._wschanges.deleteFile(uri, { recursive: recursive, ignoreIfNotExists: ignoreIfNotExists});
}
insertText(uri: DocumentUri, contents: string, line: number, column: number){
const change = this._wschanges.getTextEditChange(OptionalVersionedTextDocumentIdentifier.create(uri, null));
change.insert(Position.create(line,column),contents);
}
replaceText(uri: DocumentUri, contents: string, startLine: number, startColumn: number, endLine: number, endColumn: number){
const change = this._wschanges.getTextEditChange(OptionalVersionedTextDocumentIdentifier.create(uri,null));
change.replace(
Range.create(Position.create(startLine, startColumn), Position.create(endLine, endColumn)),
contents
);
}
replaceAllText(uri: DocumentUri, contents: string){
this.replaceText(uri, contents, 0,0,Number.MAX_VALUE, Number.MAX_VALUE);
}
deleteText(uri: DocumentUri, contents: string, startLine: number, startColumn: number, endLine: number, endColumn: number){
const change = this._wschanges.getTextEditChange(OptionalVersionedTextDocumentIdentifier.create(uri,null));
change.delete(
Range.create(Position.create(startLine, startColumn), Position.create(endLine, endColumn))
);
}
async applyChanges(): Promise<ApplyWorkspaceEditResponse>{
return connection.workspace.applyEdit(this._wschanges.edit);
}
}
您可以从您的服务器代码中调用此 class。例如
const wsu = new WSUpdates();
wsu.createFile('file.txt','Creating a new file\n');
const resp = await wsu.applyChanges();
if (!resp.applied){
console.log('Handle failure');
}
要记住的一件事是更改不能重叠在同一个文件上。