Skip to content

Commit

Permalink
Adds annotation symbol table
Browse files Browse the repository at this point in the history
  • Loading branch information
markwpearce committed Jan 3, 2025
1 parent 2e27a01 commit c666d14
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 8 deletions.
21 changes: 19 additions & 2 deletions src/PluginInterface.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { CompilerPlugin } from './interfaces';
import type { AnnotationDeclaration, CompilerPlugin } from './interfaces';
import { LogLevel, createLogger, type Logger } from './logging';
import type { TypedFunctionType } from './types/TypedFunctionType';
/*
* we use `Required` everywhere here because we expect that the methods on plugin objects will
* be optional, and we don't want to deal with `undefined`.
Expand Down Expand Up @@ -74,7 +75,7 @@ export default class PluginInterface<T extends CompilerPlugin = CompilerPlugin>
/**
* Call `event` on plugins, but allow the plugins to return promises that will be awaited before the next plugin is notified
*/
public async emitAsync<K extends keyof PluginEventArgs<T> & string>(event: K, ...args: PluginEventArgs<T>[K]): Promise< PluginEventArgs<T>[K][0]> {
public async emitAsync<K extends keyof PluginEventArgs<T> & string>(event: K, ...args: PluginEventArgs<T>[K]): Promise<PluginEventArgs<T>[K][0]> {
for (let plugin of this.plugins) {
if ((plugin as any)[event]) {
try {
Expand Down Expand Up @@ -169,4 +170,20 @@ export default class PluginInterface<T extends CompilerPlugin = CompilerPlugin>
public clear() {
this.plugins = [];
}


private annotationMap: Map<string, Array<string | TypedFunctionType | AnnotationDeclaration>>;

public getAnnotationMap() {
if (this.annotationMap) {
return this.annotationMap;
}
this.annotationMap = new Map<string, Array<string | TypedFunctionType | AnnotationDeclaration>>();
for (let plugin of this.plugins) {
if (plugin.annotations?.length > 0) {
this.annotationMap.set(plugin.name, plugin.annotations);
}
}
return this.annotationMap;
}
}
24 changes: 23 additions & 1 deletion src/Program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { globalCallables, globalFile } from './globalCallables';
import { parseManifest, getBsConst } from './preprocessor/Manifest';
import { URI } from 'vscode-uri';
import PluginInterface from './PluginInterface';
import { isBrsFile, isXmlFile, isXmlScope, isNamespaceStatement } from './astUtils/reflection';
import { isBrsFile, isXmlFile, isXmlScope, isNamespaceStatement, isTypedFunctionType, isAnnotationDeclaration } from './astUtils/reflection';
import type { FunctionStatement, MethodStatement, NamespaceStatement } from './parser/Statement';
import { BscPlugin } from './bscPlugin/BscPlugin';
import { Editor } from './astUtils/Editor';
Expand Down Expand Up @@ -54,6 +54,7 @@ import { DiagnosticManager } from './DiagnosticManager';
import { ProgramValidatorDiagnosticsTag } from './bscPlugin/validation/ProgramValidator';
import type { ProvidedSymbolInfo, BrsFile } from './files/BrsFile';
import type { XmlFile } from './files/XmlFile';
import { SymbolTable } from './SymbolTable';

const bslibNonAliasedRokuModulesPkgPath = s`source/roku_modules/rokucommunity_bslib/bslib.brs`;
const bslibAliasedRokuModulesPkgPath = s`source/roku_modules/bslib/bslib.brs`;
Expand Down Expand Up @@ -123,6 +124,9 @@ export class Program {

//TODO we might need to fix this because the isValidated clears stuff now
(this.globalScope as any).isValidated = true;

// Get declarations for all annotations from all plugins
this.populateAnnotationSymbolTable();
}


Expand Down Expand Up @@ -226,6 +230,24 @@ export class Program {
*/
public plugins: PluginInterface;

public pluginAnnotationTable = new SymbolTable('Plugin Annotations', () => this.globalScope?.symbolTable);

private populateAnnotationSymbolTable() {
for (const [pluginName, annotations] of this.plugins.getAnnotationMap().entries()) {
for (const annotation of annotations) {
if (isTypedFunctionType(annotation) && annotation.name) {
this.pluginAnnotationTable.addSymbol(annotation.name, { pluginName: pluginName }, annotation, SymbolTypeFlag.annotation);
} else if (isAnnotationDeclaration(annotation)) {
const annoType = annotation.type;
let description = (typeof annotation.description === 'string') ? annotation.description : undefined;
this.pluginAnnotationTable.addSymbol(annoType.name, { pluginName: pluginName, description: description }, annoType, SymbolTypeFlag.annotation);
} else if (typeof annotation === 'string') {
// TODO: Do we need to parse this?
}
}
}
}

private fileSymbolInformation = new Map<string, { provides: ProvidedSymbolInfo; requires: UnresolvedSymbol[] }>();

public addFileSymbolInfo(file: BrsFile) {
Expand Down
31 changes: 29 additions & 2 deletions src/ProgramBuilder.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ import { LogLevel, createLogger } from './logging';
import * as diagnosticUtils from './diagnosticUtils';
import { DiagnosticSeverity } from 'vscode-languageserver';
import { BrsFile } from './files/BrsFile';
import { expectZeroDiagnostics } from './testHelpers.spec';
import { expectTypeToBe, expectZeroDiagnostics } from './testHelpers.spec';
import type { BsConfig } from './BsConfig';
import type { BscFile } from './files/BscFile';
import { tempDir, rootDir, stagingDir } from './testHelpers.spec';
import { Deferred } from './deferred';
import type { AfterProgramCreateEvent, BsDiagnostic } from './interfaces';
import type { AfterProgramCreateEvent, BsDiagnostic, CompilerPlugin, ExtraSymbolData } from './interfaces';
import { StringType, TypedFunctionType, VoidType } from './types';
import { SymbolTypeFlag } from './SymbolTypeFlag';

describe('ProgramBuilder', () => {

Expand Down Expand Up @@ -312,6 +314,31 @@ describe('ProgramBuilder', () => {
});


describe('plugins', () => {
it('adds annotations defined in a plugin to the annotation symbol table', async () => {
builder = new ProgramBuilder();

const plugin: CompilerPlugin = {
name: 'test',
annotations: [{
type: new TypedFunctionType(VoidType.instance)
.setName('myAnnotation')
.addParameter('id', StringType.instance, false),
description: 'Extra description'
}]
};
builder.plugins.add(plugin);
await builder.load({});

const extraData: ExtraSymbolData = {};
const foundAnnotation = builder.program.pluginAnnotationTable.getSymbolType('myAnnotation', { flags: SymbolTypeFlag.annotation, data: extraData });

expectTypeToBe(foundAnnotation, TypedFunctionType);
expect(extraData.pluginName).to.eql('test');
expect(extraData.description).to.eql('Extra description');
});
});

describe('printDiagnostics', () => {

it('does not crash when a diagnostic is missing range informtaion', () => {
Expand Down
1 change: 1 addition & 0 deletions src/SymbolTable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ export class SymbolTable implements SymbolTypeGetter {
options.data.isAlias = data?.isAlias;
options.data.isInstance = data?.isInstance;
options.data.isFromDocComment = data?.isFromDocComment;
options.data.pluginName = data?.pluginName;
}
return resolvedType;
}
Expand Down
3 changes: 2 additions & 1 deletion src/SymbolTypeFlag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ export const enum SymbolTypeFlag {
private = 8,
protected = 16,
postTranspile = 32,
deprecated = 64
deprecated = 64,
annotation = 128
}
9 changes: 8 additions & 1 deletion src/astUtils/reflection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { Body, AssignmentStatement, Block, ExpressionStatement, ExitStateme
import type { LiteralExpression, BinaryExpression, CallExpression, FunctionExpression, DottedGetExpression, XmlAttributeGetExpression, IndexedGetExpression, GroupingExpression, EscapedCharCodeLiteralExpression, ArrayLiteralExpression, AALiteralExpression, UnaryExpression, VariableExpression, SourceLiteralExpression, NewExpression, CallfuncExpression, TemplateStringQuasiExpression, TemplateStringExpression, TaggedTemplateStringExpression, AnnotationExpression, FunctionParameterExpression, AAMemberExpression, TypecastExpression, TypeExpression, TypedArrayExpression, TernaryExpression, NullCoalescingExpression } from '../parser/Expression';
import type { BrsFile } from '../files/BrsFile';
import type { XmlFile } from '../files/XmlFile';
import type { BsDiagnostic, TypedefProvider } from '../interfaces';
import type { AnnotationDeclaration, BsDiagnostic, TypedefProvider } from '../interfaces';
import type { InvalidType } from '../types/InvalidType';
import type { VoidType } from '../types/VoidType';
import { InternalWalkMode } from './visitors';
Expand Down Expand Up @@ -462,3 +462,10 @@ export function isLiteralDouble(value: any): value is LiteralExpression & { type
export function isBsDiagnostic(value: any): value is BsDiagnostic {
return value.message;
}


// Plugins

export function isAnnotationDeclaration(value: any): value is AnnotationDeclaration {
return isTypedFunctionType(value.type);
}
11 changes: 10 additions & 1 deletion src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,11 @@ export interface CommentFlag {

export type CompilerPluginFactory = () => CompilerPlugin;

export interface AnnotationDeclaration {
description?: string;
type: TypedFunctionType;
}

export interface CompilerPlugin {
name: string;

Expand All @@ -229,7 +234,7 @@ export interface CompilerPlugin {
* `suite(suiteConfig as object)`
* ]
*/
annotations?: string[];
annotations?: Array<string | TypedFunctionType | AnnotationDeclaration>;

/**
* Called when plugin is initially loaded
Expand Down Expand Up @@ -1000,6 +1005,10 @@ export interface ExtraSymbolData {
* Is this type as defined in a doc comment?
*/
isFromDocComment?: boolean;
/**
* Name of plugin that defined this symbol
*/
pluginName?: string;
}

export interface GetTypeOptions {
Expand Down

0 comments on commit c666d14

Please sign in to comment.