diff --git a/package.json b/package.json index c468db8..68650b1 100644 --- a/package.json +++ b/package.json @@ -125,7 +125,8 @@ "dependencies": { "xmldom": "DotJoshJohnson/xmldom#2794915", "xpath": "^0.0.9", - "xqlint": "^0.2.9" + "xqlint": "^0.2.9", + "opener": "^1.4.1" }, "scripts": { "vscode:prepublish": "tsc" diff --git a/src/providers/Execution.ts b/src/providers/Execution.ts index 5c81f97..a403f4f 100644 --- a/src/providers/Execution.ts +++ b/src/providers/Execution.ts @@ -9,6 +9,9 @@ const CFG_XQARGS: string = 'xqueryExecutionArguments'; export class XQueryExecutionProvider { static async executeXQueryAsync(editor: vsc.TextEditor): Promise { + // this disposable will be used for creating status bar messages + let disposable: vsc.Disposable; + if (editor.document.languageId !== 'xquery') { vsc.window.showErrorMessage('This action can only be performed on an XQuery file.'); return; @@ -27,33 +30,99 @@ export class XQueryExecutionProvider { return; } + let inputFile: vsc.Uri; + disposable = vsc.window.setStatusBarMessage('Searching for XML files in folder...'); let files: vsc.Uri[] = await vsc.workspace.findFiles('**/*.xml', '', 100); + disposable.dispose(); - let qpItems: any[] = new Array(); - - files.forEach((file) => { - let filename: string = file.fsPath.replace('\\', '/'); - - qpItems.push({ // must implement vscode.QuickPickItem - label: filename.substring(filename.lastIndexOf('/') + 1), - description: file.fsPath, - file: file - }); - }); - - let selection = await vsc.window.showQuickPick(qpItems, { placeHolder: 'Please select an input file.' }); - - if (!selection) { + // user does not have a folder open - prompt for file name + if (typeof files === 'undefined') { + vsc.window.showErrorMessage('You must have a folder opened in VS Code to use this feature.'); return; } - let disposable: vsc.Disposable = vsc.window.setStatusBarMessage('Executing XQuery Script...'); + // if there is only one XML file, default it + // otherwise, prompt the user to select one from the open folder + if (files.length > 1) { + let qpItems: any[] = new Array(); + + files.forEach((file) => { + let filename: string = file.fsPath.replace('\\', '/'); + + qpItems.push({ // must implement vscode.QuickPickItem + label: filename.substring(filename.lastIndexOf('/') + 1), + description: file.fsPath, + file: file + }); + }); + + let selection = await vsc.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: number = -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 vsc.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 = vsc.window.setStatusBarMessage('Executing XQuery Script...'); args = args.map((value: string) => { return value .replace('$(script)', editor.document.uri.fsPath) - .replace('$(input)', selection.file.fsPath); + .replace('$(input)', inputFile.fsPath); }); - await ChildProcess.spawnAsync(executable, args); - disposable.dispose(); + + try { + await ChildProcess.spawnAsync(executable, args); + } + + catch (error) { + if (error.message.search(/[Ll]ine:?\s*\d+/gm) > -1) { + let match: RegExpExecArray = /[Ll]ine:?\s*\d+/gm.exec(error.message); + let line: number = (Number.parseInt(match[0].replace(/([Ll]ine:?\s*)|\s/, '')) - 1); + + let selection: string = await vsc.window.showErrorMessage(error.message, `Go to Line ${line}`); + + if (selection == `Go to Line ${line}`) { + editor.revealRange(new vsc.Range(line, 0, line, 0)); + } + } + + else { + vsc.window.showErrorMessage(error.message); + } + } + + finally { + disposable.dispose(); + } } } \ No newline at end of file diff --git a/src/providers/XPath.ts b/src/providers/XPath.ts index 374268f..168deec 100644 --- a/src/providers/XPath.ts +++ b/src/providers/XPath.ts @@ -59,7 +59,17 @@ export class XPathFeatureProvider { // run the query let xml: string = editor.document.getText(); - let nodes: Node[] = XPathEvaluator.evaluate(query, xml, ignoreDefaultNamespace); + let nodes: Node[]; + + try { + nodes = XPathEvaluator.evaluate(query, xml, ignoreDefaultNamespace); + } + + catch (error) { + console.error(error); + vsc.window.showErrorMessage(`Something went wrong while evaluating the XPath: ${error}`); + return; + } // show the results to the user let outputChannel: vsc.OutputChannel = vsc.window.createOutputChannel(OUTPUT_CHANNEL); diff --git a/src/services/ChildProcess.ts b/src/services/ChildProcess.ts index 6991eca..1290a06 100644 --- a/src/services/ChildProcess.ts +++ b/src/services/ChildProcess.ts @@ -6,15 +6,24 @@ export class ChildProcess { static async spawnAsync(executable: string, args: string[]): Promise { return new Promise((resolve, reject) => { + let output: string = ''; let 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); + reject({ code: code, message: output }); } });