forked from external/vscode-xml
		
	Add XQuery Execution
This commit is contained in:
		
							parent
							
								
									df45b80085
								
							
						
					
					
						commit
						29756e3f97
					
				
					 5 changed files with 186 additions and 0 deletions
				
			
		
							
								
								
									
										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…
	
	Add table
		
		Reference in a new issue