From ac022a41041328e872e605db9cd936c904153334 Mon Sep 17 00:00:00 2001 From: Julien Vincent Date: Sun, 8 Jan 2023 22:12:07 +0000 Subject: [PATCH 1/3] Throttle printing repl output to output window When an excessive amount of output is produced by the repl (for example as a result of a rogue loop that is printing to stdout) Calva can sometimes hang while trying to write all the output to the output window/file. The only way to resolve is to restart/reload the VSCode window. This commit introduces a new config entry `replOutputThrottleRate` which when set to a non-0 number will throttle output from the repl connection. If more output items are received than the throttle rate in a 500ms window then they will just be dropped. Addresses #942 Fixes #2010 --- CHANGELOG.md | 1 + package.json | 5 +++++ src/config.ts | 3 +++ src/results-output/results-doc.ts | 37 +++++++++++++++++++++++++++++++ 4 files changed, 46 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b496252d6..e26f622f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ Changes to Calva. ## [Unreleased] +- Fix: [Rogue loops that print output to stdout cause Calva to hang](https://github.com/BetterThanTomorrow/calva/issues/2010) ## [2.0.323] - 2023-01-07 - Fix: [Provider completions not handling errors gracefully](https://github.com/BetterThanTomorrow/calva/issues/2006) diff --git a/package.json b/package.json index 55340976e..67818a754 100644 --- a/package.json +++ b/package.json @@ -770,6 +770,11 @@ "lsp" ] }, + "calva.replOutputThrottleRate": { + "markdownDescription": "If the repl outputs too quickly then results will be dropped from the output window. Setting this to 0 will disable throttling.", + "type": "number", + "default": 100 + }, "calva.depsEdnJackInExecutable": { "markdownDescription": "Which executable should Calva Jack-in use for starting a deps.edn project? The default is to let Calva choose. It will choose `clojure` if that is installed and working. Otherwise `deps.clj`, which is bundled with Calva, will be used. (This settings has no effect on Windows, where `deps.clj` will always be used.)", "enum": [ diff --git a/src/config.ts b/src/config.ts index c61b7d911..c8745413b 100644 --- a/src/config.ts +++ b/src/config.ts @@ -14,6 +14,8 @@ const REPL_FILE_EXT = 'calva-repl'; const KEYBINDINGS_ENABLED_CONFIG_KEY = 'calva.keybindingsEnabled'; const KEYBINDINGS_ENABLED_CONTEXT_KEY = 'calva:keybindingsEnabled'; +const REPL_OUTPUT_THROTTLE_RATE_CONFIG_KEY = 'calva.replOutputThrottleRate'; + type ReplSessionType = 'clj' | 'cljs'; // include the 'file' and 'untitled' to the @@ -234,6 +236,7 @@ export { REPL_FILE_EXT, KEYBINDINGS_ENABLED_CONFIG_KEY, KEYBINDINGS_ENABLED_CONTEXT_KEY, + REPL_OUTPUT_THROTTLE_RATE_CONFIG_KEY, documentSelector, ReplSessionType, getConfig, diff --git a/src/results-output/results-doc.ts b/src/results-output/results-doc.ts index 480e78953..1b65259f9 100644 --- a/src/results-output/results-doc.ts +++ b/src/results-output/results-doc.ts @@ -17,6 +17,9 @@ import { formatAsLineComments, splitEditQueueForTextBatching } from './util'; const RESULTS_DOC_NAME = `output.${config.REPL_FILE_EXT}`; +const REPL_OUTPUT_THROTTLE_RATE = vscode.workspace + .getConfiguration() + .get(config.REPL_OUTPUT_THROTTLE_RATE_CONFIG_KEY); const PROMPT_HINT = 'Use `alt+enter` to evaluate'; const START_GREETINGS = [ @@ -331,6 +334,16 @@ export interface OnAppendedCallback { let resultsBuffer: ResultsBuffer = []; +type BufferThrottleState = { + count: number; + dropped: number; + timeout?: NodeJS.Timeout; +}; +const throttleState: BufferThrottleState = { + count: 0, + dropped: 0, +}; + async function writeNextOutputBatch() { if (!resultsBuffer[0]) { return; @@ -366,6 +379,30 @@ async function flushOutput() { /* If something must be done after a particular edit, use the onAppended callback. */ export function append(text: string, onAppended?: OnAppendedCallback): void { + if (REPL_OUTPUT_THROTTLE_RATE > 0) { + throttleState.count++; + + if (!throttleState.timeout) { + throttleState.timeout = setTimeout(() => { + if (throttleState.dropped > 0) { + resultsBuffer.push({ + text: `;; Dropped ${throttleState.dropped} items from output due to throttling\n`, + }); + void flushOutput(); + } + + throttleState.timeout = undefined; + throttleState.count = 0; + throttleState.dropped = 0; + }, 500); + } + + if (throttleState.count > REPL_OUTPUT_THROTTLE_RATE) { + throttleState.dropped++; + return; + } + } + resultsBuffer.push({ text, onAppended }); void flushOutput(); } From 99b6e9de52012a8bd02ec72d781338e0738c9119 Mon Sep 17 00:00:00 2001 From: Julien Vincent Date: Sun, 8 Jan 2023 22:18:57 +0000 Subject: [PATCH 2/3] Truncate repl output as length crosses threshold Calvas performance can be drastically affected as the size of the repl output window grows. This commit adds a config entry `replOutputMaxLines` which if set to a non-0 number will cause the output window to be truncated if it grows beyond this threshold. Fixes #804 --- CHANGELOG.md | 3 +++ package.json | 5 +++++ src/config.ts | 2 ++ src/results-output/results-doc.ts | 16 ++++++++++++++++ src/results-output/util.ts | 2 +- 5 files changed, 27 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e26f622f7..a10308ccd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ Changes to Calva. ## [Unreleased] - Fix: [Rogue loops that print output to stdout cause Calva to hang](https://github.com/BetterThanTomorrow/calva/issues/2010) +- Fix: [Output window becomes very slow when number of lines of content is very high](https://github.com/BetterThanTomorrow/calva/issues/804) +- Partial Fix: [Output window becomes very slow when number of lines of content is very high](https://github.com/BetterThanTomorrow/calva/issues/942) + ## [2.0.323] - 2023-01-07 - Fix: [Provider completions not handling errors gracefully](https://github.com/BetterThanTomorrow/calva/issues/2006) diff --git a/package.json b/package.json index 67818a754..d0add65a8 100644 --- a/package.json +++ b/package.json @@ -775,6 +775,11 @@ "type": "number", "default": 100 }, + "calva.replOutputMaxLines": { + "markdownDescription": "The maximum number of lines to retain in the repl output window. Having the repl output window grow too large can significantly affect performance. Setting this to 0 will disable truncating", + "type": "number", + "default": 1000 + }, "calva.depsEdnJackInExecutable": { "markdownDescription": "Which executable should Calva Jack-in use for starting a deps.edn project? The default is to let Calva choose. It will choose `clojure` if that is installed and working. Otherwise `deps.clj`, which is bundled with Calva, will be used. (This settings has no effect on Windows, where `deps.clj` will always be used.)", "enum": [ diff --git a/src/config.ts b/src/config.ts index c8745413b..2561c954a 100644 --- a/src/config.ts +++ b/src/config.ts @@ -15,6 +15,7 @@ const KEYBINDINGS_ENABLED_CONFIG_KEY = 'calva.keybindingsEnabled'; const KEYBINDINGS_ENABLED_CONTEXT_KEY = 'calva:keybindingsEnabled'; const REPL_OUTPUT_THROTTLE_RATE_CONFIG_KEY = 'calva.replOutputThrottleRate'; +const REPL_OUTPUT_MAX_LINES_CONFIG_KEY = 'calva.replOutputMaxLines'; type ReplSessionType = 'clj' | 'cljs'; @@ -237,6 +238,7 @@ export { KEYBINDINGS_ENABLED_CONFIG_KEY, KEYBINDINGS_ENABLED_CONTEXT_KEY, REPL_OUTPUT_THROTTLE_RATE_CONFIG_KEY, + REPL_OUTPUT_MAX_LINES_CONFIG_KEY, documentSelector, ReplSessionType, getConfig, diff --git a/src/results-output/results-doc.ts b/src/results-output/results-doc.ts index 1b65259f9..028db4b70 100644 --- a/src/results-output/results-doc.ts +++ b/src/results-output/results-doc.ts @@ -20,6 +20,10 @@ const RESULTS_DOC_NAME = `output.${config.REPL_FILE_EXT}`; const REPL_OUTPUT_THROTTLE_RATE = vscode.workspace .getConfiguration() .get(config.REPL_OUTPUT_THROTTLE_RATE_CONFIG_KEY); +const REPL_OUTPUT_MAX_LINES = vscode.workspace + .getConfiguration() + .get(config.REPL_OUTPUT_MAX_LINES_CONFIG_KEY); + const PROMPT_HINT = 'Use `alt+enter` to evaluate'; const START_GREETINGS = [ @@ -306,10 +310,22 @@ async function writeToResultsDoc({ text, onAppended }: ResultsBufferEntry): Prom const insertPosition = doc.positionAt(Infinity); const edit = new vscode.WorkspaceEdit(); const editText = util.stripAnsi(text); + + if (REPL_OUTPUT_MAX_LINES > 0 && doc.lineCount > REPL_OUTPUT_MAX_LINES) { + edit.delete( + docUri, + new vscode.Range( + new vscode.Position(0, 0), + new vscode.Position(doc.lineCount - REPL_OUTPUT_MAX_LINES, 0) + ) + ); + } + edit.insert(docUri, insertPosition, editText); if (!((await vscode.workspace.applyEdit(edit)) && (await doc.save()))) { return; } + onAppended?.( new vscode.Location(docUri, insertPosition), new vscode.Location(docUri, doc.positionAt(Infinity)) diff --git a/src/results-output/util.ts b/src/results-output/util.ts index 09d757d9a..3fc740bbd 100644 --- a/src/results-output/util.ts +++ b/src/results-output/util.ts @@ -24,7 +24,7 @@ function splitEditQueueForTextBatching( const nextBatch = takeWhile(editQueue, (value, index) => { return index < maxBatchSize && !value.onAppended; }).map((x) => x.text); - const remainingEditQueue = [...editQueue].slice(nextBatch.length); + const remainingEditQueue = editQueue.slice(nextBatch.length); return [nextBatch, remainingEditQueue]; } From eaa5493b92e156748a4de1856bde47c05081fa99 Mon Sep 17 00:00:00 2001 From: Julien Vincent Date: Tue, 10 Jan 2023 10:14:25 +0000 Subject: [PATCH 3/3] Fix incorrect changelog entry title MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Peter Strömberg --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a10308ccd..2255c30bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ Changes to Calva. - Fix: [Rogue loops that print output to stdout cause Calva to hang](https://github.com/BetterThanTomorrow/calva/issues/2010) - Fix: [Output window becomes very slow when number of lines of content is very high](https://github.com/BetterThanTomorrow/calva/issues/804) -- Partial Fix: [Output window becomes very slow when number of lines of content is very high](https://github.com/BetterThanTomorrow/calva/issues/942) +- Partial Fix: [REPL is Slow and Performance Degrades as the Output Grows](https://github.com/BetterThanTomorrow/calva/issues/942) ## [2.0.323] - 2023-01-07