additional features for parsing incomplete declarations and providing code actions to resolve them
This commit is contained in:
parent
b58def7680
commit
51722cb03d
@ -25,6 +25,8 @@ import { ZqInstance } from './zq/ZqInstance';
|
||||
import { ZqInstanceReference } from './zq/ZqInstanceReference';
|
||||
import { ZqObject } from './zq/ZqObject';
|
||||
import { ZqParser } from './zq/parser/ZqParser';
|
||||
import { ZqObjectDefinition } from './zq/ZqObjectDefinition';
|
||||
import { ZqlCodeActionsProvider } from './zqlEditor/ZqlCodeActionsProvider';
|
||||
|
||||
const cp = require('child_process');
|
||||
|
||||
@ -230,6 +232,8 @@ export function openWebBrowser(url: vscode.Uri) {
|
||||
// Your extension is activated the very first time the command is executed
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
|
||||
registerZqlEditorSupport(context);
|
||||
|
||||
outputChannel = vscode.window.createOutputChannel("Mocha");
|
||||
outputChannel.appendLine("Mocha for VSCode activated");
|
||||
|
||||
@ -278,10 +282,19 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
|
||||
let decl = ZqParser.parse(line);
|
||||
if (decl !== null) {
|
||||
if ((decl as ZqFunctionDefinition).isStub) {
|
||||
|
||||
(decl as ZqFunctionDefinition).isStub = false;
|
||||
|
||||
ed.edit(edit => edit.replace(currentLineRange, decl.toString()));
|
||||
}
|
||||
else {
|
||||
vscode.window.showErrorMessage("This ZQ function already has an implementation!");
|
||||
}
|
||||
}
|
||||
else {
|
||||
vscode.window.showErrorMessage("Please place the cursor on a ZQ function before importing its definition!");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -379,7 +392,8 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
context.subscriptions.push(cmd_suv_manager);
|
||||
|
||||
let cmd_suv_refresh = vscode.commands.registerCommand('mocha.suvmanager.suv_refresh', () => {
|
||||
vscode.window.showInformationMessage('Add code here to display SUV Manager panel!');
|
||||
|
||||
treeDataProvider.refresh();
|
||||
});
|
||||
context.subscriptions.push(cmd_suv_refresh);
|
||||
|
||||
@ -500,16 +514,38 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
});
|
||||
context.subscriptions.push(cmd_suv_up);
|
||||
|
||||
const relationshipDecorations: vscode.TextEditorDecorationType[] = [];
|
||||
let relationshipDecorations: vscode.TextEditorDecorationType[] = [];
|
||||
|
||||
const noAttrsDecorationType = vscode.window.createTextEditorDecorationType({
|
||||
before:
|
||||
{
|
||||
contentText: "<no attributes>",
|
||||
margin: "0px 4px 0px 16px",
|
||||
color: "#999999"
|
||||
}
|
||||
});
|
||||
|
||||
context.subscriptions.push(vscode.window.onDidChangeTextEditorSelection((e) => {
|
||||
if (e.textEditor.document.languageId === "zql") {
|
||||
let clz = ZqParser.parse(e.textEditor.document.getText()) as ZqClass;
|
||||
if (clz !== null)
|
||||
{
|
||||
if (clz.attributes.length === 0) {
|
||||
let attrsDef : ZqObjectDefinition | undefined = clz.sectionDefinitions.get('attributes');
|
||||
if (attrsDef !== undefined) {
|
||||
e.textEditor.setDecorations(noAttrsDecorationType, [
|
||||
new vscode.Range(attrsDef.lineIndex + 1 + 2/*hack*/, 0,
|
||||
attrsDef.lineIndex + 1 + 2 /*hack*/, 0)
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
e.textEditor.setDecorations(noAttrsDecorationType, []);
|
||||
}
|
||||
|
||||
relationshipDecorations.forEach(element => {
|
||||
e.textEditor.setDecorations(element, []);
|
||||
});
|
||||
relationshipDecorations = [];
|
||||
|
||||
clz.relationships.forEach(rel => {
|
||||
let n = "";
|
||||
@ -630,6 +666,25 @@ function indentLines(count: number, lines: string[]): string {
|
||||
retval += indent;
|
||||
return retval;
|
||||
}
|
||||
|
||||
function registerZqlEditorSupport(context: vscode.ExtensionContext) {
|
||||
context.subscriptions.push(
|
||||
vscode.languages.registerCodeActionsProvider('zql', new ZqlCodeActionsProvider(), {
|
||||
providedCodeActionKinds: ZqlCodeActionsProvider.providedCodeActionKinds
|
||||
}));
|
||||
|
||||
// const emojiDiagnostics = vscode.languages.createDiagnosticCollection("emoji");
|
||||
// context.subscriptions.push(emojiDiagnostics);
|
||||
|
||||
//subscribeToDocumentChanges(context, emojiDiagnostics);
|
||||
/*
|
||||
context.subscriptions.push(
|
||||
vscode.languages.registerCodeActionsProvider('zql', new Emojinfo(), {
|
||||
providedCodeActionKinds: Emojinfo.providedCodeActionKinds
|
||||
})
|
||||
);
|
||||
*/
|
||||
}
|
||||
/*
|
||||
|
||||
// This method is called when your extension is activated
|
||||
|
||||
@ -16,9 +16,16 @@
|
||||
// along with mocha-vscode. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import { ZqInstance } from "./ZqInstance";
|
||||
import { ZqObjectDefinition } from "./ZqObjectDefinition";
|
||||
|
||||
export class ZqClass extends ZqInstance {
|
||||
|
||||
private _sectionDefinitions: Map<string, ZqObjectDefinition> = new Map<string, ZqObjectDefinition>();
|
||||
public get sectionDefinitions() : Map<string, ZqObjectDefinition> {
|
||||
return this._sectionDefinitions;
|
||||
}
|
||||
|
||||
|
||||
toString(): string {
|
||||
|
||||
let r = "";
|
||||
|
||||
@ -22,12 +22,40 @@ import { ZqClass } from "../ZqClass";
|
||||
import { ZqFunctionDefinition } from "../ZqFunctionDefinition";
|
||||
import { ZqInstanceReference } from "../ZqInstanceReference";
|
||||
import { ZqObject } from "../ZqObject";
|
||||
import { ZqObjectDefinition } from "../ZqObjectDefinition";
|
||||
import { ZqRelationship } from "../ZqRelationship";
|
||||
import { ZqParserContext } from "./ZqParserContext";
|
||||
import { ZqTokenInfo } from "./ZqTokenInfo";
|
||||
|
||||
export class ZqParser {
|
||||
|
||||
public static readonly instanceDeclarationRegExp = new RegExp("\\s*(\\w*)\\s*(?:,\\s*(\\d*\\$\\d*))?\\s*");
|
||||
public static readonly attributeDeclarationRegExp = new RegExp("\\s*(\\w*)\\s*(?:,\\s*(\\d*\\$\\d*))?\\s*(?:\\:\\s*(\\w*))?\\s*");
|
||||
|
||||
static parseInstanceDeclaration(lineText: string): ZqInstanceReference | null {
|
||||
let result = ZqParser.instanceDeclarationRegExp.exec(lineText);
|
||||
|
||||
let name = result?.at(1);
|
||||
let instanceKey = result?.at(2);
|
||||
|
||||
if (name === undefined) {
|
||||
return null;
|
||||
}
|
||||
return new ZqInstanceReference(name === undefined ? "" : name, instanceKey === undefined ? null : InstanceKey.parse(instanceKey));
|
||||
}
|
||||
static parseAttributeDeclaration(lineText: string): ZqAttribute | null {
|
||||
let result = ZqParser.attributeDeclarationRegExp.exec(lineText);
|
||||
|
||||
let name = result?.at(1);
|
||||
let instanceKey = result?.at(2);
|
||||
let dataType = result?.at(3);
|
||||
|
||||
if (name === undefined) {
|
||||
return null;
|
||||
}
|
||||
return new ZqAttribute(name === undefined ? "" : name, instanceKey === undefined ? null : InstanceKey.parse(instanceKey), dataType === undefined ? null : dataType);
|
||||
}
|
||||
|
||||
static parse(line: string): ZqObject {
|
||||
|
||||
let idx: number = 0;
|
||||
@ -71,12 +99,14 @@ export class ZqParser {
|
||||
|
||||
obj.isStatic = nextStatic;
|
||||
obj.isStub = nextStub;
|
||||
obj.name = line.substring(token.nextStart, line.indexOf('(', token.nextStart)).trim();
|
||||
token.nextStart = token.nextStart + obj.name.length + 1;
|
||||
retval = obj;
|
||||
let name = result?.at(5);
|
||||
if (name !== undefined) {
|
||||
obj.name = name;
|
||||
}
|
||||
token.nextStart = token.nextStart + line.length + 1;
|
||||
retval = obj;
|
||||
break;
|
||||
}
|
||||
|
||||
line = line.substring(token.nextStart).trim();
|
||||
// idx = token.NextStart;
|
||||
idx = 0;
|
||||
}
|
||||
@ -112,21 +142,15 @@ function parseClass(line: string, NextStart: number) {
|
||||
|
||||
while (!ctx.endOfStream) {
|
||||
if (section === "attributes") {
|
||||
let line = ctx.readLine();
|
||||
let line = ctx.readLine().trim();
|
||||
if (line === '') {
|
||||
section = "";
|
||||
continue;
|
||||
}
|
||||
|
||||
let re = new RegExp("\\s*(\\w*)\\s*(?:,\\s*(\\d*\\$\\d*))\\s*:\\s*(\\w*)\\s*");
|
||||
let result = re.exec(line);
|
||||
|
||||
let name = result?.at(1);
|
||||
let instanceKey = result?.at(2);
|
||||
let dataType = result?.at(3);
|
||||
|
||||
if (name !== undefined && instanceKey !== undefined && dataType !== undefined) {
|
||||
obj.attributes.push(new ZqAttribute(name, InstanceKey.parse(instanceKey), dataType));
|
||||
let result = ZqParser.parseAttributeDeclaration(line);
|
||||
if (result !== null) {
|
||||
obj.attributes.push(result);
|
||||
}
|
||||
}
|
||||
else if (section === "relationships" || section === "instances") {
|
||||
@ -161,10 +185,15 @@ function parseClass(line: string, NextStart: number) {
|
||||
}
|
||||
}
|
||||
else if (section === "") {
|
||||
while (ctx.peekLine().trim() === '') {
|
||||
ctx.readLine();
|
||||
}
|
||||
|
||||
let tok = ctx.readToken();
|
||||
if (tok !== null) {
|
||||
if (tok.token === ":") {
|
||||
section = tok.value;
|
||||
obj.sectionDefinitions.set(section, new ZqObjectDefinition(ctx.lineIndex, ctx.columnIndex));
|
||||
ctx.readLine();
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,6 +83,17 @@ export class ZqParserContext {
|
||||
return before;
|
||||
}
|
||||
|
||||
peekLine() : string {
|
||||
|
||||
let idxNewline = this._value.indexOf("\n", this._currentIndex);
|
||||
let retval = this._value.substring(this._currentIndex);
|
||||
|
||||
if (idxNewline > -1) {
|
||||
retval = this._value.substring(this._currentIndex, idxNewline);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
readToken(): ZqTokenInfo | null {
|
||||
let end = this._value.length;
|
||||
|
||||
|
||||
111
src/zqlEditor/ZqlCodeActionsProvider.ts
Normal file
111
src/zqlEditor/ZqlCodeActionsProvider.ts
Normal file
@ -0,0 +1,111 @@
|
||||
// Copyright (C) 2025 Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// This file is part of mocha-vscode.
|
||||
//
|
||||
// mocha-vscode is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// mocha-vscode is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with mocha-vscode. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { InstanceKey } from '../mocha/core/InstanceKey';
|
||||
import { ZqParser } from '../zq/parser/ZqParser';
|
||||
import { ZqClass } from '../zq/ZqClass';
|
||||
|
||||
export class ZqlCodeActionsProvider implements vscode.CodeActionProvider {
|
||||
|
||||
public static readonly providedCodeActionKinds = [
|
||||
vscode.CodeActionKind.QuickFix
|
||||
];
|
||||
|
||||
private createIidImportFix(document: vscode.TextDocument, range: vscode.Range, type : string, instanceId: InstanceKey): vscode.CodeAction {
|
||||
const fix = new vscode.CodeAction("Complete instance declaration from Mocha", vscode.CodeActionKind.QuickFix);
|
||||
fix.edit = new vscode.WorkspaceEdit();
|
||||
|
||||
let line = document.lineAt(range.start);
|
||||
let lineText = line.text.trim();
|
||||
|
||||
if (type === "attribute") {
|
||||
let attr = ZqParser.parseAttributeDeclaration(lineText);
|
||||
if (attr !== null) {
|
||||
if (attr.instanceKey === null) {
|
||||
attr.instanceKey = InstanceKey.parse(instanceId.toString());
|
||||
}
|
||||
if (attr.dataType === null) {
|
||||
attr.dataType = "text";
|
||||
}
|
||||
|
||||
fix.edit.replace(document.uri, line.range, " " + attr.toString());
|
||||
}
|
||||
}
|
||||
else if (type === "instance") {
|
||||
let inst = ZqParser.parseInstanceDeclaration(lineText);
|
||||
if (inst !== null) {
|
||||
if (inst.instanceKey === null) {
|
||||
inst.instanceKey = InstanceKey.parse(instanceId.toString());
|
||||
}
|
||||
|
||||
fix.edit.replace(document.uri, line.range, " " + inst.toString());
|
||||
}
|
||||
}
|
||||
return fix;
|
||||
}
|
||||
|
||||
public provideCodeActions(document: vscode.TextDocument, range: vscode.Range): vscode.CodeAction[] | undefined {
|
||||
|
||||
let sectionName = "";
|
||||
let lineIndex = range.start.line;
|
||||
while (lineIndex > 0) {
|
||||
let szline = document.lineAt(new vscode.Position(lineIndex, 0));
|
||||
if (szline.text.endsWith(':')) {
|
||||
sectionName = szline.text.substring(0, szline.text.length - 1);
|
||||
break;
|
||||
}
|
||||
lineIndex--;
|
||||
}
|
||||
|
||||
let ary : vscode.CodeAction[] = [];
|
||||
|
||||
let line = document.lineAt(range.start).text.trim();
|
||||
if (line !== sectionName + ":") {
|
||||
|
||||
if (sectionName === "attributes") {
|
||||
// parse attribute declaration
|
||||
let result = ZqParser.parseAttributeDeclaration(line);
|
||||
if (result !== null) {
|
||||
if (result.instanceKey === null || result.dataType === null) {
|
||||
const fixImportIidFromMocha = this.createIidImportFix(document, range, "attribute", new InstanceKey(1, 1234));
|
||||
ary.push(fixImportIidFromMocha);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (sectionName === "instances") {
|
||||
// parse attribute declaration
|
||||
let result = ZqParser.parseInstanceDeclaration(line);
|
||||
if (result !== null) {
|
||||
if (result.instanceKey === null) {
|
||||
const fixImportIidFromMocha = this.createIidImportFix(document, range, "instance", new InstanceKey(1, 1234));
|
||||
ary.push(fixImportIidFromMocha);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
const COMMAND = 'mocha.suvManager.suv_select';
|
||||
const commandAction = new vscode.CodeAction('Learn more...', vscode.CodeActionKind.Empty);
|
||||
commandAction.command = { command: COMMAND, title: 'Learn more about emojis', tooltip: 'This will open the unicode emoji page.' };
|
||||
|
||||
ary.push(commandAction);
|
||||
*/
|
||||
return ary;
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user