diff --git a/README.md b/README.md
index 4ff06e8..1e2c266 100644
--- a/README.md
+++ b/README.md
@@ -17,4 +17,9 @@ Detailed release notes are available [here](https://github.com/DotJoshJohnson/vs
Run into a bug? Report it [here](https://github.com/DotJoshJohnson/vscode-xml/issues).
## Roadmap
-Check out development progress [here](https://github.com/DotJoshJohnson/vscode-xml/projects/1).
\ No newline at end of file
+Check out development progress [here](https://github.com/DotJoshJohnson/vscode-xml/projects/1).
+
+## Icon Credits
+Icons used in the XML Tree View are used under the Creative Commons 3.0 BY license.
+* "Code" icon by Dave Gandy from www.flaticon.com
+* "At" icon by FreePik from www.flaticon.com
\ No newline at end of file
diff --git a/package.json b/package.json
index 2d5fc81..cb5b023 100644
--- a/package.json
+++ b/package.json
@@ -22,7 +22,7 @@
"url": "https://github.com/DotJoshJohnson/vscode-xml/issues"
},
"engines": {
- "vscode": "^0.10.7",
+ "vscode": "^1.13.0",
"node": "^0.12.0"
},
"categories": [
@@ -46,10 +46,6 @@
"command": "xmlTools.executeXQuery",
"title": "XML Tools: Execute XQuery"
},
- {
- "command": "xmlTools.viewXmlTree",
- "title": "XML Tools: View XML Tree"
- },
{
"command": "xmlTools.formatAsXml",
"title": "XML Tools: Format as XML"
@@ -115,7 +111,15 @@
"scopeName": "source.xquery",
"path": "./languages/xquery/xquery.tmLanguage"
}
- ]
+ ],
+ "views": {
+ "explorer": [
+ {
+ "id": "xmlTreeView",
+ "name": "XML Document"
+ }
+ ]
+ }
},
"activationEvents": [
"onLanguage:xml",
@@ -124,12 +128,11 @@
"onCommand:xmlTools.minifyXml",
"onCommand:xmlTools.evaluateXPath",
"onCommand:xmlTools.executeXQuery",
- "onCommand:xmlTools.viewXmlTree",
"onCommand:xmlTools.formatAsXml"
],
"devDependencies": {
- "vscode": "^0.11.8",
- "typescript": "^1.6.2",
+ "vscode": "^1.1.0",
+ "typescript": "^2.3.4",
"gulp": "^3.9.0",
"gulp-shell": "^0.5.1"
},
diff --git a/resources/icons/attribute.dark.svg b/resources/icons/attribute.dark.svg
new file mode 100644
index 0000000..f208dc3
--- /dev/null
+++ b/resources/icons/attribute.dark.svg
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/icons/attribute.light.svg b/resources/icons/attribute.light.svg
new file mode 100644
index 0000000..2c5fc76
--- /dev/null
+++ b/resources/icons/attribute.light.svg
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/icons/element.dark.svg b/resources/icons/element.dark.svg
new file mode 100644
index 0000000..45eced5
--- /dev/null
+++ b/resources/icons/element.dark.svg
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/icons/element.light.svg b/resources/icons/element.light.svg
new file mode 100644
index 0000000..8e6cfd1
--- /dev/null
+++ b/resources/icons/element.light.svg
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Commands.ts b/src/Commands.ts
index 7a5d6f9..6cb3ef8 100644
--- a/src/Commands.ts
+++ b/src/Commands.ts
@@ -7,7 +7,6 @@ import { RangeUtil } from './utils/RangeUtil';
import { XmlFormatter } from './services/XmlFormatter';
import { XPathFeatureProvider } from './providers/XPath';
import { XQueryExecutionProvider } from './providers/Execution';
-import { XmlTreeDocumentContentProvider } from './providers/Content';
import { XmlFormattingEditProvider } from './providers/Formatting';
const CFG_SECTION: string = 'xmlTools';
@@ -32,26 +31,13 @@ export class TextEditorCommands {
static executeXQuery(editor: vsc.TextEditor, edit: vsc.TextEditorEdit): void {
XQueryExecutionProvider.executeXQueryAsync(editor);
}
-
- static async viewXmlTree(editor: vsc.TextEditor, edit: vsc.TextEditorEdit): Promise {
- try {
- await vsc.commands.executeCommand(
- 'vscode.previewHtml',
- XmlTreeDocumentContentProvider.buildUri(editor.document.uri),
- vsc.ViewColumn.Three);
- }
-
- catch (error) {
- vsc.window.showErrorMessage(`The XML Tree could not be created: ${error}`);
- }
- }
static formatAsXml(editor: vsc.TextEditor, edit: vsc.TextEditorEdit): void {
let edits: vsc.TextEdit[];
let formattingEditProvider = new XmlFormattingEditProvider();
let formattingOptions: vsc.FormattingOptions = {
- insertSpaces: editor.options.insertSpaces,
- tabSize: editor.options.tabSize
+ insertSpaces: (editor.options.insertSpaces as boolean),
+ tabSize: (editor.options.tabSize as number)
};
// if the user has selected text, only format what is selected
diff --git a/src/Extension.ts b/src/Extension.ts
index 727ccc7..8570b99 100644
--- a/src/Extension.ts
+++ b/src/Extension.ts
@@ -5,7 +5,7 @@ import { TextEditorCommands } from './Commands';
import { XmlFormattingEditProvider } from './providers/Formatting';
import { XQueryLintingFeatureProvider } from './providers/Linting';
import { XQueryCompletionItemProvider } from './providers/Completion';
-import { XmlTreeDocumentContentProvider } from './providers/Content';
+import { XmlTreeViewDataProvider } from "./providers/XmlTreeView";
export var GlobalState: vsc.Memento;
export var WorkspaceState: vsc.Memento;
@@ -26,7 +26,6 @@ export function activate(ctx: vsc.ExtensionContext) {
vsc.commands.registerTextEditorCommand('xmlTools.minifyXml', TextEditorCommands.minifyXml),
vsc.commands.registerTextEditorCommand('xmlTools.evaluateXPath', TextEditorCommands.evaluateXPath),
vsc.commands.registerTextEditorCommand('xmlTools.executeXQuery', TextEditorCommands.executeXQuery),
- vsc.commands.registerTextEditorCommand('xmlTools.viewXmlTree', TextEditorCommands.viewXmlTree),
vsc.commands.registerTextEditorCommand('xmlTools.formatAsXml', TextEditorCommands.formatAsXml)
);
@@ -38,16 +37,16 @@ export function activate(ctx: vsc.ExtensionContext) {
vsc.languages.registerCompletionItemProvider(LANG_XQUERY, new XQueryCompletionItemProvider(), ':', '$')
);
- // register workspace feature providers
- ctx.subscriptions.push(
- vsc.workspace.registerTextDocumentContentProvider(XmlTreeDocumentContentProvider.SCHEME, new XmlTreeDocumentContentProvider())
- );
-
// listen to editor events (for linting)
ctx.subscriptions.push(
vsc.window.onDidChangeActiveTextEditor(_handleChangeActiveTextEditor),
vsc.window.onDidChangeTextEditorSelection(_handleChangeTextEditorSelection)
);
+
+ // add views
+ ctx.subscriptions.push(
+ vsc.window.registerTreeDataProvider("xmlTreeView", new XmlTreeViewDataProvider(ctx))
+ );
}
export function deactivate() {
diff --git a/src/providers/Content.ts b/src/providers/Content.ts
deleted file mode 100644
index b6a094b..0000000
--- a/src/providers/Content.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-'use strict';
-
-import * as vsc from 'vscode';
-import { XmlTreeService } from '../services/XmlTreeService';
-
-export class XmlTreeDocumentContentProvider implements vsc.TextDocumentContentProvider {
-
- static get SCHEME(): string {
- return "xmltree";
- }
-
- static buildUri(sourceUri: vsc.Uri): vsc.Uri {
- let uriStr: string = `xmltree://${encodeURIComponent(sourceUri.toString())}`;
-
- let uri: vsc.Uri = vsc.Uri.parse(uriStr);
-
- return uri;
- }
-
- async provideTextDocumentContent(uri: vsc.Uri): Promise {
- let sourceUri: vsc.Uri = vsc.Uri.parse(decodeURIComponent(uri.toString().substr(10)));
- let document: vsc.TextDocument = await vsc.workspace.openTextDocument(sourceUri);
-
- let html: string = XmlTreeService.getXmlTreeHtml(document.getText());
-
- return Promise.resolve(html);
- }
-}
\ No newline at end of file
diff --git a/src/providers/XmlTreeView.ts b/src/providers/XmlTreeView.ts
new file mode 100644
index 0000000..ed084e1
--- /dev/null
+++ b/src/providers/XmlTreeView.ts
@@ -0,0 +1,128 @@
+import * as vsc from "vscode";
+import * as path from "path";
+
+let DOMParser = require("xmldom").DOMParser;
+
+export class XmlTreeViewDataProvider implements vsc.TreeDataProvider {
+ private _onDidChangeTreeData: vsc.EventEmitter = new vsc.EventEmitter();
+ private _xmlDocument: Document;
+
+ constructor(private _context: vsc.ExtensionContext) {
+ vsc.window.onDidChangeActiveTextEditor((editor) => {
+ this._refreshTree();
+ });
+
+ vsc.workspace.onDidChangeTextDocument((e) => {
+ this._refreshTree();
+ });
+ }
+
+ readonly onDidChangeTreeData: vsc.Event = this._onDidChangeTreeData.event;
+
+ get activeEditor(): vsc.TextEditor | null {
+ return vsc.window.activeTextEditor || null;
+ }
+
+ getChildren(element?: Node): Node[] {
+ if (!this._xmlDocument) {
+ this._refreshTree();
+ }
+
+ if (element) {
+ return [].concat(this._getChildAttributeArray(element), this._getChildElementArray(element));
+ }
+
+ else if (this._xmlDocument) {
+ return [ this._xmlDocument.lastChild ];
+ }
+
+ else {
+ return [];
+ }
+ }
+
+ getTreeItem(element: Node): vsc.TreeItem {
+ let treeItem = new vsc.TreeItem(element.localName);
+
+ if (this._getChildAttributeArray(element).length > 0) {
+ treeItem.collapsibleState = vsc.TreeItemCollapsibleState.Collapsed;
+ }
+
+ if (this._getChildElementArray(element).length > 0) {
+ treeItem.collapsibleState = vsc.TreeItemCollapsibleState.Collapsed;
+ }
+
+ treeItem.command = {
+ command: "revealLine",
+ title: "",
+ arguments: [{
+ lineNumber: (element as any).lineNumber - 1,
+ at: "top"
+ }]
+ };
+
+ treeItem.iconPath = this._getIcon(element);
+
+ return treeItem;
+ }
+
+ private _getChildAttributeArray(node: Node): Node[] {
+ if (!node.attributes) {
+ return [];
+ }
+
+ let array = new Array();
+
+ for (let i = 0; i < node.attributes.length; i++) {
+ array.push(node.attributes[i]);
+ }
+
+ return array;
+ }
+
+ private _getChildElementArray(node: Node): Node[] {
+ if (!node.childNodes) {
+ return [];
+ }
+
+ let array = new Array();
+
+ for (let i = 0; i < node.childNodes.length; i++) {
+ let child = node.childNodes[i];
+
+ if ((child as any).tagName) {
+ array.push(child);
+ }
+ }
+
+ return array;
+ }
+
+ private _getIcon(element: Node): any {
+ let type = "element";
+
+ if (!(element as any).tagName) {
+ type = "attribute";
+ }
+
+ let icon = {
+ dark: this._context.asAbsolutePath(path.join("resources", "icons", `${type}.dark.svg`)),
+ light: this._context.asAbsolutePath(path.join("resources", "icons", `${type}.light.svg`))
+ };
+
+ return icon;
+ }
+
+ private _refreshTree(): void {
+ if (!this.activeEditor || this.activeEditor.document.languageId !== "xml") {
+ this._xmlDocument = null;
+ this._onDidChangeTreeData.fire();
+ return;
+ }
+
+ let xml = this.activeEditor.document.getText();
+ this._xmlDocument = new DOMParser().parseFromString(xml, "text/xml");
+
+ this._onDidChangeTreeData.fire();
+ }
+}
\ No newline at end of file
diff --git a/src/services/XmlTreeService.ts b/src/services/XmlTreeService.ts
deleted file mode 100644
index f18c97b..0000000
--- a/src/services/XmlTreeService.ts
+++ /dev/null
@@ -1,222 +0,0 @@
-'use strict';
-
-let DOMParser = require('xmldom').DOMParser;
-let fs = require('fs');
-
-export class XmlTreeService {
- static getXmlTreeHtml(xml: string): string {
- let xdoc: Document = new DOMParser().parseFromString(xml, 'text/xml');
- let fontColor: string = XmlTreeService._getRecommendedTextColor();
- let html: string =
- `
-
-
-
- XML Tree View
-
-
-
-
- `;
-
- html += ``;
- html += XmlTreeService._processXmlNode(xdoc.lastChild);
- html += ` `;
-
- html +=
- `
-
-
- `;
-
- return html;
- }
-
- private static _processXmlNode(node: Node): string {
- let html: string = '';
-
- if (node.childNodes) {
- html += `${node.localName} `;
- }
-
- if (node.attributes) {
- for (let i = 0; i < node.attributes.length; i++) {
- html += `${node.attributes.item(i).localName} = '${node.attributes.item(i).value}' `;
- }
- }
-
- if (!node.childNodes && node.textContent) {
- html += `${node.textContent} `;
- }
-
- if (node.childNodes) {
- for (let i = 0; i < node.childNodes.length; i++) {
- html += XmlTreeService._processXmlNode(node.childNodes.item(i));
- }
-
- html += ` `;
- }
-
- return html;
- }
-
- private static _getRecommendedTextColor(): string {
- let color = '#AAAAAA';
- let path = '';
-
- switch (process.platform) {
- case 'darwin':
- path = `${process.env.HOME}/Library/Application Support/Code/storage.json`;
- break;
-
- case 'win32':
- path = `${process.env.APPDATA}\\Code\\storage.json`;
- break;
-
- default:
- path = `${process.env.HOME}/.config/Code/storage.json`
- break;
- }
-
- try {
- fs.accessSync(path);
-
- let json = fs.readFileSync(path, 'utf8');
- let storage = JSON.parse(json);
-
- color = (storage.theme.indexOf('vs-dark') > -1) ? '#FFFFFF' : '#000000';
- }
-
- catch (error) { }
-
- return color;
- }
-}
\ No newline at end of file