implement shceduleReparse to reduce parsing load while typing

This commit is contained in:
Dave Holoway
2020-06-26 21:35:57 +01:00
parent 8aaf6a6f0e
commit 43dbcb4d47

View File

@@ -92,7 +92,49 @@ class JavaDocInfo {
this.version = version; this.version = version;
/** @type {ParsedInfo} */ /** @type {ParsedInfo} */
this.parsed = null; this.parsed = null;
/** @type {Promise} */
this.reparseWaiter = Promise.resolve();
/** @type {{ resolve: () => void, timer: * }} */
this.waitInfo = null;
} }
/**
* Schedule this document for reparsing.
*
* To prevent redundant parsing while typing, a small delay is required
* before the reparse happens.
* When a key is pressed, `scheduleReparse()` starts a timer. If more
* keys are typed before the timer expires, the timer is restarted.
* Once typing pauses, the timer expires and the content reparsed.
*
* A `reparseWaiter` promise is used to delay the completion items
* retrieval until the reparse is complete.
*/
scheduleReparse() {
const createWaitTimer = () => {
return setTimeout(() => {
// reparse the content, resolve the reparseWaiter promise
// and reset the fields
reparse([this.uri], { includeMethods: true });
this.waitInfo.resolve();
this.waitInfo = null;
}, 250);
}
if (this.waitInfo) {
// we already have a promise pending - just restart the timer
trace('restart timer');
clearTimeout(this.waitInfo.timer);
this.waitInfo.timer = createWaitTimer();
return;
}
// create a new pending promise and start the timer
trace('start timer');
this.waitInfo = {
resolve: null,
timer: createWaitTimer(),
}
this.reparseWaiter = new Promise(resolve => this.waitInfo.resolve = resolve);
}
} }
class ParsedInfo { class ParsedInfo {
@@ -129,6 +171,7 @@ let first_parse_waiting = new Set();
* @param {{includeMethods: boolean, first_parse?: boolean}} [opts] * @param {{includeMethods: boolean, first_parse?: boolean}} [opts]
*/ */
function reparse(uris, opts) { function reparse(uris, opts) {
trace('reparse');
if (androidLibrary instanceof Promise) { if (androidLibrary instanceof Promise) {
return; return;
} }
@@ -233,7 +276,10 @@ let documents = new TextDocuments({
docinfo.content = `${docinfo.content.slice(0, start_index)}${change.text}${docinfo.content.slice(end_index)}`; docinfo.content = `${docinfo.content.slice(0, start_index)}${change.text}${docinfo.content.slice(end_index)}`;
} }
}); });
reparse([document.uri], { includeMethods: true });
docinfo.version = version;
docinfo.scheduleReparse();
return document; return document;
}, },
}); });
@@ -789,6 +835,18 @@ connection.onCompletion(
if (!docinfo || !docinfo.parsed) { if (!docinfo || !docinfo.parsed) {
return []; return [];
} }
trace('reparse waiter - ' + docinfo.version);
const preversion = docinfo.version;
await docinfo.reparseWaiter;
trace('retrieving completion items - ' + docinfo.version);
if (docinfo.version !== preversion) {
// if the content has changed, ignore the current request
/** @type {import('vscode-languageserver').CompletionList} */
return {
isIncomplete: true,
items: [],
}
}
const parsed = docinfo.parsed; const parsed = docinfo.parsed;
lastCompletionTypeMap = (parsed && parsed.typemap) || androidLibrary; lastCompletionTypeMap = (parsed && parsed.typemap) || androidLibrary;
let locals = [], sourceTypes = [], show_instances = false; let locals = [], sourceTypes = [], show_instances = false;
@@ -849,7 +907,10 @@ connection.onCompletion(
...[ ...[
...defaultCompletionTypes.types, ...defaultCompletionTypes.types,
...sourceTypes, ...sourceTypes,
].sort(sortBy.label), ] // exclude dotted (inner) types because they result in useless
// matches in the intellisense filter when . is pressed
.filter(x => !x.label.includes('.'))
.sort(sortBy.label),
...defaultCompletionTypes.packageNames, ...defaultCompletionTypes.packageNames,
].map((x,idx) => { ].map((x,idx) => {
x.sortText = `${10000+idx}-${x.label}`; x.sortText = `${10000+idx}-${x.label}`;