diff --git a/package.json b/package.json index a43c3d6..b491c20 100644 --- a/package.json +++ b/package.json @@ -176,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" } } }, 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/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 be01e22..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", () => { @@ -111,6 +112,14 @@ describe("V2XmlFormatter", () => { 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)", () => { @@ -124,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-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