import * as vscode from 'vscode' import * as prettier from 'prettier' import { ERROR_KIND } from '../consts' import { FormatErr } from '../type' // 需要增强的种类,比如目前三元运算符 需要处理 省略 ? 的场景 interface EnhancerKind { ternary?: boolean object?: boolean } export class Formatter { private dataPointMatcher = /\{\{(.*?)\}\}/g private eventBinderMatcher = /:([a-z][a-zA-Z0-9]*)="(.*?)"/g private logicNodeCondMatcher = /(If|Elif) cond="(.*?)"/ private timerCache = 0 /** * 逐行遍历文件,调用prettier引擎进行格式化 * @param document vscode.TextDocument * @param edit vscode.TextEditorEdit */ public handleCommand(document: vscode.TextDocument, edit: vscode.TextEditorEdit) { const now = Date.now() if (now - this.timerCache > 1000) { this.timerCache = now } else { return } let i = 0 while (i < document.lineCount) { const textLine = document.lineAt(i) if (!textLine.isEmptyOrWhitespace) { const text = textLine.text // 对{{ }}中的内容进行格式化 text.replace(this.dataPointMatcher, (_, p: string, index: number) => { const range = this.getRange(i, index, i, index + p.length + 4) try { const res = this.parse(p, { ternary: true }) const str = `{{${res}}}` if (this.getText(document, range) !== str) { edit.replace(range, `{{${res}}}`) } } catch (error) { const err: FormatErr = { range, error, kind: ERROR_KIND.DATA_POINT } throw err } return '' }) // 对binder中的({ })进行格式化 text.replace(this.eventBinderMatcher, (_, p1: string, p2: string, index: number) => { const start = index + p1.length + 3 const end = start + p2.length const range = this.getRange(i, start, i, end) try { const res = this.parse(p2, { object: true }) if (this.getText(document, range) !== res) { edit.replace(range, res) } } catch (error) { const err: FormatErr = { range, error, kind: ERROR_KIND.EVENT_BINDER } throw err } return '' }) // 对cond进行格式化 text.replace(this.logicNodeCondMatcher, (_, p1: string, p2: string, index) => { const start = index + p1.length + 7 const end = start + p2.length const range = this.getRange(i, start, i, end) try { const res = this.parse(p2, {}) if (this.getText(document, range) !== res.trim()) { edit.replace(range, res.trim()) } } catch (error) { const err: FormatErr = { range, error, kind: ERROR_KIND.COND } throw err } return '' }) } i++ } } private getPosition(y: number, x: number) { return new vscode.Position(y, x) } private getRange(y1: number, x1: number, y2: number, x2: number) { const start = this.getPosition(y1, x1) const end = this.getPosition(y2, x2) return new vscode.Range(start, end) } /** * 解析文本内容,并返回格式化后文本 * @param text 解析文本内容 * @param enhancerKind 增强的种类 。有些winged支持的语法,需要修饰后再放入prettier解析。 */ private parse(text: string, enhancerKind: EnhancerKind): string { try { let _text = text if (enhancerKind.ternary) { const { str, flag } = this.enhanceTernary(_text) _text = str enhancerKind.ternary = flag } _text = prettier.format(_text, { semi: false, parser: 'babel', singleQuote: true }).trim() return this.resloveEnhancer(_text, enhancerKind) } catch (error) { // console.log(error) // throw error.codeFrame return text } } /** * 增强三目运算符,winged支持省略 : , 这里会在parse之前加入 ':1' 使prettier正常工作 */ private enhanceTernary(str: string): { str: string; flag: boolean } { let flag = false if (str.lastIndexOf('?') !== -1 && str.lastIndexOf(':') === -1) { str = str + ':1' flag = true } return { str, flag } } /** * 解决增强剂的副作用 * 比如三目运算符的增强 需要 在格式化后 去除 之前加入的 ':1' */ private resloveEnhancer(str: string, enhancerKind: EnhancerKind): string { if (enhancerKind.ternary) { str = str.slice(0, str.length - 3).trim() } if (enhancerKind.object) { const regex = /(\n\s|\n)/g const regex2 = /,\}\)/ str = str.replace(regex, '').replace(regex2, ' })') } else { // 解决 cond 过长 const regex = /(\n\s|\n)/g str = str.replace(regex, '') } return str } private getText(document: vscode.TextDocument, range: vscode.Range) { return document.getText(range) } }