forked from external/vscode-xml
		
	Begin Implementing V2 Formatter
This commit is contained in:
		
							parent
							
								
									5526923772
								
							
						
					
					
						commit
						8f0bf58462
					
				
					 5 changed files with 210 additions and 57 deletions
				
			
		
							
								
								
									
										10
									
								
								package.json
									
										
									
									
									
								
							
							
						
						
									
										10
									
								
								package.json
									
										
									
									
									
								
							|  | @ -88,7 +88,7 @@ | |||
|                 }, | ||||
|                 "xmlTools.xmlFormatterImplementation": { | ||||
|                     "type": "string", | ||||
|                     "default": "classic", | ||||
|                     "default": "v2", | ||||
|                     "description": "Supported XML Formatters: classic", | ||||
|                     "scope": "resource" | ||||
|                 }, | ||||
|  | @ -122,6 +122,14 @@ | |||
|                 "key": "ctrl+shift+alt+b", | ||||
|                 "command": "xmlTools.formatXml" | ||||
|             } | ||||
|         ], | ||||
|         "languages": [ | ||||
|             { | ||||
|                 "id": "xquery", | ||||
|                 "aliases": ["XQuery", "xquery"], | ||||
|                 "extensions": [".xq",".xql",".xqm",".xqy",".xquery"], | ||||
|                 "configuration": "./languages/xquery/xquery.json" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     "scripts": { | ||||
|  |  | |||
|  | @ -1,29 +1,33 @@ | |||
| import { workspace } from "vscode"; | ||||
| import { languages, workspace } from "vscode"; | ||||
| import { ExtensionContext, WorkspaceConfiguration } from "vscode"; | ||||
| 
 | ||||
| import { XmlFormatter } 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 * as constants from "./constants"; | ||||
| 
 | ||||
| const onActivateHandlers: OnActivateHandler[] = []; | ||||
| const onDeactivateHandlers: OnDeactivateHandler[] = []; | ||||
| 
 | ||||
| export function activate(context: ExtensionContext) { | ||||
|     const workspaceConfiguration = workspace.getConfiguration(constants.extensionPrefix); | ||||
|     const config = workspace.getConfiguration(constants.extensionPrefix); | ||||
| 
 | ||||
|     onActivateHandlers.forEach(x => x(context, workspaceConfiguration)); | ||||
|     /* Formatting Features */ | ||||
|     const xmlFormatterImplementationSetting = config.get<string>("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); | ||||
| 
 | ||||
|     context.subscriptions.push( | ||||
|         languages.registerDocumentFormattingEditProvider("xml", xmlFormattingEditProvider), | ||||
|         languages.registerDocumentRangeFormattingEditProvider("xml", xmlFormattingEditProvider) | ||||
|     ); | ||||
| } | ||||
| 
 | ||||
| export function deactivate() { | ||||
|     onDeactivateHandlers.forEach(x => x()); | ||||
|     // do nothing
 | ||||
| } | ||||
| 
 | ||||
| export function onActivate(handler: OnActivateHandler): void { | ||||
|     onActivateHandlers.push(handler); | ||||
| } | ||||
| 
 | ||||
| export function onDeactivate(handler: OnDeactivateHandler): void { | ||||
|     onDeactivateHandlers.push(handler); | ||||
| } | ||||
| 
 | ||||
| export type OnActivateHandler = (context: ExtensionContext, config: WorkspaceConfiguration) => void; | ||||
| 
 | ||||
| export type OnDeactivateHandler = () => void; | ||||
|  |  | |||
							
								
								
									
										174
									
								
								src/formatting/formatters/v2-xml-formatter.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								src/formatting/formatters/v2-xml-formatter.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,174 @@ | |||
| import { XmlFormatter } from "../xml-formatter"; | ||||
| import { XmlFormattingOptions } from "../xml-formatting-options"; | ||||
| import { ClassicXmlFormatter } from "./classic-xml-formatter"; | ||||
| 
 | ||||
| /* tslint:disable no-use-before-declare */ | ||||
| export class V2XmlFormatter implements XmlFormatter { | ||||
|     formatXml(xml: string, options: XmlFormattingOptions): string { | ||||
|         xml = this.minifyXml(xml, options); | ||||
| 
 | ||||
|         let output = ""; | ||||
| 
 | ||||
|         let indentLevel = 0; | ||||
|         let location = Location.Text; | ||||
|         let lastNonTextLocation = Location.Text; // hah
 | ||||
| 
 | ||||
|         // NOTE: all "exiting" checks should appear after their associated "entering" checks
 | ||||
|         for (let i = 0; i < xml.length; i++) { | ||||
|             const cc = xml[i]; | ||||
|             const nc = xml.charAt(i + 1); | ||||
|             const nnc = xml.charAt(i + 2); | ||||
|             const pc = xml.charAt(i - 1); | ||||
| 
 | ||||
|             // entering CData
 | ||||
|             if (location === Location.Text && cc === "<" && nc === "!" && nnc === "[") { | ||||
|                 output += `${this._getIndent(options, indentLevel)}<`; | ||||
|                 location = Location.CData; | ||||
|             } | ||||
|              | ||||
|             // exiting CData
 | ||||
|             else if (location === Location.CData && cc === "]" && nc === "]" && nnc === ">") { | ||||
|                 output += "]]>"; | ||||
| 
 | ||||
|                 i += 2; | ||||
|                 lastNonTextLocation = location; | ||||
|                 location = Location.Text; | ||||
|             } | ||||
|              | ||||
|             // entering Comment
 | ||||
|             else if (location === Location.Text && cc === "<" && nc === "!" && nnc === "-") { | ||||
|                 output += `${this._getIndent(options, indentLevel)}<`; | ||||
|                 location = Location.Comment; | ||||
|             } | ||||
|              | ||||
|             // exiting Comment
 | ||||
|             else if (location === Location.Comment && cc === "-" && nc === "-" && nnc === ">") { | ||||
|                 output += "-->"; | ||||
| 
 | ||||
|                 i += 2; | ||||
|                 lastNonTextLocation = location; | ||||
|                 location = Location.Text; | ||||
|             } | ||||
|              | ||||
|             // entering SpecialTag
 | ||||
|             else if (location === Location.Text && cc === "<" && (nc === "!" || nc === "?")) { | ||||
|                 output += `${this._getIndent(options, indentLevel)}<`; | ||||
|                 location = Location.SpecialTag; | ||||
|             } | ||||
|              | ||||
|             // exiting SpecialTag
 | ||||
|             else if (location === Location.SpecialTag && cc === ">") { | ||||
|                 output += `>`; | ||||
|                 lastNonTextLocation = location; | ||||
|                 location = Location.Text; | ||||
|             } | ||||
|              | ||||
|             // entering StartTag.StartTagName
 | ||||
|             else if (location === Location.Text && cc === "<" && ["/", "!"].indexOf(nc) === -1) { | ||||
|                 // if this occurs after another tag, prepend a line break
 | ||||
|                 if (pc === ">") { | ||||
|                     output += `${options.newLine}${this._getIndent(options, indentLevel)}<`; | ||||
|                 } | ||||
| 
 | ||||
|                 else { | ||||
|                     output += `${this._getIndent(options, indentLevel)}<`; | ||||
|                 } | ||||
| 
 | ||||
|                 indentLevel++; | ||||
|                 location = Location.StartTagName; | ||||
|             } | ||||
|              | ||||
|             // exiting StartTag.StartTagName, enter StartTag
 | ||||
|             else if (location === Location.StartTagName && cc === " ") { | ||||
|                 output += " "; | ||||
|                 lastNonTextLocation = location; | ||||
|                 location = Location.StartTag; | ||||
|             } | ||||
|              | ||||
|             // entering StartTag.Attribute
 | ||||
|             else if (location === Location.StartTag && [" ", "/", ">"].indexOf(cc) === -1) { | ||||
|                 output += cc; | ||||
|                 lastNonTextLocation = location; | ||||
|                 location = Location.Attribute; | ||||
|             } | ||||
|              | ||||
|             // entering StartTag.Attribute.AttributeValue
 | ||||
|             else if (location === Location.Attribute && cc === "\"") { | ||||
|                 output += "\""; | ||||
|                 lastNonTextLocation = location; | ||||
|                 location = Location.AttributeValue; | ||||
|             } | ||||
|              | ||||
|             // exiting StartTag.Attribute.AttributeValue, entering StartTag
 | ||||
|             else if (location === Location.AttributeValue && cc === "\"") { | ||||
|                 output += "\""; | ||||
|                 lastNonTextLocation = location; | ||||
|                 location = Location.StartTag; | ||||
|             } | ||||
| 
 | ||||
|             // exiting StartTag or StartTag.StartTagName, entering Text
 | ||||
|             else if ((location === Location.StartTag || location === Location.StartTagName) && cc === ">") { | ||||
|                 // if this was a self-closing tag, we need to decrement the indent level
 | ||||
|                 if (pc === "/") { | ||||
|                     indentLevel--; | ||||
|                 } | ||||
| 
 | ||||
|                 output += ">"; | ||||
| 
 | ||||
|                 lastNonTextLocation = location; | ||||
|                 location = Location.Text; | ||||
|             } | ||||
|              | ||||
|             // entering EndTag
 | ||||
|             else if (location === Location.Text && cc === "<" && nc === "/") { | ||||
|                 indentLevel--; | ||||
| 
 | ||||
|                 // if the end tag immediately follows another end tag, add a line break and indent
 | ||||
|                 // otherwise, this should be treated as a same-line end tag(ex. <element>text</element>)
 | ||||
|                 if (lastNonTextLocation === Location.EndTag) { | ||||
|                     output += `${options.newLine}${this._getIndent(options, indentLevel)}<`; | ||||
|                 } | ||||
|                  | ||||
|                 else { | ||||
|                     output += "<"; | ||||
|                 } | ||||
| 
 | ||||
|                 location = Location.EndTag; | ||||
|             } | ||||
|              | ||||
|             // exiting EndTag, entering Text
 | ||||
|             else if (location === Location.EndTag && cc === ">") { | ||||
|                 output += ">"; | ||||
|                 lastNonTextLocation = location; | ||||
|                 location = Location.Text; | ||||
|             } | ||||
|              | ||||
|             // Text
 | ||||
|             else { | ||||
|                 output += cc; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return output; | ||||
|     } | ||||
| 
 | ||||
|     minifyXml(xml: string, options: XmlFormattingOptions): string { | ||||
|         return new ClassicXmlFormatter().minifyXml(xml, options); | ||||
|     } | ||||
| 
 | ||||
|     private _getIndent(options: XmlFormattingOptions, indentLevel: number): string { | ||||
|         return ((options.editorOptions.insertSpaces) ? " ".repeat(options.editorOptions.tabSize) : "\t").repeat(indentLevel); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| enum Location { | ||||
|     Attribute, | ||||
|     AttributeValue, | ||||
|     CData, | ||||
|     Comment, | ||||
|     EndTag, | ||||
|     SpecialTag, | ||||
|     StartTag, | ||||
|     StartTagName, | ||||
|     Text | ||||
| } | ||||
|  | @ -1,43 +1,12 @@ | |||
| import { commands, languages, workspace } from "vscode"; | ||||
| import { workspace } from "vscode"; | ||||
| import { | ||||
|     CancellationToken, DocumentFormattingEditProvider, DocumentRangeFormattingEditProvider, ExtensionContext, | ||||
|     FormattingOptions, ProviderResult, Range, TextDocument, TextEdit, TextEditor, WorkspaceConfiguration | ||||
|     CancellationToken, DocumentFormattingEditProvider, DocumentRangeFormattingEditProvider, EndOfLine, | ||||
|     FormattingOptions, ProviderResult, Range, TextDocument, TextEdit, WorkspaceConfiguration | ||||
| } from "vscode"; | ||||
| 
 | ||||
| import * as constants from "../constants"; | ||||
| import * as extension from "../extension"; | ||||
| import { XmlFormatter } from "./xml-formatter"; | ||||
| 
 | ||||
| import { ClassicXmlFormatter } from "./formatters/classic-xml-formatter"; | ||||
| 
 | ||||
| extension.onActivate((context: ExtensionContext, config: WorkspaceConfiguration) => { | ||||
|     const xmlFormatterImplementationSetting = config.get<string>("xmlFormatterImplementation"); | ||||
|     let xmlFormatterImplementation: XmlFormatter; | ||||
| 
 | ||||
|     switch (xmlFormatterImplementationSetting) { | ||||
|         case "classic": | ||||
|         default: xmlFormatterImplementation = new ClassicXmlFormatter(); break; | ||||
|     } | ||||
| 
 | ||||
|     // tslint:disable-next-line:no-use-before-declare
 | ||||
|     const xmlFormattingEditProvider = new XmlFormattingEditProvider(config, xmlFormatterImplementation); | ||||
| 
 | ||||
|     const formatAsXmlCommand = commands.registerTextEditorCommand("xmlTools.formatAsXml", (textEditor) => { | ||||
|         // TODO: implement command
 | ||||
|     }); | ||||
| 
 | ||||
|     const minifyXmlCommand = commands.registerTextEditorCommand("xmlTools.minifyXml", (textEditor: TextEditor) => { | ||||
|         // TODO: implement command
 | ||||
|     }); | ||||
| 
 | ||||
|     context.subscriptions.push( | ||||
|         formatAsXmlCommand, | ||||
|         minifyXmlCommand, | ||||
|         languages.registerDocumentFormattingEditProvider("xml", xmlFormattingEditProvider), | ||||
|         languages.registerDocumentRangeFormattingEditProvider("xml", xmlFormattingEditProvider) | ||||
|     ); | ||||
| }); | ||||
| 
 | ||||
| export class XmlFormattingEditProvider implements DocumentFormattingEditProvider, DocumentRangeFormattingEditProvider { | ||||
| 
 | ||||
|     constructor( | ||||
|  | @ -60,7 +29,7 @@ export class XmlFormattingEditProvider implements DocumentFormattingEditProvider | |||
| 
 | ||||
|         xml = this.xmlFormatter.formatXml(xml, { | ||||
|             editorOptions: options, | ||||
|             newLine: document.eol.toString(), | ||||
|             newLine: (document.eol === EndOfLine.CRLF) ? "\r\n" : "\n", | ||||
|             removeCommentsOnMinify: this.workspaceConfiguration.get<boolean>("removeCommentsOnMinify"), | ||||
|             splitXmlnsOnFormat: this.workspaceConfiguration.get<boolean>("splitXmlnsOnFormat") | ||||
|         }); | ||||
|  |  | |||
|  | @ -77,8 +77,6 @@ | |||
|         "one-line": [ | ||||
|             true, | ||||
|             "check-open-brace", | ||||
|             "check-catch", | ||||
|             "check-else", | ||||
|             "check-whitespace" | ||||
|         ], | ||||
|         "prefer-const": true, | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue