From e3e7ca9097b2d8954cf411ae82f9b21bd5b1f59c Mon Sep 17 00:00:00 2001 From: azerr Date: Sat, 18 Apr 2026 14:13:46 +0200 Subject: [PATCH] MinifyXML Minify support Fixes #609 Signed-off-by: azerr --- README.md | 1 + docs/Commands.md | 78 +++++++++++++---------- package-lock.json | 86 ++++++++++---------------- package.json | 9 +++ src/commands/clientCommandConstants.ts | 7 ++- src/commands/registerCommands.ts | 41 ++++++++++++ src/commands/serverCommandConstants.ts | 7 ++- 7 files changed, 142 insertions(+), 87 deletions(-) diff --git a/README.md b/README.md index 44d10ab0..e703e3a6 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ This VS Code extension provides support for creating and editing XML documents, * File associations * Code actions * Schema Caching + * [Minify XML](https://github.com/redhat-developer/vscode-xml/blob/main/docs/Commands.md#minify-xml-document) See the [changelog](CHANGELOG.md) for the latest release. diff --git a/docs/Commands.md b/docs/Commands.md index 7c936fdc..32dd1433 100644 --- a/docs/Commands.md +++ b/docs/Commands.md @@ -1,31 +1,47 @@ -# Commands - -[vscode-xml](https://github.com/redhat-developer/vscode-xml) provides several vscode commands which are available with `Ctrl+Shift+P`. - -![XML Commands](images/Commands/XMLCommands.png) - -## Bind to grammar/schema file - -This command triggers the [XML Binding Wizard](BindingWithGrammar.md#the-xml-binding-wizard) for the current file. - -Details on the command are described [here](BindingWithGrammar.md#command). - -## Open XML Documentation - -This command opens the `XML Documentation`. - -## Revalidate current XML file - -This command re-triggers the [XML Validation](Validation.md#xml-validation) for the current file. - -When the [Server Cache Path](Preferences.md#server-cache-path) is activated, the command removes the referenced XSD, DTD grammar from the local cache. - -## Revalidate all open XML files - -This command re-triggers the [XML Validation](Validation.md#xml-validation) for the all opened XML files. - -When the [Server Cache Path](Preferences.md#server-cache-path) is activated, the command clears the remote grammar cache and revalidates all opened files. - -## Restart XML Language Server - -This command restarts the XML language server. +# Commands + +[vscode-xml](https://github.com/redhat-developer/vscode-xml) provides several vscode commands which are available with `Ctrl+Shift+P`. + +![XML Commands](images/Commands/XMLCommands.png) + +## Bind to grammar/schema file + +This command triggers the [XML Binding Wizard](BindingWithGrammar.md#the-xml-binding-wizard) for the current file. + +Details on the command are described [here](BindingWithGrammar.md#command). + +## Open XML Documentation + +This command opens the `XML Documentation`. + +## Revalidate current XML file + +This command re-triggers the [XML Validation](Validation.md#xml-validation) for the current file. + +When the [Server Cache Path](Preferences.md#server-cache-path) is activated, the command removes the referenced XSD, DTD grammar from the local cache. + +## Revalidate all open XML files + +This command re-triggers the [XML Validation](Validation.md#xml-validation) for the all opened XML files. + +When the [Server Cache Path](Preferences.md#server-cache-path) is activated, the command clears the remote grammar cache and revalidates all opened files. + +## Restart XML Language Server + +This command restarts the XML language server. + +## Minify XML Document + +This command minifies the current XML document by removing unnecessary whitespace while preserving the document's structure and content. + +The minification can be triggered via the **Source > Minify XML** menu. + +The minification process: +- Removes all indentation and line breaks between elements +- Removes whitespace between the XML declaration and the root element +- Normalizes whitespace sequences inside text content to a single space +- Preserves whitespace in elements with `xml:space="preserve"` attribute +- Preserves content in CDATA sections +- Reduces multiple spaces between attributes to a single space + +This is useful for reducing file size before transmitting or storing XML documents. \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 18abaf94..61391a06 100644 --- a/package-lock.json +++ b/package-lock.json @@ -562,10 +562,9 @@ } }, "node_modules/@redhat-developer/vscode-redhat-telemetry/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "license": "MIT", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "engines": { "node": ">=12" }, @@ -851,13 +850,12 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, - "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.2" + "brace-expansion": "^2.0.1" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -991,13 +989,12 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, - "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.2" + "brace-expansion": "^2.0.1" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -1077,13 +1074,13 @@ } }, "node_modules/@vscode/test-cli/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.2" + "brace-expansion": "^2.0.1" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -2895,11 +2892,10 @@ } }, "node_modules/flatted": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", - "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", - "dev": true, - "license": "ISC" + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", + "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", + "dev": true }, "node_modules/for-in": { "version": "1.0.2", @@ -2986,21 +2982,6 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -4110,10 +4091,9 @@ } }, "node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "license": "ISC", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -4199,9 +4179,9 @@ } }, "node_modules/mocha/node_modules/minimatch": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", - "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "license": "ISC", "dependencies": { @@ -4729,11 +4709,10 @@ "dev": true }, "node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, - "license": "MIT", "engines": { "node": ">=8.6" }, @@ -5724,13 +5703,12 @@ } }, "node_modules/typescript-eslint/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, - "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.2" + "brace-expansion": "^2.0.1" }, "engines": { "node": ">=16 || 14 >=14.17" diff --git a/package.json b/package.json index ced8a8d8..d769f0d1 100644 --- a/package.json +++ b/package.json @@ -841,6 +841,11 @@ "command": "xml.refactor.surround.with.cdata", "title": "Surround with CDATA", "category": "XML" + }, + { + "command": "xml.minify", + "title": "Minify XML Document", + "category": "XML" } ], "menus": { @@ -872,6 +877,10 @@ { "command": "xml.refactor.surround.with.cdata", "when": "editorLangId in xml.supportedLanguageIds && XMLLSReady" + }, + { + "command": "xml.minify", + "when": "editorLangId in xml.supportedLanguageIds && XMLLSReady" } ], "editor/context": [ diff --git a/src/commands/clientCommandConstants.ts b/src/commands/clientCommandConstants.ts index 7b021f85..20c27936 100644 --- a/src/commands/clientCommandConstants.ts +++ b/src/commands/clientCommandConstants.ts @@ -74,4 +74,9 @@ export const EXECUTE_WORKSPACE_COMMAND = 'xml.workspace.executeCommand'; export const REFACTOR_SURROUND_WITH_COMMENTS = 'xml.refactor.surround.with.comments'; - export const REFACTOR_SURROUND_WITH_CDATA = 'xml.refactor.surround.with.cdata'; \ No newline at end of file + export const REFACTOR_SURROUND_WITH_CDATA = 'xml.refactor.surround.with.cdata'; + +/** + * Command to minify XML document. + */ + export const MINIFY_DOCUMENT = 'xml.minify'; \ No newline at end of file diff --git a/src/commands/registerCommands.ts b/src/commands/registerCommands.ts index 6c7c3e6a..88c2e15c 100644 --- a/src/commands/registerCommands.ts +++ b/src/commands/registerCommands.ts @@ -32,6 +32,7 @@ export async function registerClientServerCommands(context: ExtensionContext, la registerCodeLensReferencesCommands(context, languageClient); registerValidationCommands(context); registerRefactorCommands(context, languageClient); + registerMinifyCommand(context, languageClient); registerAssociationCommands(context, languageClient); registerRestartLanguageServerCommand(context, languageClient); registerConfigurationUpdateCommand(); @@ -467,3 +468,43 @@ async function surroundWith(surroundWithType: SurroundWithKind, languageClient: } } + +/** + * Register command to minify XML document + * + * @param context the extension context + * @param languageClient the language client + */ +function registerMinifyCommand(context: ExtensionContext, languageClient: LanguageClient) { + context.subscriptions.push(commands.registerCommand(ClientCommandConstants.MINIFY_DOCUMENT, async () => { + const activeEditor = window.activeTextEditor; + if (!activeEditor || activeEditor.document.languageId !== 'xml') { + return; + } + + const uri = activeEditor.document.uri; + const identifier = TextDocumentIdentifier.create(uri.toString()); + + try { + // Call the server command to get the minified text edits + const edits: TextEdit[] = await commands.executeCommand( + ClientCommandConstants.EXECUTE_WORKSPACE_COMMAND, + ServerCommandConstants.MINIFY_DOCUMENT, + identifier + ); + + if (!edits || edits.length === 0) { + return; + } + + // Apply the text edits + const workEdits = new WorkspaceEdit(); + for (const edit of edits) { + workEdits.replace(uri, languageClient.protocol2CodeConverter.asRange(edit.range), edit.newText); + } + await workspace.applyEdit(workEdits); + } catch (error) { + window.showErrorMessage('Error during XML minification: ' + error.message); + } + })); +} diff --git a/src/commands/serverCommandConstants.ts b/src/commands/serverCommandConstants.ts index 68a426ee..bb5352f5 100644 --- a/src/commands/serverCommandConstants.ts +++ b/src/commands/serverCommandConstants.ts @@ -34,4 +34,9 @@ export const CHECK_FILE_PATTERN = "xml.check.file.pattern"; /** * Command to surround with tags, comments, cdata */ - export const REFACTOR_SURROUND_WITH = "xml.refactor.surround.with"; \ No newline at end of file + export const REFACTOR_SURROUND_WITH = "xml.refactor.surround.with"; + +/** + * Command to minify XML document + */ + export const MINIFY_DOCUMENT = "xml.minify.document"; \ No newline at end of file