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
[](https://marketplace.visualstudio.com/items?itemName=DotJoshJohnson.xml)
[](https://marketplace.visualstudio.com/items?itemName=DotJoshJohnson.xml)
[](https://twitter.com/DotJoshJohnson)
[](https://gitter.im/vscode-xml/vscode-xml)
[](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"
]
}
}