diff --git a/src/bscPlugin/codeActions/CodeActionsProcessor.spec.ts b/src/bscPlugin/codeActions/CodeActionsProcessor.spec.ts
index 408dea559..07e2f3201 100644
--- a/src/bscPlugin/codeActions/CodeActionsProcessor.spec.ts
+++ b/src/bscPlugin/codeActions/CodeActionsProcessor.spec.ts
@@ -152,13 +152,16 @@ describe('CodeActionsProcessor', () => {
program.validate();
- //there should be no code actions since this is a brs file
- const codeActions = program.getCodeActions(
- file.pathAbsolute,
- // DoSometh|ing()
- util.createRange(2, 28, 2, 28)
- );
- expect(codeActions).to.be.empty;
+ //the ImportStatement code action should be missing since this is a brs file
+ expect(
+ program.getCodeActions(
+ file.pathAbsolute,
+ // DoSometh|ing()
+ util.createRange(2, 28, 2, 28)
+ ).map(x => x.title).sort()
+ ).to.eql([
+ `Add xml script import "pkg:/source/lib.brs" into component "ChildScene"`
+ ]);
});
it('suggests class imports', () => {
@@ -226,6 +229,104 @@ describe('CodeActionsProcessor', () => {
`import "pkg:/source/Animals.bs"`
]);
});
- });
+ it('sugests import script tag for function from not-imported file', () => {
+ program.setFile('components/comp1.xml', trim`
+
+
+
+
+ `);
+ const codebehind = program.setFile('components/comp1.brs', `
+ sub init()
+ doSomething()
+ end sub
+ `);
+ program.setFile('source/common.brs', `
+ sub doSomething()
+ end sub
+ `);
+ program.validate();
+
+ expectCodeActions(() => {
+ program.getCodeActions(
+ codebehind.pathAbsolute,
+ // doSo|mething()
+ util.createRange(2, 24, 2, 24)
+ );
+ }, [{
+ title: `Add xml script import "pkg:/source/common.brs" into component "child"`,
+ changes: [{
+ filePath: s`${rootDir}/components/comp1.xml`,
+ newText: ' \n',
+ type: 'insert',
+ position: util.createPosition(3, 0)
+ }]
+ }]);
+ });
+
+ it('suggests `extends=Group`', () => {
+ const file = program.setFile('components/comp1.xml', trim`
+
+
+
+ `);
+ expectCodeActions(() => {
+ program.getCodeActions(
+ file.pathAbsolute,
+ //
+ util.createRange(1, 5, 1, 5)
+ );
+ }, [{
+ title: `Extend "Group"`,
+ isPreferred: true,
+ kind: 'quickfix',
+ changes: [{
+ filePath: s`${rootDir}/components/comp1.xml`,
+ newText: ' extends="Group"',
+ type: 'insert',
+ //
+ position: util.createPosition(1, 23)
+ }]
+ }, {
+ title: `Extend "Task"`,
+ kind: 'quickfix',
+ changes: [{
+ filePath: s`${rootDir}/components/comp1.xml`,
+ newText: ' extends="Task"',
+ type: 'insert',
+ //
+ position: util.createPosition(1, 23)
+ }]
+ }, {
+ title: `Extend "ContentNode"`,
+ kind: 'quickfix',
+ changes: [{
+ filePath: s`${rootDir}/components/comp1.xml`,
+ newText: ' extends="ContentNode"',
+ type: 'insert',
+ //
+ position: util.createPosition(1, 23)
+ }]
+ }]);
+ });
+
+ it('adds attribute at end of component with multiple attributes`', () => {
+ const file = program.setFile('components/comp1.xml', trim`
+
+
+
+ `);
+ const codeActions = program.getCodeActions(
+ file.pathAbsolute,
+ //
+ util.createRange(1, 5, 1, 5)
+ );
+ expect(
+ codeActions[0].edit.changes[URI.file(s`${rootDir}/components/comp1.xml`).toString()][0].range
+ ).to.eql(
+ util.createRange(1, 51, 1, 51)
+ );
+ });
+ });
});
diff --git a/src/bscPlugin/codeActions/CodeActionsProcessor.ts b/src/bscPlugin/codeActions/CodeActionsProcessor.ts
index d86b8b0dd..da91c77e6 100644
--- a/src/bscPlugin/codeActions/CodeActionsProcessor.ts
+++ b/src/bscPlugin/codeActions/CodeActionsProcessor.ts
@@ -5,9 +5,10 @@ import type { DiagnosticMessageType } from '../../DiagnosticMessages';
import { DiagnosticCodeMap } from '../../DiagnosticMessages';
import type { BrsFile } from '../../files/BrsFile';
import type { XmlFile } from '../../files/XmlFile';
-import type { BscFile, OnGetCodeActionsEvent } from '../../interfaces';
+import type { BscFile, BsDiagnostic, OnGetCodeActionsEvent } from '../../interfaces';
import { ParseMode } from '../../parser/Parser';
import { util } from '../../util';
+import type { XmlScope } from '../../XmlScope';
export class CodeActionsProcessor {
public constructor(
@@ -19,9 +20,9 @@ export class CodeActionsProcessor {
public process() {
for (const diagnostic of this.event.diagnostics) {
if (diagnostic.code === DiagnosticCodeMap.callToUnknownFunction) {
- this.suggestFunctionImports(diagnostic as any);
+ this.handleCallToUnknownFunction(diagnostic as any);
} else if (diagnostic.code === DiagnosticCodeMap.classCouldNotBeFound) {
- this.suggestClassImports(diagnostic as any);
+ this.handleClassCouldNotBeFound(diagnostic as any);
} else if (diagnostic.code === DiagnosticCodeMap.xmlComponentMissingExtendsAttribute) {
this.addMissingExtends(diagnostic as any);
}
@@ -44,7 +45,7 @@ export class CodeActionsProcessor {
//find the position of the first import statement, or the top of the file if there is none
const insertPosition = importStatements[importStatements.length - 1]?.importToken.range?.start ?? util.createPosition(0, 0);
- //find all files that reference this function
+ //suggest importing each file that references this item
for (const file of files) {
const pkgPath = util.getRokuPkgPath(file.pkgPath);
this.event.codeActions.push(
@@ -64,30 +65,71 @@ export class CodeActionsProcessor {
}
}
- private suggestFunctionImports(diagnostic: DiagnosticMessageType<'callToUnknownFunction'>) {
- //skip if not a BrighterScript file
- if ((diagnostic.file as BrsFile).parseMode !== ParseMode.BrighterScript) {
+ /**
+ * Suggest a `