diff --git a/src/features/xmlFormatting.ts b/src/features/xmlFormatting.ts index e3bf197..5c2fe3e 100644 --- a/src/features/xmlFormatting.ts +++ b/src/features/xmlFormatting.ts @@ -1,22 +1,132 @@ 'use strict'; -import { TextEditor, TextEditorEdit } from 'vscode'; +import { +Range, +TextEdit, +TextEditor, +TextDocument, +TextEditorEdit, +CancellationToken, +FormattingOptions, +DocumentFormattingEditProvider, +DocumentRangeFormattingEditProvider +} from 'vscode'; + import { getRangeForDocument } from '../utils/RangeUtils'; -let pd = require('pretty-data').pd; - -export function formatXml(editor: TextEditor, edit: TextEditorEdit): void { - let current = editor.document.getText(); - let pretty = pd.xml(current); - let range = getRangeForDocument(editor.document); - - edit.replace(range, pretty); -} - export function linearizeXml(editor: TextEditor, edit: TextEditorEdit): void { let current = editor.document.getText(); - let linear = pd.xmlmin(current); let range = getRangeForDocument(editor.document); + + edit.replace(range, _linearizeXml(current)); +} + +export class XmlDocumentFormattingProvider implements DocumentFormattingEditProvider { + provideDocumentFormattingEdits(document: TextDocument, options: FormattingOptions, token: CancellationToken): TextEdit[] { + let current = document.getText(); + let range = getRangeForDocument(document); + + return [TextEdit.replace(range, _formatXml(current, options))]; + } +} + +export class XmlRangeFormattingProvider implements DocumentRangeFormattingEditProvider { + provideDocumentRangeFormattingEdits(document: TextDocument, range: Range, options: FormattingOptions, token: CancellationToken): TextEdit[] { + let current = document.getText(range); + + return [TextEdit.replace(range, _formatXml(current, options))]; + } +} + +function _strRepeat(source: string, count: number): string { + let output = ''; - edit.replace(range, linear); + for (let i = 0; i < count; i++) { + output += source; + } + + return output; +} + +function _linearizeXml(xml: string): string { + return xml.replace(/>\s{0,}<'); +} + +function _formatXml(xml: string, options: FormattingOptions): string { + /* This code was taken from https://github.com/vkiryukhin/pretty-data/blob/master/pretty-data.js + * and refactored to fit our needs for this extension. pretty-data is dual-licensed (MIT/GPL). + */ + + let parts = _linearizeXml(xml) + .replace(/ -1) { + output += _strRepeat(tab, deep) + parts[i]; + formatted = true; + + // end comment, CDATA, or DOCTYPE + if (parts[i].search(/-->/) > -1 || parts[i].search(/\]>/) > -1 || parts[i].search(/!DOCTYPE/) > -1) { + formatted = false; + } + } + + // end comment or CDATA + else if (parts[i].search(/-->/) > -1 || parts[i].search(/\]>/) > -1) { + output += parts[i]; + formatted = false; + } + + // + else if (/^<\w/.exec(parts[i - 1]) && /^<\/\w/.exec(parts[i]) && (/^<[\w:\-\.\,]+/.exec(parts[i - 1])[0] == /^<\/[\w:\-\.\,]+/.exec(parts[i])[0].replace('/','')) { + output += parts[i]; + if (!formatted) deep--; + } + + // + else if (parts[i].search(/<\w/) > -1 && parts[i].search(/<\//) == -1 && parts[i].search(/\/>/) == -1 ) { + output = !formatted ? output += _strRepeat(tab, deep++) + parts[i] : output += parts[i]; + } + + // ... + else if (parts[i].search(/<\w/) > -1 && parts[i].search(/<\//) > -1) { + output = !formatted ? output += _strRepeat(tab, deep) + parts[i] : output += parts[i]; + } + + // + else if (parts[i].search(/<\//) > -1) { + output = !formatted ? output += _strRepeat(tab, --deep) + parts[i] : output += parts[i]; + } + + // + else if (parts[i].search(/\/>/) > -1) { + output = !formatted ? output += _strRepeat(tab, deep) + parts[i] : output += parts[i]; + } + + // + else if (parts[i].search(/<\?/) > -1) { + output += _strRepeat(tab, deep) + parts[i]; + } + + // xmlns + else if (parts[i].search(/xmlns\:/) > -1 || parts[i].search(/xmlns\=/) > -1) { + output += _strRepeat(tab, deep) + parts[i]; + } + + else { + output += parts[i]; + } + } + + return output; } \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 9d7473f..c9ff199 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,7 +1,7 @@ 'use strict'; -import { commands, ExtensionContext } from 'vscode'; -import { formatXml, linearizeXml } from './features/xmlFormatting'; +import { commands, languages, ExtensionContext } from 'vscode'; +import { linearizeXml, XmlDocumentFormattingProvider, XmlRangeFormattingProvider } from './features/xmlFormatting'; import { evaluateXPath } from './features/xmlXPathEngine'; import { checkForUpdates } from './utils/UpdateNotifier'; @@ -10,7 +10,10 @@ export function activate(ctx: ExtensionContext) { checkForUpdates(); // register palette commands - ctx.subscriptions.push(commands.registerTextEditorCommand('xmltools.formatXml', formatXml)); ctx.subscriptions.push(commands.registerTextEditorCommand('xmltools.linearizeXml', linearizeXml)); ctx.subscriptions.push(commands.registerTextEditorCommand('xmltools.evaluateXPath', evaluateXPath)); + + // register formatting providers + ctx.subscriptions.push(languages.registerDocumentFormattingEditProvider('xml', new XmlDocumentFormattingProvider())); + ctx.subscriptions.push(languages.registerDocumentRangeFormattingEditProvider('xml', new XmlRangeFormattingProvider())); } \ No newline at end of file