Repository: DotJoshJohnson/vscode-xml Branch: master Commit: 95702e6355fb Files: 100 Total size: 144.1 KB Directory structure: gitextract_05bouxhz/ ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug-report.md │ │ ├── feature-request.md │ │ └── xml-formatter.md │ └── workflows/ │ ├── release.yml │ └── test.yml ├── .gitignore ├── .vscode/ │ ├── launch.json │ ├── settings.json │ └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── azure-pipelines.yml ├── languages/ │ └── xquery/ │ ├── xquery.json │ └── xquery.tmLanguage ├── package.json ├── src/ │ ├── common/ │ │ ├── configuration.ts │ │ ├── create-document-selector.ts │ │ ├── extension-state.ts │ │ ├── index.ts │ │ ├── native-commands.ts │ │ └── xml-traverser.ts │ ├── completion/ │ │ ├── index.ts │ │ └── xquery-completion-item-provider.ts │ ├── constants.ts │ ├── extension.ts │ ├── formatting/ │ │ ├── commands/ │ │ │ ├── formatAsXml.ts │ │ │ ├── index.ts │ │ │ ├── minifyXml.ts │ │ │ ├── textToXml.ts │ │ │ └── xmlToText.ts │ │ ├── formatters/ │ │ │ ├── classic-xml-formatter.ts │ │ │ ├── index.ts │ │ │ └── v2-xml-formatter.ts │ │ ├── index.ts │ │ ├── xml-formatter.ts │ │ ├── xml-formatting-edit-provider.ts │ │ └── xml-formatting-options.ts │ ├── linting/ │ │ ├── index.ts │ │ └── xquery-linter.ts │ ├── test/ │ │ ├── extension.test.ts │ │ ├── test-data/ │ │ │ ├── basic.formatted.xml │ │ │ ├── basic.unformatted.xml │ │ │ ├── issue-149.formatted.xml │ │ │ ├── issue-149.unformatted.xml │ │ │ ├── issue-178.formatted.xml │ │ │ ├── issue-178.unformatted.xml │ │ │ ├── issue-185.formatted.xml │ │ │ ├── issue-185.unformatted.xml │ │ │ ├── issue-187.formatted.xml │ │ │ ├── issue-187.unformatted.xml │ │ │ ├── issue-189.formatted.xml │ │ │ ├── issue-189.unformatted.xml │ │ │ ├── issue-193.formatted.xml │ │ │ ├── issue-193.unformatted.xml │ │ │ ├── issue-194.formatted.xml │ │ │ ├── issue-194.unformatted.xml │ │ │ ├── issue-200.formatted.xml │ │ │ ├── issue-200.unformatted.xml │ │ │ ├── issue-227.formatted.xml │ │ │ ├── issue-227.unformatted.xml │ │ │ ├── issue-257.formatted.xml │ │ │ ├── issue-257.unformatted.xml │ │ │ ├── issue-262.minified.xml │ │ │ ├── issue-262.unminified.xml │ │ │ ├── issue-288.formatted.xml │ │ │ ├── issue-288.unformatted.xml │ │ │ ├── issue-293.formatted.xml │ │ │ ├── issue-293.unformatted.xml │ │ │ ├── maintain-comment-formatting.formatted.xml │ │ │ ├── maintain-comment-formatting.unformatted.xml │ │ │ ├── preformatted.formatted.xml │ │ │ ├── preformatted.unformatted.xml │ │ │ ├── preserve-breaks.formatted.xml │ │ │ ├── preserve-breaks.unformatted.xml │ │ │ ├── self-closing.formatted.xml │ │ │ ├── self-closing.unformatted.xml │ │ │ ├── single-quotes.formatted.xml │ │ │ ├── single-quotes.unformatted.xml │ │ │ ├── text-only-line.formatted.xml │ │ │ ├── text-only-line.unformatted.xml │ │ │ ├── unicode.formatted.xml │ │ │ └── unicode.unformatted.xml │ │ └── test-utils/ │ │ └── test-data-loader.ts │ ├── tree-view/ │ │ ├── index.ts │ │ └── xml-tree-data-provider.ts │ ├── xpath/ │ │ ├── commands/ │ │ │ ├── evaluateXPath.ts │ │ │ ├── getCurrentXPath.ts │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── xpath-builder.ts │ │ └── xpath-evaluator.ts │ └── xquery-execution/ │ ├── child-process.ts │ ├── commands/ │ │ ├── executeXQuery.ts │ │ └── index.ts │ └── index.ts ├── tsconfig.json └── tslint.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/ISSUE_TEMPLATE/bug-report.md ================================================ --- name: Bug Report about: Something (aside from the formatter) isn't working right! --- **Description** What seems to be the problem? **Screenshots** If applicable, add screenshots to help explain your problem. **Extension Version** What version of the XML Tools extension are you using? **VS Code Version** What version of VS Code are you using? **Operating System** What OS (and version) are you using? ================================================ FILE: .github/ISSUE_TEMPLATE/feature-request.md ================================================ --- name: Feature Request about: Suggest a New Feature --- **Description** What would you like to see added to XML Tools? **Current Workarounds** Are you using a workaround in the meantime? ================================================ FILE: .github/ISSUE_TEMPLATE/xml-formatter.md ================================================ --- name: XML Formatter Bug about: Report an issue with the XML formatter. --- #### Description What seems to be the problem? #### Formatter Implementation Which XML Formatter implementation are you using (the value of your `xmlTools.xmlFormatterImplementation` setting). #### XML Tools Version What version of XML Tools are you using? #### VS Code Version What version of VS Code are you using? #### Operating System What OS (and version) are you using? ================================================ FILE: .github/workflows/release.yml ================================================ name: "Release to Marketplace" on: push: tags: - "v*" jobs: release: runs-on: "ubuntu-latest" steps: - name: "Checkout" uses: "actions/checkout@v2" - name: "Setup NodeJS" uses: "actions/setup-node@v2.1.0" - name: "Install Dependencies" run: "npm install" - name: "Run Tests" run: "npm run test" - name: "Publish to Marketplace" uses: "sigma/vsce-publish-action@v0.0.2" with: vsce_token: ${{ secrets.VSCE_TOKEN }} ================================================ FILE: .github/workflows/test.yml ================================================ name: "Run Tests" on: pull_request: branches: - "master" jobs: test: runs-on: "ubuntu-latest" steps: - name: "Checkout" uses: "actions/checkout@v2" - name: "Setup NodeJS" uses: "actions/setup-node@v2.1.0" - name: "Install Dependencies" run: "npm install" - name: "Run Tests" run: "npm run test" ================================================ FILE: .gitignore ================================================ out node_modules .vscode-test/ /*.vsix ================================================ FILE: .vscode/launch.json ================================================ // A launch configuration that compiles the extension and then opens it inside a new window { "version": "0.1.0", "configurations": [ { "name": "Extension", "type": "extensionHost", "request": "launch", "runtimeExecutable": "${execPath}", "args": ["--extensionDevelopmentPath=${workspaceRoot}" ], "stopOnEntry": false, "sourceMaps": true, "outFiles": [ "${workspaceRoot}/out/**/*.js" ], "preLaunchTask": "npm: watch" }, { "name": "Extension Tests", "type": "extensionHost", "request": "launch", "runtimeExecutable": "${execPath}", "args": ["--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test" ], "stopOnEntry": false, "sourceMaps": true, "outFiles": [ "${workspaceRoot}/out/test/**/*.js" ], "preLaunchTask": "npm: watch" } ] } ================================================ FILE: .vscode/settings.json ================================================ // Place your settings in this file to overwrite default and user settings. { "files.exclude": { "out": false // set this to true to hide the "out" folder with the compiled JS files }, "search.exclude": { "out": true // set this to false to include "out" folder in search results } } ================================================ FILE: .vscode/tasks.json ================================================ // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format { "version": "2.0.0", "tasks": [ { "type": "npm", "script": "watch", "problemMatcher": "$tsc-watch", "isBackground": true, "presentation": { "reveal": "never" }, "group": { "kind": "build", "isDefault": true } } ] } ================================================ FILE: .vscodeignore ================================================ .vscode/** .vscode-test/** out/test/** out/**/*.map src/** .gitignore tsconfig.json vsc-extension-quickstart.md ================================================ FILE: CHANGELOG.md ================================================ Detailed release notes are available [here](https://github.com/DotJoshJohnson/vscode-xml/releases). ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing to XML Tools Welcome and thank you for contributing to **XML Tools for Visual Studio Code**! This document aims to provide an overview of standards and expectations for those interested in contributing to the project. ## Asking Questions If you have any questions, please ask on Gitter or Twitter instead of submitting an issue. ## Reporting Issues Before submitting a new issue, please be sure to check for an existing issue that matches yours first. Issues that are waiting for information for more than 30 days will be closed, so please be sure to follow your issues! ## Writing Code If you would like to contribute code to the project, please follow these conventions: * Use spaces over tabs (4 spaces per tab is preferred). * Use double quotes whenever possible instead of single quotes. * Use **snake-case** for file names. * Use **PascalCase** for class and interface names. * Use **camelCase** for all other identifiers unless otherwise specified. * Prefix private members with an underscore. * Implement and maintain barrels (`index.ts` files) when creating new folders or files. * Use constants when referencing a static value more than once in your code. * Place `else` and `else if` on their own lines. * Never put opening braces (`{`) on their own line. * Always use semicolons. * Always prefer `const` whenever possible and fall back to `let` only if absolutely necessary. ### Branches and Pull Requests Always develop on a new feature branch in your fork and submit pull requests from that branch to our master branch. Don't worry about changing any version numbers - that happens in its own PR before a release. ### Formatter Changes For small bug fixes or feature additions, always add a new test case to accompany your change. If you are making large sweeping changes to how the formatter works or leveraging an external dependency for formatting XML, please create a new XmlFormatter implementation. ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2015 Josh Johnson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # XML Tools for Visual Studio Code [![Visual Studio Marketplace](https://img.shields.io/vscode-marketplace/v/DotJoshJohnson.xml.svg?style=for-the-badge)](https://marketplace.visualstudio.com/items?itemName=DotJoshJohnson.xml) [![Visual Studio Marketplace](https://img.shields.io/vscode-marketplace/r/DotJoshJohnson.xml.svg?style=for-the-badge)](https://marketplace.visualstudio.com/items?itemName=DotJoshJohnson.xml) [![](https://img.shields.io/badge/TWITTER-%40DotJohnson-blue.svg?logo=twitter&style=for-the-badge)](https://twitter.com/DotJoshJohnson) [![](https://img.shields.io/badge/gitter-join_chat-1dce73.svg?style=for-the-badge&logo=gitter-white)](https://gitter.im/vscode-xml/vscode-xml) [![Beerpay](https://img.shields.io/beerpay/DotJoshJohnson/vscode-xml.svg?style=for-the-badge)](https://beerpay.io/DotJoshJohnson/vscode-xml) ## Features * [XML Formatting](https://github.com/DotJoshJohnson/vscode-xml/wiki/xml-formatting) * [XML Tree View](https://github.com/DotJoshJohnson/vscode-xml/wiki/xml-tree-view) * [XPath Evaluation](https://github.com/DotJoshJohnson/vscode-xml/wiki/xpath-evaluation) * [XQuery Linting](https://github.com/DotJoshJohnson/vscode-xml/wiki/xquery-linting) * [XQuery Execution](https://github.com/DotJoshJohnson/vscode-xml/wiki/xquery-script-execution) * [XQuery Code Completion](https://github.com/DotJoshJohnson/vscode-xml/wiki/xquery-code-completion) ## Requirements * VS Code `1.22.2` or higher ## Extension Settings * **`xmlTools.enableXmlTreeView`:** Enables the XML Tree View for XML documents. * **`xmlTools.enableXmlTreeViewMetadata`:** Enables attribute and child element counts in the XML Document view. * **`xmlTools.enableXmlTreeViewCursorSync`:** Enables auto-reveal of elements in the XML Document view when a start tag is clicked in the editor. * **`xmlTools.enforcePrettySelfClosingTagOnFormat`:** Ensures a space is added before the forward slash at the end of a self-closing tag. * **`xmlTools.ignoreDefaultNamespace`:** Ignore default xmlns attributes when evaluating XPath. * **`xmlTools.persistXPathQuery`:** Remember the last XPath query used. * **`xmlTools.removeCommentsOnMinify`:** Remove XML comments during minification. * **`xmlTools.splitAttributesOnFormat`:** Put each attribute on a new line when formatting XML. Overrides `xmlTools.splitXmlnsOnFormat` if set to `true`. (V2 Formatter Only) * **`xmlTools.splitXmlnsOnFormat`:** Put each xmlns attribute on a new line when formatting XML. * **`xmlTools.xmlFormatterImplementation`:** Supported XML Formatters: `classic`, `v2`. * **`xmlTools.xqueryExecutionArguments`:** Arguments to be passed to the XQuery execution engine. * **`xmlTools.xqueryExecutionEngine`:** The full path to the executable to run when executing XQuery scripts. ## Release Notes Detailed release notes are available [here](https://github.com/DotJoshJohnson/vscode-xml/releases). ## Issues Run into a bug? Report it [here](https://github.com/DotJoshJohnson/vscode-xml/issues). ## 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 ================================================ FILE: azure-pipelines.yml ================================================ name: "$(Build.SourceBranchName)-$(Build.SourceVersion)$(Rev:.r)" pr: - master trigger: - "refs/tags/*" pool: vmImage: "windows-2019" steps: - task: NodeTool@0 inputs: versionSpec: "10.x" displayName: "Install NodeJS" - script: | npm install -g vsce displayName: "Install VSCE" - script: | npm install displayName: "NPM Install" - script: | vsce package --out "$(Build.ArtifactStagingDirectory)/xml-$(Build.SourceBranchName)-$(Build.SourceVersion).vsix" displayName: "VSCE Package" - task: PublishBuildArtifacts@1 inputs: pathtoPublish: $(Build.ArtifactStagingDirectory) artifactName: drop ================================================ FILE: languages/xquery/xquery.json ================================================ { "comments": { "lineComment": ["(:", ":)"], "blockComment": [ "(:~", "~:)"] }, "brackets": [ [ "{", "}" ], [ "[", "]" ], [ "(", ")" ] ] } ================================================ FILE: languages/xquery/xquery.tmLanguage ================================================ fileTypes xq xql xqm xqy xquery firstLineMatch ^\bxquery version\b.* foldingStartMarker ^\s*(<[^!?%/](?!.+?(/>|</.+?>))|<[!%]--(?!.+?--%?>)|<%[!]?(?!.+?%>))|(declare|.*\{\s*(//.*)?$) foldingStopMarker ^\s*(</[^>]+>|[/%]>|-->)\s*$|(.*\}\s*;?\s*|.*;) keyEquivalent ^~X name XQuery patterns include #Xml include #entity include #bare-ampersand begin (<\?)\s*([-_a-zA-Z0-9]+) captures 1 name punctuation.definition.tag.begin.xml 2 name entity.name.tag.xml end (\?>) name meta.tag.preprocessor.xml patterns match ([a-zA-Z-]+) name entity.other.attribute-name.xml include #doublequotedString include #singlequotedString match ^xquery version .*;$ name keyword.control.import.xquery match \b(?i:(\d+\.\d*(e[\-\+]?\d+)?))(?=[^a-zA-Z_]) name constant.numeric.float.xquery match (?<=[^0-9a-zA-Z_])(?i:(\.\d+(e[\-\+]?\d+)?)) name constant.numeric.float.xquery match \b(?i:(\d+e[\-\+]?\d+)) name constant.numeric.float.xquery match \b([1-9]+[0-9]*|0) name constant.numeric.integer.decimal.xquery match \b(import|module|schema)\b name keyword.control.import.xquery begin \(: captures 0 name punctuation.definition.comment.xquery end :\) name comment.block.xquery patterns include #block_comment comment http://www.w3.org/TR/xpath-datamodel/#types match (?<![:\-_a-zA-Z0-9])((xs:)(string|boolean|decimal|float|double|duration|dateTime|time|date|gYearMonth|gYear|gMonthDay|gDay|gMonth|hexBinary|base64Binary|anyURI|QName|NOTATION|anyAtomicType|anyType|anySimpleType|untypedAtomic|dayTimeDuration|yearMonthDuration|integer|nonPositiveInteger|negativeInteger|long|int|short|byte|nonNegativeInteger|unsignedLong|unsignedInt|unsignedShort|unsignedByte|positiveInteger|ENTITY|ID|NMTOKEN|language|NCName|Name|token|normalizedString))(?![:\-_a-zA-Z0-9]) name support.type.xquery captures 1 name punctuation.definition.variable.xquery match ((\$)(?:([\-_a-zA-Z0-9]+)((:)))?([\-_a-zA-Z0-9]+)) name variable.other.xquery match /(child|descendant|attribute|self|descendant-or-self|following-sibling|following|parent|ancestor|preceding-sibling|preceding|ancestor-or-self):: name support.constant.xquery name meta.function.xquery patterns captures 1 name storage.type.function.xquery 2 name entity.name.function.xquery match (function)\s+((?:([\-_a-zA-Z0-9]+):)?([\-_a-zA-Z0-9]+))\s*\( patterns include #function_parameters include $self begin \s*(function)\s+(?!namespace) beginCaptures 1 name storage.type.function.xquery end \( patterns captures 1 name entity.name.function.xquery match ((?:([\-_a-zA-Z0-9]+):)?([\-_a-zA-Z0-9]+)) include #function_parameters include $self captures 1 name keyword.other.xquery 2 name storage.type.variable.xquery 3 name entity.name.function.variable.xquery match (declare)\s+(variable)\s+(\$(?:[\-_a-zA-Z0-9]+:)?[\-_a-zA-Z0-9]+) name meta.variable.xquery begin (declare)\s+(variable)\s* captures 1 name keyword.other.xquery 2 name storage.type.variable.xquery end (\$(?:[\-_a-zA-Z0-9]+:)?[\-_a-zA-Z0-9]+) endCaptures 1 name entity.name.function.variable.xquery name meta.variable.xquery match \b(base-uri|boundary-space|collation|construction|copy-namespaces|declare|default|element|empty(?![-])|function|greatest|import|inherit|instance|least|module|namespace|no-inherit|no-preserve|option|order|ordered|ordering|preserve|strip|unordered|variable|xdmp:mapping|xdmp:transaction-mode)\b name keyword.other.prolog.xquery match (?<![:\-_a-zA-Z0-9])(of|as|by|in|at|or|and)(?![:\-_a-zA-Z0-9]) name keyword.operator.logical.xquery captures 1 name keyword.control.flow.xquery match (?<![:\-_a-zA-Z0-9])(for|let|return|where|if|then|else|order by|satisfies|every)(?![:\-_a-zA-Z0-9]) captures 1 name support.type.xquery match (?<![:\-_a-zA-Z0-9])(element|attribute|document|document-node\(\)|empty-sequence\(\)|schema-element|schema-attribute|processing-instruction|comment|text|node)(?![:\-_a-zA-Z0-9]) match := name keyword.operator.assignment.xquery match (?<![:\-_a-zA-Z0-9])(\+|-|<=?|>=?|eq|ne|lt|le|ge|gt|\*|div|idiv|mod)(?![:\-_a-zA-Z0-9]) name keyword.operator.arithmetic.xquery match (?<![:\-_a-zA-Z0-9])((fn:)?(abs|adjust-date-to-timezone|adjust-dateTime-to-timezone|adjust-time-to-timezone|analyze-string|avg|base-uri|boolean|ceiling|codepoint-equal|codepoints-to-string|collection|compare|concat|contains|count|current-date|current-dateTime|current-time|data|dateTime|day-from-date|day-from-dateTime|days-from-duration|deep-equal|default-collation|distinct-values|doc|doc-available|document|document-uri|empty|encode-for-uri|ends-with|error|escape-html-uri|escape-uri|exactly-one|exists|false|filter|floor|fold-left|fold-right|format-date|format-dateTime|format-number|format-time|function-arity|function-available|function-lookup|function-name|generate-id|head|hours-from-dateTime|hours-from-duration|hours-from-time|id|idref|implicit-timezone|in-scope-prefixes|index-of|insert-before|iri-to-uri|lang|last|local-name|local-name-from-QName|lower-case|map|map-pairs|matches|max|min|minutes-from-dateTime|minutes-from-duration|minutes-from-time|month-from-date|month-from-dateTime|months-from-duration|name|namespace-uri|namespace-uri-for-prefix|namespace-uri-from-QName|nilled|node-name|normalize-space|normalize-unicode|not|number|one-or-more|position|prefix-from-QName|QName|remove|replace|resolve-QName|resolve-uri|reverse|root|round|round-half-to-even|seconds-from-dateTime|seconds-from-duration|seconds-from-time|starts-with|static-base-uri|string|string-join|string-length|string-to-codepoints|subsequence|substring|substring-after|substring-before|sum|tail|timezone-from-date|timezone-from-dateTime|timezone-from-time|tokenize|trace|translate|true|type-available|unordered|unparsed-text|unparsed-text-available|upper-case|year-from-date|year-from-dateTime|years-from-duration|zero-or-one))(?=\s*\() name support.function.builtin.xquery match (?<![:\-_a-zA-Z0-9])(xdmp:(access|add-response-header|add64|address-bindable|amp|amp-roles|and64|annotation|apply|architecture|atomizable|audit|aws-url-encode|base64-decode|base64-encode|binary-decode|binary-is-external|binary-is-large|binary-is-small|binary-join|binary-key|binary-offset|binary-original-length|binary-size|cache-status|can-grant-roles|castable-as|cluster|cluster-name|collation-canonical-uri|collection-delete|collection-locks|collection-properties|commit|compressed-tree-cache-clear|compressed-tree-cache-partitions|compressed-tree-cache-size|configuration-timestamp|content-type|crypt|crypt2|customized-binary|data-directory|database|database-backup|database-backup-cancel|database-backup-purge|database-backup-status|database-backup-validate|database-forests|database-fragment-counts|database-global-nonblocking-timestamp|database-is-replica|database-maintain-last-modified|database-name|database-nonblocking-timestamp|database-partition-forests|database-path-namespaces|database-restore|database-restore-cancel|database-restore-status|database-restore-validate|databases|dayname-from-date|debug-print|decode-from-NCName|default-collections|default-in-memory-limit|default-in-memory-list-size|default-in-memory-range-index-size|default-in-memory-reverse-index-size|default-in-memory-tree-size|default-in-memory-triple-index-size|default-journal-count|default-journal-size|default-license-key|default-licensee|default-permissions|default-preallocate-journals|default-s3-domain|default-zone|delete-cluster-config-file|delete-host-config-file|describe|diacritic-less|directory|directory-create|directory-delete|directory-locks|directory-properties|disable-event|document-add-collections|document-add-permissions|document-add-properties|document-assign|document-delete|document-filter|document-forest|document-get|document-get-collections|document-get-events|document-get-permissions|document-get-properties|document-get-quality|document-insert|document-load|document-locks|document-properties|document-remove-collections|document-remove-permissions|document-remove-properties|document-set-collections|document-set-permissions|document-set-properties|document-set-property|document-set-quality|document-timestamp|dsa-generate|dump-paths|dump-xsd|duplicates|eager|ec2-host|ec2-product-code|elapsed-time|element-content-type|email|email-address|enable-event|encode-for-NCName|encoding-language-detect|estimate|eval|eval-in|excel-convert|exists|expanded-tree-cache-clear|expanded-tree-cache-partitions|expanded-tree-cache-size|external-binary|external-binary-path|external-security|filesystem-directory|filesystem-directory-create|filesystem-directory-delete|filesystem-file|filesystem-file-delete|filesystem-file-exists|filesystem-file-length|filesystem-file-rename|filesystem-filepath|foreign-cluster-status|foreign-clusters|forest|forest-backup|forest-clear|forest-combine|forest-compare|forest-copy|forest-counts|forest-databases|forest-delete|forest-directory-delete|forest-directory-exists|forest-docinfos|forest-get-readonly|forest-host|forest-name|forest-online|forest-open-replica|forest-rename|forest-restart|forest-restore|forest-rollback|forest-set-readonly|forest-status|forest-updates-allowed|forests|format-number|from-json|function|function-module|function-name|function-parameter-name|function-parameter-type|function-return-type|function-signature|functions|get|get-current-roles|get-current-user|get-current-userid|get-external-variable|get-hot-updates|get-invoked-path|get-ip|get-original-url|get-orphaned-binaries|get-request-body|get-request-client-address|get-request-client-certificate|get-request-field|get-request-field-content-type|get-request-field-filename|get-request-field-names|get-request-header|get-request-header-names|get-request-method|get-request-part-body|get-request-part-headers|get-request-path|get-request-port|get-request-protocol|get-request-url|get-request-user|get-request-username|get-response-code|get-response-encoding|get-server-field|get-server-field-names|get-session-field|get-session-field-names|get-transaction-by-xid|get-transaction-mode|get-url-rewriter-path|getenv|group|group-hosts|group-name|group-servers|groups|gss-server-negotiate|gunzip|gzip|has-privilege|hash32|hash64|hex-to-integer|hmac-md5|hmac-sha1|hmac-sha256|hmac-sha512|host|host-cores|host-cpus|host-forests|host-get-ssl-fips-enabled|host-name|host-size|host-status|hostname|hosts|http-delete|http-get|http-head|http-options|http-post|http-put|initcap|install-directory|integer-to-hex|integer-to-octal|invoke|invoke-function|invoke-in|jobject|key-from-QName|language-stemmer-normalization|language-tokenizer-normalization|lazy|ldap-lookup|ldap-search|license-key|license-key-agreement|license-key-cores|license-key-cpus|license-key-decode|license-key-encode|license-key-expires|license-key-options|license-key-size|license-key-valid|license-key-version|licensee|list-cache-clear|list-cache-partitions|list-cache-size|load|lock-acquire|lock-for-update|lock-release|log|log-level|login|logout|lshift64|match-priority|md5|merge|merge-cancel|merging|missing-directories|modules-database|modules-root|month-name-from-date|mul64|multipart-decode|multipart-encode|n3|n3-get|node-database|node-delete|node-insert-after|node-insert-before|node-insert-child|node-kind|node-output-definition|node-replace|node-uri|not64|nquad|nquad-get|octal-to-integer|or64|original-binary|parse-dateTime|parse-yymmdd|path|pdf-convert|permission|plan|plannable|platform|position|powerpoint-convert|pre-release-expires|pretty-print|privilege|privilege-roles|product-edition|product-environment|product-initials|product-name|QName-from-key|quality|quarter-from-date|query-forests|query-meters|query-trace|quote|random|read-cluster-config-file|read-host-config-file|redirect-response|remove-orphaned-binary|request|request-cancel|request-key|request-status|request-timestamp|resolve-uri|restart|rethrow|role|role-roles|rollback|rsa-generate|rshift64|save|schema-database|score|security-assert|security-database|security-version|server|server-backup|server-name|server-restore|server-status|servers|set|set-current-transaction|set-hot-updates|set-request-time-limit|set-response-code|set-response-content-type|set-response-encoding|set-server-field|set-server-field-privilege|set-session-field|set-transaction-mode|set-transaction-name|set-transaction-time-limit|sha1|sha256|sha384|sha512|shutdown|sleep|smtp-relay|spawn|spawn-function|spawn-in|sql|start-journal-archiving|step64|stop-journal-archiving|strftime|subbinary|test-future|test-lazy|test-skiplist|tidy|timestamp-to-wallclock|to-json|trace|transaction|transaction-commit|transaction-create|transaction-rollback|triggers-database|triple-cache-partitions|triple-cache-size|triple-value-cache-partitions|triple-value-cache-size|turtle|turtle-get|type|unpath|unquote|update|uri-content-type|uri-format|uri-is-file|url-decode|url-encode|user|user-external-security|user-last-login|user-roles|username|validate|value|version|wallclock-to-timestamp|week-from-date|weekday-from-date|word-convert|write-cluster-config-file|write-host-config-file|x509-certificate-extract|x509-certificate-generate|x509-crl-der2pem|x509-crl-extract|x509-crl-generate|x509-request-extract|x509-request-generate|xa-complete|xa-complete-xid|xa-complete1|xa-forget|xa-forget-xid|xa-prepare|xor64|xquery-version|xslt-eval|xslt-invoke|yearday-from-date|zip-create|zip-get|zip-manifest))(?=\s*\() name support.function.marklogic.xquery match (?<![:\-_a-zA-Z0-9])(cts:(aggregate|and-not-query|and-not-query-negative-query|and-not-query-positive-query|and-query|and-query-options|and-query-queries|approx-center|arc-intersection|avg|avg-aggregate|bearing|boost-query|boost-query-boosting-query|boost-query-matching-query|bounding-boxes|box|box-east|box-intersects|box-north|box-south|box-west|circle|circle-center|circle-intersects|circle-radius|classify|cluster|codepoint-tokenizer-class|collection-match|collection-query|collection-query-uris|collection-reference|collections|complex-polygon|complex-polygon-contains|complex-polygon-inner|complex-polygon-intersects|complex-polygon-outer|confidence|contains|correlation|count|count-aggregate|covariance|covariance-p|deregister|destination|directory-query|directory-query-depth|directory-query-uris|distance|distinctive-terms|document-fragment-query|document-fragment-query-query|document-query|document-query-uris|element-attribute-pair-geospatial-boxes|element-attribute-pair-geospatial-query|element-attribute-pair-geospatial-query-element-name|element-attribute-pair-geospatial-query-latitude-name|element-attribute-pair-geospatial-query-longitude-name|element-attribute-pair-geospatial-query-options|element-attribute-pair-geospatial-query-region|element-attribute-pair-geospatial-query-weight|element-attribute-pair-geospatial-value-match|element-attribute-pair-geospatial-values|element-attribute-range-query|element-attribute-range-query-attribute-name|element-attribute-range-query-element-name|element-attribute-range-query-operator|element-attribute-range-query-options|element-attribute-range-query-value|element-attribute-range-query-weight|element-attribute-reference|element-attribute-value-co-occurrences|element-attribute-value-geospatial-co-occurrences|element-attribute-value-match|element-attribute-value-query|element-attribute-value-query-attribute-name|element-attribute-value-query-element-name|element-attribute-value-query-options|element-attribute-value-query-text|element-attribute-value-query-weight|element-attribute-value-ranges|element-attribute-values|element-attribute-word-match|element-attribute-word-query|element-attribute-word-query-attribute-name|element-attribute-word-query-element-name|element-attribute-word-query-options|element-attribute-word-query-text|element-attribute-word-query-weight|element-attribute-words|element-child-geospatial-boxes|element-child-geospatial-query|element-child-geospatial-query-child-name|element-child-geospatial-query-element-name|element-child-geospatial-query-options|element-child-geospatial-query-region|element-child-geospatial-query-weight|element-child-geospatial-value-match|element-child-geospatial-values|element-geospatial-boxes|element-geospatial-query|element-geospatial-query-element-name|element-geospatial-query-options|element-geospatial-query-region|element-geospatial-query-weight|element-geospatial-value-match|element-geospatial-values|element-pair-geospatial-boxes|element-pair-geospatial-query|element-pair-geospatial-query-element-name|element-pair-geospatial-query-latitude-name|element-pair-geospatial-query-longitude-name|element-pair-geospatial-query-options|element-pair-geospatial-query-region|element-pair-geospatial-query-weight|element-pair-geospatial-value-match|element-pair-geospatial-values|element-query|element-query-element-name|element-query-query|element-range-query|element-range-query-element-name|element-range-query-operator|element-range-query-options|element-range-query-value|element-range-query-weight|element-reference|element-value-co-occurrences|element-value-geospatial-co-occurrences|element-value-match|element-value-query|element-value-query-element-name|element-value-query-options|element-value-query-text|element-value-query-weight|element-value-ranges|element-values|element-word-match|element-word-query|element-word-query-element-name|element-word-query-options|element-word-query-text|element-word-query-weight|element-words|field-range-query|field-range-query-field-name|field-range-query-operator|field-range-query-options|field-range-query-value|field-range-query-weight|field-reference|field-value-co-occurrences|field-value-match|field-value-query|field-value-query-field-name|field-value-query-options|field-value-query-text|field-value-query-weight|field-value-ranges|field-values|field-word-match|field-word-query|field-word-query-field-name|field-word-query-options|field-word-query-text|field-word-query-weight|field-words|fitness|frequency|geospatial-attribute-pair-reference|geospatial-co-occurrences|geospatial-element-attribute-pair-reference|geospatial-element-child-reference|geospatial-element-pair-reference|geospatial-element-reference|geospatial-path-reference|hash-terms|index-path-key|index-path-keys|index-path-ns-prefixes|linear-model|linestring|linestring-vertices|locks-query|locks-query-query|long-lat-point|matches|max|median|min|near-query|near-query-distance|near-query-options|near-query-queries|near-query-weight|not-in-query|not-in-query-negative-query|not-in-query-positive-query|not-query|not-query-query|not-query-weight|or-query|or-query-queries|parse|parse-wkt|path-geospatial-query|path-geospatial-query-options|path-geospatial-query-path-expression|path-geospatial-query-region|path-geospatial-query-weight|path-range-query|path-range-query-operator|path-range-query-options|path-range-query-path-name|path-range-query-value|path-range-query-weight|path-reference|percent-rank|percentile|point|point-latitude|point-longitude|polygon|polygon-contains|polygon-intersects|polygon-vertices|properties-query|properties-query-query|punctuation|quality|query|rank|reference|reference-parse|region|region-contains|region-intersects|register|registered-query|registered-query-ids|registered-query-options|registered-query-weight|relevance-info|remainder|reverse-query|reverse-query-nodes|reverse-query-weight|score|search|shortest-distance|show-get-query|similar-query|similar-query-nodes|similar-query-weight|space|special|stddev|stddev-p|stem|sum|sum-aggregate|term-query|term-query-term|term-query-weight|thresholds|time-series|timestamp-query|to-wkt|token|tokenize|train|triple-range-query|triple-range-query-object|triple-range-query-operator|triple-range-query-options|triple-range-query-predicate|triple-range-query-subject|triple-range-query-weight|triples|uri-match|uri-reference|uris|valid-index-path|value-co-occurrences|value-match|value-ranges|value-tuples|values|variance|variance-p|word|word-match|word-query|word-query-options|word-query-text|word-query-weight|words))(?=\s*\() name support.function.cts.xquery match (?<![:\-_a-zA-Z0-9])(xdmp:([\-_a-zA-Z0-9][\-\._a-zA-Z0-9]*:)?([\-_a-zA-Z0-9][\-\._a-zA-Z0-9]*))\s*\( name invalid.illegal.function.xdmp match (?<![:\-_a-zA-Z0-9])(cts:([\-_a-zA-Z0-9][\-\._a-zA-Z0-9]*:)?([\-_a-zA-Z0-9][\-\._a-zA-Z0-9]*))\s*\( name invalid.illegal.function.cts include #string begin (\() beginCaptures 1 name punctuation.definition.begin.xquery end (\)) endCaptures 1 name punctuation.definition.end.xquery name meta patterns include $self include #function_call repository EntityDecl begin (<!)(ENTITY)\s+(%\s+)?([:a-zA-Z_][:a-zA-Z0-9_.-]*)(\s+(?:SYSTEM|PUBLIC)\s+)? captures 1 name punctuation.definition.tag.begin.xml 2 name keyword.entity.xml 3 name punctuation.definition.entity.xml 4 name variable.entity.xml 5 name keyword.entitytype.xml end (>) patterns include #doublequotedStringXml include #singlequotedStringXml Xml patterns begin (<\?)\s*([-_a-zA-Z0-9]+) captures 1 name punctuation.definition.tag.begin.xml 2 name entity.name.tag.xml end (\?>) name meta.tag.preprocessor.xml patterns match ([a-zA-Z-]+) name entity.other.attribute-name.xml include #doublequotedString include #singlequotedString begin (<!)(DOCTYPE)\s+([:a-zA-Z_][:a-zA-Z0-9_.-]*) captures 1 name punctuation.definition.tag.begin.xml 2 name keyword.doctype.xml 3 name variable.documentroot.xml end \s*(>) name meta.tag.sgml.doctype.xml patterns include #internalSubset begin <[!%]-- captures 0 name punctuation.definition.comment.xml end --%?> name comment.block.xml begin <\? captures 0 name punctuation.definition.processing-instruction.xml end \?> name comment.processing-instruction.xml begin (<)((?:([-_a-zA-Z0-9]+)((:)))?([-_a-zA-Z0-9:]+))(?=(\s[^>]*)?></\2>) beginCaptures 1 name punctuation.definition.tag.begin.xml 3 name entity.name.tag.namespace.xml 4 name entity.name.tag.xml 5 name punctuation.separator.namespace.xml 6 name entity.name.tag.localname.xml end (>)(<)(/)(?:([-_a-zA-Z0-9]+)((:)))?([-_a-zA-Z0-9:]+)(>) endCaptures 1 name punctuation.definition.tag.end.xml 2 name punctuation.definition.tag.begin.xml meta.scope.between-tag-pair.xml 3 name punctuation.definition.tag.begin.xml 4 name entity.name.tag.namespace.xml 5 name entity.name.tag.xml 6 name punctuation.separator.namespace.xml 7 name entity.name.tag.localname.xml 8 name punctuation.definition.tag.end.xml name meta.tag.no-content.xml patterns include #tagStuff begin (</?)(?:([-_a-zA-Z0-9]+)((:)))?([-_a-zA-Z0-9:]+) captures 1 name punctuation.definition.tag.begin.xml 2 name entity.name.tag.namespace.xml 3 name entity.name.tag.xml 4 name punctuation.separator.namespace.xml 5 name entity.name.tag.localname.xml end (/?>) endCaptures 1 name punctuation.definition.tag.end.xml name meta.tag.xml patterns include #tagStuff include #entity include #bare-ampersand begin <!\[CDATA\[ beginCaptures 0 name punctuation.definition.string.begin.xml end ]]> endCaptures 0 name punctuation.definition.string.end.xml name string.unquoted.cdata.xml bare-ampersand match & name invalid.illegal.bad-ampersand.xml block_comment begin \(: end :\) patterns include #block_comment code_block begin \{ end \} name meta.code-block.xquery patterns include $self doublequotedString begin (?<![-_a-zA-Z0-9:'"]>)\s*"(?![\w\s()']*</[-_a-zA-Z0-9:]) beginCaptures 0 name punctuation.definition.string.begin.xquery end " endCaptures 0 name punctuation.definition.string.end.xquery name string.quoted.double.xquery patterns include #entity include #bare-ampersand doublequotedStringXml begin " beginCaptures 0 name punctuation.definition.string.begin.xml end " endCaptures 0 name punctuation.definition.string.end.xml name string.quoted.double.xml patterns include #entity include #bare-ampersand include #code_block entity captures 1 name punctuation.definition.constant.xml match (&)([:a-zA-Z_][:a-zA-Z0-9_.-]*|#[0-9]+|#x[0-9a-fA-F]+)(;) name constant.character.entity.xml function_call captures 1 name punctuation.definition.parameters.begin.xquery match [\-_a-zA-Z0-9]+:[\-_a-zA-Z0-9]+(?=\() name support.function.xquery function_parameters match \$([\-_a-zA-Z0-9][\-\._a-zA-Z0-9]*:)?([\-_a-zA-Z0-9][\-\._a-zA-Z0-9]*) name variable.parameter.xquery internalSubset begin (\[) captures 1 name punctuation.definition.constant.xml end (\]) name meta.internalsubset.xml patterns include #EntityDecl include #parameterEntity parameterEntity captures 1 name punctuation.definition.constant.xml 3 name punctuation.definition.constant.xml match (%)([:a-zA-Z_][:a-zA-Z0-9_.-]*)(;) name constant.character.parameter-entity.xml singlequotedString begin (?<![-_a-zA-Z0-9:'"]>)\s*'(?![\w\s()"]*</[-_a-zA-Z0-9:]) beginCaptures 0 name punctuation.definition.string.begin.xquery end ' endCaptures 0 name punctuation.definition.string.end.xquery name string.quoted.single.xquery patterns include #entity include #bare-ampersand singlequotedStringXml begin ' beginCaptures 0 name punctuation.definition.string.begin.xml end ' endCaptures 0 name punctuation.definition.string.end.xml name string.quoted.single.xml patterns include #entity include #bare-ampersand include #code_block string patterns include #singlequotedString include #doublequotedString tagStuff patterns captures 1 name entity.other.attribute-name.namespace.xml 2 name entity.other.attribute-name.xml 3 name punctuation.separator.namespace.xml 4 name entity.other.attribute-name.localname.xml match (?:([-_a-zA-Z0-9]+)((:)))?([-_a-zA-Z0-9]+)= include #doublequotedStringXml include #singlequotedStringXml scopeName source.xquery uuid cddd8a73-ed1e-4303-a649-a23e816fafa1 ================================================ FILE: package.json ================================================ { "name": "xml", "displayName": "XML Tools", "description": "XML Formatting, XQuery, and XPath Tools for Visual Studio Code", "version": "2.5.1", "preview": false, "publisher": "DotJoshJohnson", "author": { "name": "Josh Johnson", "url": "https://github.com/DotJoshJohnson" }, "galleryBanner": { "color": "#FFFFFF", "theme": "light" }, "icon": "resources/xml.png", "homepage": "https://github.com/DotJoshJohnson/vscode-xml", "repository": { "type": "git", "url": "https://github.com/DotJoshJohnson/vscode-xml.git" }, "bugs": { "url": "https://github.com/DotJoshJohnson/vscode-xml/issues" }, "engines": { "vscode": "^1.22.2" }, "categories": [ "Formatters", "Programming Languages", "Linters", "Other" ], "activationEvents": [ "onCommand:xmlTools.evaluateXPath", "onCommand:xmlTools.executeXQuery", "onCommand:xmlTools.formatAsXml", "onCommand:xmlTools.textToXml", "onCommand:xmlTools.xmlToText", "onCommand:xmlTools.minifyXml", "onLanguage:xml", "onLanguage:xquery", "onLanguage:xsl" ], "main": "./out/extension", "contributes": { "commands": [ { "command": "xmlTools.evaluateXPath", "title": "XML Tools: Evaluate XPath" }, { "command": "xmlTools.executeXQuery", "title": "XML Tools: Execute XQuery" }, { "command": "xmlTools.formatAsXml", "title": "XML Tools: Format as XML" }, { "command": "xmlTools.textToXml", "title": "XML Tools: Convert text to XML (<> -> <>)" }, { "command": "xmlTools.xmlToText", "title": "XML Tools: Convert XML to text (<> -> <>)" }, { "command": "xmlTools.getCurrentXPath", "title": "XML Tools: Get Current XPath" }, { "command": "xmlTools.minifyXml", "title": "XML Tools: Minify XML" } ], "configuration": { "title": "XML Tools Configuration", "type": "object", "properties": { "xmlTools.enableXmlTreeView": { "type": "boolean", "default": true, "description": "Enables the XML Document view in the explorer for XML documents.", "scope": "window" }, "xmlTools.enableXmlTreeViewMetadata": { "type": "boolean", "default": true, "description": "Enables attribute and child element counts in the XML Document view.", "scope": "window" }, "xmlTools.enableXmlTreeViewCursorSync": { "type": "boolean", "default": false, "description": "Enables auto-reveal of elements in the XML Document view when a start tag is clicked in the editor.", "scope": "window" }, "xmlTools.enforcePrettySelfClosingTagOnFormat": { "type": "boolean", "default": false, "description": "Enforces a space before the forward slash at the end of a self-closing XML tag.", "scope": "resource" }, "xmlTools.ignoreDefaultNamespace": { "type": "boolean", "default": true, "description": "Ignore default xmlns attributes when evaluating XPath.", "scope": "window" }, "xmlTools.persistXPathQuery": { "type": "boolean", "default": true, "description": "Remember the last XPath query used.", "scope": "window" }, "xmlTools.removeCommentsOnMinify": { "type": "boolean", "default": false, "description": "Remove XML comments during minification.", "scope": "resource" }, "xmlTools.splitAttributesOnFormat": { "type": "boolean", "default": false, "description": "Put each attribute on a new line when formatting XML. Overrides `xmlTools.splitXmlnsOnFormat` if set to `true`.", "scope": "resource" }, "xmlTools.splitXmlnsOnFormat": { "type": "boolean", "default": true, "description": "Put each xmlns attribute on a new line when formatting XML.", "scope": "resource" }, "xmlTools.xmlFormatterImplementation": { "type": "string", "enum": [ "classic", "v2" ], "default": "v2", "description": "Supported XML Formatters: classic", "scope": "window" }, "xmlTools.xqueryExecutionArguments": { "type": "array", "default": [ "-xquery", "$(script)", "-in", "$(input)", "-out", "$(input).output.xml" ], "description": "Arguments to be passed to the XQuery execution engine.", "scope": "window" }, "xmlTools.xqueryExecutionEngine": { "type": "string", "default": "", "description": "The full path to the executable to run when executing XQuery scripts.", "scope": "window" }, "xmlTools.xqueryExecutionInputLimit": { "type": "integer", "default": 100, "description": "The maximum number of input files to enumerate when executing XQuery scripts.", "scope": "window" }, "xmlTools.xqueryExecutionInputSearchPattern": { "type": "string", "default": "**/*.xml", "description": "The pattern used to search for input XML files when executing XQuery scripts.", "scope": "window" } } }, "grammars": [ { "language": "xquery", "path": "./languages/xquery/xquery.tmLanguage", "scopeName": "source.xquery" } ], "keybindings": [ { "key": "ctrl+shift+alt+x", "command": "xmlTools.evaluateXPath" }, { "key": "ctrl+shift+alt+b", "command": "xmlTools.formatAsXml" } ], "languages": [ { "id": "xml", "extensions": [ ".config", ".csproj", ".xml", ".xsd", ".xsl", ".plist", ".mobileconfig" ] }, { "id": "xquery", "aliases": [ "XQuery", "xquery" ], "extensions": [ ".xq", ".xql", ".xqm", ".xqy", ".xquery" ], "configuration": "./languages/xquery/xquery.json" } ], "menus": { "commandPalette": [ { "command": "xmlTools.evaluateXPath", "when": "editorLangId == xml" }, { "command": "xmlTools.executeXQuery", "when": "editorLangId == xquery" }, { "command": "xmlTools.getCurrentXPath", "when": "editorLangId == xml" }, { "command": "xmlTools.minifyXml", "when": "editorLangId == xml" } ], "editor/context": [ { "command": "xmlTools.minifyXml", "group": "1_modification@100", "when": "editorLangId == 'xml'" } ] }, "views": { "explorer": [ { "id": "xmlTreeView", "name": "XML Document", "when": "xmlTreeViewEnabled" } ] } }, "scripts": { "vscode:prepublish": "npm run compile", "compile": "npm run lint && tsc -p ./", "watch": "tsc -watch -p ./", "postinstall": "node ./node_modules/vscode/bin/install", "test": "npm run compile && mocha './out/test/**/*.js'", "test-windows": "npm run compile && mocha ./out/test/**/*.js", "lint": "tslint -p tslint.json --fix" }, "devDependencies": { "@types/mocha": "^2.2.42", "@types/node": "^7.0.43", "@types/xmldom": "^0.1.29", "tslint": "^5.9.1", "typescript": "^2.6.1", "vscode": "^1.1.16" }, "dependencies": { "xmldom": "^0.1.27", "xpath": "0.0.27", "xqlint": "^0.4.1" } } ================================================ FILE: src/common/configuration.ts ================================================ import { workspace, Uri } from "vscode"; const ExtensionTopLevelSection = "xmlTools"; export class Configuration { static get enableXmlTreeView(): boolean { return this._getForWindow("enableXmlTreeView"); } static get enableXmlTreeViewMetadata(): boolean { return this._getForWindow("enableXmlTreeViewMetadata"); } static get enableXmlTreeViewCursorSync(): boolean { return this._getForWindow("enableXmlTreeViewCursorSync"); } static get ignoreDefaultNamespace(): boolean { return this._getForWindow("ignoreDefaultNamespace"); } static get persistXPathQuery(): boolean { return this._getForWindow("persistXPathQuery"); } static get xmlFormatterImplementation(): string { return this._getForWindow("xmlFormatterImplementation"); } static get xqueryExecutionArguments(): string[] { return this._getForWindow("xqueryExecutionArguments"); } static get xqueryExecutionEngine(): string { return this._getForWindow("xqueryExecutionEngine"); } static get xqueryExecutionInputLimit(): number { return this._getForWindow("xqueryExecutionInputLimit"); } static get xqueryExecutionInputSearchPattern(): string { return this._getForWindow("xqueryExecutionInputSearchPattern"); } static enforcePrettySelfClosingTagOnFormat(resource: Uri): boolean { return this._getForResource("enforcePrettySelfClosingTagOnFormat", resource); } static removeCommentsOnMinify(resource: Uri): boolean { return this._getForResource("removeCommentsOnMinify", resource); } static splitAttributesOnFormat(resource: Uri): boolean { return this._getForResource("splitAttributesOnFormat", resource); } static splitXmlnsOnFormat(resource: Uri): boolean { return this._getForResource("splitXmlnsOnFormat", resource); } private static _getForResource(section: string, resource: Uri): T { return workspace.getConfiguration(ExtensionTopLevelSection, resource).get(section); } private static _getForWindow(section: string): T { return workspace.getConfiguration(ExtensionTopLevelSection).get(section); } } ================================================ FILE: src/common/create-document-selector.ts ================================================ import { DocumentFilter } from "vscode"; import * as constants from "../constants"; export function createDocumentSelector(language: string): DocumentFilter[] { return [ { language, scheme: constants.uriSchemes.file }, { language, scheme: constants.uriSchemes.untitled }, ]; } ================================================ FILE: src/common/extension-state.ts ================================================ import { ExtensionContext, Memento } from "vscode"; export class ExtensionState { private static _context: ExtensionContext; static get global(): Memento { return this._context.globalState; } static get workspace(): Memento { return this._context.workspaceState; } static configure(context: ExtensionContext): void { this._context = context; } } ================================================ FILE: src/common/index.ts ================================================ export * from "./configuration"; export * from "./create-document-selector"; export * from "./extension-state"; export * from "./native-commands"; export * from "./xml-traverser"; ================================================ FILE: src/common/native-commands.ts ================================================ import { commands } from "vscode"; export class NativeCommands { static async cursorMove(to: string, by: string): Promise { await commands.executeCommand("cursorMove", { to: to, by: by }); } static openGlobalSettings(): void { commands.executeCommand("workbench.action.openGlobalSettings"); } static setContext(key: string, value: any): void { commands.executeCommand("setContext", key, value); } } ================================================ FILE: src/common/xml-traverser.ts ================================================ import { Position } from "vscode"; import { DOMParser } from "xmldom"; export class XmlTraverser { constructor(private _xmlDocument: Document) { } get xmlDocument(): Document { return this._xmlDocument; } set xmlDocument(value: Document) { this._xmlDocument = value; } getChildAttributeArray(node: Element): any[] { if (!node.attributes) { return []; } const array = new Array(); for (let i = 0; i < node.attributes.length; i++) { array.push(node.attributes[i]); } return array; } getChildElementArray(node: Node): any[] { if (!node.childNodes) { return []; } const array = new Array(); for (let i = 0; i < node.childNodes.length; i++) { const child = node.childNodes[i]; if (this.isElement(child)) { array.push(child); } } return array; } getElementAtPosition(position: Position): Element { const node = this.getNodeAtPosition(position); return this.getNearestElementAncestor(node); } getNearestElementAncestor(node: Node): Element { if (!this.isElement) { return this.getNearestElementAncestor(node.parentNode); } return node; } getNodeAtPosition(position: Position): Node { return this._getNodeAtPositionCore(position, this._xmlDocument.documentElement); } getSiblings(node: Node): Node[] { if (this.isElement(node)) { return this.getSiblingElements(node); } return this.getSiblingAttributes(node); } getSiblingAttributes(node: Node): Node[] { return this.getChildAttributeArray(node.parentNode); } getSiblingElements(node: Node): Node[] { return this.getChildElementArray(node.parentNode); } hasSimilarSiblings(node: Node): boolean { if (!node || !node.parentNode || !this.isElement(node)) { return false; } const siblings = this.getChildElementArray(node.parentNode); return (siblings.filter(x => x.tagName === (node as Element).tagName).length > 1); } isElement(node: Node): boolean { return (!!node && !!(node as Element).tagName); } private _getNodeAtPositionCore(position: Position, contextNode: Node): Node { if (!contextNode) { return undefined; } const lineNumber = (contextNode as any).lineNumber; const columnNumber = (contextNode as any).columnNumber; const columnRange = [columnNumber, (columnNumber + (this._getNodeWidthInCharacters(contextNode) - 1))]; // for some reason, xmldom sets the column number for attributes to the "=" if (!this.isElement(contextNode)) { columnRange[0] = (columnRange[0] - contextNode.nodeName.length); } if (this._checkRange(lineNumber, position, columnRange)) { return contextNode; } if (this.isElement(contextNode)) { // if the element contains text, check to see if the cursor is present in the text const textContent = (contextNode as Element).textContent; if (textContent) { columnRange[1] = (columnRange[1] + textContent.length); if (this._checkRange(lineNumber, position, columnRange)) { return contextNode; } } const children = [...this.getChildAttributeArray(contextNode), ...this.getChildElementArray(contextNode)]; let result: Node; for (let i = 0; i < children.length; i++) { const child = children[i]; result = this._getNodeAtPositionCore(position, child); if (result) { return result; } } } return undefined; } private _checkRange(lineNumber: number, position: Position, columnRange: number[]): boolean { return (lineNumber === (position.line + 1) && ((position.character + 1) >= columnRange[0] && (position.character + 1) < columnRange[1])); } private _getNodeWidthInCharacters(node: Node) { if (this.isElement(node)) { return (node.nodeName.length + 2); } else { return (node.nodeName.length + node.nodeValue.length + 3); } } } ================================================ FILE: src/completion/index.ts ================================================ export * from "./xquery-completion-item-provider"; ================================================ FILE: src/completion/xquery-completion-item-provider.ts ================================================ import { CompletionItem, CompletionItemKind, CompletionItemProvider, Position, TextDocument } from "vscode"; const XQLint = require("xqlint").XQLint; export class XQueryCompletionItemProvider implements CompletionItemProvider { provideCompletionItems(document: TextDocument, position: Position): CompletionItem[] { const completionItems = new Array(); const linter = new XQLint(document.getText()); linter.getCompletions({ line: position.line, col: position.character }).forEach((x: any) => { completionItems.push(this._getCompletionItem(x)); }); return completionItems; } private _getCompletionItem(xqLintCompletionItem: any): CompletionItem { const completionItem = new CompletionItem(xqLintCompletionItem.name); completionItem.insertText = xqLintCompletionItem.value; switch (xqLintCompletionItem.meta) { // functions (always qualified with a colon) case "function": completionItem.kind = CompletionItemKind.Function; const funcStart = (xqLintCompletionItem.value.indexOf(":") + 1); const funcEnd = xqLintCompletionItem.value.indexOf("("); completionItem.insertText = xqLintCompletionItem.value.substring(funcStart, funcEnd); break; // variables and parameters (always qualified with a dollar sign) case "Let binding": case "Local variable": case "Window variable": case "Function parameter": completionItem.kind = CompletionItemKind.Variable; completionItem.insertText = xqLintCompletionItem.value.substring(1); break; // everything else default: completionItem.kind = CompletionItemKind.Text; break; } return completionItem; } } ================================================ FILE: src/constants.ts ================================================ export namespace commands { export const evaluateXPath = "xmlTools.evaluateXPath"; export const executeXQuery = "xmlTools.executeXQuery"; export const formatAsXml = "xmlTools.formatAsXml"; export const xmlToText = "xmlTools.xmlToText"; export const textToXml = "xmlTools.textToXml"; export const getCurrentXPath = "xmlTools.getCurrentXPath"; export const minifyXml = "xmlTools.minifyXml"; } export namespace contextKeys { export const xmlTreeViewEnabled = "xmlTreeViewEnabled"; } export namespace diagnosticCollections { export const xquery = "XQueryDiagnostics"; } export namespace languageIds { export const xml = "xml"; export const xsd = "xsd"; export const xquery = "xquery"; } export namespace nativeCommands { export const revealLine = "revealLine"; } export namespace stateKeys { export const xpathQueryHistory = "xpathQueryHistory"; export const xPathQueryLast = "xPathQueryLast"; } export namespace uriSchemes { export const file = "file"; export const untitled = "untitled"; } export namespace views { export const xmlTreeView = "xmlTreeView"; } export namespace xmlFormatterImplementations { export const classic = "classic"; export const v2 = "v2"; } ================================================ FILE: src/extension.ts ================================================ import { commands, languages, window, workspace, ExtensionContext, Memento, TextEditor, TextEditorSelectionChangeEvent, TextEditorSelectionChangeKind, DiagnosticCollection } from "vscode"; 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 { XmlTreeDataProvider } from "./tree-view"; import { evaluateXPath, getCurrentXPath } from "./xpath/commands"; import { executeXQuery } from "./xquery-execution/commands"; import * as constants from "./constants"; let diagnosticCollectionXQuery: DiagnosticCollection; export function activate(context: ExtensionContext) { ExtensionState.configure(context); const xmlXsdDocSelector = [...createDocumentSelector(constants.languageIds.xml), ...createDocumentSelector(constants.languageIds.xsd)]; const xqueryDocSelector = createDocumentSelector(constants.languageIds.xquery); /* Completion Features */ context.subscriptions.push( languages.registerCompletionItemProvider(xqueryDocSelector, new XQueryCompletionItemProvider(), ":", "$") ); /* Formatting Features */ const xmlFormattingEditProvider = new XmlFormattingEditProvider(XmlFormatterFactory.getXmlFormatter()); context.subscriptions.push( commands.registerTextEditorCommand(constants.commands.formatAsXml, formatAsXml), commands.registerTextEditorCommand(constants.commands.xmlToText, xmlToText), commands.registerTextEditorCommand(constants.commands.textToXml, textToXml), commands.registerTextEditorCommand(constants.commands.minifyXml, minifyXml), languages.registerDocumentFormattingEditProvider(xmlXsdDocSelector, xmlFormattingEditProvider), languages.registerDocumentRangeFormattingEditProvider(xmlXsdDocSelector, xmlFormattingEditProvider) ); /* Linting Features */ diagnosticCollectionXQuery = languages.createDiagnosticCollection(constants.diagnosticCollections.xquery); context.subscriptions.push( diagnosticCollectionXQuery, window.onDidChangeActiveTextEditor(_handleChangeActiveTextEditor), window.onDidChangeTextEditorSelection(_handleChangeTextEditorSelection) ); /* Tree View Features */ const treeViewDataProvider = new XmlTreeDataProvider(context); const treeView = window.createTreeView(constants.views.xmlTreeView, { treeDataProvider: treeViewDataProvider }); if (Configuration.enableXmlTreeViewCursorSync) { window.onDidChangeTextEditorSelection(x => { if (x.kind === TextEditorSelectionChangeKind.Mouse && x.selections.length > 0) { treeView.reveal(treeViewDataProvider.getNodeAtPosition(x.selections[0].start)); } }); } context.subscriptions.push( treeView ); /* XPath Features */ context.subscriptions.push( commands.registerTextEditorCommand(constants.commands.evaluateXPath, evaluateXPath), commands.registerTextEditorCommand(constants.commands.getCurrentXPath, getCurrentXPath) ); /* XQuery Features */ context.subscriptions.push( commands.registerTextEditorCommand(constants.commands.executeXQuery, executeXQuery) ); } export function deactivate() { // do nothing } function _handleContextChange(editor: TextEditor): void { const supportedSchemes = [constants.uriSchemes.file, constants.uriSchemes.untitled]; if (!editor || !editor.document || supportedSchemes.indexOf(editor.document.uri.scheme) === -1) { return; } switch (editor.document.languageId) { case constants.languageIds.xquery: diagnosticCollectionXQuery.set(editor.document.uri, new XQueryLinter().lint(editor.document.getText())); break; } } function _handleChangeActiveTextEditor(editor: TextEditor): void { _handleContextChange(editor); } function _handleChangeTextEditorSelection(e: TextEditorSelectionChangeEvent): void { _handleContextChange(e.textEditor); } ================================================ FILE: src/formatting/commands/formatAsXml.ts ================================================ import { workspace } from "vscode"; import { ProviderResult, Range, TextEdit, TextEditor, TextEditorEdit } from "vscode"; import { NativeCommands } from "../../common"; import * as constants from "../../constants"; import { XmlFormatterFactory } from "../xml-formatter"; import { XmlFormattingEditProvider } from "../xml-formatting-edit-provider"; import { XmlFormattingOptionsFactory } from "../xml-formatting-options"; export function formatAsXml(editor: TextEditor, edit: TextEditorEdit): void { const xmlFormattingEditProvider = new XmlFormattingEditProvider(XmlFormatterFactory.getXmlFormatter()); const formattingOptions = { insertSpaces: editor.options.insertSpaces, tabSize: editor.options.tabSize }; let edits: ProviderResult; if (!editor.selection.isEmpty) { edits = xmlFormattingEditProvider.provideDocumentRangeFormattingEdits( editor.document, new Range(editor.selection.start, editor.selection.end), formattingOptions, null); } else { edits = xmlFormattingEditProvider.provideDocumentFormattingEdits( editor.document, formattingOptions, null); } for (let i = 0; i < (edits as TextEdit[]).length; i++) { const textEdit = (edits as TextEdit[])[i]; editor.edit(async (editBuilder) => { editBuilder.replace(textEdit.range, textEdit.newText); // wiggle the cursor to deselect the formatted XML (is there a non-hacky way to go about this?) await NativeCommands.cursorMove("left", "character"); await NativeCommands.cursorMove("right", "character"); }); } } ================================================ FILE: src/formatting/commands/index.ts ================================================ export * from "./formatAsXml"; export * from "./minifyXml"; export * from "./xmlToText"; export * from "./textToXml"; ================================================ FILE: src/formatting/commands/minifyXml.ts ================================================ import { workspace } from "vscode"; import { ProviderResult, Range, TextEdit, TextEditor, TextEditorEdit } from "vscode"; import * as constants from "../../constants"; import { XmlFormatterFactory } from "../xml-formatter"; import { XmlFormattingEditProvider } from "../xml-formatting-edit-provider"; import { XmlFormattingOptionsFactory } from "../xml-formatting-options"; export function minifyXml(editor: TextEditor, edit: TextEditorEdit): void { const xmlFormatter = XmlFormatterFactory.getXmlFormatter(); const xmlFormattingOptions = XmlFormattingOptionsFactory.getXmlFormattingOptions({ insertSpaces: editor.options.insertSpaces, tabSize: editor.options.tabSize }, editor.document); const endPosition = editor.document.lineAt(editor.document.lineCount - 1).rangeIncludingLineBreak.end; const range = new Range(editor.document.positionAt(0), endPosition); edit.replace(range, xmlFormatter.minifyXml(editor.document.getText(), xmlFormattingOptions)); } ================================================ FILE: src/formatting/commands/textToXml.ts ================================================ import { workspace } from "vscode"; import { ProviderResult, Range, TextEdit, TextEditor, Selection } from "vscode"; import { NativeCommands } from "../../common"; import * as constants from "../../constants"; import { XmlFormatterFactory } from "../xml-formatter"; import { XmlFormattingEditProvider } from "../xml-formatting-edit-provider"; import { XmlFormattingOptionsFactory } from "../xml-formatting-options"; export function textToXml(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 txt = textEditor.document.getText(new Range(selection.start, selection.end)); const transformed = txt .replace(/</g, "<") .replace(/>/g, ">") .replace(/&/g, "&") // tslint:disable-next-line .replace(/"/g, '"') .replace(/'/g, "'"); textEdit.replace(selection, transformed); }); }); } ================================================ FILE: src/formatting/commands/xmlToText.ts ================================================ import { workspace } from "vscode"; import { ProviderResult, Range, TextEdit, TextEditor, Selection } from "vscode"; import { NativeCommands } from "../../common"; import * as constants from "../../constants"; import { XmlFormatterFactory } from "../xml-formatter"; import { XmlFormattingEditProvider } from "../xml-formatting-edit-provider"; import { XmlFormattingOptionsFactory } from "../xml-formatting-options"; export function xmlToText(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 txt = textEditor.document.getText(new Range(selection.start, selection.end)); const transformed = txt .replace(//g, ">") .replace(/&/g, "&") .replace(/"/g, """) .replace(/'/g, "'"); textEdit.replace(selection, transformed); }); }); } ================================================ FILE: src/formatting/formatters/classic-xml-formatter.ts ================================================ import { XmlFormatter } from "../xml-formatter"; import { XmlFormattingOptions } from "../xml-formatting-options"; export class ClassicXmlFormatter implements XmlFormatter { formatXml(xml: string, options: XmlFormattingOptions): string { xml = this.minifyXml(xml, options); xml = xml.replace(/ -1) { output += this._getIndent(options, level, parts[i]); inComment = true; // end /) > -1 || parts[i].search(/\]>/) > -1 || parts[i].search(/!DOCTYPE/) > -1) { inComment = false; } } else if (parts[i].search(/-->/) > -1 || parts[i].search(/\]>/) > -1) { output += parts[i]; inComment = false; } else if (/^<(\w|:)/.test(parts[i - 1]) && /^<\/(\w|:)/.test(parts[i]) && /^<[\w:\-\.\,\/]+/.exec(parts[i - 1])[0] === /^<\/[\w:\-\.\,]+/.exec(parts[i])[0].replace("/", "")) { output += parts[i]; if (!inComment) { level--; } } else if (parts[i].search(/<(\w|:)/) > -1 && parts[i].search(/<\//) === -1 && parts[i].search(/\/>/) === -1) { output = (!inComment) ? output += this._getIndent(options, level++, parts[i]) : output += parts[i]; } else if (parts[i].search(/<(\w|:)/) > -1 && parts[i].search(/<\//) > -1) { output = (!inComment) ? output += this._getIndent(options, level, parts[i]) : output += parts[i]; } else if (parts[i].search(/<\//) > -1) { output = (!inComment) ? output += this._getIndent(options, --level, parts[i]) : output += parts[i]; } else if (parts[i].search(/\/>/) > -1 && (!options.splitXmlnsOnFormat || parts[i].search(/xmlns(:|=)/) === -1)) { output = (!inComment) ? output += this._getIndent(options, level, parts[i]) : output += parts[i]; } else if (parts[i].search(/\/>/) > -1 && parts[i].search(/xmlns(:|=)/) > -1 && options.splitXmlnsOnFormat) { output = (!inComment) ? output += this._getIndent(options, level--, parts[i]) : output += parts[i]; } else if (parts[i].search(/<\?/) > -1) { output += this._getIndent(options, level, parts[i]); } else if (options.splitXmlnsOnFormat && (parts[i].search(/xmlns\:/) > -1 || parts[i].search(/xmlns\=/) > -1)) { output += this._getIndent(options, level, parts[i]); } else { output += parts[i]; } } // remove leading newline if (output[0] === options.newLine) { output = output.slice(1); } else if (output.substring(0, 1) === options.newLine) { output = output.slice(2); } return output; } minifyXml(xml: string, options: XmlFormattingOptions): string { xml = this._stripLineBreaks(options, xml); // all line breaks outside of CDATA elements and comments xml = (options.removeCommentsOnMinify) ? xml.replace(/\/g, "") : xml; xml = xml.replace(/>\s{0,}<"); // insignificant whitespace between tags xml = xml.replace(/"\s+(?=[^\s]+=)/g, "\" "); // spaces between attributes xml = xml.replace(/"\s+(?=>)/g, "\""); // spaces between the last attribute and tag close (>) xml = xml.replace(/"\s+(?=\/>)/g, "\" "); // spaces between the last attribute and tag close (/>) xml = xml.replace(/[^ <>="]\s+[^ <>="]+=/g, (match: string) => { // spaces between the node name and the first attribute return match.replace(/\s+/g, " "); }); return xml; } private _getIndent(options: XmlFormattingOptions, level: number, trailingValue?: string): string { trailingValue = trailingValue || ""; const indentPattern = (options.editorOptions.preferSpaces) ? " ".repeat(options.editorOptions.tabSize) : "\t"; return `${options.newLine}${indentPattern.repeat(level)}${trailingValue}`; } /** * Removes line breaks outside of CDATA, comment, and xml:space="preserve" blocks. */ private _stripLineBreaks(options: XmlFormattingOptions, xml: string): string { let output = ""; const inTag = false; const inTagName = false; let inCdataOrComment = false; const inAttribute = false; let preserveSpace = false; let level = 0; let levelpreserveSpaceActivated = 0; for (let i = 0; i < xml.length; i++) { const char: string = xml.charAt(i); const prev: string = xml.charAt(i - 1); const next: string = xml.charAt(i + 1); // CDATA and comments if (char === "!" && (xml.substr(i, 8) === "![CDATA[" || xml.substr(i, 3) === "!--")) { inCdataOrComment = true; } else if (char === "]" && (xml.substr(i, 3) === "]]>")) { inCdataOrComment = false; } else if (char === "-" && (xml.substr(i, 3) === "-->")) { inCdataOrComment = false; } // xml:space="preserve" if (char === ">" && prev !== "/") { level++; } else if (!inCdataOrComment && char === "/" && (prev === "<" || next === ">")) { level--; } if (char === "x" && (xml.substr(i, 20).toLowerCase() === `xml:space="preserve"`)) { preserveSpace = true; levelpreserveSpaceActivated = level; } else if (!inCdataOrComment && preserveSpace && (char === "/" && (prev === "<" || next === ">")) && (level === levelpreserveSpaceActivated)) { preserveSpace = false; } if (char.search(/[\r\n]/g) > -1 && !inCdataOrComment && !preserveSpace) { if (/\r/.test(char) && /\S|\r|\n/.test(prev) && /\S|\r|\n/.test(xml.charAt(i + options.newLine.length))) { output += char; } else if (/\n/.test(char) && /\S|\r|\n/.test(xml.charAt(i - options.newLine.length)) && /\S|\r|\n/.test(next)) { output += char; } continue; } output += char; } return output; } } ================================================ FILE: src/formatting/formatters/index.ts ================================================ export * from "./classic-xml-formatter"; export * from "./v2-xml-formatter"; ================================================ FILE: src/formatting/formatters/v2-xml-formatter.ts ================================================ import { XmlFormatter } from "../xml-formatter"; import { XmlFormattingOptions } from "../xml-formatting-options"; import { ClassicXmlFormatter } from "./classic-xml-formatter"; const MagicalStringOfWonders = "~::~MAAAGIC~::~"; /* tslint:disable no-use-before-declare */ export class V2XmlFormatter implements XmlFormatter { formatXml(xml: string, options: XmlFormattingOptions): string { // this replaces all "<" brackets inside of comments and CDATA to a magical string // so the following minification steps don't mess with comment and CDATA formatting xml = this._sanitizeCommentsAndCDATA(xml); // remove whitespace from between tags, except for line breaks xml = xml.replace(/>\s{0,} { return match.replace(/[^\S\r\n]/g, ""); }); // do some light minification to get rid of insignificant whitespace xml = xml.replace(/"\s+(?=[^\s]+=)/g, "\" "); // spaces between attributes xml = xml.replace(/"\s+(?=>)/g, "\""); // spaces between the last attribute and tag close (>) xml = xml.replace(/"\s+(?=\/>)/g, "\" "); // spaces between the last attribute and tag close (/>) xml = xml.replace(/(?!="]\s+[^ <>="]+=(?![^<]*?\]\]>)/g, (match: string) => { // spaces between the node name and the first attribute return match.replace(/\s+/g, " "); }); // the coast is clear - we can drop those "<" brackets back in xml = this._unsanitizeCommentsAndCDATA(xml); let output = ""; let indentLevel = options.initialIndentLevel || 0; let attributeQuote = ""; let lineBreakSpree = false; let lastWordCharacter: string | undefined; let inMixedContent = false; const locationHistory: Location[] = [Location.Text]; function isLastNonTextLocation(loc: Location): boolean { for (let i = (locationHistory.length - 1); i >= 0; i--) { if (locationHistory[i] !== Location.Text) { return (loc === locationHistory[i]); } } return false; } function isLocation(loc: Location): boolean { return loc === locationHistory[locationHistory.length - 1]; } function refreshMixedContentFlag(): void { inMixedContent = (isLastNonTextLocation(Location.StartTag) || isLastNonTextLocation(Location.EndTag)) && lastWordCharacter !== undefined; } function setLocation(loc: Location): void { if (loc === Location.Text) { lastWordCharacter = undefined; } locationHistory.push(loc); } // NOTE: all "exiting" checks should appear after their associated "entering" checks for (let i = 0; i < xml.length; i++) { const cc = xml[i]; const nc = xml.charAt(i + 1); const nnc = xml.charAt(i + 2); const pc = xml.charAt(i - 1); const ppc = xml.charAt(i - 2); // entering CData if (isLocation(Location.Text) && cc === "<" && nc === "!" && nnc === "[") { if (pc === ">" && ppc !== "/") { output += "<"; } else { output += `${this._getIndent(options, indentLevel)}<`; } setLocation(Location.CData); } // exiting CData else if (isLocation(Location.CData) && cc === "]" && nc === "]" && nnc === ">") { output += "]]>"; i += 2; setLocation(Location.Text); } // entering Comment else if (isLocation(Location.Text) && cc === "<" && nc === "!" && nnc === "-") { output += `${this._getIndent(options, indentLevel)}<`; setLocation(Location.Comment); } // exiting Comment else if (isLocation(Location.Comment) && cc === "-" && nc === "-" && nnc === ">") { output += "-->"; i += 2; setLocation(Location.Text); } // entering SpecialTag else if (isLocation(Location.Text) && cc === "<" && (nc === "!" || nc === "?")) { output += `${this._getIndent(options, indentLevel)}<`; setLocation(Location.SpecialTag); } // exiting SpecialTag else if (isLocation(Location.SpecialTag) && cc === ">") { output += `>`; setLocation(Location.Text); } // entering StartTag.StartTagName else if (isLocation(Location.Text) && cc === "<" && ["/", "!"].indexOf(nc) === -1) { refreshMixedContentFlag(); // if this occurs after another tag, prepend a line break // but do not add one if the previous tag was self-closing (it already adds its own) if (pc === ">" && ppc !== "/" && !inMixedContent) { output += `${options.newLine}${this._getIndent(options, indentLevel)}<`; } else if (!inMixedContent) { // removing trailing non-breaking whitespace here prevents endless indentations (issue #193) output = this._removeTrailingNonBreakingWhitespace(output); output += `${this._getIndent(options, indentLevel)}<`; } else { output += "<"; indentLevel--; } indentLevel++; setLocation(Location.StartTagName); } // exiting StartTag.StartTagName, enter StartTag else if (isLocation(Location.StartTagName) && cc === " ") { output += " "; setLocation(Location.StartTag); } // entering StartTag.Attribute else if (isLocation(Location.StartTag) && [" ", "/", ">"].indexOf(cc) === -1) { if (locationHistory[locationHistory.length - 2] === Location.AttributeValue && ((options.splitXmlnsOnFormat && xml.substr(i, 5).toLowerCase() === "xmlns") || options.splitAttributesOnFormat)) { // trim the end of output here to ensure there is no trailing whitespace (issue #288) output = this._removeTrailingNonBreakingWhitespace(output); output += `${options.newLine}${this._getIndent(options, indentLevel)}`; } output += cc; setLocation(Location.Attribute); } // entering StartTag.Attribute.AttributeValue else if (isLocation(Location.Attribute) && (cc === "\"" || cc === "'")) { output += cc; setLocation(Location.AttributeValue); attributeQuote = cc; } // exiting StartTag.Attribute.AttributeValue, entering StartTag else if (isLocation(Location.AttributeValue) && cc === attributeQuote) { output += cc; setLocation(Location.StartTag); attributeQuote = undefined; } // approaching the end of a self-closing tag where there was no whitespace (issue #149) else if ((isLocation(Location.StartTag) || isLocation(Location.StartTagName)) && cc === "/" && pc !== " " && options.enforcePrettySelfClosingTagOnFormat) { output += " /"; } // exiting StartTag or StartTag.StartTagName, entering Text else if ((isLocation(Location.StartTag) || isLocation(Location.StartTagName)) && cc === ">") { // if this was a self-closing tag, we need to decrement the indent level and add a newLine if (pc === "/") { indentLevel--; output += ">"; // only add a newline here if one doesn't already exist (issue #147) if (nc !== "\r" && nc !== "\n") { output += options.newLine; } } else { output += ">"; } // don't go directly from StartTagName to Text; go through StartTag first if (isLocation(Location.StartTagName)) { setLocation(Location.StartTag); } setLocation(Location.Text); } // entering EndTag else if (isLocation(Location.Text) && cc === "<" && nc === "/") { if (!inMixedContent) { indentLevel--; } refreshMixedContentFlag(); // if the end tag immediately follows a line break, just add an indentation // if the end tag immediately follows another end tag or a self-closing tag (issue #185), add a line break and indent // otherwise, this should be treated as a same-line end tag(ex. text) if ((pc === "\n" || lineBreakSpree) && !inMixedContent) { // removing trailing non-breaking whitespace here prevents endless indentations (issue #193) output = this._removeTrailingNonBreakingWhitespace(output); output += `${this._getIndent(options, indentLevel)}<`; lineBreakSpree = false; } else if (isLastNonTextLocation(Location.EndTag) && !inMixedContent) { output += `${options.newLine}${this._getIndent(options, indentLevel)}<`; } else if (pc === ">" && ppc === "/" && !inMixedContent) { output += `${this._getIndent(options, indentLevel)}<`; } else { output += "<"; } setLocation(Location.EndTag); } // exiting EndTag, entering Text else if (isLocation(Location.EndTag) && cc === ">") { output += ">"; setLocation(Location.Text); inMixedContent = false; } // Text else { if (cc === "\n") { lineBreakSpree = true; lastWordCharacter = undefined; } else if (lineBreakSpree && /\S/.test(cc)) { lineBreakSpree = false; } if (/[\w\d]/.test(cc)) { lastWordCharacter = cc; } output += cc; } } return output; } minifyXml(xml: string, options: XmlFormattingOptions): string { return new ClassicXmlFormatter().minifyXml(xml, options); } private _getIndent(options: XmlFormattingOptions, indentLevel: number): string { return ((options.editorOptions.insertSpaces) ? " ".repeat(options.editorOptions.tabSize) : "\t").repeat(Math.max(indentLevel, 0)); } private _removeTrailingNonBreakingWhitespace(text: string): string { return text.replace(/[^\r\n\S]+$/, ""); } private _sanitizeCommentsAndCDATA(xml: string): string { let output = ""; let inCommentOrCDATA = false; for (let i = 0; i < xml.length; i++) { const cc = xml[i]; const nc = xml.charAt(i + 1); const nnc = xml.charAt(i + 2); const pc = xml.charAt(i - 1); if (!inCommentOrCDATA && cc === "<" && nc === "!" && (nnc === "-" || nnc === "[")) { inCommentOrCDATA = true; output += (nnc === "-") ? "" : "]]>"; i += 2; } else { output += cc; } } return output; } private _unsanitizeCommentsAndCDATA(xml: string): string { return xml.replace(new RegExp(MagicalStringOfWonders, "g"), "<"); } } enum Location { Attribute, AttributeValue, CData, Comment, EndTag, SpecialTag, StartTag, StartTagName, Text } ================================================ FILE: src/formatting/index.ts ================================================ export * from "./xml-formatter"; export * from "./xml-formatting-edit-provider"; export * from "./xml-formatting-options"; ================================================ FILE: src/formatting/xml-formatter.ts ================================================ import { window, workspace } from "vscode"; import { Configuration, ExtensionState } from "../common"; import * as constants from "../constants"; import { ClassicXmlFormatter } from "./formatters/classic-xml-formatter"; import { V2XmlFormatter } from "./formatters/v2-xml-formatter"; import { XmlFormattingOptions } from "./xml-formatting-options"; export interface XmlFormatter { formatXml(xml: string, options: XmlFormattingOptions): string; minifyXml(xml: string, options: XmlFormattingOptions): string; } export class XmlFormatterFactory { private static _xmlFormatter: XmlFormatter; static getXmlFormatter(): XmlFormatter { if (XmlFormatterFactory._xmlFormatter) { return XmlFormatterFactory._xmlFormatter; } const xmlFormatterImplementationSetting = Configuration.xmlFormatterImplementation; let xmlFormatterImplementation: XmlFormatter; switch (xmlFormatterImplementationSetting) { case constants.xmlFormatterImplementations.classic: xmlFormatterImplementation = new ClassicXmlFormatter(); break; case constants.xmlFormatterImplementations.v2: default: xmlFormatterImplementation = new V2XmlFormatter(); break; } return (XmlFormatterFactory._xmlFormatter = xmlFormatterImplementation); } } ================================================ FILE: src/formatting/xml-formatting-edit-provider.ts ================================================ import { workspace } from "vscode"; import { CancellationToken, DocumentFormattingEditProvider, DocumentRangeFormattingEditProvider, EndOfLine, FormattingOptions, ProviderResult, Range, TextDocument, TextEdit } from "vscode"; import * as constants from "../constants"; import { XmlFormatter } from "./xml-formatter"; import { XmlFormattingOptionsFactory } from "./xml-formatting-options"; export class XmlFormattingEditProvider implements DocumentFormattingEditProvider, DocumentRangeFormattingEditProvider { constructor( public xmlFormatter: XmlFormatter ) { } provideDocumentFormattingEdits(document: TextDocument, options: FormattingOptions, token: CancellationToken): ProviderResult { const lastLine = document.lineAt(document.lineCount - 1); const documentRange = new Range(document.positionAt(0), lastLine.range.end); return this.provideDocumentRangeFormattingEdits(document, documentRange, options, token); } provideDocumentRangeFormattingEdits(document: TextDocument, range: Range, options: FormattingOptions, token: CancellationToken): ProviderResult { const allXml = document.getText(); let selectedXml = document.getText(range); const extFormattingOptions = XmlFormattingOptionsFactory.getXmlFormattingOptions(options, document); const selectionStartOffset = document.offsetAt(range.start); let tabCount = 0; let spaceCount = 0; for (let i = (selectionStartOffset - 1); i >= 0; i--) { const cc = allXml.charAt(i); if (/\t/.test(cc)) { tabCount++; } else if (/ /.test(cc)) { spaceCount++; } else { break; } } if (options.insertSpaces) { extFormattingOptions.initialIndentLevel = Math.ceil(spaceCount / (options.tabSize || 1)); } else { extFormattingOptions.initialIndentLevel = tabCount; } selectedXml = this.xmlFormatter.formatXml(selectedXml, extFormattingOptions); // we need to remove the leading whitespace because the formatter will add an indent before the first element selectedXml = selectedXml.replace(/^\s+/, ""); return [TextEdit.replace(range, selectedXml)]; } } ================================================ FILE: src/formatting/xml-formatting-options.ts ================================================ import { EndOfLine, FormattingOptions, TextDocument } from "vscode"; import { Configuration } from "../common"; import * as constants from "../constants"; export interface XmlFormattingOptions { editorOptions: FormattingOptions; enforcePrettySelfClosingTagOnFormat: boolean; newLine: string; removeCommentsOnMinify: boolean; splitAttributesOnFormat: boolean; splitXmlnsOnFormat: boolean; initialIndentLevel?: number; } export class XmlFormattingOptionsFactory { static getXmlFormattingOptions(formattingOptions: FormattingOptions, document: TextDocument): XmlFormattingOptions { return { editorOptions: formattingOptions, enforcePrettySelfClosingTagOnFormat: Configuration.enforcePrettySelfClosingTagOnFormat(document.uri), newLine: (document.eol === EndOfLine.CRLF) ? "\r\n" : "\n", removeCommentsOnMinify: Configuration.removeCommentsOnMinify(document.uri), splitAttributesOnFormat: Configuration.splitAttributesOnFormat(document.uri), splitXmlnsOnFormat: Configuration.splitXmlnsOnFormat(document.uri), initialIndentLevel: 0 }; } } ================================================ FILE: src/linting/index.ts ================================================ export * from "./xquery-linter"; ================================================ FILE: src/linting/xquery-linter.ts ================================================ import { Diagnostic, DiagnosticSeverity, Position, Range } from "vscode"; const XQLint = require("xqlint").XQLint; export class XQueryLinter { static SEVERITY_WARNING = 1; static SEVERITY_ERROR = 2; lint(text: string): Diagnostic[] { const linter = new XQLint(text); const diagnostics = new Array(); linter.getErrors().forEach((error: any) => { diagnostics.push(new Diagnostic( new Range( new Position(error.pos.sl, error.pos.sc), new Position(error.pos.el, error.pos.ec) ), error.message, DiagnosticSeverity.Error )); }); linter.getWarnings().forEach((warning: any) => { diagnostics.push(new Diagnostic( new Range( new Position(warning.pos.sl, warning.pos.sc), new Position(warning.pos.el, warning.pos.ec) ), warning.message, DiagnosticSeverity.Warning )); }); return diagnostics; } } ================================================ FILE: src/test/extension.test.ts ================================================ import * as assert from "assert"; import { FormattingOptions } from "vscode"; import { TestDataLoader } from "./test-utils/test-data-loader"; import { XmlFormatter, XmlFormattingOptions } from "../formatting"; import { V2XmlFormatter } from "../formatting/formatters"; describe("V2XmlFormatter", () => { const xmlFormatter = new V2XmlFormatter(); describe("#formatXml(xml, options)", () => { const options = { editorOptions: { insertSpaces: true, tabSize: 4 }, enforcePrettySelfClosingTagOnFormat: false, newLine: "\r\n", removeCommentsOnMinify: false, splitAttributesOnFormat: false, splitXmlnsOnFormat: true }; it("should handle basic XML", () => { testFormatter(xmlFormatter, options, "basic"); }); it("should handle unicode element names", () => { testFormatter(xmlFormatter, options, "unicode"); }); it("should handle self-closing elements", () => { testFormatter(xmlFormatter, options, "self-closing"); }); it("should handle text-only lines", () => { testFormatter(xmlFormatter, options, "text-only-line"); }); it("should handle preformatted xml", () => { testFormatter(xmlFormatter, options, "preformatted"); }); it("should preserve line breaks between elements", () => { testFormatter(xmlFormatter, options, "preserve-breaks"); }); it("should maintain comment formatting", () => { testFormatter(xmlFormatter, options, "maintain-comment-formatting"); }); it("should handle single-quotes in attributes", () => { testFormatter(xmlFormatter, options, "single-quotes"); }); it("should not add extra line breaks before start tags", () => { testFormatter(xmlFormatter, options, "issue-178"); }); it("should allow users to enforce space before self-closing tag slash", () => { options.enforcePrettySelfClosingTagOnFormat = true; testFormatter(xmlFormatter, options, "issue-149"); options.enforcePrettySelfClosingTagOnFormat = false; }); it("should properly format closing tag after self-closing tag", () => { testFormatter(xmlFormatter, options, "issue-185"); }); it("should support single quotes within double-quoptes attributes and vice-versa", () => { testFormatter(xmlFormatter, options, "issue-187"); }); it("should not ruin attributes with unusual characters", () => { testFormatter(xmlFormatter, options, "issue-189"); }); it("should not add extra line breaks before closing tags", () => { testFormatter(xmlFormatter, options, "issue-193"); }); it("should not add extra whitespace before CDATA", () => { testFormatter(xmlFormatter, options, "issue-194"); }); it("should support mixed content", () => { testFormatter(xmlFormatter, options, "issue-200"); }); it("should not remove spaces between the node name and the first attribute within CDATA", () => { testFormatter(xmlFormatter, options, "issue-227"); }); it("should handle mixed content as a child of another element", () => { testFormatter(xmlFormatter, options, "issue-257"); }); it("should not touch CDATA content", () => { testFormatter(xmlFormatter, options, "issue-293"); }); it("should not add trailing whitespace", () => { testFormatter(xmlFormatter, options, "issue-288"); }); }); describe("#minifyXml(xml, options)", () => { const options = { editorOptions: { insertSpaces: true, tabSize: 4 }, enforcePrettySelfClosingTagOnFormat: false, newLine: "\r\n", removeCommentsOnMinify: false, splitAttributesOnFormat: false, splitXmlnsOnFormat: true }; it("should preserve whitespace on minify if xml:space is set to 'preserve-whitespace'", () => { testMinifier(xmlFormatter, options, "issue-262"); }); }); }); function testFormatter(xmlFormatter: XmlFormatter, options: XmlFormattingOptions, fileLabel: string): void { const expectedFormattedXml = TestDataLoader.load(`${fileLabel}.formatted.xml`).replace(/\r/g, ""); const unformattedXml = TestDataLoader.load(`${fileLabel}.unformatted.xml`); const actualFormattedXml = xmlFormatter.formatXml(unformattedXml, options).replace(/\r/g, ""); // tslint:disable-next-line assert.ok((actualFormattedXml === expectedFormattedXml), `Actual formatted XML does not match expected formatted XML.\n\nACTUAL\n${actualFormattedXml.replace(/\s/g, "~ws~")}\n\nEXPECTED\n${expectedFormattedXml.replace(/\s/g, "~ws~")}`); } function testMinifier(xmlFormatter: XmlFormatter, options: XmlFormattingOptions, fileLabel: string): void { const expectedMinifiedXml = TestDataLoader.load(`${fileLabel}.minified.xml`).replace(/\r/g, ""); const unminifiedXml = TestDataLoader.load(`${fileLabel}.unminified.xml`); const actualMinifiedXml = xmlFormatter.minifyXml(unminifiedXml, options).replace(/\r/g, ""); // tslint:disable-next-line assert.ok((actualMinifiedXml === expectedMinifiedXml), `Actual minified XML does not match expected minified XML.\n\nACTUAL\n${actualMinifiedXml.replace(/\s/g, "~ws~")}\n\nEXPECTED\n${expectedMinifiedXml.replace(/\s/g, "~ws~")}`); } ================================================ FILE: src/test/test-data/basic.formatted.xml ================================================ text ================================================ FILE: src/test/test-data/basic.unformatted.xml ================================================ text ================================================ FILE: src/test/test-data/issue-149.formatted.xml ================================================ One Three Five ================================================ FILE: src/test/test-data/issue-149.unformatted.xml ================================================ OneThreeFive ================================================ FILE: src/test/test-data/issue-178.formatted.xml ================================================ One Three Five ================================================ FILE: src/test/test-data/issue-178.unformatted.xml ================================================ OneThreeFive ================================================ FILE: src/test/test-data/issue-185.formatted.xml ================================================ ================================================ FILE: src/test/test-data/issue-185.unformatted.xml ================================================ ================================================ FILE: src/test/test-data/issue-187.formatted.xml ================================================ Debug latest false None true ================================================ FILE: src/test/test-data/issue-187.unformatted.xml ================================================ Debug latest false None true ================================================ FILE: src/test/test-data/issue-189.formatted.xml ================================================ ================================================ FILE: src/test/test-data/issue-189.unformatted.xml ================================================ ================================================ FILE: src/test/test-data/issue-193.formatted.xml ================================================ ================================================ FILE: src/test/test-data/issue-193.unformatted.xml ================================================ ================================================ FILE: src/test/test-data/issue-194.formatted.xml ================================================ This is ok ================================================ FILE: src/test/test-data/issue-194.unformatted.xml ================================================ This is ok ================================================ FILE: src/test/test-data/issue-200.formatted.xml ================================================ beginning text data another data end text ================================================ FILE: src/test/test-data/issue-200.unformatted.xml ================================================ beginning textdataanother dataend text ================================================ FILE: src/test/test-data/issue-227.formatted.xml ================================================ ================================================ FILE: src/test/test-data/issue-227.unformatted.xml ================================================ ================================================ FILE: src/test/test-data/issue-257.formatted.xml ================================================ WARNING: Unmatched element: ================================================ FILE: src/test/test-data/issue-257.unformatted.xml ================================================ WARNING: Unmatched element: ================================================ FILE: src/test/test-data/issue-262.minified.xml ================================================ 1. 2. 3. 4.1. 2. 3. 4.1. 2. 3. 4.1. 2. 3. 4. ================================================ FILE: src/test/test-data/issue-262.unminified.xml ================================================ 1. 2. 3. 4. 1. 2. 3. 4. 1. 2. 3. 4. 1. 2. 3. 4. ================================================ FILE: src/test/test-data/issue-288.formatted.xml ================================================ ================================================ FILE: src/test/test-data/issue-288.unformatted.xml ================================================ ================================================ FILE: src/test/test-data/issue-293.formatted.xml ================================================ val ]]> ================================================ FILE: src/test/test-data/issue-293.unformatted.xml ================================================ val ]]> ================================================ FILE: src/test/test-data/maintain-comment-formatting.formatted.xml ================================================ text ================================================ FILE: src/test/test-data/maintain-comment-formatting.unformatted.xml ================================================ text ================================================ FILE: src/test/test-data/preformatted.formatted.xml ================================================ text text text text text ================================================ FILE: src/test/test-data/preformatted.unformatted.xml ================================================ text text text text text ================================================ FILE: src/test/test-data/preserve-breaks.formatted.xml ================================================ text text text text text ================================================ FILE: src/test/test-data/preserve-breaks.unformatted.xml ================================================ text text text text text ================================================ FILE: src/test/test-data/self-closing.formatted.xml ================================================ ================================================ FILE: src/test/test-data/self-closing.unformatted.xml ================================================ ================================================ FILE: src/test/test-data/single-quotes.formatted.xml ================================================ ================================================ FILE: src/test/test-data/single-quotes.unformatted.xml ================================================ ================================================ FILE: src/test/test-data/text-only-line.formatted.xml ================================================ Text1 Text2 ================================================ FILE: src/test/test-data/text-only-line.unformatted.xml ================================================ Text1 Text2 ================================================ FILE: src/test/test-data/unicode.formatted.xml ================================================ <Имя> text ================================================ FILE: src/test/test-data/unicode.unformatted.xml ================================================ <Имя>text ================================================ FILE: src/test/test-utils/test-data-loader.ts ================================================ import * as fs from "fs"; export class TestDataLoader { static load(fileName: string): string { return fs.readFileSync(`${__dirname}/../../../src/test/test-data/${fileName}`, "UTF-8"); } } ================================================ FILE: src/tree-view/index.ts ================================================ export * from "./xml-tree-data-provider"; ================================================ FILE: src/tree-view/xml-tree-data-provider.ts ================================================ import { window, workspace } from "vscode"; import { Event, EventEmitter, ExtensionContext, Position, TextEditor, TreeDataProvider, TreeItem, TreeItemCollapsibleState } from "vscode"; import * as path from "path"; import { DOMParser } from "xmldom"; import { Configuration, NativeCommands, XmlTraverser } from "../common"; import * as constants from "../constants"; export class XmlTreeDataProvider implements TreeDataProvider { private _onDidChangeTreeData: EventEmitter = new EventEmitter(); private _xmlDocument: Document; private _xmlTraverser: XmlTraverser; constructor(private _context: ExtensionContext) { window.onDidChangeActiveTextEditor(() => { this._refreshTree(); }); workspace.onDidChangeTextDocument(() => { this._refreshTree(); }); this._refreshTree(); } onDidChangeTreeData = this._onDidChangeTreeData.event; get activeEditor(): TextEditor { return window.activeTextEditor || null; } getTreeItem(element: Node): TreeItem | Thenable { const enableMetadata = Configuration.enableXmlTreeViewMetadata; const enableSync = Configuration.enableXmlTreeViewCursorSync; const treeItem = new TreeItem(element.localName); if (!this._xmlTraverser.isElement(element)) { treeItem.label = `${element.localName} = "${element.nodeValue}"`; } else if (enableMetadata) { const childAttributes = this._xmlTraverser.getChildAttributeArray(element); const childElements = this._xmlTraverser.getChildElementArray(element); const totalChildren = (childAttributes.length + childElements.length); if (totalChildren > 0) { treeItem.label += " ("; if (childAttributes.length > 0) { treeItem.label += `attributes: ${childAttributes.length}, `; treeItem.collapsibleState = TreeItemCollapsibleState.Collapsed; } if (childElements.length > 0) { treeItem.label += `children: ${childElements.length}, `; treeItem.collapsibleState = TreeItemCollapsibleState.Collapsed; } treeItem.label = treeItem.label.substr(0, treeItem.label.length - 2); treeItem.label += ")"; } if (this._xmlTraverser.hasSimilarSiblings(element) && enableSync) { treeItem.label += ` [line ${(element as any).lineNumber}]`; } } treeItem.command = { command: constants.nativeCommands.revealLine, title: "", arguments: [{ lineNumber: (element as any).lineNumber - 1, at: "top" }] }; treeItem.iconPath = this._getIcon(element); return treeItem; } getChildren(element?: Node): Node[] | Thenable { if (!this._xmlDocument) { this._refreshTree(); } if (this._xmlTraverser.isElement(element)) { return [].concat(this._xmlTraverser.getChildAttributeArray(element), this._xmlTraverser.getChildElementArray(element)); } else if (this._xmlDocument) { return [this._xmlDocument.lastChild]; } else { return []; } } getParent(element: Node): Node { if ((!element || !element.parentNode || !element.parentNode.parentNode) && !(element as any).ownerElement) { return undefined; } return element.parentNode || (element as any).ownerElement; } getNodeAtPosition(position: Position): Node { return this._xmlTraverser.getNodeAtPosition(position); } private _getIcon(element: Node): any { let type = "element"; if (!this._xmlTraverser.isElement(element)) { type = "attribute"; } const 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 !== constants.languageIds.xml) { NativeCommands.setContext(constants.contextKeys.xmlTreeViewEnabled, false); this._xmlDocument = null; this._onDidChangeTreeData.fire(); return; } const enableTreeView = Configuration.enableXmlTreeView; NativeCommands.setContext(constants.contextKeys.xmlTreeViewEnabled, enableTreeView); const xml = this.activeEditor.document.getText(); try { this._xmlDocument = new DOMParser({ errorHandler: () => { throw new Error("Invalid Document"); }, locator: {} }).parseFromString(xml, "text/xml"); } catch { this._xmlDocument = new DOMParser().parseFromString("", "text/xml"); } finally { this._xmlTraverser = this._xmlTraverser || new XmlTraverser(this._xmlDocument); this._xmlTraverser.xmlDocument = this._xmlDocument; } this._onDidChangeTreeData.fire(); } } ================================================ FILE: src/xpath/commands/evaluateXPath.ts ================================================ import { window } from "vscode"; import { TextEditor, TextEditorEdit, ViewColumn } from "vscode"; import { Configuration, ExtensionState } from "../../common"; import * as constants from "../../constants"; import { EvaluatorResult, EvaluatorResultType, XPathEvaluator } from "../xpath-evaluator"; class HistoricQuery { constructor(uri: string, query: string) { this.uri = uri; this.query = query; } uri: string; query: string; } export async function evaluateXPath(editor: TextEditor, edit: TextEditorEdit): Promise { // if there is no workspace, we will track queries in the global Memento const memento = ExtensionState.workspace || ExtensionState.global; // get the xpath persistence setting const persistQueries = Configuration.persistXPathQuery; // get the last query if there is one for this document // if not, try pulling the last query ran, regardless of document // NOTE: if the user has focus on the output channel when opening the xquery prompt, the channel is the "active" document const history = memento.get(constants.stateKeys.xpathQueryHistory, new Array()); const globalLastQuery = memento.get(constants.stateKeys.xPathQueryLast, ""); const lastQuery = history.find(x => { return (x.uri === editor.document.uri.toString()); }); // set the inital display value and prompt the user let query = (lastQuery) ? lastQuery.query : globalLastQuery; query = await window.showInputBox({ placeHolder: "XPath Query", prompt: "Please enter an XPath query to evaluate.", value: query }); // showInputBox() will return undefined if the user dimissed the prompt if (!query) { return; } const ignoreDefaultNamespace = Configuration.ignoreDefaultNamespace; // run the query const xml = editor.document.getText(); let evalResult: EvaluatorResult; try { evalResult = XPathEvaluator.evaluate(query, xml, ignoreDefaultNamespace); } catch (error) { console.error(error); window.showErrorMessage(`Something went wrong while evaluating the XPath: ${error}`); return; } // show the results to the user const outputChannel = window.createOutputChannel("XPath Results"); outputChannel.clear(); outputChannel.appendLine(`XPath Query: ${query}`); outputChannel.append("\n"); if (evalResult.type === EvaluatorResultType.NODE_COLLECTION) { (evalResult.result as Node[]).forEach((node: any) => { outputChannel.appendLine(`[Line ${node.lineNumber}] ${node.localName}: ${node.textContent}`); }); } else { outputChannel.appendLine(`[Result]: ${evalResult.result}`); } outputChannel.show(false); if (persistQueries) { const historicQuery = new HistoricQuery(editor.document.uri.toString(), query); const affectedIndex = history.findIndex(x => x.uri === historicQuery.uri); if (affectedIndex === -1) { history.push(historicQuery); } else { history[affectedIndex].query = query; } memento.update(constants.stateKeys.xpathQueryHistory, history); memento.update(constants.stateKeys.xPathQueryLast, query); } } ================================================ FILE: src/xpath/commands/getCurrentXPath.ts ================================================ import { window } from "vscode"; import { TextEditor, TextEditorEdit } from "vscode"; import { DOMParser } from "xmldom"; import { XPathBuilder } from "../xpath-builder"; export function getCurrentXPath(editor: TextEditor, edit: TextEditorEdit): void { if (!editor.selection) { window.showInformationMessage("Please put your cursor in an element or attribute name."); return; } const document = new DOMParser().parseFromString(editor.document.getText()); const xpath = new XPathBuilder(document).build(editor.selection.start); window.showInputBox({ value: xpath, valueSelection: undefined }); } ================================================ FILE: src/xpath/commands/index.ts ================================================ export * from "./evaluateXPath"; export * from "./getCurrentXPath"; ================================================ FILE: src/xpath/index.ts ================================================ export * from "./xpath-builder"; export * from "./xpath-evaluator"; ================================================ FILE: src/xpath/xpath-builder.ts ================================================ import { Position } from "vscode"; import { DOMParser } from "xmldom"; import { XmlTraverser } from "../common"; export class XPathBuilder { private _xmlTraverser: XmlTraverser; constructor(private _xmlDocument: Document) { this._xmlTraverser = new XmlTraverser(this._xmlDocument); } build(position: Position): string { const selectedNode = this._xmlTraverser.getNodeAtPosition(position); return this._buildCore(selectedNode); } private _buildCore(selectedNode: Node): string { if (selectedNode === this._xmlDocument.documentElement) { return `/${selectedNode.nodeName}`; } if (!this._xmlTraverser.isElement(selectedNode)) { return `${this._buildCore((selectedNode as any).ownerElement)}/@${selectedNode.nodeName}`; } else if (this._xmlTraverser.hasSimilarSiblings(selectedNode)) { const siblings = this._xmlTraverser.getSiblings(selectedNode); const xPathIndex = (siblings.indexOf(selectedNode) + 1); return `${this._buildCore(selectedNode.parentNode)}/${selectedNode.nodeName}[${xPathIndex}]`; } else { return `${this._buildCore(selectedNode.parentNode)}/${selectedNode.nodeName}`; } } } ================================================ FILE: src/xpath/xpath-evaluator.ts ================================================ import * as xpath from "xpath"; import { SelectedValue, XPathSelect } from "xpath"; import { DOMParser } from "xmldom"; export class EvaluatorResult { type: EvaluatorResultType; result: Node[] | number | string | boolean; } export class EvaluatorResultType { static SCALAR_TYPE = 0; static NODE_COLLECTION = 1; } export class XPathResultTypes { static ANY_TYPE = 0; static NUMBER_TYPE = 1; static STRING_TYPE = 2; static BOOLEAN_TYPE = 3; static UNORDERED_NODE_ITERATOR_TYPE = 4; static ORDERED_NODE_ITERATOR_TYPE = 5; static UNORDERED_NODE_SNAPSHOT_TYPE = 6; static ORDERED_NODE_SNAPSHOT_TYPE = 7; static ANY_UNORDERED_NODE_TYPE = 8; static FIRST_ORDERED_NODE_TYPE = 9; } export class XPathEvaluator { static evaluate(query: string, xml: string, ignoreDefaultNamespace: boolean): EvaluatorResult { if (ignoreDefaultNamespace) { xml = xml.replace(/xmlns=".+"/g, (match: string) => { return match.replace(/xmlns/g, "xmlns:default"); }); } const nodes = new Array(); const xdoc: Document = new DOMParser().parseFromString(xml, "text/xml"); const resolver = (xpath as any).createNSResolver(xdoc); const xPathResult = xpath.evaluate(query, xdoc, resolver, 0, null); const evaluatorResult = new EvaluatorResult(); evaluatorResult.type = EvaluatorResultType.SCALAR_TYPE; switch (xPathResult.resultType) { case XPathResultTypes.NUMBER_TYPE: evaluatorResult.result = xPathResult.numberValue; break; case XPathResultTypes.STRING_TYPE: evaluatorResult.result = xPathResult.stringValue; break; case XPathResultTypes.BOOLEAN_TYPE: evaluatorResult.result = xPathResult.booleanValue; break; case XPathResultTypes.UNORDERED_NODE_ITERATOR_TYPE: case XPathResultTypes.ORDERED_NODE_ITERATOR_TYPE: evaluatorResult.result = xPathResult.booleanValue; let node: Node; while (node = xPathResult.iterateNext()) { nodes.push(node); } evaluatorResult.result = nodes; evaluatorResult.type = EvaluatorResultType.NODE_COLLECTION; break; } return evaluatorResult; } } ================================================ FILE: src/xquery-execution/child-process.ts ================================================ const child_process = require("child_process"); export class ChildProcess { static async spawn(executable: string, args: string[]): Promise { return new Promise((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 }); } }); }); } } ================================================ FILE: src/xquery-execution/commands/executeXQuery.ts ================================================ import { window, workspace } from "vscode"; import { Disposable, Range, TextEditor, TextEditorEdit, Uri } from "vscode"; import * as constants from "../../constants"; import { ChildProcess } from "../child-process"; import { Configuration, NativeCommands } from "../../common"; export async function executeXQuery(editor: TextEditor, edit: TextEditorEdit): Promise { // 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 = Configuration.xqueryExecutionEngine; let args = Configuration.xqueryExecutionArguments || []; if (!executable || executable === "") { const action = await window.showWarningMessage("An XQuery execution engine has not been defined.", "Define Now"); if (action === "Define Now") { NativeCommands.openGlobalSettings(); } return; } let inputFile: Uri; disposable = window.setStatusBarMessage("Searching for XML files in folder..."); const searchPattern = Configuration.xqueryExecutionInputSearchPattern; const inputLimit = Configuration.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(); 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].search(/out|result/) !== -1) { 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((value: string) => { return value .replace("$(script)", editor.document.uri.fsPath) .replace("$(input)", inputFile.fsPath) .replace("$(project)", (workspace.workspaceFolders) ? workspace.workspaceFolders[0].uri.fsPath : ""); }); 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(); } } ================================================ FILE: src/xquery-execution/commands/index.ts ================================================ export * from "./executeXQuery"; ================================================ FILE: src/xquery-execution/index.ts ================================================ export * from "./child-process"; ================================================ FILE: tsconfig.json ================================================ { "compilerOptions": { "module": "commonjs", "target": "es6", "outDir": "out", "lib": [ "dom", "es6" ], "sourceMap": true, "rootDir": "src", "strict": true, "strictNullChecks": false }, "exclude": [ "node_modules", ".vscode-test" ] } ================================================ FILE: tslint.json ================================================ { "rules": { "arrow-return-shorthand": true, "callable-types": true, "class-name": true, "comment-format": [ true, "check-space" ], "curly": true, "deprecation": { "severity": "warn" }, "eofline": true, "forin": true, "import-blacklist": [ true, "rxjs", "rxjs/Rx" ], "import-spacing": true, "indent": [ true, "spaces", 4 ], "interface-over-type-literal": true, "label-position": true, "max-line-length": [ true, 165 ], "member-access": false, "member-ordering": [ true, { "order": [ "static-field", "instance-field", "static-method", "instance-method" ] } ], "no-arg": true, "no-bitwise": true, "no-console": [ true, "debug", "info", "time", "timeEnd", "trace" ], "no-construct": true, "no-debugger": true, "no-duplicate-super": true, "no-empty": false, "no-empty-interface": true, "no-eval": true, "no-inferrable-types": [ true, "ignore-params" ], "no-misused-new": true, "no-non-null-assertion": true, "no-shadowed-variable": true, "no-string-literal": false, "no-string-throw": true, "no-switch-case-fall-through": true, "no-trailing-whitespace": true, "no-unnecessary-initializer": true, "no-unused-expression": true, "no-use-before-declare": true, "no-var-keyword": true, "object-literal-sort-keys": false, "one-line": [ true, "check-open-brace", "check-whitespace" ], "prefer-const": true, "quotemark": [ true, "double" ], "radix": true, "semicolon": [ true, "always" ], "triple-equals": [ true, "allow-null-check" ], "typedef-whitespace": [ true, { "call-signature": "nospace", "index-signature": "nospace", "parameter": "nospace", "property-declaration": "nospace", "variable-declaration": "nospace" } ], "unified-signatures": true, "variable-name": false, "whitespace": [ true, "check-branch", "check-decl", "check-operator", "check-separator", "check-type" ] } }