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 { ZqInstanceReference } from './zq/ZqInstanceReference';
|
||||||
import { ZqObject } from './zq/ZqObject';
|
import { ZqObject } from './zq/ZqObject';
|
||||||
import { ZqParser } from './zq/parser/ZqParser';
|
import { ZqParser } from './zq/parser/ZqParser';
|
||||||
|
import { ZqObjectDefinition } from './zq/ZqObjectDefinition';
|
||||||
|
import { ZqlCodeActionsProvider } from './zqlEditor/ZqlCodeActionsProvider';
|
||||||
|
|
||||||
const cp = require('child_process');
|
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
|
// Your extension is activated the very first time the command is executed
|
||||||
export function activate(context: vscode.ExtensionContext) {
|
export function activate(context: vscode.ExtensionContext) {
|
||||||
|
|
||||||
|
registerZqlEditorSupport(context);
|
||||||
|
|
||||||
outputChannel = vscode.window.createOutputChannel("Mocha");
|
outputChannel = vscode.window.createOutputChannel("Mocha");
|
||||||
outputChannel.appendLine("Mocha for VSCode activated");
|
outputChannel.appendLine("Mocha for VSCode activated");
|
||||||
|
|
||||||
@ -278,9 +282,18 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
|
|
||||||
let decl = ZqParser.parse(line);
|
let decl = ZqParser.parse(line);
|
||||||
if (decl !== null) {
|
if (decl !== null) {
|
||||||
(decl as ZqFunctionDefinition).isStub = false;
|
if ((decl as ZqFunctionDefinition).isStub) {
|
||||||
|
|
||||||
ed.edit(edit => edit.replace(currentLineRange, decl.toString()));
|
(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);
|
context.subscriptions.push(cmd_suv_manager);
|
||||||
|
|
||||||
let cmd_suv_refresh = vscode.commands.registerCommand('mocha.suvmanager.suv_refresh', () => {
|
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);
|
context.subscriptions.push(cmd_suv_refresh);
|
||||||
|
|
||||||
@ -500,16 +514,38 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
});
|
});
|
||||||
context.subscriptions.push(cmd_suv_up);
|
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) => {
|
context.subscriptions.push(vscode.window.onDidChangeTextEditorSelection((e) => {
|
||||||
if (e.textEditor.document.languageId === "zql") {
|
if (e.textEditor.document.languageId === "zql") {
|
||||||
let clz = ZqParser.parse(e.textEditor.document.getText()) as ZqClass;
|
let clz = ZqParser.parse(e.textEditor.document.getText()) as ZqClass;
|
||||||
if (clz !== null)
|
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 => {
|
relationshipDecorations.forEach(element => {
|
||||||
e.textEditor.setDecorations(element, []);
|
e.textEditor.setDecorations(element, []);
|
||||||
});
|
});
|
||||||
|
relationshipDecorations = [];
|
||||||
|
|
||||||
clz.relationships.forEach(rel => {
|
clz.relationships.forEach(rel => {
|
||||||
let n = "";
|
let n = "";
|
||||||
@ -630,6 +666,25 @@ function indentLines(count: number, lines: string[]): string {
|
|||||||
retval += indent;
|
retval += indent;
|
||||||
return retval;
|
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
|
// 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/>.
|
// along with mocha-vscode. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import { ZqInstance } from "./ZqInstance";
|
import { ZqInstance } from "./ZqInstance";
|
||||||
|
import { ZqObjectDefinition } from "./ZqObjectDefinition";
|
||||||
|
|
||||||
export class ZqClass extends ZqInstance {
|
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 {
|
toString(): string {
|
||||||
|
|
||||||
let r = "";
|
let r = "";
|
||||||
|
|||||||
@ -22,12 +22,40 @@ import { ZqClass } from "../ZqClass";
|
|||||||
import { ZqFunctionDefinition } from "../ZqFunctionDefinition";
|
import { ZqFunctionDefinition } from "../ZqFunctionDefinition";
|
||||||
import { ZqInstanceReference } from "../ZqInstanceReference";
|
import { ZqInstanceReference } from "../ZqInstanceReference";
|
||||||
import { ZqObject } from "../ZqObject";
|
import { ZqObject } from "../ZqObject";
|
||||||
|
import { ZqObjectDefinition } from "../ZqObjectDefinition";
|
||||||
import { ZqRelationship } from "../ZqRelationship";
|
import { ZqRelationship } from "../ZqRelationship";
|
||||||
import { ZqParserContext } from "./ZqParserContext";
|
import { ZqParserContext } from "./ZqParserContext";
|
||||||
import { ZqTokenInfo } from "./ZqTokenInfo";
|
import { ZqTokenInfo } from "./ZqTokenInfo";
|
||||||
|
|
||||||
export class ZqParser {
|
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 {
|
static parse(line: string): ZqObject {
|
||||||
|
|
||||||
let idx: number = 0;
|
let idx: number = 0;
|
||||||
@ -71,12 +99,14 @@ export class ZqParser {
|
|||||||
|
|
||||||
obj.isStatic = nextStatic;
|
obj.isStatic = nextStatic;
|
||||||
obj.isStub = nextStub;
|
obj.isStub = nextStub;
|
||||||
obj.name = line.substring(token.nextStart, line.indexOf('(', token.nextStart)).trim();
|
let name = result?.at(5);
|
||||||
token.nextStart = token.nextStart + obj.name.length + 1;
|
if (name !== undefined) {
|
||||||
|
obj.name = name;
|
||||||
|
}
|
||||||
|
token.nextStart = token.nextStart + line.length + 1;
|
||||||
retval = obj;
|
retval = obj;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
line = line.substring(token.nextStart).trim();
|
|
||||||
// idx = token.NextStart;
|
// idx = token.NextStart;
|
||||||
idx = 0;
|
idx = 0;
|
||||||
}
|
}
|
||||||
@ -112,21 +142,15 @@ function parseClass(line: string, NextStart: number) {
|
|||||||
|
|
||||||
while (!ctx.endOfStream) {
|
while (!ctx.endOfStream) {
|
||||||
if (section === "attributes") {
|
if (section === "attributes") {
|
||||||
let line = ctx.readLine();
|
let line = ctx.readLine().trim();
|
||||||
if (line === '') {
|
if (line === '') {
|
||||||
section = "";
|
section = "";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let re = new RegExp("\\s*(\\w*)\\s*(?:,\\s*(\\d*\\$\\d*))\\s*:\\s*(\\w*)\\s*");
|
let result = ZqParser.parseAttributeDeclaration(line);
|
||||||
let result = re.exec(line);
|
if (result !== null) {
|
||||||
|
obj.attributes.push(result);
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (section === "relationships" || section === "instances") {
|
else if (section === "relationships" || section === "instances") {
|
||||||
@ -161,10 +185,15 @@ function parseClass(line: string, NextStart: number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (section === "") {
|
else if (section === "") {
|
||||||
|
while (ctx.peekLine().trim() === '') {
|
||||||
|
ctx.readLine();
|
||||||
|
}
|
||||||
|
|
||||||
let tok = ctx.readToken();
|
let tok = ctx.readToken();
|
||||||
if (tok !== null) {
|
if (tok !== null) {
|
||||||
if (tok.token === ":") {
|
if (tok.token === ":") {
|
||||||
section = tok.value;
|
section = tok.value;
|
||||||
|
obj.sectionDefinitions.set(section, new ZqObjectDefinition(ctx.lineIndex, ctx.columnIndex));
|
||||||
ctx.readLine();
|
ctx.readLine();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -83,6 +83,17 @@ export class ZqParserContext {
|
|||||||
return before;
|
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 {
|
readToken(): ZqTokenInfo | null {
|
||||||
let end = this._value.length;
|
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