Skip to content

Commit eafad41

Browse files
authored
lsp: Fix reading large incoming json-rpc messages (#241)
1 parent 8a56ff7 commit eafad41

2 files changed

Lines changed: 56 additions & 42 deletions

File tree

src/langs/blueprint/blueprint.js

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,9 @@ export function setup({ data_dir, document }) {
3737
return xml;
3838
},
3939
async decompile(text) {
40-
const { blp } = await lspc.request(
41-
"x-blueprint/decompile",
42-
{
43-
text,
44-
},
45-
{ timeout: 5000 },
46-
);
40+
const { blp } = await lspc.request("x-blueprint/decompile", {
41+
text,
42+
});
4743
return blp;
4844
},
4945
};

src/lsp/LSPClient.js

Lines changed: 53 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import Gio from "gi://Gio";
33

44
import { LSPError } from "./LSP.js";
55

6-
import { getPid, promiseTask, once } from "../../troll/src/util.js";
6+
import { getPid, once } from "../../troll/src/util.js";
77

88
const { addSignalMethods } = imports.signals;
99

@@ -17,6 +17,26 @@ const clientInfo = {
1717
version: pkg.version,
1818
};
1919

20+
Gio._promisify(
21+
Gio.InputStream.prototype,
22+
"read_bytes_async",
23+
"read_bytes_finish",
24+
);
25+
Gio._promisify(Gio.InputStream.prototype, "read_all_async", "read_all_finish");
26+
Gio._promisify(Gio.InputStream.prototype, "close_async", "close_finish");
27+
Gio._promisify(
28+
Gio.DataInputStream.prototype,
29+
"read_line_async",
30+
"read_line_finish",
31+
);
32+
Gio._promisify(Gio.OutputStream.prototype, "close_async", "close_finish");
33+
Gio._promisify(
34+
Gio.OutputStream.prototype,
35+
"write_bytes_async",
36+
"write_bytes_finish",
37+
);
38+
Gio._promisify(Gio.Subprocess.prototype, "wait_async", "wait_finish");
39+
2040
export default class LSPClient {
2141
constructor(argv, { rootUri, uri, languageId, buffer }) {
2242
this.argv = argv;
@@ -76,8 +96,10 @@ export default class LSPClient {
7696
}
7797

7898
async stop() {
79-
await promiseTask(this.stdin, "close_async", "close_finish", null);
80-
await promiseTask(this.stdout, "close_async", "close_finish", null);
99+
await Promise.all([
100+
this.stdin.close_async(null),
101+
this.stdout.close_async(null),
102+
]);
81103
// this.proc?.force_exit();
82104
this.proc.send_signal(15);
83105
}
@@ -134,15 +156,13 @@ export default class LSPClient {
134156
flags = flags | Gio.SubprocessFlags.STDERR_SILENCE;
135157

136158
this.proc = Gio.Subprocess.new(this.argv, flags);
137-
this.proc.wait_async(null, (_self, res) => {
138-
try {
139-
this.proc.wait_finish(res);
140-
} catch (err) {
141-
logError(err);
142-
}
143-
this.emit("exit");
144-
// this._start_process();
145-
});
159+
this.proc
160+
.wait_async(null)
161+
.then(() => {
162+
this.emit("exit");
163+
// this._start_process();
164+
})
165+
.catch(logError);
146166
this.stdin = this.proc.get_stdin_pipe();
147167
this.stdout = new Gio.DataInputStream({
148168
base_stream: this.proc.get_stdout_pipe(),
@@ -156,11 +176,8 @@ export default class LSPClient {
156176
const headers = Object.create(null);
157177

158178
while (true) {
159-
const [bytes] = await promiseTask(
160-
this.stdout,
161-
"read_line_async",
162-
"read_line_finish",
163-
0,
179+
const [bytes] = await this.stdout.read_line_async(
180+
GLib.PRIORITY_DEFAULT,
164181
null,
165182
);
166183
if (!bytes) break;
@@ -177,15 +194,23 @@ export default class LSPClient {
177194
}
178195

179196
async _read_content(length) {
180-
const bytes = await promiseTask(
181-
this.stdout,
182-
"read_bytes_async",
183-
"read_bytes_finish",
184-
length,
185-
0,
186-
null,
187-
);
188-
const str = decoder_utf8.decode(bytes.toArray());
197+
// read_bytes is limited by some underlying max buffer size
198+
// see https://github.com/sonnyp/Workbench/issues/240#issuecomment-1475387647
199+
// read_all is not supported in GJS so we do this instead
200+
// https://gitlab.gnome.org/GNOME/gjs/-/issues/501
201+
const uint8 = new Uint8Array(length);
202+
let read = 0;
203+
while (read < length) {
204+
const bytes = await this.stdout.read_bytes_async(
205+
length - read,
206+
GLib.PRIORITY_DEFAULT,
207+
null,
208+
);
209+
uint8.set(bytes.toArray(), read);
210+
read += bytes.get_size();
211+
}
212+
213+
const str = decoder_utf8.decode(uint8);
189214
try {
190215
return JSON.parse(str);
191216
} catch (err) {
@@ -232,14 +257,7 @@ export default class LSPClient {
232257
this.stdin.flush();
233258
}
234259

235-
await promiseTask(
236-
this.stdin,
237-
"write_bytes_async",
238-
"write_bytes_finish",
239-
bytes,
240-
GLib.PRIORITY_DEFAULT,
241-
null,
242-
);
260+
await this.stdin.write_bytes_async(bytes, GLib.PRIORITY_DEFAULT, null);
243261

244262
this.emit("output", message);
245263
}
@@ -253,7 +271,7 @@ export default class LSPClient {
253271
});
254272
const [result] = await once(this, `result::${id}`, {
255273
error: `error::${id}`,
256-
timeout: 1000,
274+
timeout: 5000,
257275
});
258276
return result;
259277
}

0 commit comments

Comments
 (0)