wlh320 / rime-ls

A language server for Rime input method engine 通过 LSP 代码补全使用 Rime 输入法
BSD 3-Clause "New" or "Revised" License
198 stars 13 forks source link

didChange with full text can't work #16

Closed saierXP closed 6 months ago

saierXP commented 1 year ago

执行全部文本变动的通知,插入新值这里似乎发生了死锁,服务器后续不工作也没有报错,客户端也无法正常连接了。 https://github.com/wlh320/rime-ls/blob/0b8ad0ca5bfb42bdee1899b754030910c58a1445/src/lsp.rs#L60-L63

通过把这里的 获取文本rope的操作,放到下面进行文本局部插入的if分支里,就可以进行全部文本替换和局部插入。 https://github.com/wlh320/rime-ls/blob/0b8ad0ca5bfb42bdee1899b754030910c58a1445/src/lsp.rs#L333-L336

不懂Rust,不知道上面修改后的原理,我去看dashMap的文档和issue里提到过insert会有死锁的问题。

JavaScript lsp客户端测试代码 需要nodejs和 vscode-jsonrpc 库依赖 根据实际修改初始化的initializationOptions值 切换下面didChange部分range更改和全文更改的注释,观察服务器返回的补全,文本打开默认值是zh ```js const net = require('net'); const { createMessageConnection, SocketMessageReader, SocketMessageWriter } = require("vscode-jsonrpc"); const sleep = (delay) => new Promise((resolve) => setTimeout(resolve, delay)) function main() { const argv = process.argv; const client = new net.Socket(); client.connect(9257, '127.0.0.1', () => { console.log('Connected to LSP server'); }); const reader = new SocketMessageReader(client); const writer = new SocketMessageWriter(client); const connection = createMessageConnection(reader, writer); connection.onNotification('window/logMessage', (params) => { console.log('Received message:', params); }); connection.onNotification('window/workDoneProgress/create', (params) => { console.log('Received :', params); }); connection.onRequest('workspace/executeCommand', (params) => { console.log('Received command:', params); }); connection.listen(); client.on('close', () => { console.log('Connection closed'); }); connection.onNotification((method, params) => { console.log(`Notification: ${method} ${JSON.stringify(params, null, 2)}`); }); connection.sendRequest("initialize", { "initializationOptions": { "shared_data_dir": "D:\\App\\Rime\\weasel-0.15.0\\data", "user_data_dir": "D:\\Temp\\Rime\\lsp", "log_dir": "D:\\Temp\\Rime\\lsp\\log", "max_candidates": 10, "trigger_characters": [], "schema_trigger_character": "&" }, "capabilities": { "textDocument": { "synchronization": { "dynamicRegistration": true } }, "window": { "showMessage": { "messageActionItem": { "additionalPropertiesSupport": false } } } } }) .then(async (e) => { console.log("Resp: ", JSON.stringify(e, null, 2)) await connection.sendNotification("initialized", {}) await connection.sendNotification("textDocument/didOpen", { "textDocument": { "languageId": "text", "text": "zh", "uri": "file:///res://Temp/Rime/test.txt", "version": 1, } }); await sleep(300); connection.sendNotification("textDocument/didChange", { "textDocument": { "uri": "file:///res://Temp/Rime/test.txt", "version": 100 }, "contentChanges": [{ "text": "qi", "range": { "start": { "line": 0, "character": 0 }, "end": { "line": 0, "character": 0 } } }], "rangeLength": 0 }); // connection.sendNotification("textDocument/didChange",{"textDocument":{"uri":"file:///res://Temp/Rime/test.txt","version":100}, "contentChanges":[{"text":"kou"}]}); await sleep(600); var c = await connection.sendRequest("textDocument/completion", { "textDocument": { "uri": "file:///res://Temp/Rime/test.txt", }, "position": { "line": 0, "character": 2, }, }); console.log("Comp:", JSON.stringify(c, null, 2)); }); } main(); ```
wlh320 commented 1 year ago

感谢,确实可以稳定复现死锁问题,原因我认为跟 dashmap 的文档说的一样,就是在插入时当前线程还有一个对该 map 内的 value 的引用。

把获取 dashmap 内容的引用的代码放进第一个 if 分支会比较合理,避免了进入第二个分支时在外面的不必要的引用。