microsoft / monaco-editor

A browser based code editor
https://microsoft.github.io/monaco-editor/
MIT License
40.23k stars 3.58k forks source link

[Bug] Loading bulk models is causing major delay in intellisense #3355

Open manikantag opened 2 years ago

manikantag commented 2 years ago

Reproducible in vscode.dev or in VS Code Desktop?

Reproducible in the monaco editor playground?

Monaco Editor Playground Code

// ========== Example #1: Loading .definition models instead of .ts ======================
monaco.languages.typescript.javascriptDefaults.setEagerModelSync(false);
monaco.languages.typescript.typescriptDefaults.setEagerModelSync(false);

monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({
    noSemanticValidation: true,
    noSyntaxValidation: true,
    noSuggestionDiagnostics: true
});

monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({
    noSemanticValidation: true,
    noSyntaxValidation: true,
    noSuggestionDiagnostics: true
});

monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
    target: monaco.languages.typescript.ScriptTarget.ES2016,
    allowNonTsExtensions: true,
    moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
    module: monaco.languages.typescript.ModuleKind.CommonJS,
    noEmit: true,
    typeRoots: ["node_modules/@types"]
});

// Case #1: Load 2000 .d.ts files with each having 1 function declaration
let extraLibs = []
for (let i = 0; i < 2000; i++) {
    extraLibs.push(
        {
        content: `export declare function next${i}() : string`,
        filePath:  monaco.Uri.file(`/node_modules/@types/external${i}/index.d.ts`).toString(true)
        }
    );
}

// Case #2: Load 1 .d.ts file with 2000 function declarations
/*let def = ''
for (let i = 0; i < 2000; i++) {
    def = def.concat(`export declare function next${i}() : string\n`);
}
let extraLibs = []
extraLibs.push(
    {
    content: def,
    filePath:  monaco.Uri.file(`/node_modules/@types/external1/index.d.ts`).toString(true)
    }
);*/

monaco.languages.typescript.typescriptDefaults.setExtraLibs(extraLibs);

extraLibs.forEach((lib) => monaco.editor.createModel(lib.content, "typescript", monaco.Uri.parse(lib.filePath)))

var jsCode = `import * as x from "external1"
const tt : string = x.next1();`;

monaco.editor.create(document.getElementById("container"), {
    model: monaco.editor.createModel(jsCode, "typescript", monaco.Uri.file("main.tsx")),
});

// ========== Example #2: Loading .ts models instead of definitions ======================
/*monaco.languages.typescript.javascriptDefaults.setEagerModelSync(true);
monaco.languages.typescript.typescriptDefaults.setEagerModelSync(true);

monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({
    noSemanticValidation: true,
    noSyntaxValidation: true,
    noSuggestionDiagnostics: true
});

monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({
    noSemanticValidation: true,
    noSyntaxValidation: true,
    noSuggestionDiagnostics: true
});

editor = monaco.editor.create(
    document.getElementById('container'),
    {
        minimap: { enabled: true },
        scrollBeyondLastLine: true,
        // model: null,
        language: 'typescript',
        theme: 'vs-dark',
        value: 'function () {}\n\n'
    }
);

// simulate 2000 .ts files
for (var fileIdx = 0; fileIdx < 2000; fileIdx++) {
    for (var fnIdx = 0; fnIdx < 1; fnIdx++) {
        monaco.editor.createModel(`
            function test_${fileIdx}_${fnIdx}() {}
            function fn_${fileIdx}_${fnIdx}() {}
            function foo_${fileIdx}_${fnIdx}() {}
            function bar_${fileIdx}_${fnIdx}() {}
            function out_${fileIdx}_${fnIdx}() {}
            `,
            'typescript'
        );
    }
}*/

Reproduction Steps

Press Ctrl + Space after x. in the playground output (in the 2nd line: const tt : string = x.next1();). Suggestions dialog shows 'Loading...' indicator for 12-15 seconds before showing the eligible candidates.

for (let i = 0; i < 2000; i++) {
    extraLibs.push(
        {
        content: `export declare function next${i}() : string`,
        filePath:  monaco.Uri.file(`/node_modules/@types/external${i}/index.d.ts`).toString(true)
        }
    );
}

Actual (Problematic) Behavior

Adding bulk of definition or ts file models is causing the 'Loading...' indicator for 12-15 seconds for every intellisense request. Not just the initial request.

Expected Behavior

Intellisense requests should complete with in reasonable time which otherwise directly impacts the developer experience

Additional Context

I posted this as a question in Stackoverflow (https://stackoverflow.com/questions/73936684/performant-way-to-load-2000-ts-models-into-monaco-editor-for-intellisense), but the solutions suggested (loading definition files) also didn't worked.

My observation: When I have bulk of files (ex: 2000), then the intellisense is taking longer time. But when I add 200K function declarations to 1 definition file, the perf is very good.

manikantag commented 2 years ago

image

Just to see what is chocking the perf, I ran perf audit. Not sure if it is expected, but the same methods (like, getCodeFixesAtPosition) is getting called multiple times even for the same caret position.

Can this perf issue be solved if I use standalone typescript language server? Thanks

hediet commented 1 year ago

I can reproduce. However, what do you need this for:

extraLibs.forEach((lib) => monaco.editor.createModel(lib.content, "typescript", monaco.Uri.parse(lib.filePath)))
manikantag commented 1 year ago

Thanks for confirming the issue.

I'm adding all the source files as models so that I'll get IntelliSense from all of them. Is there a way to get the same without adding them as models?

Thanks.