Commit 1c4ed43b authored by George Wang's avatar George Wang
Browse files

Initial commit, starting from version 0.0.5

parents
# Change Log
All notable changes to the "lisa-lang" extension will be documented in this file.
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
## [Unreleased]
- Initial release
# LISA+ README
## Features
The extension provides the essential LISA+ language support, such as syntax highlighting, IntelliSense. Also, the basic support for sgrepo and sgproj files are implemented to facilitate the Fast Models development
## Requirements
N/A
## Usage
The extension provides command "LISA+: Scan Workspace" to scan the workspace which offers better IntelliSense results.
It runs automatically when the extension is activated. You can also trigger the command by pressing <kbd>shift</kbd> + <kbd>command</kbd> + <kbd>P</kbd>, then type "LISA+: Scan Workspace"
## Known Issues
N/A
## Release Notes
### 0.0.5
* Added support of "Go To Definition" for LISA+ components
### 0.0.4
* Further enhancements on the IntelliSense for ports with extra information
### 0.0.3
* Added auto-completion of ports, protocols, compositions and components
* Added "LISA+: Scan Workspace" command
* Added support for sgrepo files
* Added support for sgproj files
* Minor bug fixes
### 0.0.2
* Enhancement on the auto-completion
* Minor bug fixes
### 0.0.1
Initial release of LISA+ Language Support
-----------------------------------------------------------------------------------------------------------
{
"comments": {
// symbol used for single line comment. Remove this entry if your language does not support line comments
"lineComment": "//",
// symbols used for start and end a block comment. Remove this entry if your language does not support block comments
"blockComment": [ "/*", "*/" ]
},
// symbols used as brackets
"brackets": [
["{", "}"],
["[", "]"],
["(", ")"]
],
// symbols that are auto closed when typing
"autoClosingPairs": [
["{", "}"],
["[", "]"],
["(", ")"],
["\"", "\""],
["'", "'"],
["<", ">"]
],
// symbols that that can be used to surround a selection
"surroundingPairs": [
["{", "}"],
["[", "]"],
["(", ")"],
["\"", "\""],
["'", "'"],
["<", ">"]
]
}
\ No newline at end of file
This diff is collapsed.
{
"name": "lisa-lang",
"displayName": "LISA+",
"description": "LISA+ Language Support for VSCode",
"version": "0.0.5",
"publisher": "GeorgeWang0402",
"repository": {
"type": "git",
"url": "https://test.com"
},
"bugs": {
"url": "https://test.com/issues"
},
"engines": {
"vscode": "^1.40.0"
},
"categories": [
"Programming Languages"
],
"activationEvents": [
"onLanguage:lisa",
"onLanguage:sgproj",
"onLanguage:sgrepo",
"onCommand:lisa.scan"
],
"contributes": {
"languages": [
{
"id": "lisa",
"aliases": [
"LISA+",
"lisa",
"LISA"
],
"extensions": [
".lisa"
],
"configuration": "./language-configuration.json"
},
{
"id": "sgproj",
"aliases": [
"sgproj"
],
"extensions": [
".sgproj"
],
"configuration": "./language-configuration.json"
},
{
"id": "sgrepo",
"aliases": [
"sgrepo"
],
"extensions": [
".sgrepo"
],
"configuration": "./language-configuration.json"
}
],
"grammars": [
{
"language": "lisa",
"scopeName": "source.lisa",
"path": "./syntaxes/lisa.tmLanguage.json",
"embeddedLanguages": {
"meta.lisa.c++": "c++"
}
},
{
"language": "sgproj",
"scopeName": "source.sgproj",
"path": "./syntaxes/sgproj.tmLanguage.json"
},
{
"language": "sgrepo",
"scopeName": "source.sgrepo",
"path": "./syntaxes/sgrepo.tmLanguage.json"
}
],
"commands": [
{
"command": "lisa.scan",
"title": "LISA+: Scan Workspace"
}
]
},
"main": "./out/extension.js",
"scripts": {
"vscode:prepublish": "npm run compile",
"compile": "tsc -p ./",
"lint": "tslint -p ./",
"watch": "tsc -watch -p ./"
},
"devDependencies": {
"@types/node": "^12.12.21",
"@types/vscode": "^1.40.0",
"tslint": "^5.20.1",
"typescript": "^3.7.3"
},
"dependencies": {
"vsce": "^1.71.0"
}
}
import * as vscode from 'vscode';
import * as fs from 'fs';
import { start } from 'repl';
class LISADocumentHighlightProvider implements vscode.DocumentHighlightProvider {
public provideDocumentHighlights(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken)
{
let editor = vscode.window.activeTextEditor;
if (editor)
{
let cursor = editor.selection.start;
let word_range = document.getWordRangeAtPosition(cursor);
let highlights = []
if (word_range)
{
highlights.push(new vscode.DocumentHighlight(word_range, vscode.DocumentHighlightKind.Text));
return highlights;
}
}
return null;
}
}
let lisa_files: vscode.Uri[] = []
let component_ports = new Map();
let component_definitions = new Map();
class LISADefinitionProvider implements vscode.DefinitionProvider {
public provideDefinition(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken)
{
let editor = vscode.window.activeTextEditor;
if (editor)
{
let cursor = editor.selection.start;
let word_range = document.getWordRangeAtPosition(cursor);
let word = document.getText(word_range);
if (component_definitions.has(word))
{
let definitions = component_definitions.get(word);
return definitions[0];
}
}
return null;
}
}
export function activate(context: vscode.ExtensionContext): void {
let instances_map = new Map();
let provider_variables = vscode.languages.registerCompletionItemProvider('lisa', {
provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext) {
let first_line = document.lineAt(0);
let last_line = document.lineAt(document.lineCount - 1);
let doc_range = new vscode.Range(first_line.range.start, last_line.range.end);
let text = document.getText(doc_range);
let composition_regexp = new RegExp("composition\\s*{(.*?)}", "gms");
let composition = composition_regexp.exec(text);
let component_regexp = new RegExp('([\\w\\d]+)\\s*:\\s*([\\w\\d]+)\\(.*?\\);', 'gms');
instances_map.clear();
let instances: string[] = [];
let components: string[] = []
if (composition)
{
let matches = composition[0].matchAll(component_regexp);
for (let match of matches)
{
if (!instances.includes(match[1]))
{
instances.push(match[1]);
}
if (!components.includes(match[2]))
{
components.push(match[2]);
}
// console.log("DEBUG: instance - " + match[1] + " Component - " + match[2]);
instances_map.set(match[1], match[2]);
}
}
let simpleCompletion_compositions = [];
for (let instance of instances)
{
let completionItem = new vscode.CompletionItem(instance);
completionItem.kind = vscode.CompletionItemKind.Variable;
simpleCompletion_compositions.push(completionItem);
}
let simpleCompletion_components = []
for (let component of components)
{
let completionItem = new vscode.CompletionItem(component);
completionItem.kind = vscode.CompletionItemKind.Module;
simpleCompletion_compositions.push(completionItem);
}
return simpleCompletion_compositions;
}
});
let provider_ports = vscode.languages.registerCompletionItemProvider('lisa', {
provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext) {
let first_line = document.lineAt(0);
let last_line = document.lineAt(document.lineCount - 1);
let doc_range = new vscode.Range(first_line.range.start, last_line.range.end);
let text = document.getText(doc_range);
let port_regexp = new RegExp("^\\s*(internal)?\\s*(master|slave)\\s*(addressable)?\\s*port\\s*<(.*?)>\\s*([\\w\\d]+)", "gms")
let protocols: string[] = []
let self_ports = []
let matches = text.matchAll(port_regexp);
for (let match of matches)
{
self_ports.push(Array.from(match))
if (!protocols.includes(match[4]))
{
protocols.push(match[4]);
}
}
let simpleCompletion_protocols = [];
for (let protocol of protocols)
{
simpleCompletion_protocols.push(new vscode.CompletionItem(protocol, vscode.CompletionItemKind.Class));
}
let simpleCompletion_ports = [];
let linePrefix = document.lineAt(position).text.substr(0, position.character);
if (linePrefix.endsWith('self.')) {
for (let port of self_ports)
{
let item = new vscode.CompletionItem(port[5], vscode.CompletionItemKind.Field);
item.documentation = new vscode.MarkdownString();
item.documentation.appendMarkdown("* **Internal:** " + ((port[1] == "internal")? "True":"False") + "\n");
item.documentation.appendMarkdown("* **Type:** " + port[2] + "\n");
item.documentation.appendMarkdown("* **Addressable:** " + ((port[3] == "addressable")? "True":"False") + "\n");
item.documentation.appendMarkdown("* **Protocol:** " + port[4] + "\n");
simpleCompletion_ports.push(item);
}
return simpleCompletion_ports;
}
else
{
let words = linePrefix.slice(0, -1).trim().split(' ');
let instance = words[words.length - 1];
// console.log("DEBUG: instance - " + instance);
if(instances_map.has(instance))
{
if(component_ports.has(instances_map.get(instance)))
{
// console.log("DEBUG: component - " + instances_map.get(instance));
let ports = component_ports.get(instances_map.get(instance));
for(let port of ports)
{
let item = new vscode.CompletionItem(port[5], vscode.CompletionItemKind.Field);
item.documentation = new vscode.MarkdownString();
item.documentation.appendMarkdown("* **Internal:** " + ((port[1] == "internal")? "True":"False") + "\n");
item.documentation.appendMarkdown("* **Type:** " + port[2] + "\n");
item.documentation.appendMarkdown("* **Addressable:** " + ((port[3] == "addressable")? "True":"False") + "\n");
item.documentation.appendMarkdown("* **Protocol:** " + port[4] + "\n");
// console.log("DEBUG: port - " + port[5]);
// console.log("DEBUG: " + item.documentation);
simpleCompletion_ports.push(item);
}
return simpleCompletion_ports;
}
}
}
return simpleCompletion_protocols;
}
}, '.'
);
let provider_sections = vscode.languages.registerCompletionItemProvider('lisa', {
provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext) {
// Simple completion items for section keywords
let sections = ["component", "includes", "properties", "resources", "composition", "connection"];
let simpleCompletion_sections = []
for (let section of sections) {
simpleCompletion_sections.push(new vscode.CompletionItem(section));
}
return simpleCompletion_sections;
}
});
let provider_types = vscode.languages.registerCompletionItemProvider('lisa', {
provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext) {
// Simple completion items for type keywords
let types = ["master", "slave", "internal", "extern", "enum", "union", "class", "struct", "port", "self"];
let simpleCompletion_types = []
for (let type of types) {
simpleCompletion_types.push(new vscode.CompletionItem(type));
}
return simpleCompletion_types;
}
});
let provider_resources = vscode.languages.registerCompletionItemProvider('lisa', {
provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext) {
// Command completion for PARAMETER
const commandCompletion_params = new vscode.CompletionItem('PARAMETER');
commandCompletion_params.kind = vscode.CompletionItemKind.Keyword;
commandCompletion_params.insertText = 'PARAMETER { description(""), type(""), default(0), min(0), max(0xFFFF), runtime(false) } parameter_name;';
// commandCompletion_params.command = { command: 'editor.action.triggerSuggest', title: 'Re-trigger completions...' };
// Command completion for REGISTER
const commandCompletion_register = new vscode.CompletionItem('REGISTER');
commandCompletion_register.kind = vscode.CompletionItemKind.Keyword;
commandCompletion_register.insertText = 'REGISTER { description(""), type(uint32_t), bitwidth(32), read_function("debug_read"), write_function("debug_write"), reg_number(0x..) } register_name;';
// commandCompletion_register.command = { command: 'editor.action.triggerSuggest', title: 'Re-trigger completions...' };
// Command completion for MEMORY
const commandCompletion_memory = new vscode.CompletionItem('MEMORY');
commandCompletion_memory.kind = vscode.CompletionItemKind.Keyword;
commandCompletion_memory.insertText = 'MEMORY { description(""), attribute(read_write), mau_size(32), read_function(""), write_function("") } memory_name[x];';
// commandCompletion_memory.command = { command: 'editor.action.triggerSuggest', title: 'Re-trigger completions...' };
// Command completion for master
const commandCompletion_master = new vscode.CompletionItem('master');
commandCompletion_master.kind = vscode.CompletionItemKind.Keyword;
commandCompletion_master.insertText = 'master port<> port_name;';
// commandCompletion_master.command = { command: 'editor.action.triggerSuggest', title: 'Re-trigger completions...' };
// Command completion for slave
const commandCompletion_slave = new vscode.CompletionItem('slave');
commandCompletion_slave.kind = vscode.CompletionItemKind.Keyword;
commandCompletion_slave.insertText = 'slave port<> port_name;';
// commandCompletion_slave.command = { command: 'editor.action.triggerSuggest', title: 'Re-trigger completions...' };
// Command completion for port
const commandCompletion_port = new vscode.CompletionItem('port');
commandCompletion_port.kind = vscode.CompletionItemKind.Keyword;
commandCompletion_port.insertText = 'port<> port_name;';
let autocompletions = [
commandCompletion_params,
commandCompletion_register,
commandCompletion_memory,
commandCompletion_master,
commandCompletion_slave,
commandCompletion_port
];
return autocompletions;
}
});
const provider2 = vscode.languages.registerCompletionItemProvider(
'lisa',
{
provideCompletionItems(document: vscode.TextDocument, position: vscode.Position) {
// get all text until the `position` and check if it reads `console.`
// and if so then complete if `log`, `warn`, and `error`
let linePrefix = document.lineAt(position).text.substr(0, position.character);
if (!linePrefix.endsWith('console.')) {
return undefined;
}
return [
new vscode.CompletionItem('log', vscode.CompletionItemKind.Method),
new vscode.CompletionItem('warn', vscode.CompletionItemKind.Method),
new vscode.CompletionItem('error', vscode.CompletionItemKind.Method),
];
}
},
'.' // triggered whenever a '.' is being typed
);
context.subscriptions.push(provider_sections, provider_types, provider_resources, provider_variables, provider_ports);
//context.subscriptions.push(vscode.languages.registerDocumentHighlightProvider('lisa', new LISADocumentHighlightProvider()));
const command_scan = "lisa.scan";
const scan_handler = async () => {
lisa_files = await vscode.workspace.findFiles('**/*.lisa')
for (let file of lisa_files)
{
let content = fs.readFileSync(file.path, 'utf-8');
let component_regexp = new RegExp("component\\s+([\\w\\d]+)\\s*\\{(.*?)^\\}", "gms");
let port_regexp = new RegExp("^\\s*(internal)?\\s*(master|slave)\\s*(addressable)?\\s*port\\s*<(.*?)>\\s*([\\w\\d]+)", "gms");
let matches = content.matchAll(component_regexp);
for (let match of matches)
{
let name = match[1];
// Gather component definitions
let lines = content.split('\n');
let lineCount = -1;
for(let line of lines)
{
lineCount += 1;
let pos = line.search("component\\s+"+name);
if(pos != -1) // Found the line
{
let position = new vscode.Position(lineCount, pos);
let location = new vscode.Location(file, position);
if(!component_definitions.has(name))
{
component_definitions.set(name, [location]);
}
else
{
let defs = component_definitions.get(name);
defs.push(location);
component_definitions.set(name, defs);
}
break;
}
}
// console.log("Component: " + name)
// fs.appendFileSync("/Users/zhewan02/Work/lisa-lang/src/log.txt", "\nComponent: " + name);
let text = match[2];
let ports = text.matchAll(port_regexp);
let ports_instances = [];
for (let port of ports)
{
// let documentation = " * Port: " + port[5] + "\n"
// documentation += " * Internal: " + ((port[1] == "internal")? "True":"False") + "\n";
// documentation += " * Type: " + port[2] + "\n";
// documentation += " * Addressable: " + ((port[3] == "addressable")? "True":"False") + "\n";
// documentation += " * Protocol: " + port[4] + "\n";
// group 1: internal, group 2: master/slave, group 3: addressable, group 4: protocol, group 5: port name
ports_instances.push(Array.from(port));
// fs.appendFileSync("/Users/zhewan02/Work/lisa-lang/src/log.txt", "\n => \n" + documentation,);
}
component_ports.set(name, Array.from(ports_instances));
}
}
console.log("INFO: Workspace scan accomplished")
};
context.subscriptions.push(vscode.commands.registerCommand(command_scan, scan_handler));
// support for sgrepo files
let provider_sgrepo = vscode.languages.registerCompletionItemProvider(
'sgrepo',
{
provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext) {
// Simple completion items for keywords
let keywords = ["component", "path", "files"];
let simpleCompletion_sgrepo = []
for (let keyword of keywords) {
simpleCompletion_sgrepo.push(new vscode.CompletionItem(keyword));
}
const commandCompletion_component = new vscode.CompletionItem('component');
commandCompletion_component.kind = vscode.CompletionItemKind.Keyword;
commandCompletion_component.insertText = 'component ""\n{\n path = \"\";\n}';
simpleCompletion_sgrepo.push(commandCompletion_component);
const commandCompletion_files = new vscode.CompletionItem('files');
commandCompletion_files.kind = vscode.CompletionItemKind.Keyword;
commandCompletion_files.insertText = 'files\n{\n path = \"\";\n}';
simpleCompletion_sgrepo.push(commandCompletion_files);
let linePrefix = document.lineAt(position).text.substr(0, position.character);
if (linePrefix.endsWith('path = \"')) {
let file_paths = []
for(let lisa of lisa_files)
{
file_paths.push(new vscode.CompletionItem(lisa.path, vscode.CompletionItemKind.File));
}
return file_paths;
}
return simpleCompletion_sgrepo;
}
},
"\""
);
context.subscriptions.push(provider_sgrepo);
let provider_definitions = vscode.languages.registerDefinitionProvider(
'lisa', new LISADefinitionProvider());
context.subscriptions.push(provider_definitions);
vscode.commands.executeCommand("lisa.scan");
}
export function detactive() {
}
\ No newline at end of file
{
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
"name": "LISA+",
"patterns": [
{
"include": "#expression"
},
{
"include": "#strings"
}
],
"repository": {
"expression": {