@theia/ai-code-completion 1.72.0-next.5 → 1.72.0-next.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/browser/code-completion-agent.d.ts.map +1 -1
- package/lib/browser/code-completion-agent.js +5 -1
- package/lib/browser/code-completion-agent.js.map +1 -1
- package/lib/browser/code-completion-agent.spec.d.ts +2 -0
- package/lib/browser/code-completion-agent.spec.d.ts.map +1 -0
- package/lib/browser/code-completion-agent.spec.js +172 -0
- package/lib/browser/code-completion-agent.spec.js.map +1 -0
- package/lib/browser/code-completion-variable-contribution.spec.js +36 -17
- package/lib/browser/code-completion-variable-contribution.spec.js.map +1 -1
- package/package.json +8 -8
- package/src/browser/code-completion-agent.spec.ts +208 -0
- package/src/browser/code-completion-agent.ts +7 -1
- package/src/browser/code-completion-variable-contribution.spec.ts +36 -20
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code-completion-agent.d.ts","sourceRoot":"","sources":["../../src/browser/code-completion-agent.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EACH,KAAK,EAAE,sBAAsB,EAC7B,qBAAqB,EAAE,wBAAwB,EAAE,aAAa,EAC9D,gBAAgB,EAEnB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAgB,OAAO,EAAO,eAAe,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"code-completion-agent.d.ts","sourceRoot":"","sources":["../../src/browser/code-completion-agent.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EACH,KAAK,EAAE,sBAAsB,EAC7B,qBAAqB,EAAE,wBAAwB,EAAE,aAAa,EAC9D,gBAAgB,EAEnB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAgB,OAAO,EAAO,eAAe,EAAE,MAAM,aAAa,CAAC;AAG1E,OAAO,KAAK,MAAM,MAAM,2BAA2B,CAAC;AAEpD,OAAO,EAAE,2BAA2B,EAAE,MAAM,iCAAiC,CAAC;AAI9E,eAAO,MAAM,mBAAmB,eAAgC,CAAC;AACjE,MAAM,WAAW,mBAAoB,SAAQ,KAAK;IAC9C,wBAAwB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAC/E,OAAO,EAAE,MAAM,CAAC,SAAS,CAAC,uBAAuB,EAAE,KAAK,EAAE,MAAM,CAAC,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAAA;CACnJ;AAED,qBACa,uBAAwB,YAAW,mBAAmB;IAE/D,SAAS,CAAC,oBAAoB,EAAE,oBAAoB,CAAC;IAE/C,wBAAwB,CAC1B,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,UAAU,EAC/B,QAAQ,EAAE,MAAM,CAAC,QAAQ,EACzB,OAAO,EAAE,MAAM,CAAC,SAAS,CAAC,uBAAuB,EACjD,KAAK,EAAE,MAAM,CAAC,iBAAiB,GAChC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,iBAAiB,GAAG,SAAS,CAAC;IAuF1D,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC;IAG1B,SAAS,CAAC,qBAAqB,EAAE,qBAAqB,CAAC;IAGvD,SAAS,CAAC,aAAa,EAAE,aAAa,CAAC;IAGvC,SAAS,CAAC,eAAe,EAAE,eAAe,CAAC;IAG3C,SAAS,CAAC,aAAa,EAAE,2BAA2B,CAAC;IAErD,EAAE,SAAqB;IACvB,IAAI,SAAqB;IACzB,WAAW,SAGsD;IACjE,OAAO,EAAE,gBAAgB,EAAE,CAAyB;IACpD,yBAAyB,EAAE,wBAAwB,EAAE,CAKnD;IACF,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,CAAM;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,CAAM;IAClC,QAAQ,CAAC,sBAAsB,EAAE,sBAAsB,EAAE,CAKvD;CACL"}
|
|
@@ -20,6 +20,7 @@ const tslib_1 = require("tslib");
|
|
|
20
20
|
const browser_1 = require("@theia/ai-core/lib/browser");
|
|
21
21
|
const common_1 = require("@theia/ai-core/lib/common");
|
|
22
22
|
const core_1 = require("@theia/core");
|
|
23
|
+
const frontend_application_config_provider_1 = require("@theia/core/lib/browser/frontend-application-config-provider");
|
|
23
24
|
const inversify_1 = require("@theia/core/shared/inversify");
|
|
24
25
|
const monaco = require("@theia/monaco-editor-core");
|
|
25
26
|
const code_completion_prompt_template_1 = require("./code-completion-prompt-template");
|
|
@@ -30,7 +31,7 @@ let CodeCompletionAgentImpl = class CodeCompletionAgentImpl {
|
|
|
30
31
|
constructor() {
|
|
31
32
|
this.id = 'Code Completion';
|
|
32
33
|
this.name = 'Code Completion';
|
|
33
|
-
this.description = core_1.nls.localize('theia/ai/completion/agent/description', 'This agent provides inline code completion in the code editor in
|
|
34
|
+
this.description = core_1.nls.localize('theia/ai/completion/agent/description', 'This agent provides inline code completion in the code editor in {0}.', frontend_application_config_provider_1.FrontendApplicationConfigProvider.get().applicationName);
|
|
34
35
|
this.prompts = code_completion_prompt_template_1.codeCompletionPrompts;
|
|
35
36
|
this.languageModelRequirements = [
|
|
36
37
|
{
|
|
@@ -82,6 +83,9 @@ let CodeCompletionAgentImpl = class CodeCompletionAgentImpl {
|
|
|
82
83
|
settings: {
|
|
83
84
|
stream: false
|
|
84
85
|
},
|
|
86
|
+
// Inline completion is a one-shot text replacement; reasoning/thinking adds latency
|
|
87
|
+
// and can cause models (e.g. Anthropic adaptive thinking) to emit no text at all.
|
|
88
|
+
reasoning: { level: 'off' },
|
|
85
89
|
agentId: this.id,
|
|
86
90
|
sessionId,
|
|
87
91
|
requestId,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code-completion-agent.js","sourceRoot":"","sources":["../../src/browser/code-completion-agent.ts"],"names":[],"mappings":";AAAA,gFAAgF;AAChF,yCAAyC;AACzC,EAAE;AACF,2EAA2E;AAC3E,mEAAmE;AACnE,wCAAwC;AACxC,EAAE;AACF,4EAA4E;AAC5E,8EAA8E;AAC9E,6EAA6E;AAC7E,yDAAyD;AACzD,uDAAuD;AACvD,EAAE;AACF,gFAAgF;AAChF,gFAAgF;;;;AAEhF,wDAAkE;AAClE,sDAKmC;AACnC,sCAA0E;AAC1E,4DAAyE;AACzE,oDAAoD;AACpD,uFAA0E;AAC1E,mFAA8E;AAE9E,2EAA6E;AAEhE,QAAA,mBAAmB,GAAG,MAAM,CAAC,qBAAqB,CAAC,CAAC;AAO1D,IAAM,uBAAuB,GAA7B,MAAM,uBAAuB;IAA7B;
|
|
1
|
+
{"version":3,"file":"code-completion-agent.js","sourceRoot":"","sources":["../../src/browser/code-completion-agent.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-completion-agent.spec.d.ts","sourceRoot":"","sources":["../../src/browser/code-completion-agent.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// *****************************************************************************
|
|
3
|
+
// Copyright (C) 2026 EclipseSource and others.
|
|
4
|
+
//
|
|
5
|
+
// This program and the accompanying materials are made available under the
|
|
6
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
7
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
8
|
+
//
|
|
9
|
+
// This Source Code may also be made available under the following Secondary
|
|
10
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
11
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
12
|
+
// with the GNU Classpath Exception which is available at
|
|
13
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
14
|
+
//
|
|
15
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
16
|
+
// *****************************************************************************
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
const jsdom_1 = require("@theia/core/lib/browser/test/jsdom");
|
|
19
|
+
let disableJSDOM = (0, jsdom_1.enableJSDOM)();
|
|
20
|
+
const frontend_application_config_provider_1 = require("@theia/core/lib/browser/frontend-application-config-provider");
|
|
21
|
+
frontend_application_config_provider_1.FrontendApplicationConfigProvider.set({});
|
|
22
|
+
const chai_1 = require("chai");
|
|
23
|
+
const sinon = require("sinon");
|
|
24
|
+
const editor_api_1 = require("@theia/monaco-editor-core/esm/vs/editor/editor.api");
|
|
25
|
+
const code_completion_agent_1 = require("./code-completion-agent");
|
|
26
|
+
disableJSDOM();
|
|
27
|
+
describe('CodeCompletionAgentImpl', () => {
|
|
28
|
+
let agent;
|
|
29
|
+
let model;
|
|
30
|
+
let sendRequestStub;
|
|
31
|
+
let capturedRequest;
|
|
32
|
+
let postProcessStub;
|
|
33
|
+
const inlineContext = {
|
|
34
|
+
triggerKind: editor_api_1.languages.InlineCompletionTriggerKind.Automatic,
|
|
35
|
+
selectedSuggestionInfo: undefined,
|
|
36
|
+
includeInlineEdits: false,
|
|
37
|
+
includeInlineCompletions: false,
|
|
38
|
+
requestIssuedDateTime: Date.now(),
|
|
39
|
+
earliestShownDateTime: Date.now()
|
|
40
|
+
};
|
|
41
|
+
before(() => {
|
|
42
|
+
disableJSDOM = (0, jsdom_1.enableJSDOM)();
|
|
43
|
+
frontend_application_config_provider_1.FrontendApplicationConfigProvider.set({});
|
|
44
|
+
});
|
|
45
|
+
beforeEach(() => {
|
|
46
|
+
// Minimal stub: the agent only passes the model through to promptService (stubbed below).
|
|
47
|
+
model = { uri: editor_api_1.Uri.file('/tmp/code-completion-agent.spec.ts') };
|
|
48
|
+
capturedRequest = undefined;
|
|
49
|
+
sendRequestStub = sinon.stub().callsFake((_lm, req) => {
|
|
50
|
+
capturedRequest = req;
|
|
51
|
+
return Promise.resolve({ text: 'COMPLETION' });
|
|
52
|
+
});
|
|
53
|
+
postProcessStub = sinon.stub().callsFake((text) => text);
|
|
54
|
+
agent = new code_completion_agent_1.CodeCompletionAgentImpl();
|
|
55
|
+
Object.assign(agent, {
|
|
56
|
+
languageModelService: { sendRequest: sendRequestStub },
|
|
57
|
+
languageModelRegistry: {
|
|
58
|
+
selectLanguageModel: sinon.stub().resolves({ id: 'fake/model' })
|
|
59
|
+
},
|
|
60
|
+
promptService: {
|
|
61
|
+
getResolvedPromptFragment: sinon.stub().resolves({ text: 'prompt text' }),
|
|
62
|
+
getPromptVariantInfo: sinon.stub().returns({ variantId: 'v1', isCustomized: false })
|
|
63
|
+
},
|
|
64
|
+
progressService: {
|
|
65
|
+
showProgress: sinon.stub().resolves({ cancel: () => { } })
|
|
66
|
+
},
|
|
67
|
+
postProcessor: { postProcess: postProcessStub },
|
|
68
|
+
logger: { error: () => { } }
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
afterEach(() => {
|
|
72
|
+
sinon.restore();
|
|
73
|
+
});
|
|
74
|
+
after(() => {
|
|
75
|
+
disableJSDOM();
|
|
76
|
+
});
|
|
77
|
+
it('disables reasoning on the LLM request', async () => {
|
|
78
|
+
const tokenSource = new editor_api_1.CancellationTokenSource();
|
|
79
|
+
await agent.provideInlineCompletions(model, new editor_api_1.Position(1, 1), inlineContext, tokenSource.token);
|
|
80
|
+
(0, chai_1.expect)(sendRequestStub.calledOnce).to.be.true;
|
|
81
|
+
(0, chai_1.expect)(capturedRequest?.reasoning).to.deep.equal({ level: 'off' });
|
|
82
|
+
});
|
|
83
|
+
it('disables streaming on the LLM request', async () => {
|
|
84
|
+
const tokenSource = new editor_api_1.CancellationTokenSource();
|
|
85
|
+
await agent.provideInlineCompletions(model, new editor_api_1.Position(1, 1), inlineContext, tokenSource.token);
|
|
86
|
+
(0, chai_1.expect)(capturedRequest?.settings?.stream).to.equal(false);
|
|
87
|
+
});
|
|
88
|
+
it('forwards Monaco\'s cancellation token to the LLM request', async () => {
|
|
89
|
+
// Forwarded so explicit user cancellation (e.g. Esc) propagates to providers that honor it,
|
|
90
|
+
// such as Anthropic's `stream.abort()`.
|
|
91
|
+
const tokenSource = new editor_api_1.CancellationTokenSource();
|
|
92
|
+
await agent.provideInlineCompletions(model, new editor_api_1.Position(1, 1), inlineContext, tokenSource.token);
|
|
93
|
+
(0, chai_1.expect)(capturedRequest?.cancellationToken).to.equal(tokenSource.token);
|
|
94
|
+
});
|
|
95
|
+
it('discards the response when the token is cancelled before returning', async () => {
|
|
96
|
+
const tokenSource = new editor_api_1.CancellationTokenSource();
|
|
97
|
+
sendRequestStub.callsFake((_lm, req) => {
|
|
98
|
+
capturedRequest = req;
|
|
99
|
+
// Cancel after the request was issued so the post-`sendRequest` check fires.
|
|
100
|
+
tokenSource.cancel();
|
|
101
|
+
return Promise.resolve({ text: 'LATE_COMPLETION' });
|
|
102
|
+
});
|
|
103
|
+
const result = await agent.provideInlineCompletions(model, new editor_api_1.Position(1, 1), inlineContext, tokenSource.token);
|
|
104
|
+
(0, chai_1.expect)(result).to.be.undefined;
|
|
105
|
+
});
|
|
106
|
+
it('bails before sending the request when the token is already cancelled', async () => {
|
|
107
|
+
const tokenSource = new editor_api_1.CancellationTokenSource();
|
|
108
|
+
tokenSource.cancel();
|
|
109
|
+
const result = await agent.provideInlineCompletions(model, new editor_api_1.Position(1, 1), inlineContext, tokenSource.token);
|
|
110
|
+
(0, chai_1.expect)(result).to.be.undefined;
|
|
111
|
+
(0, chai_1.expect)(sendRequestStub.called).to.be.false;
|
|
112
|
+
});
|
|
113
|
+
it('returns undefined when no language model is available', async () => {
|
|
114
|
+
Object.assign(agent, {
|
|
115
|
+
languageModelRegistry: { selectLanguageModel: sinon.stub().resolves(undefined) }
|
|
116
|
+
});
|
|
117
|
+
const tokenSource = new editor_api_1.CancellationTokenSource();
|
|
118
|
+
const result = await agent.provideInlineCompletions(model, new editor_api_1.Position(1, 1), inlineContext, tokenSource.token);
|
|
119
|
+
(0, chai_1.expect)(result).to.be.undefined;
|
|
120
|
+
(0, chai_1.expect)(sendRequestStub.called).to.be.false;
|
|
121
|
+
});
|
|
122
|
+
it('returns undefined when no prompt is resolved', async () => {
|
|
123
|
+
Object.assign(agent, {
|
|
124
|
+
promptService: {
|
|
125
|
+
getResolvedPromptFragment: sinon.stub().resolves(undefined),
|
|
126
|
+
getPromptVariantInfo: sinon.stub().returns(undefined)
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
const tokenSource = new editor_api_1.CancellationTokenSource();
|
|
130
|
+
const result = await agent.provideInlineCompletions(model, new editor_api_1.Position(1, 1), inlineContext, tokenSource.token);
|
|
131
|
+
(0, chai_1.expect)(result).to.be.undefined;
|
|
132
|
+
(0, chai_1.expect)(sendRequestStub.called).to.be.false;
|
|
133
|
+
});
|
|
134
|
+
it('post-processes the completion text before returning it', async () => {
|
|
135
|
+
postProcessStub.callsFake((text) => `<<${text}>>`);
|
|
136
|
+
const tokenSource = new editor_api_1.CancellationTokenSource();
|
|
137
|
+
const result = await agent.provideInlineCompletions(model, new editor_api_1.Position(2, 3), inlineContext, tokenSource.token);
|
|
138
|
+
(0, chai_1.expect)(postProcessStub.calledOnceWithExactly('COMPLETION')).to.be.true;
|
|
139
|
+
(0, chai_1.expect)(result.items[0].insertText).to.equal('<<COMPLETION>>');
|
|
140
|
+
});
|
|
141
|
+
it('uses the requested cursor position as the completion range', async () => {
|
|
142
|
+
const tokenSource = new editor_api_1.CancellationTokenSource();
|
|
143
|
+
const result = await agent.provideInlineCompletions(model, new editor_api_1.Position(5, 7), inlineContext, tokenSource.token);
|
|
144
|
+
const range = result.items[0].range;
|
|
145
|
+
(0, chai_1.expect)(range.startLineNumber).to.equal(5);
|
|
146
|
+
(0, chai_1.expect)(range.startColumn).to.equal(7);
|
|
147
|
+
(0, chai_1.expect)(range.endLineNumber).to.equal(5);
|
|
148
|
+
(0, chai_1.expect)(range.endColumn).to.equal(7);
|
|
149
|
+
});
|
|
150
|
+
it('cancels the progress indicator regardless of outcome', async () => {
|
|
151
|
+
const progressCancel = sinon.spy();
|
|
152
|
+
Object.assign(agent, {
|
|
153
|
+
progressService: { showProgress: sinon.stub().resolves({ cancel: progressCancel }) }
|
|
154
|
+
});
|
|
155
|
+
const tokenSource = new editor_api_1.CancellationTokenSource();
|
|
156
|
+
await agent.provideInlineCompletions(model, new editor_api_1.Position(1, 1), inlineContext, tokenSource.token);
|
|
157
|
+
(0, chai_1.expect)(progressCancel.calledOnce).to.be.true;
|
|
158
|
+
});
|
|
159
|
+
it('swallows errors and clears the progress indicator', async () => {
|
|
160
|
+
const progressCancel = sinon.spy();
|
|
161
|
+
Object.assign(agent, {
|
|
162
|
+
progressService: { showProgress: sinon.stub().resolves({ cancel: progressCancel }) }
|
|
163
|
+
});
|
|
164
|
+
sendRequestStub.rejects(new Error('boom'));
|
|
165
|
+
sinon.stub(console, 'error');
|
|
166
|
+
const tokenSource = new editor_api_1.CancellationTokenSource();
|
|
167
|
+
const result = await agent.provideInlineCompletions(model, new editor_api_1.Position(1, 1), inlineContext, tokenSource.token);
|
|
168
|
+
(0, chai_1.expect)(result).to.be.undefined;
|
|
169
|
+
(0, chai_1.expect)(progressCancel.calledOnce).to.be.true;
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
//# sourceMappingURL=code-completion-agent.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-completion-agent.spec.js","sourceRoot":"","sources":["../../src/browser/code-completion-agent.spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,23 +15,51 @@
|
|
|
15
15
|
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
16
16
|
// *****************************************************************************
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
-
const frontend_application_config_provider_1 = require("@theia/core/lib/browser/frontend-application-config-provider");
|
|
19
|
-
const jsdom_1 = require("@theia/core/lib/browser/test/jsdom");
|
|
20
|
-
let disableJSDOM = (0, jsdom_1.enableJSDOM)();
|
|
21
|
-
frontend_application_config_provider_1.FrontendApplicationConfigProvider.set({});
|
|
22
18
|
const common_1 = require("@theia/core/lib/common");
|
|
23
19
|
const inversify_1 = require("@theia/core/shared/inversify");
|
|
24
20
|
const editor_api_1 = require("@theia/monaco-editor-core/esm/vs/editor/editor.api");
|
|
25
21
|
const chai_1 = require("chai");
|
|
26
|
-
const sinon = require("sinon");
|
|
27
22
|
const code_completion_variable_contribution_1 = require("./code-completion-variable-contribution");
|
|
28
23
|
const code_completion_variables_1 = require("./code-completion-variables");
|
|
29
|
-
|
|
24
|
+
// A minimal fake ITextModel covering just the methods the contribution touches.
|
|
25
|
+
// Avoids calling `editor.createModel`, which lazily initializes Monaco's
|
|
26
|
+
// StandaloneThemeService and requires browser globals (CSS, matchMedia) that
|
|
27
|
+
// JSDOM does not expose.
|
|
28
|
+
function createFakeModel(text, languageId, uri) {
|
|
29
|
+
const lines = text.split('\n');
|
|
30
|
+
return {
|
|
31
|
+
uri,
|
|
32
|
+
getLanguageId: () => languageId,
|
|
33
|
+
getLineCount: () => lines.length,
|
|
34
|
+
getLineMaxColumn: (line) => lines[line - 1].length + 1,
|
|
35
|
+
getPositionAt: (offset) => {
|
|
36
|
+
let remaining = offset;
|
|
37
|
+
for (let i = 0; i < lines.length; i++) {
|
|
38
|
+
if (remaining <= lines[i].length) {
|
|
39
|
+
return { lineNumber: i + 1, column: remaining + 1 };
|
|
40
|
+
}
|
|
41
|
+
remaining -= lines[i].length + 1; // account for '\n'
|
|
42
|
+
}
|
|
43
|
+
const last = lines.length - 1;
|
|
44
|
+
return { lineNumber: lines.length, column: lines[last].length + 1 };
|
|
45
|
+
},
|
|
46
|
+
getValueInRange: (range) => {
|
|
47
|
+
const parts = [];
|
|
48
|
+
for (let line = range.startLineNumber; line <= range.endLineNumber; line++) {
|
|
49
|
+
const lineText = lines[line - 1];
|
|
50
|
+
const start = line === range.startLineNumber ? range.startColumn - 1 : 0;
|
|
51
|
+
const end = line === range.endLineNumber ? range.endColumn - 1 : lineText.length;
|
|
52
|
+
parts.push(lineText.substring(start, end));
|
|
53
|
+
}
|
|
54
|
+
return parts.join('\n');
|
|
55
|
+
},
|
|
56
|
+
dispose: () => { },
|
|
57
|
+
};
|
|
58
|
+
}
|
|
30
59
|
describe('CodeCompletionVariableContribution', () => {
|
|
31
60
|
let contribution;
|
|
32
61
|
let model;
|
|
33
62
|
before(() => {
|
|
34
|
-
disableJSDOM = (0, jsdom_1.enableJSDOM)();
|
|
35
63
|
const container = new inversify_1.Container();
|
|
36
64
|
container.bind(common_1.PreferenceService).toConstantValue({
|
|
37
65
|
get: () => 1000,
|
|
@@ -40,16 +68,7 @@ describe('CodeCompletionVariableContribution', () => {
|
|
|
40
68
|
contribution = container.get(code_completion_variable_contribution_1.CodeCompletionVariableContribution);
|
|
41
69
|
});
|
|
42
70
|
beforeEach(() => {
|
|
43
|
-
model =
|
|
44
|
-
sinon.stub(model, 'getLanguageId').returns('javascript');
|
|
45
|
-
});
|
|
46
|
-
afterEach(() => {
|
|
47
|
-
model.dispose();
|
|
48
|
-
});
|
|
49
|
-
after(() => {
|
|
50
|
-
// Disable JSDOM after all tests
|
|
51
|
-
disableJSDOM();
|
|
52
|
-
model.dispose();
|
|
71
|
+
model = createFakeModel('//line 1\nconsole.\n//line 2', 'javascript', editor_api_1.Uri.file('/home/user/workspace/test.js'));
|
|
53
72
|
});
|
|
54
73
|
describe('canResolve', () => {
|
|
55
74
|
it('should be able to resolve the file from the CodeCompletionVariableContext', () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code-completion-variable-contribution.spec.js","sourceRoot":"","sources":["../../src/browser/code-completion-variable-contribution.spec.ts"],"names":[],"mappings":";AAAA,gFAAgF;AAChF,wCAAwC;AACxC,EAAE;AACF,2EAA2E;AAC3E,mEAAmE;AACnE,wCAAwC;AACxC,EAAE;AACF,4EAA4E;AAC5E,8EAA8E;AAC9E,6EAA6E;AAC7E,yDAAyD;AACzD,uDAAuD;AACvD,EAAE;AACF,gFAAgF;AAChF,gFAAgF;;AAEhF,
|
|
1
|
+
{"version":3,"file":"code-completion-variable-contribution.spec.js","sourceRoot":"","sources":["../../src/browser/code-completion-variable-contribution.spec.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theia/ai-code-completion",
|
|
3
|
-
"version": "1.72.0-next.
|
|
3
|
+
"version": "1.72.0-next.50+f26609f54",
|
|
4
4
|
"description": "Theia - AI Core",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"@theia/ai-core": "1.72.0-next.
|
|
7
|
-
"@theia/core": "1.72.0-next.
|
|
8
|
-
"@theia/filesystem": "1.72.0-next.
|
|
6
|
+
"@theia/ai-core": "1.72.0-next.50+f26609f54",
|
|
7
|
+
"@theia/core": "1.72.0-next.50+f26609f54",
|
|
8
|
+
"@theia/filesystem": "1.72.0-next.50+f26609f54",
|
|
9
9
|
"@theia/monaco-editor-core": "1.108.201",
|
|
10
|
-
"@theia/output": "1.72.0-next.
|
|
11
|
-
"@theia/workspace": "1.72.0-next.
|
|
12
|
-
"tslib": "^2.
|
|
10
|
+
"@theia/output": "1.72.0-next.50+f26609f54",
|
|
11
|
+
"@theia/workspace": "1.72.0-next.50+f26609f54",
|
|
12
|
+
"tslib": "^2.8.1"
|
|
13
13
|
},
|
|
14
14
|
"main": "lib/common",
|
|
15
15
|
"publishConfig": {
|
|
@@ -51,5 +51,5 @@
|
|
|
51
51
|
"nyc": {
|
|
52
52
|
"extends": "../../configs/nyc.json"
|
|
53
53
|
},
|
|
54
|
-
"gitHead": "
|
|
54
|
+
"gitHead": "f26609f5411c7101a4b2f540584b8163a6036131"
|
|
55
55
|
}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2026 EclipseSource and others.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { enableJSDOM } from '@theia/core/lib/browser/test/jsdom';
|
|
18
|
+
let disableJSDOM = enableJSDOM();
|
|
19
|
+
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
|
|
20
|
+
FrontendApplicationConfigProvider.set({});
|
|
21
|
+
|
|
22
|
+
import { UserRequest } from '@theia/ai-core/lib/common';
|
|
23
|
+
import { expect } from 'chai';
|
|
24
|
+
import * as sinon from 'sinon';
|
|
25
|
+
import { CancellationTokenSource, editor, IRange, languages, Position, Uri } from '@theia/monaco-editor-core/esm/vs/editor/editor.api';
|
|
26
|
+
import { CodeCompletionAgentImpl } from './code-completion-agent';
|
|
27
|
+
|
|
28
|
+
disableJSDOM();
|
|
29
|
+
|
|
30
|
+
describe('CodeCompletionAgentImpl', () => {
|
|
31
|
+
let agent: CodeCompletionAgentImpl;
|
|
32
|
+
let model: editor.ITextModel;
|
|
33
|
+
let sendRequestStub: sinon.SinonStub;
|
|
34
|
+
let capturedRequest: UserRequest | undefined;
|
|
35
|
+
let postProcessStub: sinon.SinonStub;
|
|
36
|
+
|
|
37
|
+
const inlineContext: languages.InlineCompletionContext = {
|
|
38
|
+
triggerKind: languages.InlineCompletionTriggerKind.Automatic,
|
|
39
|
+
selectedSuggestionInfo: undefined,
|
|
40
|
+
includeInlineEdits: false,
|
|
41
|
+
includeInlineCompletions: false,
|
|
42
|
+
requestIssuedDateTime: Date.now(),
|
|
43
|
+
earliestShownDateTime: Date.now()
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
before(() => {
|
|
47
|
+
disableJSDOM = enableJSDOM();
|
|
48
|
+
FrontendApplicationConfigProvider.set({});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
beforeEach(() => {
|
|
52
|
+
// Minimal stub: the agent only passes the model through to promptService (stubbed below).
|
|
53
|
+
model = { uri: Uri.file('/tmp/code-completion-agent.spec.ts') } as editor.ITextModel;
|
|
54
|
+
|
|
55
|
+
capturedRequest = undefined;
|
|
56
|
+
sendRequestStub = sinon.stub().callsFake((_lm: unknown, req: UserRequest) => {
|
|
57
|
+
capturedRequest = req;
|
|
58
|
+
return Promise.resolve({ text: 'COMPLETION' });
|
|
59
|
+
});
|
|
60
|
+
postProcessStub = sinon.stub().callsFake((text: string) => text);
|
|
61
|
+
|
|
62
|
+
agent = new CodeCompletionAgentImpl();
|
|
63
|
+
Object.assign(agent, {
|
|
64
|
+
languageModelService: { sendRequest: sendRequestStub },
|
|
65
|
+
languageModelRegistry: {
|
|
66
|
+
selectLanguageModel: sinon.stub().resolves({ id: 'fake/model' })
|
|
67
|
+
},
|
|
68
|
+
promptService: {
|
|
69
|
+
getResolvedPromptFragment: sinon.stub().resolves({ text: 'prompt text' }),
|
|
70
|
+
getPromptVariantInfo: sinon.stub().returns({ variantId: 'v1', isCustomized: false })
|
|
71
|
+
},
|
|
72
|
+
progressService: {
|
|
73
|
+
showProgress: sinon.stub().resolves({ cancel: () => { /* no-op */ } })
|
|
74
|
+
},
|
|
75
|
+
postProcessor: { postProcess: postProcessStub },
|
|
76
|
+
logger: { error: () => { /* no-op */ } }
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
afterEach(() => {
|
|
81
|
+
sinon.restore();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
after(() => {
|
|
85
|
+
disableJSDOM();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('disables reasoning on the LLM request', async () => {
|
|
89
|
+
const tokenSource = new CancellationTokenSource();
|
|
90
|
+
await agent.provideInlineCompletions(model, new Position(1, 1), inlineContext, tokenSource.token);
|
|
91
|
+
expect(sendRequestStub.calledOnce).to.be.true;
|
|
92
|
+
expect(capturedRequest?.reasoning).to.deep.equal({ level: 'off' });
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('disables streaming on the LLM request', async () => {
|
|
96
|
+
const tokenSource = new CancellationTokenSource();
|
|
97
|
+
await agent.provideInlineCompletions(model, new Position(1, 1), inlineContext, tokenSource.token);
|
|
98
|
+
expect(capturedRequest?.settings?.stream).to.equal(false);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('forwards Monaco\'s cancellation token to the LLM request', async () => {
|
|
102
|
+
// Forwarded so explicit user cancellation (e.g. Esc) propagates to providers that honor it,
|
|
103
|
+
// such as Anthropic's `stream.abort()`.
|
|
104
|
+
const tokenSource = new CancellationTokenSource();
|
|
105
|
+
await agent.provideInlineCompletions(model, new Position(1, 1), inlineContext, tokenSource.token);
|
|
106
|
+
expect(capturedRequest?.cancellationToken).to.equal(tokenSource.token);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('discards the response when the token is cancelled before returning', async () => {
|
|
110
|
+
const tokenSource = new CancellationTokenSource();
|
|
111
|
+
sendRequestStub.callsFake((_lm: unknown, req: UserRequest) => {
|
|
112
|
+
capturedRequest = req;
|
|
113
|
+
// Cancel after the request was issued so the post-`sendRequest` check fires.
|
|
114
|
+
tokenSource.cancel();
|
|
115
|
+
return Promise.resolve({ text: 'LATE_COMPLETION' });
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const result = await agent.provideInlineCompletions(model, new Position(1, 1), inlineContext, tokenSource.token);
|
|
119
|
+
|
|
120
|
+
expect(result).to.be.undefined;
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('bails before sending the request when the token is already cancelled', async () => {
|
|
124
|
+
const tokenSource = new CancellationTokenSource();
|
|
125
|
+
tokenSource.cancel();
|
|
126
|
+
|
|
127
|
+
const result = await agent.provideInlineCompletions(model, new Position(1, 1), inlineContext, tokenSource.token);
|
|
128
|
+
|
|
129
|
+
expect(result).to.be.undefined;
|
|
130
|
+
expect(sendRequestStub.called).to.be.false;
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('returns undefined when no language model is available', async () => {
|
|
134
|
+
Object.assign(agent, {
|
|
135
|
+
languageModelRegistry: { selectLanguageModel: sinon.stub().resolves(undefined) }
|
|
136
|
+
});
|
|
137
|
+
const tokenSource = new CancellationTokenSource();
|
|
138
|
+
|
|
139
|
+
const result = await agent.provideInlineCompletions(model, new Position(1, 1), inlineContext, tokenSource.token);
|
|
140
|
+
|
|
141
|
+
expect(result).to.be.undefined;
|
|
142
|
+
expect(sendRequestStub.called).to.be.false;
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('returns undefined when no prompt is resolved', async () => {
|
|
146
|
+
Object.assign(agent, {
|
|
147
|
+
promptService: {
|
|
148
|
+
getResolvedPromptFragment: sinon.stub().resolves(undefined),
|
|
149
|
+
getPromptVariantInfo: sinon.stub().returns(undefined)
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
const tokenSource = new CancellationTokenSource();
|
|
153
|
+
|
|
154
|
+
const result = await agent.provideInlineCompletions(model, new Position(1, 1), inlineContext, tokenSource.token);
|
|
155
|
+
|
|
156
|
+
expect(result).to.be.undefined;
|
|
157
|
+
expect(sendRequestStub.called).to.be.false;
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('post-processes the completion text before returning it', async () => {
|
|
161
|
+
postProcessStub.callsFake((text: string) => `<<${text}>>`);
|
|
162
|
+
const tokenSource = new CancellationTokenSource();
|
|
163
|
+
|
|
164
|
+
const result = await agent.provideInlineCompletions(model, new Position(2, 3), inlineContext, tokenSource.token);
|
|
165
|
+
|
|
166
|
+
expect(postProcessStub.calledOnceWithExactly('COMPLETION')).to.be.true;
|
|
167
|
+
expect(result!.items[0].insertText).to.equal('<<COMPLETION>>');
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('uses the requested cursor position as the completion range', async () => {
|
|
171
|
+
const tokenSource = new CancellationTokenSource();
|
|
172
|
+
|
|
173
|
+
const result = await agent.provideInlineCompletions(model, new Position(5, 7), inlineContext, tokenSource.token);
|
|
174
|
+
|
|
175
|
+
const range = result!.items[0].range as IRange;
|
|
176
|
+
expect(range.startLineNumber).to.equal(5);
|
|
177
|
+
expect(range.startColumn).to.equal(7);
|
|
178
|
+
expect(range.endLineNumber).to.equal(5);
|
|
179
|
+
expect(range.endColumn).to.equal(7);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('cancels the progress indicator regardless of outcome', async () => {
|
|
183
|
+
const progressCancel = sinon.spy();
|
|
184
|
+
Object.assign(agent, {
|
|
185
|
+
progressService: { showProgress: sinon.stub().resolves({ cancel: progressCancel }) }
|
|
186
|
+
});
|
|
187
|
+
const tokenSource = new CancellationTokenSource();
|
|
188
|
+
|
|
189
|
+
await agent.provideInlineCompletions(model, new Position(1, 1), inlineContext, tokenSource.token);
|
|
190
|
+
|
|
191
|
+
expect(progressCancel.calledOnce).to.be.true;
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('swallows errors and clears the progress indicator', async () => {
|
|
195
|
+
const progressCancel = sinon.spy();
|
|
196
|
+
Object.assign(agent, {
|
|
197
|
+
progressService: { showProgress: sinon.stub().resolves({ cancel: progressCancel }) }
|
|
198
|
+
});
|
|
199
|
+
sendRequestStub.rejects(new Error('boom'));
|
|
200
|
+
sinon.stub(console, 'error');
|
|
201
|
+
const tokenSource = new CancellationTokenSource();
|
|
202
|
+
|
|
203
|
+
const result = await agent.provideInlineCompletions(model, new Position(1, 1), inlineContext, tokenSource.token);
|
|
204
|
+
|
|
205
|
+
expect(result).to.be.undefined;
|
|
206
|
+
expect(progressCancel.calledOnce).to.be.true;
|
|
207
|
+
});
|
|
208
|
+
});
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
UserRequest
|
|
23
23
|
} from '@theia/ai-core/lib/common';
|
|
24
24
|
import { generateUuid, ILogger, nls, ProgressService } from '@theia/core';
|
|
25
|
+
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
|
|
25
26
|
import { inject, injectable, named } from '@theia/core/shared/inversify';
|
|
26
27
|
import * as monaco from '@theia/monaco-editor-core';
|
|
27
28
|
import { codeCompletionPrompts } from './code-completion-prompt-template';
|
|
@@ -89,6 +90,9 @@ export class CodeCompletionAgentImpl implements CodeCompletionAgent {
|
|
|
89
90
|
settings: {
|
|
90
91
|
stream: false
|
|
91
92
|
},
|
|
93
|
+
// Inline completion is a one-shot text replacement; reasoning/thinking adds latency
|
|
94
|
+
// and can cause models (e.g. Anthropic adaptive thinking) to emit no text at all.
|
|
95
|
+
reasoning: { level: 'off' },
|
|
92
96
|
agentId: this.id,
|
|
93
97
|
sessionId,
|
|
94
98
|
requestId,
|
|
@@ -146,7 +150,9 @@ export class CodeCompletionAgentImpl implements CodeCompletionAgent {
|
|
|
146
150
|
id = 'Code Completion';
|
|
147
151
|
name = 'Code Completion';
|
|
148
152
|
description =
|
|
149
|
-
nls.localize('theia/ai/completion/agent/description',
|
|
153
|
+
nls.localize('theia/ai/completion/agent/description',
|
|
154
|
+
'This agent provides inline code completion in the code editor in {0}.',
|
|
155
|
+
FrontendApplicationConfigProvider.get().applicationName);
|
|
150
156
|
prompts: PromptVariantSet[] = codeCompletionPrompts;
|
|
151
157
|
languageModelRequirements: LanguageModelRequirement[] = [
|
|
152
158
|
{
|
|
@@ -14,28 +14,55 @@
|
|
|
14
14
|
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
15
|
// *****************************************************************************
|
|
16
16
|
|
|
17
|
-
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
|
|
18
|
-
import { enableJSDOM } from '@theia/core/lib/browser/test/jsdom';
|
|
19
|
-
let disableJSDOM = enableJSDOM();
|
|
20
|
-
FrontendApplicationConfigProvider.set({});
|
|
21
|
-
|
|
22
17
|
import { PreferenceService } from '@theia/core/lib/common';
|
|
23
18
|
import { Container } from '@theia/core/shared/inversify';
|
|
24
19
|
import { editor, languages, Uri } from '@theia/monaco-editor-core/esm/vs/editor/editor.api';
|
|
25
20
|
import { expect } from 'chai';
|
|
26
|
-
import * as sinon from 'sinon';
|
|
27
21
|
import { CodeCompletionVariableContext } from './code-completion-variable-context';
|
|
28
22
|
import { CodeCompletionVariableContribution } from './code-completion-variable-contribution';
|
|
29
23
|
import { FILE, LANGUAGE, PREFIX, SUFFIX } from './code-completion-variables';
|
|
30
24
|
|
|
31
|
-
|
|
25
|
+
// A minimal fake ITextModel covering just the methods the contribution touches.
|
|
26
|
+
// Avoids calling `editor.createModel`, which lazily initializes Monaco's
|
|
27
|
+
// StandaloneThemeService and requires browser globals (CSS, matchMedia) that
|
|
28
|
+
// JSDOM does not expose.
|
|
29
|
+
function createFakeModel(text: string, languageId: string, uri: Uri): editor.ITextModel {
|
|
30
|
+
const lines = text.split('\n');
|
|
31
|
+
return {
|
|
32
|
+
uri,
|
|
33
|
+
getLanguageId: () => languageId,
|
|
34
|
+
getLineCount: () => lines.length,
|
|
35
|
+
getLineMaxColumn: (line: number) => lines[line - 1].length + 1,
|
|
36
|
+
getPositionAt: (offset: number) => {
|
|
37
|
+
let remaining = offset;
|
|
38
|
+
for (let i = 0; i < lines.length; i++) {
|
|
39
|
+
if (remaining <= lines[i].length) {
|
|
40
|
+
return { lineNumber: i + 1, column: remaining + 1 };
|
|
41
|
+
}
|
|
42
|
+
remaining -= lines[i].length + 1; // account for '\n'
|
|
43
|
+
}
|
|
44
|
+
const last = lines.length - 1;
|
|
45
|
+
return { lineNumber: lines.length, column: lines[last].length + 1 };
|
|
46
|
+
},
|
|
47
|
+
getValueInRange: (range: { startLineNumber: number; startColumn: number; endLineNumber: number; endColumn: number }) => {
|
|
48
|
+
const parts: string[] = [];
|
|
49
|
+
for (let line = range.startLineNumber; line <= range.endLineNumber; line++) {
|
|
50
|
+
const lineText = lines[line - 1];
|
|
51
|
+
const start = line === range.startLineNumber ? range.startColumn - 1 : 0;
|
|
52
|
+
const end = line === range.endLineNumber ? range.endColumn - 1 : lineText.length;
|
|
53
|
+
parts.push(lineText.substring(start, end));
|
|
54
|
+
}
|
|
55
|
+
return parts.join('\n');
|
|
56
|
+
},
|
|
57
|
+
dispose: () => { /* no-op */ },
|
|
58
|
+
} as unknown as editor.ITextModel;
|
|
59
|
+
}
|
|
32
60
|
|
|
33
61
|
describe('CodeCompletionVariableContribution', () => {
|
|
34
62
|
let contribution: CodeCompletionVariableContribution;
|
|
35
63
|
let model: editor.ITextModel;
|
|
36
64
|
|
|
37
65
|
before(() => {
|
|
38
|
-
disableJSDOM = enableJSDOM();
|
|
39
66
|
const container = new Container();
|
|
40
67
|
container.bind(PreferenceService).toConstantValue({
|
|
41
68
|
get: () => 1000,
|
|
@@ -45,18 +72,7 @@ describe('CodeCompletionVariableContribution', () => {
|
|
|
45
72
|
});
|
|
46
73
|
|
|
47
74
|
beforeEach(() => {
|
|
48
|
-
model =
|
|
49
|
-
sinon.stub(model, 'getLanguageId').returns('javascript');
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
afterEach(() => {
|
|
53
|
-
model.dispose();
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
after(() => {
|
|
57
|
-
// Disable JSDOM after all tests
|
|
58
|
-
disableJSDOM();
|
|
59
|
-
model.dispose();
|
|
75
|
+
model = createFakeModel('//line 1\nconsole.\n//line 2', 'javascript', Uri.file('/home/user/workspace/test.js'));
|
|
60
76
|
});
|
|
61
77
|
|
|
62
78
|
describe('canResolve', () => {
|