diff --git a/package.json b/package.json index 2bf2125..a446ad6 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "vscode-basex", "displayName": "BaseX tools", "description": "BaseX tools: XQuery, XML, XPath Tools for Visual Studio Code", - "version": "0.0.48", + "version": "0.0.52", "preview": true, "publisher": "quodatum", "author": "Andy Bunce (https://github.com/Quodatum)", @@ -12,13 +12,13 @@ "theme": "light" }, "icon": "resources/basex.png", - "homepage": "https://github.com/Quodatum/vscode-xml", + "homepage": "https://github.com/Quodatum/vscode-basex", "repository": { "type": "git", - "url": "git+https://github.com/Quodatum/vscode-xml.git" + "url": "git+https://github.com/Quodatum/vscode-basex.git" }, "bugs": { - "url": "https://github.com/Quodatum/vscode-xml/issues" + "url": "https://github.com/Quodatum/vscode-basex/issues" }, "engines": { "vscode": "^1.63.0" @@ -27,6 +27,7 @@ "Formatters", "Programming Languages", "Linters", + "Snippets", "Other" ], "activationEvents": [ @@ -70,16 +71,21 @@ { "command": "basexTools.minifyXml", "title": "BaseX Tools: Minify XML" + }, + { + "command": "basexTools.getAST", + "title": "BaseX Tools: AST" } ], "configuration": [ { "id": "xmltree", "title": "XML tree view ", + "properties": { "basexTools.xmlTree.enableTreeView": { "type": "boolean", - "default": true, + "default": false, "description": "Enables the XML Document view in the explorer for XML documents.", "scope": "window" }, @@ -114,8 +120,44 @@ "scope": "window" } } - }, - { + },{ + "id": "xquery", + "title": "XQuery ", + "properties":{ + "basexTools.xquery.executionArguments": { + "type": "array", + "default": [ + "-xquery", + "$(script)", + "-in", + "$(input)", + "-out", + "$(input).output.xml" + ], + "description": "Arguments to be passed to the XQuery execution engine.", + "scope": "window" + }, + "basexTools.xquery.executionEngine": { + "type": "string", + "default": "", + "description": "The full path to the executable to run when executing XQuery scripts.", + "scope": "window" + }, + "basexTools.xquery.executionInputLimit": { + "type": "integer", + "default": 100, + "description": "The maximum number of input files to enumerate when executing XQuery scripts.", + "scope": "window" + }, + "basexTools.xquery.executionInputSearchPattern": { + "type": "string", + "default": "**/*.xml", + "description": "The pattern used to search for input XML files when executing XQuery scripts.", + "scope": "window" + } + } + + },{ "title": "BaseX Tools ", "type": "object", "properties": { @@ -153,39 +195,8 @@ "default": "v2", "description": "Supported XML Formatters: classic", "scope": "window" - }, - "basexTools.xqueryExecutionArguments": { - "type": "array", - "default": [ - "-xquery", - "$(script)", - "-in", - "$(input)", - "-out", - "$(input).output.xml" - ], - "description": "Arguments to be passed to the XQuery execution engine.", - "scope": "window" - }, - "basexTools.xqueryExecutionEngine": { - "type": "string", - "default": "", - "description": "The full path to the executable to run when executing XQuery scripts.", - "scope": "window" - }, - "basexTools.xqueryExecutionInputLimit": { - "type": "integer", - "default": 100, - "description": "The maximum number of input files to enumerate when executing XQuery scripts.", - "scope": "window" - }, - "basexTools.xqueryExecutionInputSearchPattern": { - "type": "string", - "default": "**/*.xml", - "description": "The pattern used to search for input XML files when executing XQuery scripts.", - "scope": "window" } - } + } } ], "grammars": [ @@ -195,6 +206,12 @@ "scopeName": "source.xquery" } ], + "snippets": [ + { + "language": "xquery", + "path": "./snippets.jsonc" + } + ], "keybindings": [ { "key": "ctrl+shift+alt+x", diff --git a/resources/basex.png b/resources/basex.png index d737358..675f416 100644 Binary files a/resources/basex.png and b/resources/basex.png differ diff --git a/snippets.jsonc b/snippets.jsonc new file mode 100644 index 0000000..2149379 --- /dev/null +++ b/snippets.jsonc @@ -0,0 +1,120 @@ +{ + + "for": { + "prefix": "for", + "body": "for $${1:item} in ${2:expr}" + }, + "return": { + "prefix": "ret", + "body": "return ${1:expr}" + }, + "import": { + "prefix": "import", + "body": "import module namespace ${1:ns} = '${2:http://www.example.com/}';", + "description": "Import module" + }, + "module": { + "prefix": "module", + "body": "module namespace ${1:ns} = '${2:http://www.example.com}';" + }, + "every": { + "prefix": "every", + "body": "every $${1:varname} in ${2:expr} satisfies ${3:expr}" + }, + "some": { + "prefix": "some", + "body": "some $${1:varname} in ${2:expr} satisfies ${3:expr}" + }, + "function": { + "prefix": "df", + "body": [ + "(:~ $${2:name} :)", + "declare function ${1:ns}:${2:name}(){", + "${3:expr}", + "};" + ], + "description": "declare a function" + }, + "declare variable": { + "prefix": "dv", + "body": [ + "(:~ $${1:varname} :)", + "declare variable $${1:varname} := ${2:expr};", + "" + ], + "description": "declare variable" + }, + "switch": { + "prefix": "sw", + "body": [ + "switch(${1:foo})", + "case ${2:foo} return ${3:true}", + "default return ${4:false}" + ], + "description": "switch statement" + }, + "typeswitch": { + "prefix": "type", + "body": [ + "typeswitch(${1:foo})", + "case ${2:foo} return ${3:true}", + "default return ${4:false}" + ], + "description": "typeswitch statement" + }, + "try": { + "prefix": "try", + "body": [ + "try { ${1:expr} } ", + "catch ${2:*} { ${3:expr} }" + ], + "description": "switch statement" + }, + "tumbling": { + "prefix": "tumbling", + "body": [ + "for tumbling window $${1:varname} in ${2:expr}", + "start at $${3:start} when ${4:expr}", + "end at $${5:end} when ${6:expr}", + "return ${7:expr}" + ], + "description": "tumbling window" + }, + "sliding": { + "prefix": "sliding", + "body": [ + "for sliding window $${1:varname} in ${2:expr}", + "start at $${3:start} when ${4:expr}", + "end at $${5:end} when ${6:expr}", + "return ${7:expr}" + ], + "description": "sliding window" + } +} +//snippet if +// if(${1:true}) then ${2:expr} else ${3:true} + +// default return ${4:false} + + +//snippet let +// let $${1:varname} := ${2:expr} +//snippet group +// group by $${1:varname} := ${2:expr} +//snippet order +// order by ${1:expr} ${2:descending} +//snippet stable +// stable order by ${1:expr} +//snippet count +// count $${1:varname} +//snippet ordered +// ordered { ${1:expr} } +//snippet unordered +// unordered { ${1:expr} } +//snippet treat +// treat as ${1:expr} +//snippet castable +// castable as ${1:atomicType} +//snippet cast +// cast as ${1:atomicType} + diff --git a/src/common/configuration.ts b/src/common/configuration.ts index 4e4346e..fbca6b2 100644 --- a/src/common/configuration.ts +++ b/src/common/configuration.ts @@ -11,7 +11,7 @@ export class Configuration { return this._getForWindow("xmlTree.enableViewMetadata"); } - static get enableViewCursorSync(): boolean { + static get enableXmlTreeViewCursorSync(): boolean { return this._getForWindow("xmlTree.enableViewCursorSync"); } @@ -28,19 +28,19 @@ export class Configuration { } static get xqueryExecutionArguments(): string[] { - return this._getForWindow("xqueryExecutionArguments"); + return this._getForWindow("xquery.executionArguments"); } static get xqueryExecutionEngine(): string { - return this._getForWindow("xqueryExecutionEngine"); + return this._getForWindow("xquery.executionEngine"); } static get xqueryExecutionInputLimit(): number { - return this._getForWindow("xqueryExecutionInputLimit"); + return this._getForWindow("xquery.executionInputLimit"); } static get xqueryExecutionInputSearchPattern(): string { - return this._getForWindow("xqueryExecutionInputSearchPattern"); + return this._getForWindow("xquery.executionInputSearchPattern"); } static enforcePrettySelfClosingTagOnFormat(resource: Uri): boolean { diff --git a/src/common/logger.ts b/src/common/logger.ts index b391174..6ca1994 100644 --- a/src/common/logger.ts +++ b/src/common/logger.ts @@ -4,6 +4,7 @@ import { OutputChannel, window } from "vscode"; const ver = require("@quodatum/xqlint").version; const _channel:OutputChannel = window.createOutputChannel("BaseX"); + function logdate(){ return (new Date()).toISOString().slice(0, 19).replace(/-/g, "/").replace("T", " "); } diff --git a/src/constants.ts b/src/constants.ts index c9478f1..641878c 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -7,6 +7,7 @@ export namespace commands { export const textToXml = "basexTools.textToXml"; export const getCurrentXPath = "basexTools.getCurrentXPath"; export const minifyXml = "basexTools.minifyXml"; + export const getAST = "basexTools.getAST"; } export namespace contextKeys { diff --git a/src/extension.ts b/src/extension.ts index 5485279..6efe5f9 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -7,8 +7,8 @@ import { channel } from "./common/logger"; import { createDocumentSelector, ExtensionState, Configuration } from "./common"; import { XQueryCompletionItemProvider } from "./completion"; import { XmlFormatterFactory, XmlFormattingEditProvider } from "./formatting"; -import { formatAsXml, minifyXml, xmlToText, textToXml } from "./formatting/commands"; -import { XQueryLinter } from "./linting"; +import { formatAsXml, minifyXml, xmlToText, textToXml } from "./formatting/commands"; +import { XQueryLinter,getAst } from "./linting"; import { XmlTreeDataProvider } from "./tree-view"; import { evaluateXPath, getCurrentXPath } from "./xpath/commands"; import { executeXQuery } from "./xquery-execution/commands"; @@ -39,6 +39,7 @@ export function activate(context: ExtensionContext) { commands.registerTextEditorCommand(constants.commands.xmlToText, xmlToText), commands.registerTextEditorCommand(constants.commands.textToXml, textToXml), commands.registerTextEditorCommand(constants.commands.minifyXml, minifyXml), + commands.registerTextEditorCommand(constants.commands.getAST, getAst), languages.registerDocumentFormattingEditProvider(xmlXsdDocSelector, xmlFormattingEditProvider), languages.registerDocumentRangeFormattingEditProvider(xmlXsdDocSelector, xmlFormattingEditProvider), diff --git a/src/formatting/commands/index.ts b/src/formatting/commands/index.ts index fe7023a..8e7437c 100644 --- a/src/formatting/commands/index.ts +++ b/src/formatting/commands/index.ts @@ -2,3 +2,4 @@ export * from "./formatAsXml"; export * from "./minifyXml"; export * from "./xmlToText"; + diff --git a/src/formatting/xquery-formatting-provider.ts b/src/formatting/xquery-formatting-provider.ts index cd1d4f7..0da9ca1 100644 --- a/src/formatting/xquery-formatting-provider.ts +++ b/src/formatting/xquery-formatting-provider.ts @@ -27,7 +27,7 @@ export class XQueryFormatter implements DocumentFormattingEditProvider, Document provideDocumentRangeFormattingEdits(document: TextDocument, range: Range, _options: FormattingOptions, _token: CancellationToken): ProviderResult { const selected = document.getText(range); - const result = format(selected); + const result = format(selected, document); return [TextEdit.replace(range, result)]; } } diff --git a/src/linting/getAST.ts b/src/linting/getAST.ts new file mode 100644 index 0000000..b792df9 --- /dev/null +++ b/src/linting/getAST.ts @@ -0,0 +1,23 @@ + +import { Range, TextEditor, Selection } from "vscode"; +import { channel } from "../common/logger"; +// eslint-disable-next-line @typescript-eslint/no-var-requires +const XQLint = require("@quodatum/xqlint").XQLint; + +export function getAst(textEditor: TextEditor): void { + textEditor.edit(textEdit => { + const selections = textEditor.selections; + selections.forEach(selection => { + if (selection.isEmpty) { + selection = new Selection( + textEditor.document.positionAt(0), + textEditor.document.positionAt(textEditor.document.getText().length) + ); + } + const text = textEditor.document.getText(new Range(selection.start, selection.end)); + const linter = new XQLint(text); + const ast=linter.getAST(); + channel.appendLine(ast); + }); + }); +} \ No newline at end of file diff --git a/src/linting/index.ts b/src/linting/index.ts index 4ab4df0..e7c5add 100644 --- a/src/linting/index.ts +++ b/src/linting/index.ts @@ -1 +1,2 @@ export * from "./xquery-linter"; +export * from "./getAST"; diff --git a/src/symbols/symbols.ts b/src/symbols/symbols.ts index 9dd1d32..62862c4 100644 --- a/src/symbols/symbols.ts +++ b/src/symbols/symbols.ts @@ -1,44 +1,56 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import { XQLint } from '@quodatum/xqlint'; -import * as vscode from 'vscode'; +import {SymbolKind, DocumentSymbol, DocumentSymbolProvider, + Range, Position, TextDocument,CancellationToken} from 'vscode'; import { channel } from "../common/logger"; // // This class handles Symbols // -function makeSymbol(name: string, description: string, icon: vscode.SymbolKind, pos: any) { - const spos = new vscode.Position(pos.sl, pos.sc); - const epos = new vscode.Position(pos.el, pos.ec); - const fullrange = new vscode.Range(spos, epos); - const selrange = new vscode.Range(spos, spos); - return new vscode.DocumentSymbol(name, description, icon, fullrange, selrange); +function makeSymbol(name: string, description: string, icon: SymbolKind, pos: any) { + const spos = new Position(pos.sl, pos.sc); + const epos = new Position(pos.el, pos.ec); + const fullrange = new Range(spos, epos); + const selrange = new Range(spos, spos); + return new DocumentSymbol(name, description, icon, fullrange, selrange); } -export class Symbols implements vscode.DocumentSymbolProvider { - +export class Symbols implements DocumentSymbolProvider { provideDocumentSymbols = async ( - document: vscode.TextDocument, - token: vscode.CancellationToken - ): Promise => { + document: TextDocument, + token: CancellationToken + ): Promise => { + channel.log("Symbols: " + document.uri); - const symbols: vscode.DocumentSymbol[] = []; + const symbols: DocumentSymbol[] = []; const text = document.getText(); const linter = new (XQLint as any)(text, { "styleCheck": false }); + const xqdoc = linter.getXQDoc(); channel.log("got xqdoc"); + const prolog=new Range(0,0,0,0) + symbols.push(makeSymbol(xqdoc.moduleNamespace || "Main", xqdoc.description, SymbolKind.Module, prolog)) + + let vars=makeSymbol("Variables", "", SymbolKind.Variable, prolog) + vars.children=[] // type: type, // pos: pos, // qname: qname, // annotations: {} xqdoc.variables.forEach(v => { const name = v.name; - const info = makeSymbol(name, "var", vscode.SymbolKind.Variable, v.pos) - symbols.push(info); + const info = makeSymbol(name, "", SymbolKind.Variable, v.pos) + vars.children.push(info); }); + + const funs=makeSymbol("Variables", "", SymbolKind.Function, prolog) + funs.children=[] xqdoc.functions.forEach(v => { - const name = v.name; - const info = makeSymbol(name, "Fu", vscode.SymbolKind.Function, v.pos) - symbols.push(info); + const name = v.name +"#" + v.params.length; + const info = makeSymbol(name, "", SymbolKind.Function, v.pos) + funs.children.push(info); }); + symbols.push(vars) + symbols.push(funs) channel.log("Symbols done " + document.uri); return symbols; }; diff --git a/src/xquery-execution/child-process.ts b/src/xquery-execution/child-process.ts index 161f963..45db33d 100644 --- a/src/xquery-execution/child-process.ts +++ b/src/xquery-execution/child-process.ts @@ -1,4 +1,4 @@ -const child_process = require("child_process"); +import child_process = require("child_process"); export class ChildProcess { static async spawn(executable: string, args: string[]): Promise {