diff --git a/package.json b/package.json index ca89fab..b491c20 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "xml", "displayName": "XML Tools", "description": "XML Formatting, XQuery, and XPath Tools for Visual Studio Code", - "version": "2.5.1", + "version": "2.6.0", "preview": false, "publisher": "DotJoshJohnson", "author": { @@ -72,6 +72,10 @@ { "command": "xmlTools.minifyXml", "title": "XML Tools: Minify XML" + }, + { + "command": "xmlTools.minifyXmlSelection", + "title": "XML Tools: Minify XML (Selection)" } ], "configuration": { @@ -159,7 +163,7 @@ "type": "string", "default": "", "description": "The full path to the executable to run when executing XQuery scripts.", - "scope": "window" + "scope": "machine" }, "xmlTools.xqueryExecutionInputLimit": { "type": "integer", @@ -172,6 +176,12 @@ "default": "**/*.xml", "description": "The pattern used to search for input XML files when executing XQuery scripts.", "scope": "window" + }, + "xmlTools.preserveSpacesBetweenAttributes": { + "type": "boolean", + "default": false, + "description": "Preserves any spaces between attributes during formatting.", + "scope": "resource" } } }, @@ -245,6 +255,16 @@ "command": "xmlTools.minifyXml", "group": "1_modification@100", "when": "editorLangId == 'xml'" + }, + { + "command": "xmlTools.minifyXmlSelection", + "group": "1_modification@100", + "when": "editorLangId == 'xml' && editorHasSelection" + }, + { + "command": "xmlTools.getCurrentXPath", + "group": "z_commands", + "when": "editorLangId == 'xml'" } ] }, @@ -280,4 +300,4 @@ "xpath": "0.0.27", "xqlint": "^0.4.1" } -} +} \ No newline at end of file diff --git a/src/common/configuration.ts b/src/common/configuration.ts index b1461d0..2d6dffc 100644 --- a/src/common/configuration.ts +++ b/src/common/configuration.ts @@ -59,11 +59,15 @@ export class Configuration { return this._getForResource("splitXmlnsOnFormat", resource); } + static preserveSpacesBetweenAttributes(resource: Uri): boolean { + return this._getForResource("preserveSpacesBetweenAttributes", resource); + } + private static _getForResource(section: string, resource: Uri): T { return workspace.getConfiguration(ExtensionTopLevelSection, resource).get(section); } - private static _getForWindow(section: string): T { + private static _getForWindow(section: string): T { return workspace.getConfiguration(ExtensionTopLevelSection).get(section); } } diff --git a/src/constants.ts b/src/constants.ts index 07d969e..81ebbd0 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -6,6 +6,7 @@ export namespace commands { export const textToXml = "xmlTools.textToXml"; export const getCurrentXPath = "xmlTools.getCurrentXPath"; export const minifyXml = "xmlTools.minifyXml"; + export const minifyXmlSelection = "xmlTools.minifyXmlSelection"; } export namespace contextKeys { diff --git a/src/extension.ts b/src/extension.ts index 17b41b2..e91b4c5 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,12 +1,12 @@ import { commands, languages, window, workspace, ExtensionContext, Memento, TextEditor, TextEditorSelectionChangeEvent, TextEditorSelectionChangeKind, DiagnosticCollection - } from "vscode"; +} 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 { formatAsXml, minifyXml, xmlToText, textToXml, minifyXmlSelection } from "./formatting/commands"; import { XQueryLinter } from "./linting"; import { XmlTreeDataProvider } from "./tree-view"; import { evaluateXPath, getCurrentXPath } from "./xpath/commands"; @@ -35,6 +35,7 @@ export function activate(context: ExtensionContext) { commands.registerTextEditorCommand(constants.commands.xmlToText, xmlToText), commands.registerTextEditorCommand(constants.commands.textToXml, textToXml), commands.registerTextEditorCommand(constants.commands.minifyXml, minifyXml), + commands.registerTextEditorCommand(constants.commands.minifyXmlSelection, minifyXmlSelection), languages.registerDocumentFormattingEditProvider(xmlXsdDocSelector, xmlFormattingEditProvider), languages.registerDocumentRangeFormattingEditProvider(xmlXsdDocSelector, xmlFormattingEditProvider) ); @@ -91,7 +92,7 @@ function _handleContextChange(editor: TextEditor): void { switch (editor.document.languageId) { case constants.languageIds.xquery: - diagnosticCollectionXQuery.set(editor.document.uri, new XQueryLinter().lint(editor.document.getText())); + diagnosticCollectionXQuery.set(editor.document.uri, new XQueryLinter().lint(editor.document.getText())); break; } } diff --git a/src/formatting/commands/index.ts b/src/formatting/commands/index.ts index 3dc6ea2..35a228e 100644 --- a/src/formatting/commands/index.ts +++ b/src/formatting/commands/index.ts @@ -2,3 +2,4 @@ export * from "./formatAsXml"; export * from "./minifyXml"; export * from "./xmlToText"; export * from "./textToXml"; +export * from "./minifyXmlSelection"; diff --git a/src/formatting/commands/minifyXmlSelection.ts b/src/formatting/commands/minifyXmlSelection.ts new file mode 100644 index 0000000..b63762d --- /dev/null +++ b/src/formatting/commands/minifyXmlSelection.ts @@ -0,0 +1,19 @@ +import { workspace } from "vscode"; +import { Range, TextEditor, TextEditorEdit } from "vscode"; + +import { XmlFormatterFactory } from "../xml-formatter"; +import { XmlFormattingOptionsFactory } from "../xml-formatting-options"; + +export function minifyXmlSelection(editor: TextEditor, edit: TextEditorEdit): void { + const xmlFormatter = XmlFormatterFactory.getXmlFormatter(); + const xmlFormattingOptions = XmlFormattingOptionsFactory.getXmlFormattingOptions({ + insertSpaces: editor.options.insertSpaces, + tabSize: editor.options.tabSize + }, editor.document); + + editor.selections.reverse().forEach(selection => { + const range = new Range(selection.start, selection.end); + + edit.replace(range, xmlFormatter.minifyXml(editor.document.getText(range), xmlFormattingOptions)); + }); +} diff --git a/src/formatting/formatters/v2-xml-formatter.ts b/src/formatting/formatters/v2-xml-formatter.ts index 281f631..55a6bcd 100644 --- a/src/formatting/formatters/v2-xml-formatter.ts +++ b/src/formatting/formatters/v2-xml-formatter.ts @@ -17,12 +17,18 @@ export class V2XmlFormatter implements XmlFormatter { }); // do some light minification to get rid of insignificant whitespace - xml = xml.replace(/"\s+(?=[^\s]+=)/g, "\" "); // spaces between attributes + if (!options.preserveSpacesBetweenAttributes) { + 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, " "); - }); + + if (!options.preserveSpacesBetweenAttributes) { + 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); diff --git a/src/formatting/xml-formatting-options.ts b/src/formatting/xml-formatting-options.ts index 7c5185a..bbfb0d9 100644 --- a/src/formatting/xml-formatting-options.ts +++ b/src/formatting/xml-formatting-options.ts @@ -1,7 +1,6 @@ import { EndOfLine, FormattingOptions, TextDocument } from "vscode"; import { Configuration } from "../common"; -import * as constants from "../constants"; export interface XmlFormattingOptions { editorOptions: FormattingOptions; @@ -11,6 +10,7 @@ export interface XmlFormattingOptions { splitAttributesOnFormat: boolean; splitXmlnsOnFormat: boolean; initialIndentLevel?: number; + preserveSpacesBetweenAttributes: boolean; } export class XmlFormattingOptionsFactory { @@ -22,7 +22,8 @@ export class XmlFormattingOptionsFactory { removeCommentsOnMinify: Configuration.removeCommentsOnMinify(document.uri), splitAttributesOnFormat: Configuration.splitAttributesOnFormat(document.uri), splitXmlnsOnFormat: Configuration.splitXmlnsOnFormat(document.uri), - initialIndentLevel: 0 + initialIndentLevel: 0, + preserveSpacesBetweenAttributes: Configuration.preserveSpacesBetweenAttributes(document.uri) }; } } diff --git a/src/test/extension.test.ts b/src/test/extension.test.ts index dc4e04d..8ab4884 100644 --- a/src/test/extension.test.ts +++ b/src/test/extension.test.ts @@ -21,7 +21,8 @@ describe("V2XmlFormatter", () => { newLine: "\r\n", removeCommentsOnMinify: false, splitAttributesOnFormat: false, - splitXmlnsOnFormat: true + splitXmlnsOnFormat: true, + preserveSpacesBetweenAttributes: false }; it("should handle basic XML", () => { @@ -107,6 +108,18 @@ describe("V2XmlFormatter", () => { it("should not add trailing whitespace", () => { testFormatter(xmlFormatter, options, "issue-288"); }); + + it("should handle mixed content on the same line as another element without error", () => { + testFormatter(xmlFormatter, options, "issue-294"); + }); + + it("should optionally preserve whitespace between attributes", () => { + options.preserveSpacesBetweenAttributes = true; + + testFormatter(xmlFormatter, options, "issue-308"); + + options.preserveSpacesBetweenAttributes = false; + }); }); describe("#minifyXml(xml, options)", () => { @@ -120,7 +133,8 @@ describe("V2XmlFormatter", () => { newLine: "\r\n", removeCommentsOnMinify: false, splitAttributesOnFormat: false, - splitXmlnsOnFormat: true + splitXmlnsOnFormat: true, + preserveSpacesBetweenAttributes: false }; it("should preserve whitespace on minify if xml:space is set to 'preserve-whitespace'", () => { diff --git a/src/test/test-data/issue-294.formatted.xml b/src/test/test-data/issue-294.formatted.xml new file mode 100644 index 0000000..df31b2b --- /dev/null +++ b/src/test/test-data/issue-294.formatted.xml @@ -0,0 +1,7 @@ + + +
+ ThisCausesBug + +
+
\ No newline at end of file diff --git a/src/test/test-data/issue-294.unformatted.xml b/src/test/test-data/issue-294.unformatted.xml new file mode 100644 index 0000000..2fdc9de --- /dev/null +++ b/src/test/test-data/issue-294.unformatted.xml @@ -0,0 +1,7 @@ + + +
+ ThisCausesBug + +
+
\ No newline at end of file diff --git a/src/test/test-data/issue-308.formatted.xml b/src/test/test-data/issue-308.formatted.xml new file mode 100644 index 0000000..1764642 --- /dev/null +++ b/src/test/test-data/issue-308.formatted.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/test-data/issue-308.unformatted.xml b/src/test/test-data/issue-308.unformatted.xml new file mode 100644 index 0000000..1764642 --- /dev/null +++ b/src/test/test-data/issue-308.unformatted.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file