Add XQuery Execution
This commit is contained in:
parent
df45b80085
commit
29756e3f97
12
package.json
12
package.json
@ -123,6 +123,18 @@
|
||||
"default": "",
|
||||
"description": "The full path to the executable to run when executing XQuery scripts.",
|
||||
"scope": "resource"
|
||||
},
|
||||
"xmlTools.xqueryExecutionInputLimit": {
|
||||
"type": "integer",
|
||||
"default": 100,
|
||||
"description": "The maximum number of input files to enumerate when executing XQuery scripts.",
|
||||
"scope": "resource"
|
||||
},
|
||||
"xmlTools.xqueryExecutionInputSearchPattern": {
|
||||
"type": "string",
|
||||
"default": "**/*.xml",
|
||||
"description": "The pattern used to search for input XML files when executing XQuery scripts.",
|
||||
"scope": "resource"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -2,6 +2,7 @@ export const extensionPrefix = "xmlTools";
|
||||
|
||||
export namespace commands {
|
||||
export const evaluateXPath = "xmlTools.evaluateXPath";
|
||||
export const executeXQuery = "xmlTools.executeXQuery";
|
||||
export const formatAsXml = "xmlTools.formatAsXml";
|
||||
export const minifyXml = "xmlTools.minifyXml";
|
||||
}
|
||||
@ -17,6 +18,10 @@ export namespace configKeys {
|
||||
export const removeCommentsOnMinify = "removeCommentsOnMinify";
|
||||
export const splitAttributesOnFormat = "splitAttributesOnFormat";
|
||||
export const splitXmlnsOnFormat = "splitXmlnsOnFormat";
|
||||
export const xqueryExecutionArguments = "xqueryExecutionArguments";
|
||||
export const xqueryExecutionEngine = "xqueryExecutionEngine";
|
||||
export const xqueryExecutionInputLimit = "xqueryExecutionInputLimit";
|
||||
export const xqueryExecutionInputSearchPattern = "xqueryExecutionInputSearchPattern";
|
||||
}
|
||||
|
||||
export namespace diagnosticCollections {
|
||||
@ -30,6 +35,7 @@ export namespace languageIds {
|
||||
|
||||
export namespace nativeCommands {
|
||||
export const cursorMove = "cursorMove";
|
||||
export const openGlobalSettings = "workbench.action.openGlobalSettings";
|
||||
export const revealLine = "revealLine";
|
||||
export const setContext = "setContext";
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import { XmlFormattingEditProvider } from "./formatting/xml-formatting-edit-prov
|
||||
import { XQueryLinter } from "./linting/xquery-linter";
|
||||
import { XmlTreeDataProvider } from "./tree-view/xml-tree-data-provider";
|
||||
import { evaluateXPath } from "./xpath/commands/evaluateXPath";
|
||||
import { executeXQuery } from "./xquery-execution/commands/executeXQuery";
|
||||
|
||||
import * as constants from "./constants";
|
||||
|
||||
@ -50,6 +51,11 @@ export function activate(context: ExtensionContext) {
|
||||
context.subscriptions.push(
|
||||
commands.registerTextEditorCommand(constants.commands.evaluateXPath, evaluateXPath)
|
||||
);
|
||||
|
||||
/* XQuery Features */
|
||||
context.subscriptions.push(
|
||||
commands.registerTextEditorCommand(constants.commands.executeXQuery, executeXQuery)
|
||||
);
|
||||
}
|
||||
|
||||
export function deactivate() {
|
||||
|
30
src/xquery-execution/child-process.ts
Normal file
30
src/xquery-execution/child-process.ts
Normal file
@ -0,0 +1,30 @@
|
||||
const child_process = require("child_process");
|
||||
|
||||
export class ChildProcess {
|
||||
static async spawn(executable: string, args: string[]): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
|
||||
let output = "";
|
||||
const handle = child_process.spawn(executable, args);
|
||||
|
||||
handle.stdout.on("data", (data: string) => {
|
||||
output += data;
|
||||
});
|
||||
|
||||
handle.stderr.on("data", (data: string) => {
|
||||
output += data;
|
||||
});
|
||||
|
||||
handle.on("close", (code: string) => {
|
||||
if (code === "0") {
|
||||
resolve();
|
||||
}
|
||||
|
||||
else {
|
||||
reject({ code: code, message: output });
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
}
|
132
src/xquery-execution/commands/executeXQuery.ts
Normal file
132
src/xquery-execution/commands/executeXQuery.ts
Normal file
@ -0,0 +1,132 @@
|
||||
import { commands, window, workspace } from "vscode";
|
||||
import { Disposable, Range, TextEditor, TextEditorEdit, Uri } from "vscode";
|
||||
|
||||
import * as constants from "../../constants";
|
||||
|
||||
import { ChildProcess } from "../child-process";
|
||||
|
||||
export async function executeXQuery(editor: TextEditor, edit: TextEditorEdit): Promise<void> {
|
||||
const config = workspace.getConfiguration(constants.extensionPrefix);
|
||||
|
||||
// this disposable will be used for creating status bar messages
|
||||
let disposable: Disposable;
|
||||
|
||||
if (editor.document.languageId !== constants.languageIds.xquery) {
|
||||
window.showErrorMessage("This action can only be performed on an XQuery file.");
|
||||
return;
|
||||
}
|
||||
|
||||
const executable = config.get<string>(constants.configKeys.xqueryExecutionEngine, null);
|
||||
let args = config.get<string[]>(constants.configKeys.xqueryExecutionArguments, []);
|
||||
|
||||
if (!executable || executable === "") {
|
||||
const action = await window.showWarningMessage("An XQuery execution engine has not been defined.", "Define Now");
|
||||
|
||||
if (action === "Define Now") {
|
||||
commands.executeCommand(constants.nativeCommands.openGlobalSettings);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let inputFile: Uri;
|
||||
disposable = window.setStatusBarMessage("Searching for XML files in folder...");
|
||||
|
||||
const searchPattern = config.get<string>(constants.configKeys.xqueryExecutionInputSearchPattern);
|
||||
const inputLimit = config.get<number>(constants.configKeys.xqueryExecutionInputLimit);
|
||||
|
||||
const files = await workspace.findFiles(searchPattern, "", inputLimit);
|
||||
|
||||
disposable.dispose();
|
||||
|
||||
// user does not have a folder open - prompt for file name
|
||||
if (typeof files === "undefined") {
|
||||
window.showErrorMessage("You must have a folder opened in VS Code to use this feature.");
|
||||
return;
|
||||
}
|
||||
|
||||
// if there is only one XML file, default it
|
||||
// otherwise, prompt the user to select one from the open folder
|
||||
if (files.length > 1) {
|
||||
const qpItems = new Array<any>();
|
||||
|
||||
files.forEach((file) => {
|
||||
const filename = file.fsPath.replace("\\", "/");
|
||||
|
||||
qpItems.push({ // must implement vscode.QuickPickItem
|
||||
label: filename.substring(filename.lastIndexOf("/") + 1),
|
||||
description: file.fsPath,
|
||||
file: file
|
||||
});
|
||||
});
|
||||
|
||||
const selection = await window.showQuickPick(qpItems, { placeHolder: "Please select an input file." });
|
||||
|
||||
if (!selection) {
|
||||
return;
|
||||
}
|
||||
|
||||
inputFile = selection.file;
|
||||
}
|
||||
|
||||
else {
|
||||
inputFile = files[0];
|
||||
}
|
||||
|
||||
// prompt for output file name
|
||||
let outputPath: string = null;
|
||||
let outputPathPos = -1;
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (i > 0) {
|
||||
if (args[i - 1].search(/out|result/)) {
|
||||
outputPath = args[i];
|
||||
outputPathPos = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (outputPath) {
|
||||
outputPath = await window.showInputBox({
|
||||
placeHolder: "ex. C:\\TEMP\XQueryOutput\\MyOutputFile.xml",
|
||||
prompt: "Please specify the output file path. Existing file behavior is determined by the execution engine you have specified.",
|
||||
value: outputPath
|
||||
});
|
||||
|
||||
args[outputPathPos] = outputPath;
|
||||
}
|
||||
|
||||
// call out to the execution engine
|
||||
disposable = window.setStatusBarMessage("Executing XQuery Script...");
|
||||
args = args.map<string>((value: string) => {
|
||||
return value
|
||||
.replace("$(script)", editor.document.uri.fsPath)
|
||||
.replace("$(input)", inputFile.fsPath)
|
||||
.replace("$(project)", workspace.rootPath);
|
||||
});
|
||||
|
||||
try {
|
||||
await ChildProcess.spawn(executable, args);
|
||||
}
|
||||
|
||||
catch (error) {
|
||||
if (error.message.search(/[Ll]ine:?\s*\d+/gm) > -1) {
|
||||
const match: RegExpExecArray = /[Ll]ine:?\s*\d+/gm.exec(error.message);
|
||||
const line: number = (Number.parseInt(match[0].replace(/([Ll]ine:?\s*)|\s/, "")) - 1);
|
||||
|
||||
const selection: string = await window.showErrorMessage(error.message, `Go to Line ${line}`);
|
||||
|
||||
if (selection === `Go to Line ${line}`) {
|
||||
editor.revealRange(new Range(line, 0, line, 0));
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
window.showErrorMessage(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
finally {
|
||||
disposable.dispose();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user