From d37a8b70fd2bc62ebef8559fd160ac0c97dc4978 Mon Sep 17 00:00:00 2001 From: Josh Johnson Date: Thu, 1 Mar 2018 21:22:53 -0500 Subject: [PATCH] Add Format As XML Command --- src/extension.ts | 18 ++----- src/formatting/commands/formatAsXml.ts | 53 +++++++++++++++++++ src/formatting/xml-formatter.ts | 26 +++++++++ .../xml-formatting-edit-provider.ts | 9 +--- src/formatting/xml-formatting-options.ts | 19 ++++++- 5 files changed, 104 insertions(+), 21 deletions(-) create mode 100644 src/formatting/commands/formatAsXml.ts diff --git a/src/extension.ts b/src/extension.ts index 706f351..3b35182 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,10 +1,9 @@ -import { languages, window, workspace } from "vscode"; +import { languages, window, workspace, commands } from "vscode"; import { ExtensionContext, TextEditor, TextEditorSelectionChangeEvent, WorkspaceConfiguration } from "vscode"; -import { XmlFormatter } from "./formatting/xml-formatter"; +import { FormatAsXmlCommandName, formatAsXml } from "./formatting/commands/formatAsXml"; +import { XmlFormatterFactory } from "./formatting/xml-formatter"; import { XmlFormattingEditProvider } from "./formatting/xml-formatting-edit-provider"; -import { ClassicXmlFormatter } from "./formatting/formatters/classic-xml-formatter"; -import { V2XmlFormatter } from "./formatting/formatters/v2-xml-formatter"; import { XQueryLinter } from "./linting/xquery-linter"; import * as constants from "./constants"; @@ -13,17 +12,10 @@ export function activate(context: ExtensionContext) { const config = workspace.getConfiguration(constants.extensionPrefix); /* Formatting Features */ - const xmlFormatterImplementationSetting = config.get("xmlFormatterImplementation"); - let xmlFormatterImplementation: XmlFormatter; - - switch (xmlFormatterImplementationSetting) { - case "v2": xmlFormatterImplementation = new V2XmlFormatter(); break; - case "classic": default: xmlFormatterImplementation = new ClassicXmlFormatter(); break; - } - - const xmlFormattingEditProvider = new XmlFormattingEditProvider(config, xmlFormatterImplementation); + const xmlFormattingEditProvider = new XmlFormattingEditProvider(config, XmlFormatterFactory.getXmlFormatter()); context.subscriptions.push( + commands.registerTextEditorCommand(FormatAsXmlCommandName, formatAsXml), languages.registerDocumentFormattingEditProvider("xml", xmlFormattingEditProvider), languages.registerDocumentRangeFormattingEditProvider("xml", xmlFormattingEditProvider) ); diff --git a/src/formatting/commands/formatAsXml.ts b/src/formatting/commands/formatAsXml.ts new file mode 100644 index 0000000..bd29f43 --- /dev/null +++ b/src/formatting/commands/formatAsXml.ts @@ -0,0 +1,53 @@ +import { commands, 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 const FormatAsXmlCommandName = "formatAsXml"; + +export function formatAsXml(editor: TextEditor, edit: TextEditorEdit): void { + const xmlFormattingEditProvider = new XmlFormattingEditProvider(workspace.getConfiguration(constants.extensionPrefix), 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 commands.executeCommand("cursorMove", { + to: "left", + by: "character" + }); + await commands.executeCommand("cursorMove", { + to: "right", + by: "character" + }); + }); + } +} diff --git a/src/formatting/xml-formatter.ts b/src/formatting/xml-formatter.ts index 2ad9111..2dd90d7 100644 --- a/src/formatting/xml-formatter.ts +++ b/src/formatting/xml-formatter.ts @@ -1,6 +1,32 @@ +import { workspace } from "vscode"; + +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 = workspace.getConfiguration(constants.extensionPrefix).get("xmlFormatterImplementation"); + let xmlFormatterImplementation: XmlFormatter; + + switch (xmlFormatterImplementationSetting) { + case "classic": xmlFormatterImplementation = new ClassicXmlFormatter(); break; + case "v2": default: xmlFormatterImplementation = new V2XmlFormatter(); break; + } + + return (XmlFormatterFactory._xmlFormatter = xmlFormatterImplementation); + } +} \ No newline at end of file diff --git a/src/formatting/xml-formatting-edit-provider.ts b/src/formatting/xml-formatting-edit-provider.ts index a6c117b..2fd9007 100644 --- a/src/formatting/xml-formatting-edit-provider.ts +++ b/src/formatting/xml-formatting-edit-provider.ts @@ -6,6 +6,7 @@ import { import * as constants from "../constants"; import { XmlFormatter } from "./xml-formatter"; +import { XmlFormattingOptionsFactory } from "./xml-formatting-options"; export class XmlFormattingEditProvider implements DocumentFormattingEditProvider, DocumentRangeFormattingEditProvider { @@ -27,13 +28,7 @@ export class XmlFormattingEditProvider implements DocumentFormattingEditProvider let xml = document.getText(range); - xml = this.xmlFormatter.formatXml(xml, { - editorOptions: options, - newLine: (document.eol === EndOfLine.CRLF) ? "\r\n" : "\n", - removeCommentsOnMinify: this.workspaceConfiguration.get("removeCommentsOnMinify"), - splitAttributesOnFormat: this.workspaceConfiguration.get("splitAttributesOnFormat"), - splitXmlnsOnFormat: this.workspaceConfiguration.get("splitXmlnsOnFormat") - }); + xml = this.xmlFormatter.formatXml(xml, XmlFormattingOptionsFactory.getXmlFormattingOptions(options, document.eol)); return [ TextEdit.replace(range, xml) ]; } diff --git a/src/formatting/xml-formatting-options.ts b/src/formatting/xml-formatting-options.ts index bafed99..fe4670c 100644 --- a/src/formatting/xml-formatting-options.ts +++ b/src/formatting/xml-formatting-options.ts @@ -1,4 +1,7 @@ -import { FormattingOptions } from "vscode"; +import { workspace } from "vscode"; +import { EndOfLine, FormattingOptions } from "vscode"; + +import * as constants from "../constants"; export interface XmlFormattingOptions { editorOptions: FormattingOptions; @@ -7,3 +10,17 @@ export interface XmlFormattingOptions { splitAttributesOnFormat: boolean; splitXmlnsOnFormat: boolean; } + +export class XmlFormattingOptionsFactory { + static getXmlFormattingOptions(formattingOptions: FormattingOptions, eol: EndOfLine): XmlFormattingOptions { + const config = workspace.getConfiguration(constants.extensionPrefix); + + return { + editorOptions: formattingOptions, + newLine: (eol === EndOfLine.CRLF) ? "\r\n" : "\n", + removeCommentsOnMinify: config.get("removeCommentsOnMinify"), + splitAttributesOnFormat: config.get("splitAttributesOnFormat"), + splitXmlnsOnFormat: config.get("splitXmlnsOnFormat") + }; + } +} \ No newline at end of file