Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sets a hard limit of number of scopes to check for completions #1244

Merged
merged 3 commits into from
Jul 8, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 31 additions & 6 deletions src/bscPlugin/completions/CompletionsProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ import { createIdentifier } from '../../astUtils/creators';
import type { FunctionExpression } from '../../parser/Expression';
import { LogLevel } from '../../Logger';


const SCOPES_FOR_COMPLETION = 3;

export class CompletionsProcessor {
constructor(
private event: ProvideCompletionsEvent
Expand All @@ -34,11 +37,9 @@ export class CompletionsProcessor {
public process() {
let file = this.event.file;
this.event.program.logger.time(LogLevel.log, ['Processing completions'], () => {
//find the scopes for this file
let scopesForFile = this.event.program.getScopesForFile(file);

//if there are no scopes, include the global scope so we at least get the built-in functions
scopesForFile = scopesForFile.length > 0 ? scopesForFile : [this.event.program.globalScope];
// Find the scopes for this file - Only process the first few scopes
const scopesToProcess = this.getScopesForCompletion(file);

//get the completions from all scopes for this file
let completionResults: CompletionItem[] = [];
Expand All @@ -53,6 +54,7 @@ export class CompletionsProcessor {
this.event.completions.push(...this.getScriptImportCompletions(file.program, file.pkgPath, scriptImport));
return;
}

const results = this.getBrsFileCompletions(this.event.position, file);
completionResults = results.scoped;
globalResults = results.global;
Expand All @@ -67,13 +69,35 @@ export class CompletionsProcessor {
for (let completion of allCompletions) {
let key = `${completion.label}-${completion.kind}`;
keyCounts.set(key, keyCounts.has(key) ? keyCounts.get(key) + 1 : 1);
if (keyCounts.get(key) === scopesForFile.length) {
if (keyCounts.get(key) === scopesToProcess.length) {
this.event.completions.push(completion);
}
}
});
}

private getScopesForCompletion(file: BscFile) {
//find the scopes for this file
let scopesForFile = this.event.program?.getScopesForFile(file) ?? [];

//if there are no scopes, include the global scope so we at least get the built-in functions
if (this.event.program) {
scopesForFile = scopesForFile.length > 0 ? scopesForFile : [this.event.program.globalScope];
}

// Only process the first few scopes. This might result in missing completions,
// but it's better than wasting TONS of cycles building essentially the same completions over and over
const scopesToProcess = scopesForFile.slice(0, SCOPES_FOR_COMPLETION);

// always include the source scope if applicable to this file
let sourceScope = scopesForFile.find(x => x.name === 'source');
if (sourceScope && !scopesToProcess.includes(sourceScope)) {
//replace the first scope with the source scope so we process a consistent number of scopes if possible
scopesToProcess[0] = sourceScope;
}
return scopesToProcess;
}


/**
* Get all available completions for the specified position
Expand Down Expand Up @@ -265,7 +289,8 @@ export class CompletionsProcessor {
globalCompletions.push(...this.getSymbolsCompletion(globalSymbols));
}

for (const scope of this.event.scopes) {
const scopesToProcess = this.getScopesForCompletion(file);
for (const scope of scopesToProcess) {
if (tokenKind === TokenKind.StringLiteral || tokenKind === TokenKind.TemplateStringQuasi) {
result.push(...this.getStringLiteralCompletions(scope, currentToken));
continue;
Expand Down