From 9cbbf729c004cd3a656157dc3e9f261db044c7b2 Mon Sep 17 00:00:00 2001 From: landaiqing Date: Thu, 18 Sep 2025 00:13:07 +0800 Subject: [PATCH] :fire: Remove powershell prettier plugin --- .../common/prettier/plugins/powershell/ast.ts | 391 --------- .../plugins/powershell/code-generator.ts | 566 ------------ .../plugins/powershell/formatter-rules.ts | 440 ---------- .../prettier/plugins/powershell/index.ts | 208 ----- .../prettier/plugins/powershell/lexer.ts | 722 --------------- .../prettier/plugins/powershell/parser.ts | 821 ------------------ .../codeblock/lang-parser/languages.ts | 6 +- 7 files changed, 1 insertion(+), 3153 deletions(-) delete mode 100644 frontend/src/common/prettier/plugins/powershell/ast.ts delete mode 100644 frontend/src/common/prettier/plugins/powershell/code-generator.ts delete mode 100644 frontend/src/common/prettier/plugins/powershell/formatter-rules.ts delete mode 100644 frontend/src/common/prettier/plugins/powershell/index.ts delete mode 100644 frontend/src/common/prettier/plugins/powershell/lexer.ts delete mode 100644 frontend/src/common/prettier/plugins/powershell/parser.ts diff --git a/frontend/src/common/prettier/plugins/powershell/ast.ts b/frontend/src/common/prettier/plugins/powershell/ast.ts deleted file mode 100644 index 177c16b..0000000 --- a/frontend/src/common/prettier/plugins/powershell/ast.ts +++ /dev/null @@ -1,391 +0,0 @@ -/** - * PowerShell AST 节点定义 - * 定义抽象语法树的各种节点类型 - */ - -import { Token } from './lexer'; - -export interface ASTNode { - type: string; - start: number; - end: number; - line: number; - column: number; -} - -export interface ScriptBlockAst extends ASTNode { - type: 'ScriptBlock'; - statements: StatementAst[]; -} - -export interface StatementAst extends ASTNode { - type: string; -} - -export interface ExpressionAst extends ASTNode { - type: string; -} - -// 管道表达式 -export interface PipelineAst extends StatementAst { - type: 'Pipeline'; - elements: PipelineElementAst[]; -} - -export interface PipelineElementAst extends ASTNode { - type: 'PipelineElement'; - expression: ExpressionAst; -} - -// 命令表达式 -export interface CommandAst extends ExpressionAst { - type: 'Command'; - commandName: string; - parameters: ParameterAst[]; - arguments: ExpressionAst[]; -} - -export interface ParameterAst extends ASTNode { - type: 'Parameter'; - name: string; - value?: ExpressionAst; -} - -// 赋值表达式 -export interface AssignmentAst extends StatementAst { - type: 'Assignment'; - left: ExpressionAst; - operator: string; - right: ExpressionAst; -} - -// 变量表达式 -export interface VariableAst extends ExpressionAst { - type: 'Variable'; - name: string; -} - -// 字面量表达式 -export interface LiteralAst extends ExpressionAst { - type: 'Literal'; - value: any; - literalType: 'String' | 'Number' | 'Boolean' | 'Null'; -} - -// 数组表达式 -export interface ArrayAst extends ExpressionAst { - type: 'Array'; - elements: ExpressionAst[]; -} - -// 哈希表表达式 -export interface HashtableAst extends ExpressionAst { - type: 'Hashtable'; - entries: HashtableEntryAst[]; -} - -export interface HashtableEntryAst extends ASTNode { - type: 'HashtableEntry'; - key: ExpressionAst; - value: ExpressionAst; -} - -// 函数定义 -export interface FunctionDefinitionAst extends StatementAst { - type: 'FunctionDefinition'; - name: string; - parameters: ParameterAst[]; - body: ScriptBlockAst; -} - -// 控制流结构 -export interface IfStatementAst extends StatementAst { - type: 'IfStatement'; - condition: ExpressionAst; - ifBody: ScriptBlockAst; - elseIfClauses: ElseIfClauseAst[]; - elseBody?: ScriptBlockAst; -} - -export interface ElseIfClauseAst extends ASTNode { - type: 'ElseIfClause'; - condition: ExpressionAst; - body: ScriptBlockAst; -} - -export interface WhileStatementAst extends StatementAst { - type: 'WhileStatement'; - condition: ExpressionAst; - body: ScriptBlockAst; -} - -export interface ForStatementAst extends StatementAst { - type: 'ForStatement'; - initializer?: ExpressionAst; - condition?: ExpressionAst; - iterator?: ExpressionAst; - body: ScriptBlockAst; -} - -export interface ForEachStatementAst extends StatementAst { - type: 'ForEachStatement'; - variable: VariableAst; - iterable: ExpressionAst; - body: ScriptBlockAst; -} - -export interface SwitchStatementAst extends StatementAst { - type: 'SwitchStatement'; - value: ExpressionAst; - clauses: SwitchClauseAst[]; -} - -export interface SwitchClauseAst extends ASTNode { - type: 'SwitchClause'; - pattern: ExpressionAst; - body: ScriptBlockAst; -} - -export interface TryStatementAst extends StatementAst { - type: 'TryStatement'; - body: ScriptBlockAst; - catchClauses: CatchClauseAst[]; - finallyClause?: FinallyClauseAst; -} - -export interface CatchClauseAst extends ASTNode { - type: 'CatchClause'; - exceptionType?: string; - body: ScriptBlockAst; -} - -export interface FinallyClauseAst extends ASTNode { - type: 'FinallyClause'; - body: ScriptBlockAst; -} - -// 二元操作表达式 -export interface BinaryExpressionAst extends ExpressionAst { - type: 'BinaryExpression'; - left: ExpressionAst; - operator: string; - right: ExpressionAst; -} - -// 一元操作表达式 -export interface UnaryExpressionAst extends ExpressionAst { - type: 'UnaryExpression'; - operator: string; - operand: ExpressionAst; -} - -// 括号表达式 -export interface ParenthesizedExpressionAst extends ExpressionAst { - type: 'ParenthesizedExpression'; - expression: ExpressionAst; -} - -// 方法调用表达式 -export interface MethodCallAst extends ExpressionAst { - type: 'MethodCall'; - object: ExpressionAst; - methodName: string; - arguments: ExpressionAst[]; -} - -// 属性访问表达式 -export interface PropertyAccessAst extends ExpressionAst { - type: 'PropertyAccess'; - object: ExpressionAst; - propertyName: string; -} - -// 索引访问表达式 -export interface IndexAccessAst extends ExpressionAst { - type: 'IndexAccess'; - object: ExpressionAst; - index: ExpressionAst; -} - -// 注释节点 -export interface CommentAst extends ASTNode { - type: 'Comment'; - text: string; - isMultiline: boolean; -} - -// 空白节点 -export interface WhitespaceAst extends ASTNode { - type: 'Whitespace'; - text: string; -} - -// 工厂函数,用于创建AST节点 -export class ASTNodeFactory { - static createScriptBlock(statements: StatementAst[], start: number, end: number, line: number, column: number): ScriptBlockAst { - return { - type: 'ScriptBlock', - statements, - start, - end, - line, - column - }; - } - - static createPipeline(elements: PipelineElementAst[], start: number, end: number, line: number, column: number): PipelineAst { - return { - type: 'Pipeline', - elements, - start, - end, - line, - column - }; - } - - static createCommand(commandName: string, parameters: ParameterAst[], args: ExpressionAst[], start: number, end: number, line: number, column: number): CommandAst { - return { - type: 'Command', - commandName, - parameters, - arguments: args, - start, - end, - line, - column - }; - } - - static createAssignment(left: ExpressionAst, operator: string, right: ExpressionAst, start: number, end: number, line: number, column: number): AssignmentAst { - return { - type: 'Assignment', - left, - operator, - right, - start, - end, - line, - column - }; - } - - static createVariable(name: string, start: number, end: number, line: number, column: number): VariableAst { - return { - type: 'Variable', - name, - start, - end, - line, - column - }; - } - - static createLiteral(value: any, literalType: 'String' | 'Number' | 'Boolean' | 'Null', start: number, end: number, line: number, column: number): LiteralAst { - return { - type: 'Literal', - value, - literalType, - start, - end, - line, - column - }; - } - - static createBinaryExpression(left: ExpressionAst, operator: string, right: ExpressionAst, start: number, end: number, line: number, column: number): BinaryExpressionAst { - return { - type: 'BinaryExpression', - left, - operator, - right, - start, - end, - line, - column - }; - } - - static createIfStatement(condition: ExpressionAst, ifBody: ScriptBlockAst, elseIfClauses: ElseIfClauseAst[], elseBody: ScriptBlockAst | undefined, start: number, end: number, line: number, column: number): IfStatementAst { - return { - type: 'IfStatement', - condition, - ifBody, - elseIfClauses, - elseBody, - start, - end, - line, - column - }; - } - - static createFunctionDefinition(name: string, parameters: ParameterAst[], body: ScriptBlockAst, start: number, end: number, line: number, column: number): FunctionDefinitionAst { - return { - type: 'FunctionDefinition', - name, - parameters, - body, - start, - end, - line, - column - }; - } - - static createComment(text: string, isMultiline: boolean, start: number, end: number, line: number, column: number): CommentAst { - return { - type: 'Comment', - text, - isMultiline, - start, - end, - line, - column - }; - } -} - -// AST访问者模式接口 -export interface ASTVisitor { - visitScriptBlock(node: ScriptBlockAst): T; - visitPipeline(node: PipelineAst): T; - visitCommand(node: CommandAst): T; - visitAssignment(node: AssignmentAst): T; - visitVariable(node: VariableAst): T; - visitLiteral(node: LiteralAst): T; - visitBinaryExpression(node: BinaryExpressionAst): T; - visitIfStatement(node: IfStatementAst): T; - visitFunctionDefinition(node: FunctionDefinitionAst): T; - visitComment(node: CommentAst): T; -} - -// AST遍历工具类 -export class ASTTraverser { - static traverse(node: ASTNode, visitor: Partial>): T | undefined { - switch (node.type) { - case 'ScriptBlock': - return visitor.visitScriptBlock?.(node as ScriptBlockAst); - case 'Pipeline': - return visitor.visitPipeline?.(node as PipelineAst); - case 'Command': - return visitor.visitCommand?.(node as CommandAst); - case 'Assignment': - return visitor.visitAssignment?.(node as AssignmentAst); - case 'Variable': - return visitor.visitVariable?.(node as VariableAst); - case 'Literal': - return visitor.visitLiteral?.(node as LiteralAst); - case 'BinaryExpression': - return visitor.visitBinaryExpression?.(node as BinaryExpressionAst); - case 'IfStatement': - return visitor.visitIfStatement?.(node as IfStatementAst); - case 'FunctionDefinition': - return visitor.visitFunctionDefinition?.(node as FunctionDefinitionAst); - case 'Comment': - return visitor.visitComment?.(node as CommentAst); - default: - return undefined; - } - } -} diff --git a/frontend/src/common/prettier/plugins/powershell/code-generator.ts b/frontend/src/common/prettier/plugins/powershell/code-generator.ts deleted file mode 100644 index ee80bf1..0000000 --- a/frontend/src/common/prettier/plugins/powershell/code-generator.ts +++ /dev/null @@ -1,566 +0,0 @@ -/** - * PowerShell 代码生成器 - * 遍历AST并根据格式化规则生成格式化的PowerShell代码 - */ - -import { - ASTNode, - ScriptBlockAst, - StatementAst, - ExpressionAst, - PipelineAst, - CommandAst, - AssignmentAst, - VariableAst, - LiteralAst, - BinaryExpressionAst, - IfStatementAst, - FunctionDefinitionAst, - ParameterAst, - CommentAst, - PipelineElementAst, - ElseIfClauseAst, - ASTTraverser -} from './ast'; -import { FormatterRules, FormatterOptions } from './formatter-rules'; - -export class PowerShellCodeGenerator { - private rules: FormatterRules; - private indentLevel: number = 0; - private output: string[] = []; - private currentLineLength: number = 0; - private needsNewline: boolean = false; - private lastWasComment: boolean = false; - - constructor(options: Partial = {}) { - this.rules = new FormatterRules(options); - } - - /** - * 生成格式化的PowerShell代码 - */ - public generate(ast: ScriptBlockAst, comments: CommentAst[] = []): string { - this.output = []; - this.indentLevel = 0; - this.currentLineLength = 0; - this.needsNewline = false; - this.lastWasComment = false; - - // 首先处理文档开头的注释 - this.generateLeadingComments(comments); - - // 生成主体代码 - this.generateScriptBlock(ast); - - // 处理文档末尾 - this.handleFinalNewline(); - - const result = this.output.join(''); - return this.postProcess(result); - } - - private generateScriptBlock(node: ScriptBlockAst): void { - for (let i = 0; i < node.statements.length; i++) { - const statement = node.statements[i]; - const nextStatement = i < node.statements.length - 1 ? node.statements[i + 1] : null; - - this.generateStatement(statement); - - // 在语句之间添加适当的空行 - if (nextStatement) { - this.addStatementSeparation(statement, nextStatement); - } - } - } - - private generateStatement(statement: StatementAst): void { - switch (statement.type) { - case 'Pipeline': - this.generatePipeline(statement as PipelineAst); - break; - case 'Assignment': - this.generateAssignment(statement as AssignmentAst); - break; - case 'IfStatement': - this.generateIfStatement(statement as IfStatementAst); - break; - case 'FunctionDefinition': - this.generateFunctionDefinition(statement as FunctionDefinitionAst); - break; - case 'RawText': - // 处理解析失败时的原始文本 - this.append((statement as any).value); - return; // 不需要添加额外的换行 - default: - this.append(`/* Unsupported statement type: ${statement.type} */`); - break; - } - - this.ensureNewline(); - } - - private generatePipeline(pipeline: PipelineAst): void { - if (!this.rules.formatPipelines) { - // 简单连接所有元素 - for (let i = 0; i < pipeline.elements.length; i++) { - if (i > 0) { - this.append(' | '); - } - this.generatePipelineElement(pipeline.elements[i]); - } - return; - } - - const style = this.rules.getPipelineStyle(pipeline.elements.length); - - if (style === 'multiline') { - this.generateMultilinePipeline(pipeline); - } else { - this.generateOnelinePipeline(pipeline); - } - } - - private generateOnelinePipeline(pipeline: PipelineAst): void { - for (let i = 0; i < pipeline.elements.length; i++) { - if (i > 0) { - this.append(' | '); - } - this.generatePipelineElement(pipeline.elements[i]); - } - } - - private generateMultilinePipeline(pipeline: PipelineAst): void { - for (let i = 0; i < pipeline.elements.length; i++) { - if (i > 0) { - this.appendLine(' |'); - this.appendIndent(); - } - this.generatePipelineElement(pipeline.elements[i]); - } - } - - private generatePipelineElement(element: PipelineElementAst): void { - this.generateExpression(element.expression); - } - - private generateExpression(expression: ExpressionAst): void { - switch (expression.type) { - case 'Command': - this.generateCommand(expression as CommandAst); - break; - case 'Variable': - this.generateVariable(expression as VariableAst); - break; - case 'Literal': - this.generateLiteral(expression as LiteralAst); - break; - case 'BinaryExpression': - this.generateBinaryExpression(expression as BinaryExpressionAst); - break; - case 'ParenthesizedExpression': - this.append('('); - this.generateExpression((expression as any).expression); - this.append(')'); - break; - case 'Array': - this.generateArray(expression as any); - break; - case 'Hashtable': - this.generateHashtable(expression as any); - break; - case 'ScriptBlockExpression': - this.generateScriptBlockExpression(expression as any); - break; - default: - this.append(`/* Unsupported expression type: ${expression.type} */`); - break; - } - } - - private generateCommand(command: CommandAst): void { - // 保持cmdlet名称的连字符,不进行破坏性的格式化 - let commandName = command.commandName; - - // 只有在明确指定要改变大小写时才进行格式化 - // 但绝对不能删除连字符 - if (this.rules.shouldFormatCommandCase()) { - commandName = this.rules.formatCommandCase(commandName); - } - - this.append(commandName); - - // 生成参数 - for (const param of command.parameters) { - this.append(' '); - this.generateParameter(param); - } - - // 生成位置参数 - for (const arg of command.arguments) { - this.append(' '); - this.generateExpression(arg); - } - } - - private generateParameter(parameter: ParameterAst): void { - const paramName = this.rules.formatParameterCase(parameter.name); - this.append(paramName); - - if (parameter.value) { - this.append(' '); - this.generateExpression(parameter.value); - } - } - - private generateVariable(variable: VariableAst): void { - const formattedName = this.rules.formatVariableCase(variable.name); - this.append(formattedName); - } - - private generateLiteral(literal: LiteralAst): void { - if (literal.literalType === 'String') { - const formattedString = this.rules.formatQuotes(literal.value as string); - this.append(formattedString); - } else { - this.append(String(literal.value)); - } - } - - private generateBinaryExpression(expression: BinaryExpressionAst): void { - this.generateExpression(expression.left); - - // 根据PowerShell官方规范,属性访问操作符绝对不能加空格 - if (expression.operator === '.' || - expression.operator === '::' || - expression.operator === '[' || - expression.operator === ']' || - expression.operator === '@{') { - // 属性访问是PowerShell面向对象的核心,必须保持紧凑 - this.append(expression.operator); - } else { - // 使用格式化规则处理其他操作符 - const formattedOperator = this.rules.formatOperatorSpacing(expression.operator); - this.append(formattedOperator); - } - - this.generateExpression(expression.right); - } - - private generateAssignment(assignment: AssignmentAst): void { - this.generateExpression(assignment.left); - - const formattedOperator = this.rules.formatOperatorSpacing(assignment.operator); - this.append(formattedOperator); - - this.generateExpression(assignment.right); - } - - private generateIfStatement(ifStmt: IfStatementAst): void { - // if 条件 - this.append('if '); - this.append(this.rules.formatParentheses('')); - this.append('('); - this.generateExpression(ifStmt.condition); - this.append(')'); - - // if 主体 - this.append(this.rules.getBraceStart()); - this.appendLine(''); - this.indent(); - this.generateScriptBlock(ifStmt.ifBody); - this.outdent(); - this.appendIndent(); - this.append('}'); - - // elseif 子句 - for (const elseIfClause of ifStmt.elseIfClauses) { - this.generateElseIfClause(elseIfClause); - } - - // else 子句 - if (ifStmt.elseBody) { - this.append(' else'); - this.append(this.rules.getBraceStart()); - this.appendLine(''); - this.indent(); - this.generateScriptBlock(ifStmt.elseBody); - this.outdent(); - this.appendIndent(); - this.append('}'); - } - } - - private generateElseIfClause(elseIf: ElseIfClauseAst): void { - this.append(' elseif ('); - this.generateExpression(elseIf.condition); - this.append(')'); - this.append(this.rules.getBraceStart()); - this.appendLine(''); - this.indent(); - this.generateScriptBlock(elseIf.body); - this.outdent(); - this.appendIndent(); - this.append('}'); - } - - private generateFunctionDefinition(func: FunctionDefinitionAst): void { - // 函数前的空行 - if (this.rules.blankLinesAroundFunctions > 0) { - for (let i = 0; i < this.rules.blankLinesAroundFunctions; i++) { - this.appendLine(''); - } - } - - this.append('function '); - this.append(func.name); - - // 参数列表 - if (func.parameters.length > 0) { - this.append('('); - for (let i = 0; i < func.parameters.length; i++) { - if (i > 0) { - this.append(this.rules.formatComma()); - } - this.generateParameter(func.parameters[i]); - } - this.append(')'); - } - - // 函数体 - this.append(this.rules.getBraceStart()); - this.appendLine(''); - this.indent(); - this.generateScriptBlock(func.body); - this.outdent(); - this.appendIndent(); - this.append('}'); - - // 函数后的空行 - if (this.rules.blankLinesAroundFunctions > 0) { - for (let i = 0; i < this.rules.blankLinesAroundFunctions; i++) { - this.appendLine(''); - } - } - } - - private generateLeadingComments(comments: CommentAst[]): void { - const leadingComments = comments.filter(c => this.isLeadingComment(c)); - for (const comment of leadingComments) { - this.generateComment(comment); - this.appendLine(''); - } - } - - private generateComment(comment: CommentAst): void { - if (!this.rules.formatComments) { - this.append(comment.text); - return; - } - - if (comment.isMultiline) { - this.generateMultilineComment(comment.text); - } else { - this.generateSingleLineComment(comment.text); - } - - this.lastWasComment = true; - } - - private generateArray(arrayExpr: any): void { - this.append('@('); - if (arrayExpr.elements && arrayExpr.elements.length > 0) { - for (let i = 0; i < arrayExpr.elements.length; i++) { - if (i > 0) { - this.append(this.rules.formatComma()); - } - this.generateExpression(arrayExpr.elements[i]); - } - } - this.append(')'); - } - - private generateHashtable(hashtableExpr: any): void { - this.append('@{'); - - if (hashtableExpr.entries && hashtableExpr.entries.length > 0) { - // 强制使用紧凑格式,避免换行问题 - for (let i = 0; i < hashtableExpr.entries.length; i++) { - const entry = hashtableExpr.entries[i]; - - this.generateExpression(entry.key); - this.append('='); - this.generateExpression(entry.value); - - // 如果不是最后一个条目,添加分号和空格 - if (i < hashtableExpr.entries.length - 1) { - this.append('; '); - } - } - } - - this.append('}'); - } - - private generateScriptBlockExpression(scriptBlockExpr: any): void { - this.append('{'); - - // 对原始内容应用基本的格式化规则 - if (scriptBlockExpr.rawContent) { - const formattedContent = this.formatScriptBlockContent(scriptBlockExpr.rawContent); - this.append(formattedContent); - } else if (scriptBlockExpr.expression) { - // 兼容旧格式 - this.generateExpression(scriptBlockExpr.expression); - } - - this.append('}'); - } - - private formatScriptBlockContent(content: string): string { - if (!content || !content.trim()) { - return content; - } - - // 应用PowerShell官方规范的格式化规则 - let formatted = content.trim(); - - // 1. 保护所有属性访问操作符 - 这是最关键的 - // 匹配所有形式的属性访问:$var.Property, $_.Property, $obj.Method.Property等 - formatted = formatted.replace(/(\$[a-zA-Z_][a-zA-Z0-9_]*|\$_)\s*\.\s*([a-zA-Z_][a-zA-Z0-9_]*)/g, '$1.$2'); - - // 2. 保护方法调用中的点号 - formatted = formatted.replace(/(\w)\s*\.\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\(/g, '$1.$2('); - - // 3. 确保数字单位不被分离 - formatted = formatted.replace(/(\d+)\s*(KB|MB|GB|TB|PB)/gi, '$1$2'); - - // 4. PowerShell比较和逻辑操作符需要前后空格 - const powershellOps = [ - '-eq', '-ne', '-lt', '-le', '-gt', '-ge', - '-like', '-notlike', '-match', '-notmatch', - '-contains', '-notcontains', '-in', '-notin', - '-is', '-isnot', '-as', '-and', '-or', '-not', '-xor' - ]; - - for (const op of powershellOps) { - const regex = new RegExp(`\\s*${op.replace('-', '\\-')}\\s*`, 'gi'); - formatted = formatted.replace(regex, ` ${op} `); - } - - // 5. 清理多余空格,但保护属性访问 - formatted = formatted.replace(/\s{2,}/g, ' ').trim(); - - // 6. 最终检查:确保没有属性访问被破坏 - formatted = formatted.replace(/(\$\w+|\$_)\s+\.\s*/g, '$1.'); - - return formatted; - } - - - private generateSingleLineComment(text: string): void { - // 确保单行注释以 # 开头 - const cleanText = text.startsWith('#') ? text : `# ${text}`; - this.append(cleanText); - } - - private generateMultilineComment(text: string): void { - // 多行注释保持原格式 - this.append(text); - } - - private isLeadingComment(comment: CommentAst): boolean { - // 简单判断:如果注释在文档开头,就认为是前导注释 - return comment.line <= 3; - } - - private addStatementSeparation(current: StatementAst, next: StatementAst): void { - // 函数之间添加空行 - if (current.type === 'FunctionDefinition' || next.type === 'FunctionDefinition') { - this.appendLine(''); - } - - // 控制结构前添加空行 - if (next.type === 'IfStatement' && !this.lastWasComment) { - this.appendLine(''); - } - } - - private handleFinalNewline(): void { - if (this.rules.insertFinalNewline && this.output.length > 0) { - const lastLine = this.output[this.output.length - 1]; - if (!lastLine.endsWith(this.rules.getNewline())) { - this.appendLine(''); - } - } - } - - private postProcess(code: string): string { - let result = code; - - // 清理多余的空行 - if (this.rules.maxConsecutiveEmptyLines >= 0) { - const maxEmpty = this.rules.maxConsecutiveEmptyLines; - const emptyLinePattern = new RegExp(`(${this.rules.getNewline()}){${maxEmpty + 2},}`, 'g'); - const replacement = this.rules.getNewline().repeat(maxEmpty + 1); - result = result.replace(emptyLinePattern, replacement); - } - - // 清理行尾空白 - if (this.rules.trimTrailingWhitespace) { - result = result.replace(/ +$/gm, ''); - } - - return result; - } - - // 辅助方法 - private append(text: string): void { - this.output.push(text); - this.currentLineLength += text.length; - this.needsNewline = false; - this.lastWasComment = false; - } - - private appendLine(text: string): void { - this.output.push(text + this.rules.getNewline()); - this.currentLineLength = 0; - this.needsNewline = false; - this.lastWasComment = false; - } - - private appendIndent(): void { - const indent = this.rules.getIndent(this.indentLevel); - this.append(indent); - } - - private ensureNewline(): void { - if (!this.needsNewline) { - this.appendLine(''); - this.needsNewline = true; - } - } - - private indent(): void { - this.indentLevel++; - } - - private outdent(): void { - this.indentLevel = Math.max(0, this.indentLevel - 1); - } - - private shouldWrapLine(): boolean { - return this.currentLineLength > this.rules.printWidth; - } -} - -/** - * 便捷函数:格式化PowerShell AST - */ -export function formatPowerShellAST( - ast: ScriptBlockAst, - comments: CommentAst[] = [], - options: Partial = {} -): string { - const generator = new PowerShellCodeGenerator(options); - return generator.generate(ast, comments); -} diff --git a/frontend/src/common/prettier/plugins/powershell/formatter-rules.ts b/frontend/src/common/prettier/plugins/powershell/formatter-rules.ts deleted file mode 100644 index b589d76..0000000 --- a/frontend/src/common/prettier/plugins/powershell/formatter-rules.ts +++ /dev/null @@ -1,440 +0,0 @@ -/** - * PowerShell 格式化规则引擎 - * 定义各种可配置的代码格式化规则和策略 - */ - -export interface FormatterOptions { - // 基本格式化选项 - indentSize: number; // 缩进大小 - useTabsForIndentation: boolean; // 使用制表符还是空格 - printWidth: number; // 行最大长度 - endOfLine: 'lf' | 'crlf' | 'cr' | 'auto'; // 行尾符类型 - - // 空格和间距 - spaceAroundOperators: boolean; // 操作符周围的空格 - spaceAfterCommas: boolean; // 逗号后的空格 - spaceAfterSemicolons: boolean; // 分号后的空格 - spaceInsideParentheses: boolean; // 括号内的空格 - spaceInsideBrackets: boolean; // 方括号内的空格 - spaceInsideBraces: boolean; // 大括号内的空格 - - // 换行和空行 - maxConsecutiveEmptyLines: number; // 最大连续空行数 - insertFinalNewline: boolean; // 文件末尾插入换行符 - trimTrailingWhitespace: boolean; // 删除行尾空白 - blankLinesAroundFunctions: number; // 函数前后的空行数 - blankLinesAroundClasses: number; // 类前后的空行数 - blankLinesAroundIfStatements: boolean; // if语句前后的空行 - - // 括号和大括号 - braceStyle: 'allman' | 'otbs' | 'stroustrup'; // 大括号风格 - alwaysParenthesizeArrowFunctions: boolean; // 箭头函数总是用括号 - - // PowerShell特定选项 - formatPipelines: boolean; // 格式化管道 - pipelineStyle: 'oneline' | 'multiline' | 'auto'; // 管道风格 - formatParameters: boolean; // 格式化参数 - parameterAlignment: 'left' | 'right' | 'auto'; // 参数对齐方式 - formatHashtables: boolean; // 格式化哈希表 - hashtableStyle: 'compact' | 'expanded'; // 哈希表风格 - formatArrays: boolean; // 格式化数组 - arrayStyle: 'compact' | 'expanded'; // 数组风格 - formatComments: boolean; // 格式化注释 - commentAlignment: 'left' | 'preserve'; // 注释对齐方式 - - // 命名和大小写 - preferredCommandCase: 'lowercase' | 'uppercase' | 'pascalcase' | 'preserve'; // 命令大小写 - preferredParameterCase: 'lowercase' | 'uppercase' | 'pascalcase' | 'preserve'; // 参数大小写 - preferredVariableCase: 'camelcase' | 'pascalcase' | 'preserve'; // 变量大小写 - - // 引号和字符串 - quotestyle: 'single' | 'double' | 'preserve'; // 引号风格 - escapeNonAscii: boolean; // 转义非ASCII字符 - - // 长度和换行 - wrapLongLines: boolean; // 自动换行长行 - wrapParameters: boolean; // 换行长参数列表 - wrapArrays: boolean; // 换行长数组 - wrapHashtables: boolean; // 换行长哈希表 -} - -export const DEFAULT_OPTIONS: FormatterOptions = { - // 基本选项 - indentSize: 4, - useTabsForIndentation: false, - printWidth: 120, - endOfLine: 'auto', - - // 空格设置 - spaceAroundOperators: true, - spaceAfterCommas: true, - spaceAfterSemicolons: true, - spaceInsideParentheses: false, - spaceInsideBrackets: false, - spaceInsideBraces: true, - - // 空行设置 - maxConsecutiveEmptyLines: 2, - insertFinalNewline: true, - trimTrailingWhitespace: true, - blankLinesAroundFunctions: 1, - blankLinesAroundClasses: 1, - blankLinesAroundIfStatements: false, - - // 括号风格 - braceStyle: 'otbs', // One True Brace Style - alwaysParenthesizeArrowFunctions: false, - - // PowerShell特定 - formatPipelines: true, - pipelineStyle: 'auto', - formatParameters: true, - parameterAlignment: 'left', - formatHashtables: true, - hashtableStyle: 'compact', - formatArrays: true, - arrayStyle: 'compact', - formatComments: true, - commentAlignment: 'preserve', - - // 命名约定 - preferredCommandCase: 'pascalcase', - preferredParameterCase: 'preserve', - preferredVariableCase: 'preserve', - - // 字符串设置 - quotestyle: 'preserve', - escapeNonAscii: false, - - // 长度处理 - wrapLongLines: true, - wrapParameters: true, - wrapArrays: true, - wrapHashtables: true -}; - -/** - * 格式化规则类,包含各种格式化策略的实现 - */ -export class FormatterRules { - private options: FormatterOptions; - - constructor(options: Partial = {}) { - this.options = { ...DEFAULT_OPTIONS, ...options }; - } - - /** - * 获取缩进字符串 - */ - getIndent(level: number): string { - if (level <= 0) return ''; - - const indentChar = this.options.useTabsForIndentation ? '\t' : ' '; - const indentSize = this.options.useTabsForIndentation ? 1 : this.options.indentSize; - - return indentChar.repeat(level * indentSize); - } - - /** - * 获取换行符 - */ - getNewline(): string { - switch (this.options.endOfLine) { - case 'lf': return '\n'; - case 'crlf': return '\r\n'; - case 'cr': return '\r'; - case 'auto': - default: - // 在浏览器环境中默认使用 LF - return '\n'; - } - } - - /** - * 格式化操作符周围的空格 - */ - formatOperatorSpacing(operator: string): string { - if (!this.options.spaceAroundOperators) { - return operator; - } - - // PowerShell语法中绝对不能加空格的操作符(官方规范) - const noSpaceOperators = [ - '.', '::', // 属性访问和静态成员访问 - 这是PowerShell面向对象的核心 - '[', ']', // 数组索引和类型转换 - '(', ')', '{', '}', // 括号 - '@{', // 哈希表字面量开始 - ';', // 哈希表和语句分隔符 - '-', // cmdlet连字符(Get-ChildItem中的-) - '::' // 静态成员访问 - ]; - - if (noSpaceOperators.includes(operator)) { - return operator; - } - - // PowerShell比较操作符需要空格 - const powershellOperators = ['-eq', '-ne', '-lt', '-le', '-gt', '-ge', - '-like', '-notlike', '-match', '-notmatch', - '-contains', '-notcontains', '-in', '-notin', - '-is', '-isnot', '-as', '-and', '-or', '-not', '-xor']; - - if (powershellOperators.some(op => operator.toLowerCase() === op)) { - return ` ${operator} `; - } - - // 算术和赋值操作符需要空格 - const spaceOperators = ['=', '+=', '-=', '*=', '/=', '%=', '+', '*', '/', '%']; - if (spaceOperators.includes(operator)) { - return ` ${operator} `; - } - - return operator; - } - - /** - * 格式化逗号后的空格 - */ - formatComma(): string { - return this.options.spaceAfterCommas ? ', ' : ','; - } - - /** - * 格式化分号后的空格 - */ - formatSemicolon(): string { - return this.options.spaceAfterSemicolons ? '; ' : ';'; - } - - /** - * 格式化括号内的空格 - */ - formatParentheses(content: string): string { - if (this.options.spaceInsideParentheses) { - return `( ${content} )`; - } - return `(${content})`; - } - - /** - * 格式化方括号内的空格 - */ - formatBrackets(content: string): string { - if (this.options.spaceInsideBrackets) { - return `[ ${content} ]`; - } - return `[${content}]`; - } - - /** - * 格式化大括号内的空格 - */ - formatBraces(content: string): string { - if (this.options.spaceInsideBraces) { - return `{ ${content} }`; - } - return `{${content}}`; - } - - /** - * 获取大括号的开始位置 - */ - getBraceStart(): string { - switch (this.options.braceStyle) { - case 'allman': - return this.getNewline() + '{'; - case 'stroustrup': - return this.getNewline() + '{'; - case 'otbs': - default: - return ' {'; - } - } - - /** - * 格式化命令名的大小写 - */ - formatCommandCase(command: string): string { - switch (this.options.preferredCommandCase) { - case 'lowercase': - return command.toLowerCase(); - case 'uppercase': - return command.toUpperCase(); - case 'pascalcase': - return this.toPascalCasePreservingHyphens(command); - case 'preserve': - default: - return command; - } - } - - /** - * 检查是否应该格式化命令大小写 - */ - shouldFormatCommandCase(): boolean { - return this.options.preferredCommandCase !== 'preserve'; - } - - /** - * 格式化参数名的大小写 - */ - formatParameterCase(parameter: string): string { - switch (this.options.preferredParameterCase) { - case 'lowercase': - return parameter.toLowerCase(); - case 'uppercase': - return parameter.toUpperCase(); - case 'pascalcase': - return this.toPascalCase(parameter); - case 'preserve': - default: - return parameter; - } - } - - /** - * 格式化变量名的大小写 - */ - formatVariableCase(variable: string): string { - if (!variable.startsWith('$')) { - return variable; - } - - const variableName = variable.substring(1); - let formattedName: string; - - switch (this.options.preferredVariableCase) { - case 'camelcase': - formattedName = this.toCamelCase(variableName); - break; - case 'pascalcase': - formattedName = this.toPascalCase(variableName); - break; - case 'preserve': - default: - formattedName = variableName; - break; - } - - return '$' + formattedName; - } - - /** - * 格式化字符串引号 - */ - formatQuotes(value: string): string { - if (this.options.quotestyle === 'preserve') { - return value; - } - - const content = this.extractStringContent(value); - - switch (this.options.quotestyle) { - case 'single': - return `'${content.replace(/'/g, "''")}'`; - case 'double': - return `"${content.replace(/"/g, '""')}"`; - default: - return value; - } - } - - /** - * 检查是否需要换行 - */ - shouldWrapLine(line: string): boolean { - return this.options.wrapLongLines && line.length > this.options.printWidth; - } - - /** - * 获取管道样式 - */ - getPipelineStyle(elementCount: number): 'oneline' | 'multiline' { - switch (this.options.pipelineStyle) { - case 'oneline': - return 'oneline'; - case 'multiline': - return 'multiline'; - case 'auto': - default: - return elementCount > 2 ? 'multiline' : 'oneline'; - } - } - - /** - * 获取哈希表样式 - */ - getHashtableStyle(entryCount: number): 'compact' | 'expanded' { - if (this.options.hashtableStyle === 'compact') { - return 'compact'; - } - if (this.options.hashtableStyle === 'expanded') { - return 'expanded'; - } - // auto logic: 对于小型哈希表默认使用compact,避免不必要的换行 - return entryCount > 5 ? 'expanded' : 'compact'; - } - - /** - * 获取数组样式 - */ - getArrayStyle(elementCount: number): 'compact' | 'expanded' { - if (this.options.arrayStyle === 'compact') { - return 'compact'; - } - if (this.options.arrayStyle === 'expanded') { - return 'expanded'; - } - // auto logic could be added here - return elementCount > 5 ? 'expanded' : 'compact'; - } - - // 辅助方法 - private toPascalCase(str: string): string { - return str.split(/[-_\s]/) - .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) - .join(''); - } - - /** - * 转换为PascalCase但保留连字符(专门用于PowerShell cmdlet) - */ - private toPascalCasePreservingHyphens(str: string): string { - return str.split('-') - .map(part => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()) - .join('-'); - } - - private toCamelCase(str: string): string { - const pascalCase = this.toPascalCase(str); - return pascalCase.charAt(0).toLowerCase() + pascalCase.slice(1); - } - - private extractStringContent(str: string): string { - if ((str.startsWith('"') && str.endsWith('"')) || - (str.startsWith("'") && str.endsWith("'"))) { - return str.slice(1, -1); - } - return str; - } - - // Getter methods for options - get indentSize(): number { return this.options.indentSize; } - get printWidth(): number { return this.options.printWidth; } - get maxConsecutiveEmptyLines(): number { return this.options.maxConsecutiveEmptyLines; } - get insertFinalNewline(): boolean { return this.options.insertFinalNewline; } - get trimTrailingWhitespace(): boolean { return this.options.trimTrailingWhitespace; } - get blankLinesAroundFunctions(): number { return this.options.blankLinesAroundFunctions; } - get formatPipelines(): boolean { return this.options.formatPipelines; } - get formatParameters(): boolean { return this.options.formatParameters; } - get formatHashtables(): boolean { return this.options.formatHashtables; } - get formatArrays(): boolean { return this.options.formatArrays; } - get formatComments(): boolean { return this.options.formatComments; } - - /** - * 创建规则的副本,可以重写部分选项 - */ - withOptions(overrides: Partial): FormatterRules { - return new FormatterRules({ ...this.options, ...overrides }); - } -} diff --git a/frontend/src/common/prettier/plugins/powershell/index.ts b/frontend/src/common/prettier/plugins/powershell/index.ts deleted file mode 100644 index 7d595be..0000000 --- a/frontend/src/common/prettier/plugins/powershell/index.ts +++ /dev/null @@ -1,208 +0,0 @@ -/** - * Prettier Plugin for PowerShell file formatting - Modular Version - * - * This plugin provides support for formatting PowerShell files (.ps1, .psm1, .psd1) - * using a modular architecture with lexer, parser, AST, and code generator. - */ -import type { Plugin, Parser, Printer, AstPath, Doc } from 'prettier'; -import { PowerShellLexer } from './lexer'; -import { PowerShellParser } from './parser'; -import { ScriptBlockAst, CommentAst } from './ast'; -import { formatPowerShellAST } from './code-generator'; -import { FormatterOptions, DEFAULT_OPTIONS } from './formatter-rules'; - -// PowerShell格式化结果接口 -interface PowerShellParseResult { - ast: ScriptBlockAst; - comments: CommentAst[]; - originalText: string; -} - -const parserName = 'powershell'; - -// 语言配置 -const languages = [ - { - name: 'PowerShell', - aliases: ['powershell', 'pwsh', 'posh'], - parsers: [parserName], - extensions: ['.ps1', '.psm1', '.psd1'], - filenames: ['profile.ps1'], - tmScope: 'source.powershell', - aceMode: 'powershell', - linguistLanguageId: 295, - vscodeLanguageIds: ['powershell'] - }, -]; - -// 解析器配置 -const powershellParser: Parser = { - parse: parseCode, - astFormat: 'powershell', - locStart: (node: PowerShellParseResult) => 0, - locEnd: (node: PowerShellParseResult) => node.originalText.length, -}; - -/** - * 解析PowerShell代码 - */ -async function parseCode(text: string, parsers?: any, options?: any): Promise { - try { - // 词法分析 - const lexer = new PowerShellLexer(text); - const tokens = lexer.tokenize(); - - // 语法分析 - const parser = new PowerShellParser(tokens, text); - const ast = parser.parse(); - const comments = parser.getComments(); - - return { - ast, - comments, - originalText: text - }; - } catch (error) { - console.warn('PowerShell parsing failed, using fallback:', error); - - // 解析失败时,创建一个包含原始文本的简单AST - // 这样可以确保格式化失败时返回原始代码而不是空内容 - return { - ast: { - type: 'ScriptBlock', - statements: [{ - type: 'RawText', - value: text, - start: 0, - end: text.length, - line: 1, - column: 1 - } as any], - start: 0, - end: text.length, - line: 1, - column: 1 - }, - comments: [], - originalText: text - }; - } -} - -/** - * PowerShell代码打印器 - */ -const printPowerShell = (path: AstPath, options: any): Doc => { - const parseResult = path.node; - - try { - // 构建格式化选项 - 优先保持原有格式,避免破坏PowerShell语法 - const formatterOptions: Partial = { - indentSize: options.tabWidth || DEFAULT_OPTIONS.indentSize, - useTabsForIndentation: options.useTabs || DEFAULT_OPTIONS.useTabsForIndentation, - printWidth: options.printWidth || DEFAULT_OPTIONS.printWidth, - spaceAroundOperators: true, - formatPipelines: true, - formatParameters: true, - formatHashtables: true, - hashtableStyle: 'compact', // 强制使用紧凑格式,避免不必要的换行 - formatArrays: true, - arrayStyle: 'compact', - formatComments: true, - maxConsecutiveEmptyLines: 1, - insertFinalNewline: true, - trimTrailingWhitespace: true, - blankLinesAroundFunctions: 1, - braceStyle: 'otbs', - preferredCommandCase: 'preserve', // 保持原有命令大小写,不破坏语法 - preferredParameterCase: 'preserve', - preferredVariableCase: 'preserve', - quotestyle: 'preserve', - wrapLongLines: true - }; - - // 使用新的模块化格式化器 - const formattedCode = formatPowerShellAST( - parseResult.ast, - parseResult.comments, - formatterOptions - ); - - return formattedCode; - } catch (error) { - console.warn('PowerShell formatting failed, returning original code:', error); - return parseResult.originalText; - } -}; - -// 打印器配置 -const powershellPrinter: Printer = { - print: printPowerShell, -}; - -// 插件选项配置 -const options = { - // PowerShell特定格式化选项 - powershellBraceStyle: { - type: 'choice' as const, - category: 'PowerShell', - default: DEFAULT_OPTIONS.braceStyle, - description: 'PowerShell大括号样式', - choices: [ - { value: 'allman', description: 'Allman风格(大括号另起一行)' }, - { value: 'otbs', description: '1TBS风格(大括号同行)' }, - { value: 'stroustrup', description: 'Stroustrup风格' } - ] - }, - powershellCommandCase: { - type: 'choice' as const, - category: 'PowerShell', - default: DEFAULT_OPTIONS.preferredCommandCase, - description: 'PowerShell命令大小写风格', - choices: [ - { value: 'lowercase', description: '小写' }, - { value: 'uppercase', description: '大写' }, - { value: 'pascalcase', description: 'Pascal大小写' }, - { value: 'preserve', description: '保持原样' } - ] - }, - powershellPipelineStyle: { - type: 'choice' as const, - category: 'PowerShell', - default: DEFAULT_OPTIONS.pipelineStyle, - description: 'PowerShell管道样式', - choices: [ - { value: 'oneline', description: '单行' }, - { value: 'multiline', description: '多行' }, - { value: 'auto', description: '自动' } - ] - }, - powershellSpaceAroundOperators: { - type: 'boolean' as const, - category: 'PowerShell', - default: DEFAULT_OPTIONS.spaceAroundOperators, - description: '在操作符周围添加空格' - }, - powershellMaxEmptyLines: { - type: 'int' as const, - category: 'PowerShell', - default: DEFAULT_OPTIONS.maxConsecutiveEmptyLines, - description: '最大连续空行数' - } -}; - -const powershellPlugin: Plugin = { - languages, - parsers: { - [parserName]: powershellParser, - }, - printers: { - [parserName]: powershellPrinter, - }, - options, -}; - -export default powershellPlugin; -export { languages }; -export const parsers = powershellPlugin.parsers; -export const printers = powershellPlugin.printers; \ No newline at end of file diff --git a/frontend/src/common/prettier/plugins/powershell/lexer.ts b/frontend/src/common/prettier/plugins/powershell/lexer.ts deleted file mode 100644 index 1913594..0000000 --- a/frontend/src/common/prettier/plugins/powershell/lexer.ts +++ /dev/null @@ -1,722 +0,0 @@ -/** - * PowerShell 词法分析器 (Lexer) - * 将PowerShell代码分解为tokens,用于后续的语法分析和格式化 - */ - -export enum TokenType { - // 字面量 - STRING = 'STRING', - NUMBER = 'NUMBER', - VARIABLE = 'VARIABLE', - - // 关键字 - KEYWORD = 'KEYWORD', - FUNCTION = 'FUNCTION', - - // 操作符 - OPERATOR = 'OPERATOR', - ASSIGNMENT = 'ASSIGNMENT', - COMPARISON = 'COMPARISON', - LOGICAL = 'LOGICAL', - ARITHMETIC = 'ARITHMETIC', - - // 分隔符 - LEFT_PAREN = 'LEFT_PAREN', - RIGHT_PAREN = 'RIGHT_PAREN', - LEFT_BRACE = 'LEFT_BRACE', - RIGHT_BRACE = 'RIGHT_BRACE', - LEFT_BRACKET = 'LEFT_BRACKET', - RIGHT_BRACKET = 'RIGHT_BRACKET', - SEMICOLON = 'SEMICOLON', - COMMA = 'COMMA', - DOT = 'DOT', - PIPE = 'PIPE', - - // 特殊 - WHITESPACE = 'WHITESPACE', - NEWLINE = 'NEWLINE', - COMMENT = 'COMMENT', - MULTILINE_COMMENT = 'MULTILINE_COMMENT', - HERE_STRING = 'HERE_STRING', - - // 控制结构 - IF = 'IF', - ELSE = 'ELSE', - ELSEIF = 'ELSEIF', - WHILE = 'WHILE', - FOR = 'FOR', - FOREACH = 'FOREACH', - SWITCH = 'SWITCH', - TRY = 'TRY', - CATCH = 'CATCH', - FINALLY = 'FINALLY', - - // 其他 - IDENTIFIER = 'IDENTIFIER', - CMDLET = 'CMDLET', - PARAMETER = 'PARAMETER', - EOF = 'EOF', - UNKNOWN = 'UNKNOWN' -} - -export interface Token { - type: TokenType; - value: string; - line: number; - column: number; - startIndex: number; - endIndex: number; -} - -export class PowerShellLexer { - private code: string; - private position: number = 0; - private line: number = 1; - private column: number = 1; - private tokens: Token[] = []; - - // PowerShell关键字 - private readonly keywords = new Set([ - 'if', 'else', 'elseif', 'switch', 'while', 'for', 'foreach', 'do', - 'try', 'catch', 'finally', 'throw', 'return', 'break', 'continue', - 'function', 'filter', 'param', 'begin', 'process', 'end', - 'class', 'enum', 'using', 'namespace', 'workflow', 'configuration', - 'dynamicparam', 'exit' - ]); - - // PowerShell比较操作符 - private readonly comparisonOperators = new Set([ - '-eq', '-ne', '-lt', '-le', '-gt', '-ge', - '-like', '-notlike', '-match', '-notmatch', - '-contains', '-notcontains', '-in', '-notin', - '-is', '-isnot', '-as' - ]); - - // PowerShell逻辑操作符 - private readonly logicalOperators = new Set([ - '-and', '-or', '-not', '-xor', '-band', '-bor', '-bxor', '-bnot' - ]); - - constructor(code: string) { - this.code = code; - } - - /** - * 对代码进行词法分析,返回token数组 - */ - public tokenize(): Token[] { - this.position = 0; - this.line = 1; - this.column = 1; - this.tokens = []; - - while (this.position < this.code.length) { - this.skipWhitespace(); - - if (this.position >= this.code.length) { - break; - } - - const token = this.nextToken(); - if (token) { - this.tokens.push(token); - } - } - - this.tokens.push({ - type: TokenType.EOF, - value: '', - line: this.line, - column: this.column, - startIndex: this.position, - endIndex: this.position - }); - - return this.tokens; - } - - private nextToken(): Token | null { - const startPos = this.position; - const startLine = this.line; - const startColumn = this.column; - - const char = this.code[this.position]; - - // 处理换行 - if (char === '\n') { - this.advance(); - return this.createToken(TokenType.NEWLINE, '\n', startPos, startLine, startColumn); - } - - // 处理注释 - if (char === '#') { - return this.tokenizeComment(startPos, startLine, startColumn); - } - - // 处理多行注释 - if (char === '<' && this.peek() === '#') { - return this.tokenizeMultilineComment(startPos, startLine, startColumn); - } - - // 处理字符串 - if (char === '"' || char === "'") { - return this.tokenizeString(startPos, startLine, startColumn); - } - - // 处理Here-String - if (char === '@' && (this.peek() === '"' || this.peek() === "'")) { - return this.tokenizeHereString(startPos, startLine, startColumn); - } - - // 处理哈希表字面量 @{ - if (char === '@' && this.peek() === '{') { - this.advance(); // skip '@' - this.advance(); // skip '{' - return this.createToken(TokenType.LEFT_BRACE, '@{', startPos, startLine, startColumn); - } - - // 处理变量 - if (char === '$') { - return this.tokenizeVariable(startPos, startLine, startColumn); - } - - // 处理数字 - if (this.isDigit(char) || (char === '.' && this.isDigit(this.peek()))) { - return this.tokenizeNumber(startPos, startLine, startColumn); - } - - // 处理操作符和分隔符 - const operatorToken = this.tokenizeOperator(startPos, startLine, startColumn); - if (operatorToken) { - return operatorToken; - } - - // 优先处理PowerShell比较操作符(以-开头) - if (char === '-' && this.isIdentifierStart(this.peek())) { - const potentialOperator = this.peekPowerShellOperator(); - if (potentialOperator) { - return this.tokenizePowerShellOperator(startPos, startLine, startColumn); - } - // 如果不是操作符,可能是参数 - return this.tokenizeParameter(startPos, startLine, startColumn); - } - - // 处理标识符(包括cmdlet和关键字) - if (this.isIdentifierStart(char)) { - return this.tokenizeIdentifier(startPos, startLine, startColumn); - } - - // 处理PowerShell特殊字符 - if (char === '?') { - this.advance(); - return this.createToken(TokenType.OPERATOR, char, startPos, startLine, startColumn); - } - - // 处理独立的减号(可能是负数或减法) - if (char === '-') { - this.advance(); - return this.createToken(TokenType.ARITHMETIC, char, startPos, startLine, startColumn); - } - - // 处理其他可能的特殊字符,作为标识符处理而不是未知字符 - if (this.isPrintableChar(char)) { - this.advance(); - return this.createToken(TokenType.IDENTIFIER, char, startPos, startLine, startColumn); - } - - // 真正的未知字符(非打印字符等) - this.advance(); - return this.createToken(TokenType.UNKNOWN, char, startPos, startLine, startColumn); - } - - private tokenizeComment(startPos: number, startLine: number, startColumn: number): Token { - let value = ''; - while (this.position < this.code.length && this.code[this.position] !== '\n') { - value += this.code[this.position]; - this.advance(); - } - return this.createToken(TokenType.COMMENT, value, startPos, startLine, startColumn); - } - - private tokenizeMultilineComment(startPos: number, startLine: number, startColumn: number): Token { - let value = ''; - this.advance(); // skip '<' - this.advance(); // skip '#' - value += '<#'; - - while (this.position < this.code.length - 1) { - if (this.code[this.position] === '#' && this.code[this.position + 1] === '>') { - value += '#>'; - this.advance(); - this.advance(); - break; - } - value += this.code[this.position]; - this.advance(); - } - - return this.createToken(TokenType.MULTILINE_COMMENT, value, startPos, startLine, startColumn); - } - - private tokenizeString(startPos: number, startLine: number, startColumn: number): Token { - const quote = this.code[this.position]; - let value = quote; - this.advance(); - - while (this.position < this.code.length) { - const char = this.code[this.position]; - value += char; - - if (char === quote) { - this.advance(); - break; - } - - // 处理转义字符 - if (char === '`' && quote === '"') { - this.advance(); - if (this.position < this.code.length) { - value += this.code[this.position]; - this.advance(); - } - } else { - this.advance(); - } - } - - return this.createToken(TokenType.STRING, value, startPos, startLine, startColumn); - } - - private tokenizeHereString(startPos: number, startLine: number, startColumn: number): Token { - const quote = this.code[this.position + 1]; // " or ' - let value = `@${quote}`; - this.advance(); // skip '@' - this.advance(); // skip quote - - while (this.position < this.code.length - 1) { - if (this.code[this.position] === quote && this.code[this.position + 1] === '@') { - value += `${quote}@`; - this.advance(); - this.advance(); - break; - } - value += this.code[this.position]; - this.advance(); - } - - return this.createToken(TokenType.HERE_STRING, value, startPos, startLine, startColumn); - } - - private tokenizeVariable(startPos: number, startLine: number, startColumn: number): Token { - let value = '$'; - this.advance(); // skip '$' - - // 处理特殊变量如 $_, $$, $^ - const specialVars = ['_', '$', '^', '?']; - if (specialVars.includes(this.code[this.position])) { - value += this.code[this.position]; - this.advance(); - return this.createToken(TokenType.VARIABLE, value, startPos, startLine, startColumn); - } - - // 处理大括号变量 ${variable name} - if (this.code[this.position] === '{') { - this.advance(); // skip '{' - value += '{'; - while (this.position < this.code.length && this.code[this.position] !== '}') { - value += this.code[this.position]; - this.advance(); - } - if (this.position < this.code.length) { - value += '}'; - this.advance(); // skip '}' - } - return this.createToken(TokenType.VARIABLE, value, startPos, startLine, startColumn); - } - - // 普通变量名 - while (this.position < this.code.length && this.isIdentifierChar(this.code[this.position])) { - value += this.code[this.position]; - this.advance(); - } - - return this.createToken(TokenType.VARIABLE, value, startPos, startLine, startColumn); - } - - private tokenizeNumber(startPos: number, startLine: number, startColumn: number): Token { - let value = ''; - let hasDecimal = false; - - while (this.position < this.code.length) { - const char = this.code[this.position]; - - if (this.isDigit(char)) { - value += char; - this.advance(); - } else if (char === '.' && !hasDecimal && this.isDigit(this.peek())) { - hasDecimal = true; - value += char; - this.advance(); - } else { - break; - } - } - - // 检查是否有PowerShell数字单位后缀(KB, MB, GB, TB, PB) - const unitPattern = /^(KB|MB|GB|TB|PB)/i; - const remainingCode = this.code.substring(this.position); - const unitMatch = remainingCode.match(unitPattern); - - if (unitMatch) { - value += unitMatch[0]; // 使用 [0] 获取完整匹配 - // 移动position到单位后面 - for (let i = 0; i < unitMatch[0].length; i++) { - this.advance(); - } - } - - return this.createToken(TokenType.NUMBER, value, startPos, startLine, startColumn); - } - - private tokenizeOperator(startPos: number, startLine: number, startColumn: number): Token | null { - const char = this.code[this.position]; - - // 双字符操作符 - const twoChar = this.code.substring(this.position, this.position + 2); - const doubleOperators = ['==', '!=', '<=', '>=', '++', '--', '+=', '-=', '*=', '/=', '%=']; - - if (doubleOperators.includes(twoChar)) { - this.advance(); - this.advance(); - return this.createToken(TokenType.OPERATOR, twoChar, startPos, startLine, startColumn); - } - - // 单字符操作符 - switch (char) { - case '=': - this.advance(); - return this.createToken(TokenType.ASSIGNMENT, char, startPos, startLine, startColumn); - case '+': - case '*': - case '/': - case '%': - this.advance(); - return this.createToken(TokenType.ARITHMETIC, char, startPos, startLine, startColumn); - case '-': - // 不在这里处理'-',让PowerShell操作符检查优先处理 - return null; - case '(': - this.advance(); - return this.createToken(TokenType.LEFT_PAREN, char, startPos, startLine, startColumn); - case ')': - this.advance(); - return this.createToken(TokenType.RIGHT_PAREN, char, startPos, startLine, startColumn); - case '{': - this.advance(); - return this.createToken(TokenType.LEFT_BRACE, char, startPos, startLine, startColumn); - case '}': - this.advance(); - return this.createToken(TokenType.RIGHT_BRACE, char, startPos, startLine, startColumn); - case '[': - // 检查是否是PowerShell类型转换 [type] - const typePattern = this.peekTypeConversion(); - if (typePattern) { - return this.tokenizeTypeConversion(startPos, startLine, startColumn); - } - this.advance(); - return this.createToken(TokenType.LEFT_BRACKET, char, startPos, startLine, startColumn); - case ']': - this.advance(); - return this.createToken(TokenType.RIGHT_BRACKET, char, startPos, startLine, startColumn); - case ';': - this.advance(); - return this.createToken(TokenType.SEMICOLON, char, startPos, startLine, startColumn); - case ',': - this.advance(); - return this.createToken(TokenType.COMMA, char, startPos, startLine, startColumn); - case '.': - this.advance(); - return this.createToken(TokenType.DOT, char, startPos, startLine, startColumn); - case '|': - this.advance(); - return this.createToken(TokenType.PIPE, char, startPos, startLine, startColumn); - } - - return null; - } - - private tokenizeIdentifier(startPos: number, startLine: number, startColumn: number): Token { - let value = ''; - - // 改进的标识符识别,支持PowerShell cmdlet格式(动词-名词) - while (this.position < this.code.length) { - const char = this.code[this.position]; - - if (this.isIdentifierChar(char)) { - value += char; - this.advance(); - } else if (char === '-' && value.length > 0 && this.isIdentifierStart(this.peek())) { - // 检查是否是cmdlet格式(动词-名词) - const nextPart = this.peekIdentifierPart(); - if (nextPart && !this.isPowerShellOperator('-' + nextPart)) { - // 这是cmdlet名字的一部分,继续 - value += char; - this.advance(); - } else { - // 这可能是操作符,停止 - break; - } - } else { - break; - } - } - - const lowerValue = value.toLowerCase(); - - // 检查是否是关键字 - if (this.keywords.has(lowerValue)) { - return this.createToken(this.getKeywordTokenType(lowerValue), value, startPos, startLine, startColumn); - } - - // 检查是否是函数(以动词-名词格式) - if (this.isCmdletName(value)) { - return this.createToken(TokenType.CMDLET, value, startPos, startLine, startColumn); - } - - return this.createToken(TokenType.IDENTIFIER, value, startPos, startLine, startColumn); - } - - private tokenizeParameter(startPos: number, startLine: number, startColumn: number): Token { - let value = ''; - - while (this.position < this.code.length && (this.isIdentifierChar(this.code[this.position]) || this.code[this.position] === '-')) { - value += this.code[this.position]; - this.advance(); - } - - const lowerValue = value.toLowerCase(); - - // 检查是否是比较操作符 - if (this.comparisonOperators.has(lowerValue)) { - return this.createToken(TokenType.COMPARISON, value, startPos, startLine, startColumn); - } - - // 检查是否是逻辑操作符 - if (this.logicalOperators.has(lowerValue)) { - return this.createToken(TokenType.LOGICAL, value, startPos, startLine, startColumn); - } - - return this.createToken(TokenType.PARAMETER, value, startPos, startLine, startColumn); - } - - private getKeywordTokenType(keyword: string): TokenType { - switch (keyword) { - case 'if': return TokenType.IF; - case 'else': return TokenType.ELSE; - case 'elseif': return TokenType.ELSEIF; - case 'while': return TokenType.WHILE; - case 'for': return TokenType.FOR; - case 'foreach': return TokenType.FOREACH; - case 'switch': return TokenType.SWITCH; - case 'try': return TokenType.TRY; - case 'catch': return TokenType.CATCH; - case 'finally': return TokenType.FINALLY; - case 'function': return TokenType.FUNCTION; - default: return TokenType.KEYWORD; - } - } - - private isCmdletName(name: string): boolean { - // PowerShell cmdlet通常遵循 Verb-Noun 格式,可能包含多个连字符 - const verbNounPattern = /^[A-Za-z]+(-[A-Za-z]+)+$/; - return verbNounPattern.test(name); - } - - private peekPowerShellOperator(): string | null { - // 检查是否是PowerShell比较或逻辑操作符 - const operatorPatterns = [ - '-eq', '-ne', '-lt', '-le', '-gt', '-ge', - '-like', '-notlike', '-match', '-notmatch', - '-contains', '-notcontains', '-in', '-notin', - '-is', '-isnot', '-as', - '-and', '-or', '-not', '-xor', - '-band', '-bor', '-bxor', '-bnot' - ]; - - for (const op of operatorPatterns) { - if (this.matchesOperator(op)) { - return op; - } - } - return null; - } - - private matchesOperator(operator: string): boolean { - if (this.position + operator.length > this.code.length) { - return false; - } - - const substr = this.code.substring(this.position, this.position + operator.length); - if (substr.toLowerCase() !== operator.toLowerCase()) { - return false; - } - - // 确保操作符后面不是字母数字字符(避免匹配部分单词) - const nextChar = this.position + operator.length < this.code.length - ? this.code[this.position + operator.length] - : ' '; - return !this.isIdentifierChar(nextChar); - } - - private tokenizePowerShellOperator(startPos: number, startLine: number, startColumn: number): Token { - const operator = this.peekPowerShellOperator(); - if (!operator) { - // 如果不是操作符,作为参数处理 - return this.tokenizeParameter(startPos, startLine, startColumn); - } - - // 消费操作符字符 - for (let i = 0; i < operator.length; i++) { - this.advance(); - } - - const lowerOp = operator.toLowerCase(); - - // 确定操作符类型 - if (this.comparisonOperators.has(lowerOp)) { - return this.createToken(TokenType.COMPARISON, operator, startPos, startLine, startColumn); - } else if (this.logicalOperators.has(lowerOp)) { - return this.createToken(TokenType.LOGICAL, operator, startPos, startLine, startColumn); - } else { - return this.createToken(TokenType.OPERATOR, operator, startPos, startLine, startColumn); - } - } - - private peekIdentifierPart(): string | null { - if (this.position + 1 >= this.code.length) { - return null; - } - - let result = ''; - let pos = this.position + 1; // 跳过连字符 - - while (pos < this.code.length && this.isIdentifierChar(this.code[pos])) { - result += this.code[pos]; - pos++; - } - - return result.length > 0 ? result : null; - } - - private isPowerShellOperator(text: string): boolean { - const lowerText = text.toLowerCase(); - return this.comparisonOperators.has(lowerText) || this.logicalOperators.has(lowerText); - } - - private peekTypeConversion(): string | null { - // 检查是否是PowerShell类型转换,如 [int], [string], [datetime] 等 - if (this.code[this.position] !== '[') { - return null; - } - - let pos = this.position + 1; // 跳过 '[' - let typeContent = ''; - - // 查找类型名称 - while (pos < this.code.length && this.code[pos] !== ']') { - typeContent += this.code[pos]; - pos++; - } - - if (pos >= this.code.length || this.code[pos] !== ']') { - return null; // 没有找到匹配的 ']' - } - - // 检查是否是有效的PowerShell类型 - const validTypes = [ - 'int', 'int32', 'int64', 'string', 'bool', 'boolean', 'char', 'byte', - 'double', 'float', 'decimal', 'long', 'short', 'datetime', 'timespan', - 'array', 'hashtable', 'object', 'psobject', 'xml', 'scriptblock', - 'guid', 'uri', 'version', 'regex', 'mailaddress', 'ipaddress' - ]; - - const lowerType = typeContent.toLowerCase().trim(); - if (validTypes.includes(lowerType) || lowerType.includes('.')) { - return `[${typeContent}]`; - } - - return null; - } - - private tokenizeTypeConversion(startPos: number, startLine: number, startColumn: number): Token { - const typeConversion = this.peekTypeConversion(); - if (!typeConversion) { - // 这不应该发生,但作为安全措施 - this.advance(); - return this.createToken(TokenType.LEFT_BRACKET, '[', startPos, startLine, startColumn); - } - - // 消费整个类型转换 - for (let i = 0; i < typeConversion.length; i++) { - this.advance(); - } - - return this.createToken(TokenType.IDENTIFIER, typeConversion, startPos, startLine, startColumn); - } - - private isIdentifierStart(char: string): boolean { - return /[a-zA-Z_]/.test(char); - } - - private isIdentifierChar(char: string): boolean { - return /[a-zA-Z0-9_]/.test(char); - } - - private isDigit(char: string): boolean { - return char >= '0' && char <= '9'; - } - - private isPrintableChar(char: string): boolean { - // 检查是否为可打印字符(非控制字符) - const charCode = char.charCodeAt(0); - return charCode >= 32 && charCode <= 126; - } - - private advance(): void { - if (this.position < this.code.length) { - if (this.code[this.position] === '\n') { - this.line++; - this.column = 1; - } else { - this.column++; - } - this.position++; - } - } - - private peek(): string { - return this.position + 1 < this.code.length ? this.code[this.position + 1] : ''; - } - - private skipWhitespace(): void { - while (this.position < this.code.length) { - const char = this.code[this.position]; - if (char === ' ' || char === '\t' || char === '\r') { - this.advance(); - } else { - break; - } - } - } - - private createToken(type: TokenType, value: string, startPos: number, line: number, column: number): Token { - return { - type, - value, - line, - column, - startIndex: startPos, - endIndex: this.position - }; - } -} diff --git a/frontend/src/common/prettier/plugins/powershell/parser.ts b/frontend/src/common/prettier/plugins/powershell/parser.ts deleted file mode 100644 index a4a7424..0000000 --- a/frontend/src/common/prettier/plugins/powershell/parser.ts +++ /dev/null @@ -1,821 +0,0 @@ -/** - * PowerShell 语法分析器 (Parser) - * 将词法分析器产生的tokens转换为抽象语法树(AST) - */ - -import { Token, TokenType } from './lexer'; -import { - ASTNode, - ScriptBlockAst, - StatementAst, - ExpressionAst, - PipelineAst, - CommandAst, - AssignmentAst, - VariableAst, - LiteralAst, - BinaryExpressionAst, - IfStatementAst, - FunctionDefinitionAst, - ParameterAst, - ASTNodeFactory, - CommentAst, - PipelineElementAst, - ElseIfClauseAst, - UnaryExpressionAst, - ParenthesizedExpressionAst -} from './ast'; - -export class PowerShellParser { - private tokens: Token[]; - private currentIndex: number = 0; - private comments: CommentAst[] = []; - - private originalCode: string; - - constructor(tokens: Token[], originalCode: string = '') { - this.tokens = tokens; - this.currentIndex = 0; - this.originalCode = originalCode; - } - - /** - * 解析tokens生成AST - */ - public parse(): ScriptBlockAst { - const statements: StatementAst[] = []; - - while (!this.isAtEnd()) { - // 跳过空白和换行 - this.skipWhitespaceAndNewlines(); - - if (this.isAtEnd()) { - break; - } - - // 处理注释 - if (this.match(TokenType.COMMENT, TokenType.MULTILINE_COMMENT)) { - const comment = this.parseComment(); - this.comments.push(comment); - continue; - } - - const statement = this.parseStatement(); - if (statement) { - statements.push(statement); - } - } - - const start = this.tokens.length > 0 ? this.tokens[0].startIndex : 0; - const end = this.tokens.length > 0 ? this.tokens[this.tokens.length - 1].endIndex : 0; - const line = this.tokens.length > 0 ? this.tokens[0].line : 1; - const column = this.tokens.length > 0 ? this.tokens[0].column : 1; - - return ASTNodeFactory.createScriptBlock(statements, start, end, line, column); - } - - public getComments(): CommentAst[] { - return this.comments; - } - - private parseStatement(): StatementAst | null { - // 函数定义 - if (this.check(TokenType.FUNCTION)) { - return this.parseFunctionDefinition(); - } - - // 控制流语句 - if (this.check(TokenType.IF)) { - return this.parseIfStatement(); - } - - // 赋值或管道 - return this.parsePipeline(); - } - - private parseFunctionDefinition(): FunctionDefinitionAst { - const start = this.current().startIndex; - const line = this.current().line; - const column = this.current().column; - - this.consume(TokenType.FUNCTION, "Expected 'function'"); - - // 函数名可能是CMDLET类型(如Get-Something)或IDENTIFIER - let nameToken: Token; - if (this.check(TokenType.CMDLET)) { - nameToken = this.consume(TokenType.CMDLET, "Expected function name"); - } else { - nameToken = this.consume(TokenType.IDENTIFIER, "Expected function name"); - } - const name = nameToken.value; - - // 解析参数 - const parameters: ParameterAst[] = []; - if (this.match(TokenType.LEFT_PAREN)) { - if (!this.check(TokenType.RIGHT_PAREN)) { - do { - const param = this.parseParameter(); - if (param) { - parameters.push(param); - } - } while (this.match(TokenType.COMMA)); - } - this.consume(TokenType.RIGHT_PAREN, "Expected ')' after parameters"); - } - - // 解析函数体 - const body = this.parseScriptBlock(); - - const end = this.previous().endIndex; - - return ASTNodeFactory.createFunctionDefinition(name, parameters, body, start, end, line, column); - } - - private parseIfStatement(): IfStatementAst { - const start = this.current().startIndex; - const line = this.current().line; - const column = this.current().column; - - this.consume(TokenType.IF, "Expected 'if'"); - - // PowerShell的if语句可能有括号,也可能没有 - const hasParens = this.check(TokenType.LEFT_PAREN); - if (hasParens) { - this.consume(TokenType.LEFT_PAREN, "Expected '(' after 'if'"); - } - - const condition = this.parseExpression(); - - if (hasParens) { - this.consume(TokenType.RIGHT_PAREN, "Expected ')' after if condition"); - } - - const ifBody = this.parseScriptBlock(); - - const elseIfClauses: ElseIfClauseAst[] = []; - let elseBody: ScriptBlockAst | undefined; - - // 处理 elseif 子句 - while (this.match(TokenType.ELSEIF)) { - const elseIfStart = this.previous().startIndex; - const elseIfLine = this.previous().line; - const elseIfColumn = this.previous().column; - - this.consume(TokenType.LEFT_PAREN, "Expected '(' after 'elseif'"); - const elseIfCondition = this.parseExpression(); - this.consume(TokenType.RIGHT_PAREN, "Expected ')' after elseif condition"); - const elseIfBody = this.parseScriptBlock(); - - const elseIfEnd = this.previous().endIndex; - - elseIfClauses.push({ - type: 'ElseIfClause', - condition: elseIfCondition, - body: elseIfBody, - start: elseIfStart, - end: elseIfEnd, - line: elseIfLine, - column: elseIfColumn - }); - } - - // 处理 else 子句 - if (this.match(TokenType.ELSE)) { - elseBody = this.parseScriptBlock(); - } - - const end = this.previous().endIndex; - - return ASTNodeFactory.createIfStatement(condition, ifBody, elseIfClauses, elseBody, start, end, line, column); - } - - private parsePipeline(): PipelineAst { - const start = this.current().startIndex; - const line = this.current().line; - const column = this.current().column; - - const elements: PipelineElementAst[] = []; - - // 解析第一个元素 - const firstElement = this.parsePipelineElement(); - elements.push(firstElement); - - // 解析管道链 - while (this.match(TokenType.PIPE)) { - const element = this.parsePipelineElement(); - elements.push(element); - } - - const end = this.previous().endIndex; - - return ASTNodeFactory.createPipeline(elements, start, end, line, column); - } - - private parsePipelineElement(): PipelineElementAst { - const start = this.current().startIndex; - const line = this.current().line; - const column = this.current().column; - - const expression = this.parseAssignment(); - const end = this.previous().endIndex; - - return { - type: 'PipelineElement', - expression, - start, - end, - line, - column - }; - } - - private parseAssignment(): ExpressionAst { - const expr = this.parseLogicalOr(); - - if (this.match(TokenType.ASSIGNMENT)) { - const operator = this.previous().value; - const right = this.parseAssignment(); - - return ASTNodeFactory.createAssignment( - expr, - operator, - right, - expr.start, - right.end, - expr.line, - expr.column - ); - } - - return expr; - } - - private parseLogicalOr(): ExpressionAst { - let expr = this.parseLogicalAnd(); - - while (this.match(TokenType.LOGICAL)) { - const operator = this.previous().value.toLowerCase(); - if (operator === '-or' || operator === '-xor') { - const right = this.parseLogicalAnd(); - expr = ASTNodeFactory.createBinaryExpression( - expr, - this.previous().value, // 使用原始大小写 - right, - expr.start, - right.end, - expr.line, - expr.column - ); - } else { - // 如果不是预期的操作符,回退 - this.currentIndex--; - break; - } - } - - return expr; - } - - private parseLogicalAnd(): ExpressionAst { - let expr = this.parseComparison(); - - while (this.match(TokenType.LOGICAL)) { - const operator = this.previous().value.toLowerCase(); - if (operator === '-and') { - const right = this.parseComparison(); - expr = ASTNodeFactory.createBinaryExpression( - expr, - this.previous().value, // 使用原始大小写 - right, - expr.start, - right.end, - expr.line, - expr.column - ); - } else { - // 如果不是预期的操作符,回退 - this.currentIndex--; - break; - } - } - - return expr; - } - - private parseComparison(): ExpressionAst { - let expr = this.parseArithmetic(); - - while (this.match(TokenType.COMPARISON)) { - const operator = this.previous().value; - const right = this.parseArithmetic(); - expr = ASTNodeFactory.createBinaryExpression( - expr, - operator, - right, - expr.start, - right.end, - expr.line, - expr.column - ); - } - - return expr; - } - - private parseArithmetic(): ExpressionAst { - let expr = this.parseMultiplicative(); - - while (this.match(TokenType.ARITHMETIC)) { - const token = this.previous(); - if (token.value === '+' || token.value === '-') { - const operator = token.value; - const right = this.parseMultiplicative(); - expr = ASTNodeFactory.createBinaryExpression( - expr, - operator, - right, - expr.start, - right.end, - expr.line, - expr.column - ); - } - } - - return expr; - } - - private parseMultiplicative(): ExpressionAst { - let expr = this.parseUnary(); - - while (this.match(TokenType.ARITHMETIC)) { - const token = this.previous(); - if (token.value === '*' || token.value === '/' || token.value === '%') { - const operator = token.value; - const right = this.parseUnary(); - expr = ASTNodeFactory.createBinaryExpression( - expr, - operator, - right, - expr.start, - right.end, - expr.line, - expr.column - ); - } - } - - return expr; - } - - private parseUnary(): ExpressionAst { - if (this.match(TokenType.LOGICAL)) { - const token = this.previous(); - const operator = token.value.toLowerCase(); - if (operator === '-not') { - const operand = this.parseUnary(); - return { - type: 'UnaryExpression', - operator: token.value, // 使用原始大小写 - operand, - start: token.startIndex, - end: operand.end, - line: token.line, - column: token.column - } as UnaryExpressionAst; - } else { - // 如果不是-not,回退token - this.currentIndex--; - } - } - - // 处理算术一元操作符(+, -) - if (this.match(TokenType.ARITHMETIC)) { - const token = this.previous(); - if (token.value === '+' || token.value === '-') { - const operand = this.parseUnary(); - return { - type: 'UnaryExpression', - operator: token.value, - operand, - start: token.startIndex, - end: operand.end, - line: token.line, - column: token.column - } as UnaryExpressionAst; - } else { - // 如果不是一元操作符,回退 - this.currentIndex--; - } - } - - return this.parsePrimary(); - } - - private parsePrimary(): ExpressionAst { - // 变量 - if (this.match(TokenType.VARIABLE)) { - const token = this.previous(); - return ASTNodeFactory.createVariable( - token.value, - token.startIndex, - token.endIndex, - token.line, - token.column - ); - } - - // 字符串字面量 - if (this.match(TokenType.STRING, TokenType.HERE_STRING)) { - const token = this.previous(); - return ASTNodeFactory.createLiteral( - token.value, - 'String', - token.startIndex, - token.endIndex, - token.line, - token.column - ); - } - - // 数字字面量 - if (this.match(TokenType.NUMBER)) { - const token = this.previous(); - const value = parseFloat(token.value); - return ASTNodeFactory.createLiteral( - value, - 'Number', - token.startIndex, - token.endIndex, - token.line, - token.column - ); - } - - // 命令调用 - 扩展支持更多token类型 - if (this.match(TokenType.CMDLET, TokenType.IDENTIFIER)) { - return this.parseCommand(); - } - - // 处理看起来像cmdlet但被错误标记的标识符 - if (this.check(TokenType.IDENTIFIER) && this.current().value.includes('-')) { - this.advance(); - return this.parseCommand(); - } - - // 哈希表 @{...} - if (this.check(TokenType.LEFT_BRACE) && this.current().value === '@{') { - return this.parseHashtable(); - } - - // 脚本块表达式 {...} - 已在parseHashtableValue中处理 - // 这里不需要处理,因为独立的脚本块很少见 - - // 括号表达式 - if (this.match(TokenType.LEFT_PAREN)) { - const expr = this.parseExpression(); - this.consume(TokenType.RIGHT_PAREN, "Expected ')' after expression"); - return { - type: 'ParenthesizedExpression', - expression: expr, - start: this.previous().startIndex, - end: this.previous().endIndex, - line: this.previous().line, - column: this.previous().column - } as ParenthesizedExpressionAst; - } - - // 对于不认识的token,作为普通标识符处理而不是抛出异常 - const token = this.advance(); - return ASTNodeFactory.createLiteral( - token.value, - 'String', // 将未识别的token作为字符串处理 - token.startIndex, - token.endIndex, - token.line, - token.column - ); - } - - private parseCommand(): CommandAst { - const start = this.previous().startIndex; - const line = this.previous().line; - const column = this.previous().column; - const commandName = this.previous().value; - - const parameters: ParameterAst[] = []; - const args: ExpressionAst[] = []; - - // 解析参数和参数值 - while (!this.isAtEnd() && - !this.check(TokenType.PIPE) && - !this.check(TokenType.NEWLINE) && - !this.check(TokenType.SEMICOLON) && - !this.check(TokenType.RIGHT_PAREN) && - !this.check(TokenType.RIGHT_BRACE)) { - - if (this.match(TokenType.PARAMETER)) { - const paramToken = this.previous(); - const param: ParameterAst = { - type: 'Parameter', - name: paramToken.value, - start: paramToken.startIndex, - end: paramToken.endIndex, - line: paramToken.line, - column: paramToken.column - }; - - // 检查参数是否有值 - if (!this.check(TokenType.PARAMETER) && - !this.check(TokenType.PIPE) && - !this.check(TokenType.NEWLINE) && - !this.check(TokenType.SEMICOLON)) { - param.value = this.parsePrimary(); - } - - parameters.push(param); - } else { - // 位置参数 - const arg = this.parsePrimary(); - args.push(arg); - } - } - - const end = this.previous().endIndex; - - return ASTNodeFactory.createCommand(commandName, parameters, args, start, end, line, column); - } - - private parseParameter(): ParameterAst | null { - if (this.match(TokenType.PARAMETER)) { - const token = this.previous(); - const param: ParameterAst = { - type: 'Parameter', - name: token.value, - start: token.startIndex, - end: token.endIndex, - line: token.line, - column: token.column - }; - - // 检查是否有参数值 - if (this.match(TokenType.ASSIGNMENT)) { - param.value = this.parseExpression(); - } - - return param; - } - return null; - } - - private parseScriptBlock(): ScriptBlockAst { - const start = this.current().startIndex; - const line = this.current().line; - const column = this.current().column; - - this.consume(TokenType.LEFT_BRACE, "Expected '{'"); - - const statements: StatementAst[] = []; - - while (!this.check(TokenType.RIGHT_BRACE) && !this.isAtEnd()) { - this.skipWhitespaceAndNewlines(); - - if (this.check(TokenType.RIGHT_BRACE)) { - break; - } - - const statement = this.parseStatement(); - if (statement) { - statements.push(statement); - } - } - - this.consume(TokenType.RIGHT_BRACE, "Expected '}'"); - - const end = this.previous().endIndex; - - return ASTNodeFactory.createScriptBlock(statements, start, end, line, column); - } - - private parseExpression(): ExpressionAst { - return this.parseAssignment(); - } - - private parseComment(): CommentAst { - const token = this.previous(); - const isMultiline = token.type === TokenType.MULTILINE_COMMENT; - - return ASTNodeFactory.createComment( - token.value, - isMultiline, - token.startIndex, - token.endIndex, - token.line, - token.column - ); - } - - // 辅助方法 - private match(...types: TokenType[]): boolean { - for (const type of types) { - if (this.check(type)) { - this.advance(); - return true; - } - } - return false; - } - - private check(type: TokenType): boolean { - if (this.isAtEnd()) return false; - return this.current().type === type; - } - - private advance(): Token { - if (!this.isAtEnd()) this.currentIndex++; - return this.previous(); - } - - private isAtEnd(): boolean { - return this.currentIndex >= this.tokens.length || this.current().type === TokenType.EOF; - } - - private current(): Token { - if (this.currentIndex >= this.tokens.length) { - return this.tokens[this.tokens.length - 1]; - } - return this.tokens[this.currentIndex]; - } - - private previous(): Token { - return this.tokens[this.currentIndex - 1]; - } - - private consume(type: TokenType, message: string): Token { - if (this.check(type)) return this.advance(); - - const current = this.current(); - throw new Error(`${message}. Got ${current.type}(${current.value}) at line ${current.line}, column ${current.column}`); - } - - - private parseHashtable(): ExpressionAst { - const start = this.current().startIndex; - const line = this.current().line; - const column = this.current().column; - - // 消费 @{ - this.advance(); - - const entries: any[] = []; - - // 解析哈希表内容 - if (!this.check(TokenType.RIGHT_BRACE)) { - do { - // 解析键 - 只接受简单的标识符或字符串 - const key = this.parseHashtableKey(); - - // 消费 = - this.consume(TokenType.ASSIGNMENT, "Expected '=' after hashtable key"); - - // 解析值 - const value = this.parseHashtableValue(); - - entries.push({ - type: 'HashtableEntry', - key, - value, - start: key.start, - end: value.end, - line: key.line, - column: key.column - }); - - } while (this.match(TokenType.SEMICOLON)); - } - - this.consume(TokenType.RIGHT_BRACE, "Expected '}' after hashtable entries"); - const end = this.previous().endIndex; - - return { - type: 'Hashtable', - entries, - start, - end, - line, - column - } as any; - } - - private parseHashtableKey(): ExpressionAst { - // 哈希表键只能是简单的标识符或字符串 - if (this.match(TokenType.STRING, TokenType.HERE_STRING)) { - const token = this.previous(); - return ASTNodeFactory.createLiteral( - token.value, - 'String', - token.startIndex, - token.endIndex, - token.line, - token.column - ); - } - - // 接受各种可能的标识符类型作为哈希表键 - if (this.match(TokenType.IDENTIFIER, TokenType.CMDLET, TokenType.KEYWORD)) { - const token = this.previous(); - return ASTNodeFactory.createLiteral( - token.value, - 'String', - token.startIndex, - token.endIndex, - token.line, - token.column - ); - } - - // 对于任何其他类型的token,尝试作为字面量处理 - const currentToken = this.current(); - this.advance(); - return ASTNodeFactory.createLiteral( - currentToken.value, - 'String', - currentToken.startIndex, - currentToken.endIndex, - currentToken.line, - currentToken.column - ); - } - - private parseHashtableValue(): ExpressionAst { - // 哈希表值可以是任何表达式 - if (this.check(TokenType.LEFT_BRACE)) { - // 这是一个脚本块 {expression} - 完全绕过复杂解析 - const start = this.current().startIndex; - const line = this.current().line; - const column = this.current().column; - - // 直接从原始代码中提取脚本块内容 - const startPos = this.current().startIndex; - this.advance(); // 消费 { - - let braceLevel = 1; - let endPos = this.current().startIndex; - - // 找到匹配的右大括号位置 - while (!this.isAtEnd() && braceLevel > 0) { - const token = this.current(); - if (token.type === TokenType.LEFT_BRACE) { - braceLevel++; - } else if (token.type === TokenType.RIGHT_BRACE) { - braceLevel--; - if (braceLevel === 0) { - endPos = token.startIndex; - break; - } - } - this.advance(); - } - - this.consume(TokenType.RIGHT_BRACE, "Expected '}' after script block"); - const end = this.previous().endIndex; - - // 从原始代码中提取内容(从 { 后到 } 前) - const rawContent = this.getOriginalCodeSlice(startPos + 1, endPos); - - return { - type: 'ScriptBlockExpression', - rawContent: rawContent.trim(), // 去掉首尾空白 - start, - end, - line, - column - } as any; - } - - // 对于其他值,使用简单的解析 - return this.parsePrimary(); - } - - private getOriginalCodeSlice(start: number, end: number): string { - // 直接从原始代码中提取片段 - if (this.originalCode) { - return this.originalCode.substring(start, end); - } - - // 回退到基于token重建(如果没有原始代码) - let result = ''; - for (const token of this.tokens) { - if (token.startIndex >= start && token.endIndex <= end) { - result += token.value; - } - } - return result; - } - - private skipWhitespaceAndNewlines(): void { - while (this.match(TokenType.WHITESPACE, TokenType.NEWLINE)) { - // 继续跳过 - } - } -} diff --git a/frontend/src/views/editor/extensions/codeblock/lang-parser/languages.ts b/frontend/src/views/editor/extensions/codeblock/lang-parser/languages.ts index 1c5a579..59d40c5 100644 --- a/frontend/src/views/editor/extensions/codeblock/lang-parser/languages.ts +++ b/frontend/src/views/editor/extensions/codeblock/lang-parser/languages.ts @@ -46,7 +46,6 @@ import * as shellPrettierPlugin from "@/common/prettier/plugins/shell"; import tomlPrettierPlugin from "@/common/prettier/plugins/toml"; import clojurePrettierPlugin from "@cospaia/prettier-plugin-clojure"; import groovyPrettierPlugin from "@/common/prettier/plugins/groovy"; -import powershellPrettierPlugin from "@/common/prettier/plugins/powershell"; import scalaPrettierPlugin from "@/common/prettier/plugins/scala"; import * as prettierPluginEstree from "prettier/plugins/estree"; @@ -146,10 +145,7 @@ export const LANGUAGES: LanguageInfo[] = [ parser: "groovy", plugins: [groovyPrettierPlugin] }), - new LanguageInfo("ps1", "PowerShell", StreamLanguage.define(powerShell).parser,{ - parser: "powershell", - plugins: [powershellPrettierPlugin] - }), + new LanguageInfo("ps1", "PowerShell", StreamLanguage.define(powerShell).parser), new LanguageInfo("dart", "Dart", null), // 暂无解析器 new LanguageInfo("scala", "Scala", StreamLanguage.define(scala).parser,{ parser: "scala",