Skip to content

终端 API

概述

Trae 终端 API 提供了强大的终端集成功能,允许开发者创建、管理和控制终端实例。通过终端 API,可以执行命令、处理输入输出、自定义终端行为等。

核心概念

终端接口

typescript
interface Terminal {
  readonly name: string;
  readonly processId: Thenable<number | undefined>;
  readonly creationOptions: Readonly<TerminalOptions | ExtensionTerminalOptions>;
  readonly exitStatus: TerminalExitStatus | undefined;
  readonly state: TerminalState;
  
  sendText(text: string, addNewLine?: boolean): void;
  show(preserveFocus?: boolean): void;
  hide(): void;
  dispose(): void;
}

interface TerminalOptions {
  name?: string;
  shellPath?: string;
  shellArgs?: string[] | string;
  cwd?: string | Uri;
  env?: { [key: string]: string | null | undefined };
  strictEnv?: boolean;
  hideFromUser?: boolean;
  message?: string;
  iconPath?: Uri | { light: Uri; dark: Uri } | ThemeIcon;
  color?: ThemeColor;
  location?: TerminalLocation | TerminalEditorLocationOptions | TerminalSplitLocationOptions;
  isTransient?: boolean;
}

interface ExtensionTerminalOptions {
  name: string;
  pty: Pseudoterminal;
  iconPath?: Uri | { light: Uri; dark: Uri } | ThemeIcon;
  color?: ThemeColor;
  location?: TerminalLocation | TerminalEditorLocationOptions | TerminalSplitLocationOptions;
  isTransient?: boolean;
}

interface TerminalExitStatus {
  readonly code: number | undefined;
  readonly reason: TerminalExitReason;
}

enum TerminalExitReason {
  Unknown = 0,
  Shutdown = 1,
  Process = 2,
  User = 3,
  Extension = 4
}

interface TerminalState {
  readonly isInteractedWith: boolean;
}

伪终端接口

typescript
interface Pseudoterminal {
  onDidWrite: Event<string>;
  onDidOverrideDimensions?: Event<TerminalDimensions | undefined>;
  onDidClose?: Event<number | void>;
  onDidChangeName?: Event<string>;
  
  open(initialDimensions: TerminalDimensions | undefined): void;
  close(): void;
  handleInput?(data: string): void;
  setDimensions?(dimensions: TerminalDimensions): void;
}

interface TerminalDimensions {
  readonly columns: number;
  readonly rows: number;
}

API 参考

终端管理

基础终端操作

typescript
import { window, Terminal, TerminalOptions } from '@trae/api';

// 创建终端
const createTerminal = (options?: TerminalOptions): Terminal => {
  return window.createTerminal(options);
};

// 创建命名终端
const createNamedTerminal = (name: string, shellPath?: string): Terminal => {
  return window.createTerminal({
    name,
    shellPath,
    iconPath: new ThemeIcon('terminal'),
    color: new ThemeColor('terminal.ansiBlue')
  });
};

// 获取活动终端
const getActiveTerminal = (): Terminal | undefined => {
  return window.activeTerminal;
};

// 获取所有终端
const getAllTerminals = (): readonly Terminal[] => {
  return window.terminals;
};

// 显示终端
const showTerminal = (terminal: Terminal, preserveFocus = false): void => {
  terminal.show(preserveFocus);
};

// 发送文本到终端
const sendTextToTerminal = (terminal: Terminal, text: string, addNewLine = true): void => {
  terminal.sendText(text, addNewLine);
};

// 关闭终端
const closeTerminal = (terminal: Terminal): void => {
  terminal.dispose();
};

// 监听终端事件
const onDidOpenTerminal = (listener: (terminal: Terminal) => void): Disposable => {
  return window.onDidOpenTerminal(listener);
};

const onDidCloseTerminal = (listener: (terminal: Terminal) => void): Disposable => {
  return window.onDidCloseTerminal(listener);
};

const onDidChangeActiveTerminal = (
  listener: (terminal: Terminal | undefined) => void
): Disposable => {
  return window.onDidChangeActiveTerminal(listener);
};

const onDidChangeTerminalState = (
  listener: (terminal: Terminal) => void
): Disposable => {
  return window.onDidChangeTerminalState(listener);
};

高级终端配置

typescript
// 创建具有自定义环境的终端
const createTerminalWithEnv = (env: Record<string, string>): Terminal => {
  return window.createTerminal({
    name: 'Custom Environment',
    env: {
      ...process.env,
      ...env,
      CUSTOM_VAR: 'custom_value'
    },
    cwd: workspace.rootPath,
    shellPath: '/bin/bash',
    shellArgs: ['--login']
  });
};

// 创建开发环境终端
const createDevTerminal = (): Terminal => {
  return window.createTerminal({
    name: 'Development',
    env: {
      NODE_ENV: 'development',
      DEBUG: '*',
      PATH: `${workspace.rootPath}/node_modules/.bin:${process.env.PATH}`
    },
    cwd: workspace.rootPath,
    iconPath: new ThemeIcon('gear'),
    color: new ThemeColor('terminal.ansiGreen')
  });
};

// 创建生产环境终端
const createProdTerminal = (): Terminal => {
  return window.createTerminal({
    name: 'Production',
    env: {
      NODE_ENV: 'production'
    },
    iconPath: new ThemeIcon('server'),
    color: new ThemeColor('terminal.ansiRed')
  });
};

// 创建分割终端
const createSplitTerminal = (parentTerminal: Terminal): Terminal => {
  return window.createTerminal({
    name: 'Split Terminal',
    location: {
      parentTerminal,
      splitDirection: TerminalSplitDirection.Right
    }
  });
};

// 创建编辑器终端
const createEditorTerminal = (): Terminal => {
  return window.createTerminal({
    name: 'Editor Terminal',
    location: TerminalLocation.Editor
  });
};

自定义伪终端

基础伪终端实现

typescript
class CustomPseudoterminal implements Pseudoterminal {
  private writeEmitter = new EventEmitter<string>();
  private closeEmitter = new EventEmitter<number | void>();
  private nameEmitter = new EventEmitter<string>();
  
  onDidWrite: Event<string> = this.writeEmitter.event;
  onDidClose: Event<number | void> = this.closeEmitter.event;
  onDidChangeName: Event<string> = this.nameEmitter.event;
  
  private currentDirectory = process.cwd();
  private commandHistory: string[] = [];
  private historyIndex = -1;
  private currentLine = '';
  private cursorPosition = 0;
  
  open(initialDimensions: TerminalDimensions | undefined): void {
    this.writeEmitter.fire('Welcome to Custom Terminal!\r\n');
    this.showPrompt();
  }
  
  close(): void {
    this.writeEmitter.fire('\r\nTerminal closed.\r\n');
    this.closeEmitter.fire(0);
  }
  
  handleInput(data: string): void {
    switch (data) {
      case '\r': // Enter
        this.executeCommand();
        break;
      case '\x7f': // Backspace
        this.handleBackspace();
        break;
      case '\x1b[A': // Up arrow
        this.navigateHistory(-1);
        break;
      case '\x1b[B': // Down arrow
        this.navigateHistory(1);
        break;
      case '\x1b[C': // Right arrow
        this.moveCursor(1);
        break;
      case '\x1b[D': // Left arrow
        this.moveCursor(-1);
        break;
      case '\x03': // Ctrl+C
        this.handleInterrupt();
        break;
      default:
        if (data >= ' ' && data <= '~') {
          this.insertCharacter(data);
        }
    }
  }
  
  private showPrompt(): void {
    const prompt = `\r\n${this.currentDirectory}$ `;
    this.writeEmitter.fire(prompt);
    this.currentLine = '';
    this.cursorPosition = 0;
  }
  
  private executeCommand(): void {
    this.writeEmitter.fire('\r\n');
    
    const command = this.currentLine.trim();
    if (command) {
      this.commandHistory.push(command);
      this.historyIndex = this.commandHistory.length;
      this.processCommand(command);
    } else {
      this.showPrompt();
    }
  }
  
  private processCommand(command: string): void {
    const [cmd, ...args] = command.split(' ');
    
    switch (cmd) {
      case 'echo':
        this.writeEmitter.fire(args.join(' ') + '\r\n');
        break;
      case 'pwd':
        this.writeEmitter.fire(this.currentDirectory + '\r\n');
        break;
      case 'cd':
        this.changeDirectory(args[0] || process.env.HOME || '/');
        break;
      case 'ls':
        this.listDirectory();
        break;
      case 'clear':
        this.writeEmitter.fire('\x1b[2J\x1b[H');
        break;
      case 'help':
        this.showHelp();
        break;
      case 'exit':
        this.close();
        return;
      default:
        this.writeEmitter.fire(`Command not found: ${cmd}\r\n`);
    }
    
    this.showPrompt();
  }
  
  private changeDirectory(path: string): void {
    try {
      const newPath = path.startsWith('/') ? path : `${this.currentDirectory}/${path}`;
      // 这里应该验证路径是否存在
      this.currentDirectory = newPath;
      this.nameEmitter.fire(`Terminal - ${this.currentDirectory}`);
    } catch (error) {
      this.writeEmitter.fire(`cd: ${path}: No such file or directory\r\n`);
    }
  }
  
  private listDirectory(): void {
    // 模拟 ls 命令
    const files = ['file1.txt', 'file2.js', 'directory1/', 'directory2/'];
    this.writeEmitter.fire(files.join('  ') + '\r\n');
  }
  
  private showHelp(): void {
    const helpText = [
      'Available commands:',
      '  echo <text>  - Print text',
      '  pwd          - Print working directory',
      '  cd <path>    - Change directory',
      '  ls           - List directory contents',
      '  clear        - Clear screen',
      '  help         - Show this help',
      '  exit         - Exit terminal',
      ''
    ].join('\r\n');
    
    this.writeEmitter.fire(helpText);
  }
  
  private handleBackspace(): void {
    if (this.cursorPosition > 0) {
      this.currentLine = 
        this.currentLine.slice(0, this.cursorPosition - 1) +
        this.currentLine.slice(this.cursorPosition);
      this.cursorPosition--;
      this.redrawLine();
    }
  }
  
  private insertCharacter(char: string): void {
    this.currentLine = 
      this.currentLine.slice(0, this.cursorPosition) +
      char +
      this.currentLine.slice(this.cursorPosition);
    this.cursorPosition++;
    this.redrawLine();
  }
  
  private moveCursor(direction: number): void {
    const newPosition = this.cursorPosition + direction;
    if (newPosition >= 0 && newPosition <= this.currentLine.length) {
      this.cursorPosition = newPosition;
      this.writeEmitter.fire(direction > 0 ? '\x1b[C' : '\x1b[D');
    }
  }
  
  private navigateHistory(direction: number): void {
    const newIndex = this.historyIndex + direction;
    if (newIndex >= 0 && newIndex < this.commandHistory.length) {
      this.historyIndex = newIndex;
      this.currentLine = this.commandHistory[this.historyIndex];
      this.cursorPosition = this.currentLine.length;
      this.redrawLine();
    } else if (newIndex === this.commandHistory.length) {
      this.historyIndex = newIndex;
      this.currentLine = '';
      this.cursorPosition = 0;
      this.redrawLine();
    }
  }
  
  private redrawLine(): void {
    // 清除当前行并重绘
    this.writeEmitter.fire('\r\x1b[K');
    this.writeEmitter.fire(`${this.currentDirectory}$ ${this.currentLine}`);
    
    // 移动光标到正确位置
    const promptLength = this.currentDirectory.length + 2;
    const targetPosition = promptLength + this.cursorPosition;
    const currentPosition = promptLength + this.currentLine.length;
    
    if (targetPosition < currentPosition) {
      const moves = currentPosition - targetPosition;
      this.writeEmitter.fire('\x1b[D'.repeat(moves));
    }
  }
  
  private handleInterrupt(): void {
    this.writeEmitter.fire('^C');
    this.showPrompt();
  }
}

// 使用自定义伪终端
const createCustomTerminal = (): Terminal => {
  const pty = new CustomPseudoterminal();
  
  return window.createTerminal({
    name: 'Custom Terminal',
    pty,
    iconPath: new ThemeIcon('terminal-bash'),
    color: new ThemeColor('terminal.ansiCyan')
  });
};

专用伪终端实现

typescript
// 日志查看器终端
class LogViewerPseudoterminal implements Pseudoterminal {
  private writeEmitter = new EventEmitter<string>();
  private closeEmitter = new EventEmitter<number | void>();
  
  onDidWrite: Event<string> = this.writeEmitter.event;
  onDidClose: Event<number | void> = this.closeEmitter.event;
  
  private logFile: string;
  private watcher?: FileSystemWatcher;
  private isFollowing = true;
  
  constructor(logFile: string) {
    this.logFile = logFile;
  }
  
  open(initialDimensions: TerminalDimensions | undefined): void {
    this.writeEmitter.fire(`Watching log file: ${this.logFile}\r\n`);
    this.writeEmitter.fire('Press Ctrl+C to stop following, Ctrl+D to exit\r\n\r\n');
    
    this.startWatching();
    this.loadExistingContent();
  }
  
  close(): void {
    this.stopWatching();
    this.closeEmitter.fire(0);
  }
  
  handleInput(data: string): void {
    switch (data) {
      case '\x03': // Ctrl+C
        this.toggleFollowing();
        break;
      case '\x04': // Ctrl+D
        this.close();
        break;
      case 'r':
        this.refresh();
        break;
    }
  }
  
  private async loadExistingContent(): Promise<void> {
    try {
      const content = await workspace.fs.readFile(Uri.file(this.logFile));
      const lines = content.toString().split('\n');
      
      // 显示最后 100 行
      const recentLines = lines.slice(-100);
      for (const line of recentLines) {
        if (line.trim()) {
          this.writeEmitter.fire(this.formatLogLine(line) + '\r\n');
        }
      }
    } catch (error) {
      this.writeEmitter.fire(`Error reading log file: ${error.message}\r\n`);
    }
  }
  
  private startWatching(): void {
    this.watcher = workspace.createFileSystemWatcher(this.logFile);
    
    this.watcher.onDidChange(() => {
      if (this.isFollowing) {
        this.loadNewContent();
      }
    });
  }
  
  private stopWatching(): void {
    if (this.watcher) {
      this.watcher.dispose();
      this.watcher = undefined;
    }
  }
  
  private async loadNewContent(): Promise<void> {
    // 实现增量读取新内容的逻辑
    // 这里简化为重新读取文件
    try {
      const content = await workspace.fs.readFile(Uri.file(this.logFile));
      const lines = content.toString().split('\n');
      const newLines = lines.slice(-10); // 获取最新的 10 行
      
      for (const line of newLines) {
        if (line.trim()) {
          this.writeEmitter.fire(this.formatLogLine(line) + '\r\n');
        }
      }
    } catch (error) {
      // 忽略读取错误
    }
  }
  
  private formatLogLine(line: string): string {
    // 添加颜色和格式化
    if (line.includes('ERROR')) {
      return `\x1b[31m${line}\x1b[0m`; // 红色
    } else if (line.includes('WARN')) {
      return `\x1b[33m${line}\x1b[0m`; // 黄色
    } else if (line.includes('INFO')) {
      return `\x1b[32m${line}\x1b[0m`; // 绿色
    } else if (line.includes('DEBUG')) {
      return `\x1b[36m${line}\x1b[0m`; // 青色
    }
    
    return line;
  }
  
  private toggleFollowing(): void {
    this.isFollowing = !this.isFollowing;
    const status = this.isFollowing ? 'Following' : 'Paused';
    this.writeEmitter.fire(`\r\n[${status}] Press Ctrl+C to ${this.isFollowing ? 'pause' : 'resume'}\r\n`);
  }
  
  private refresh(): void {
    this.writeEmitter.fire('\x1b[2J\x1b[H'); // 清屏
    this.writeEmitter.fire(`Refreshing log file: ${this.logFile}\r\n\r\n`);
    this.loadExistingContent();
  }
}

// 进程监控终端
class ProcessMonitorPseudoterminal implements Pseudoterminal {
  private writeEmitter = new EventEmitter<string>();
  private closeEmitter = new EventEmitter<number | void>();
  
  onDidWrite: Event<string> = this.writeEmitter.event;
  onDidClose: Event<number | void> = this.closeEmitter.event;
  
  private process?: ChildProcess;
  private command: string;
  private args: string[];
  private options: SpawnOptions;
  
  constructor(command: string, args: string[] = [], options: SpawnOptions = {}) {
    this.command = command;
    this.args = args;
    this.options = options;
  }
  
  open(initialDimensions: TerminalDimensions | undefined): void {
    this.writeEmitter.fire(`Starting process: ${this.command} ${this.args.join(' ')}\r\n`);
    this.startProcess();
  }
  
  close(): void {
    this.stopProcess();
    this.closeEmitter.fire(0);
  }
  
  handleInput(data: string): void {
    if (this.process && this.process.stdin) {
      this.process.stdin.write(data);
    }
  }
  
  private startProcess(): void {
    try {
      this.process = spawn(this.command, this.args, {
        ...this.options,
        stdio: ['pipe', 'pipe', 'pipe']
      });
      
      this.process.stdout?.on('data', (data) => {
        this.writeEmitter.fire(data.toString());
      });
      
      this.process.stderr?.on('data', (data) => {
        this.writeEmitter.fire(`\x1b[31m${data.toString()}\x1b[0m`); // 红色错误输出
      });
      
      this.process.on('close', (code) => {
        this.writeEmitter.fire(`\r\nProcess exited with code ${code}\r\n`);
        this.closeEmitter.fire(code || 0);
      });
      
      this.process.on('error', (error) => {
        this.writeEmitter.fire(`\r\nProcess error: ${error.message}\r\n`);
        this.closeEmitter.fire(1);
      });
    } catch (error) {
      this.writeEmitter.fire(`Failed to start process: ${error.message}\r\n`);
      this.closeEmitter.fire(1);
    }
  }
  
  private stopProcess(): void {
    if (this.process) {
      this.process.kill();
      this.process = undefined;
    }
  }
}

终端管理器

终端会话管理

typescript
class TerminalManager {
  private terminals: Map<string, Terminal> = new Map();
  private disposables: Disposable[] = [];
  
  constructor() {
    this.setupEventListeners();
  }
  
  private setupEventListeners(): void {
    this.disposables.push(
      window.onDidOpenTerminal(terminal => {
        this.onTerminalOpened(terminal);
      }),
      
      window.onDidCloseTerminal(terminal => {
        this.onTerminalClosed(terminal);
      }),
      
      window.onDidChangeActiveTerminal(terminal => {
        this.onActiveTerminalChanged(terminal);
      })
    );
  }
  
  private onTerminalOpened(terminal: Terminal): void {
    const id = this.generateTerminalId(terminal);
    this.terminals.set(id, terminal);
    
    console.log(`Terminal opened: ${terminal.name} (${id})`);
  }
  
  private onTerminalClosed(terminal: Terminal): void {
    const id = this.findTerminalId(terminal);
    if (id) {
      this.terminals.delete(id);
      console.log(`Terminal closed: ${terminal.name} (${id})`);
    }
  }
  
  private onActiveTerminalChanged(terminal: Terminal | undefined): void {
    if (terminal) {
      console.log(`Active terminal changed to: ${terminal.name}`);
    } else {
      console.log('No active terminal');
    }
  }
  
  private generateTerminalId(terminal: Terminal): string {
    return `${terminal.name}-${Date.now()}`;
  }
  
  private findTerminalId(terminal: Terminal): string | undefined {
    for (const [id, t] of this.terminals) {
      if (t === terminal) {
        return id;
      }
    }
    return undefined;
  }
  
  // 创建预定义终端
  createDevelopmentTerminal(): Terminal {
    const terminal = window.createTerminal({
      name: 'Development',
      env: {
        NODE_ENV: 'development',
        DEBUG: '*'
      },
      iconPath: new ThemeIcon('gear'),
      color: new ThemeColor('terminal.ansiGreen')
    });
    
    // 发送初始化命令
    terminal.sendText('echo "Development environment ready"');
    
    return terminal;
  }
  
  createTestTerminal(): Terminal {
    const terminal = window.createTerminal({
      name: 'Test Runner',
      iconPath: new ThemeIcon('beaker'),
      color: new ThemeColor('terminal.ansiYellow')
    });
    
    return terminal;
  }
  
  createBuildTerminal(): Terminal {
    const terminal = window.createTerminal({
      name: 'Build',
      iconPath: new ThemeIcon('tools'),
      color: new ThemeColor('terminal.ansiBlue')
    });
    
    return terminal;
  }
  
  // 终端查找和管理
  findTerminalByName(name: string): Terminal | undefined {
    return Array.from(this.terminals.values()).find(t => t.name === name);
  }
  
  getOrCreateTerminal(name: string, factory: () => Terminal): Terminal {
    let terminal = this.findTerminalByName(name);
    
    if (!terminal || terminal.exitStatus) {
      terminal = factory();
    }
    
    return terminal;
  }
  
  closeAllTerminals(): void {
    for (const terminal of this.terminals.values()) {
      terminal.dispose();
    }
    this.terminals.clear();
  }
  
  getTerminalCount(): number {
    return this.terminals.size;
  }
  
  getActiveTerminals(): Terminal[] {
    return Array.from(this.terminals.values()).filter(t => !t.exitStatus);
  }
  
  dispose(): void {
    this.disposables.forEach(d => d.dispose());
    this.closeAllTerminals();
  }
}

终端命令执行器

typescript
class TerminalCommandExecutor {
  private terminal: Terminal;
  private commandQueue: QueuedCommand[] = [];
  private isExecuting = false;
  
  constructor(terminal: Terminal) {
    this.terminal = terminal;
  }
  
  async executeCommand(
    command: string,
    options: CommandExecutionOptions = {}
  ): Promise<CommandResult> {
    return new Promise((resolve, reject) => {
      const queuedCommand: QueuedCommand = {
        command,
        options,
        resolve,
        reject,
        timestamp: Date.now()
      };
      
      this.commandQueue.push(queuedCommand);
      this.processQueue();
    });
  }
  
  private async processQueue(): Promise<void> {
    if (this.isExecuting || this.commandQueue.length === 0) {
      return;
    }
    
    this.isExecuting = true;
    
    while (this.commandQueue.length > 0) {
      const queuedCommand = this.commandQueue.shift()!;
      await this.executeQueuedCommand(queuedCommand);
    }
    
    this.isExecuting = false;
  }
  
  private async executeQueuedCommand(queuedCommand: QueuedCommand): Promise<void> {
    const { command, options, resolve, reject } = queuedCommand;
    
    try {
      // 确保终端可见
      if (options.showTerminal !== false) {
        this.terminal.show(options.preserveFocus);
      }
      
      // 发送命令
      this.terminal.sendText(command, true);
      
      // 如果需要等待完成
      if (options.waitForCompletion) {
        const result = await this.waitForCommandCompletion(command, options.timeout);
        resolve(result);
      } else {
        resolve({ success: true, output: '', exitCode: 0 });
      }
    } catch (error) {
      reject(error);
    }
  }
  
  private async waitForCommandCompletion(
    command: string,
    timeout = 30000
  ): Promise<CommandResult> {
    // 这里需要实现命令完成检测逻辑
    // 由于 Terminal API 不直接提供输出捕获,这是一个简化的实现
    
    return new Promise((resolve, reject) => {
      const timer = setTimeout(() => {
        reject(new Error(`Command timeout: ${command}`));
      }, timeout);
      
      // 模拟命令执行完成
      setTimeout(() => {
        clearTimeout(timer);
        resolve({
          success: true,
          output: 'Command executed successfully',
          exitCode: 0
        });
      }, 1000);
    });
  }
  
  async executeBatch(commands: string[], options: BatchExecutionOptions = {}): Promise<BatchResult> {
    const results: CommandResult[] = [];
    
    for (const command of commands) {
      try {
        const result = await this.executeCommand(command, {
          ...options,
          waitForCompletion: true
        });
        
        results.push(result);
        
        // 如果命令失败且设置了停止执行
        if (!result.success && options.stopOnError) {
          break;
        }
      } catch (error) {
        results.push({
          success: false,
          output: error.message,
          exitCode: 1
        });
        
        if (options.stopOnError) {
          break;
        }
      }
    }
    
    return {
      results,
      allSuccessful: results.every(r => r.success),
      failedCount: results.filter(r => !r.success).length
    };
  }
  
  clearQueue(): void {
    this.commandQueue.forEach(cmd => {
      cmd.reject(new Error('Command cancelled'));
    });
    this.commandQueue = [];
  }
  
  getQueueLength(): number {
    return this.commandQueue.length;
  }
}

interface QueuedCommand {
  command: string;
  options: CommandExecutionOptions;
  resolve: (result: CommandResult) => void;
  reject: (error: Error) => void;
  timestamp: number;
}

interface CommandExecutionOptions {
  showTerminal?: boolean;
  preserveFocus?: boolean;
  waitForCompletion?: boolean;
  timeout?: number;
}

interface CommandResult {
  success: boolean;
  output: string;
  exitCode: number;
}

interface BatchExecutionOptions extends CommandExecutionOptions {
  stopOnError?: boolean;
  delay?: number;
}

interface BatchResult {
  results: CommandResult[];
  allSuccessful: boolean;
  failedCount: number;
}

终端主题和样式

终端外观定制

typescript
class TerminalThemeManager {
  private static readonly THEME_COLORS = {
    // 标准 ANSI 颜色
    black: '\x1b[30m',
    red: '\x1b[31m',
    green: '\x1b[32m',
    yellow: '\x1b[33m',
    blue: '\x1b[34m',
    magenta: '\x1b[35m',
    cyan: '\x1b[36m',
    white: '\x1b[37m',
    
    // 亮色
    brightBlack: '\x1b[90m',
    brightRed: '\x1b[91m',
    brightGreen: '\x1b[92m',
    brightYellow: '\x1b[93m',
    brightBlue: '\x1b[94m',
    brightMagenta: '\x1b[95m',
    brightCyan: '\x1b[96m',
    brightWhite: '\x1b[97m',
    
    // 背景色
    bgBlack: '\x1b[40m',
    bgRed: '\x1b[41m',
    bgGreen: '\x1b[42m',
    bgYellow: '\x1b[43m',
    bgBlue: '\x1b[44m',
    bgMagenta: '\x1b[45m',
    bgCyan: '\x1b[46m',
    bgWhite: '\x1b[47m',
    
    // 重置
    reset: '\x1b[0m',
    bold: '\x1b[1m',
    dim: '\x1b[2m',
    italic: '\x1b[3m',
    underline: '\x1b[4m',
    blink: '\x1b[5m',
    reverse: '\x1b[7m',
    strikethrough: '\x1b[9m'
  };
  
  static colorize(text: string, color: keyof typeof TerminalThemeManager.THEME_COLORS): string {
    return `${this.THEME_COLORS[color]}${text}${this.THEME_COLORS.reset}`;
  }
  
  static createColoredText(text: string, options: ColorOptions): string {
    let result = '';
    
    if (options.foreground) {
      result += this.THEME_COLORS[options.foreground];
    }
    
    if (options.background) {
      result += this.THEME_COLORS[options.background];
    }
    
    if (options.bold) {
      result += this.THEME_COLORS.bold;
    }
    
    if (options.italic) {
      result += this.THEME_COLORS.italic;
    }
    
    if (options.underline) {
      result += this.THEME_COLORS.underline;
    }
    
    result += text + this.THEME_COLORS.reset;
    
    return result;
  }
  
  static createProgressBar(progress: number, width = 20): string {
    const filled = Math.floor((progress / 100) * width);
    const empty = width - filled;
    
    const bar = '█'.repeat(filled) + '░'.repeat(empty);
    const percentage = `${progress.toFixed(1)}%`;
    
    return this.colorize(`[${bar}] ${percentage}`, 'cyan');
  }
  
  static createTable(data: any[], headers: string[]): string {
    const rows = [headers, ...data.map(row => headers.map(h => String(row[h] || '')))];
    const columnWidths = headers.map((_, i) => 
      Math.max(...rows.map(row => row[i].length))
    );
    
    const separator = '+' + columnWidths.map(w => '-'.repeat(w + 2)).join('+') + '+';
    
    let result = separator + '\r\n';
    
    // 表头
    result += '|' + headers.map((header, i) => 
      ` ${this.colorize(header.padEnd(columnWidths[i]), 'bold')} `
    ).join('|') + '|\r\n';
    
    result += separator + '\r\n';
    
    // 数据行
    for (let i = 1; i < rows.length; i++) {
      result += '|' + rows[i].map((cell, j) => 
        ` ${cell.padEnd(columnWidths[j])} `
      ).join('|') + '|\r\n';
    }
    
    result += separator;
    
    return result;
  }
  
  static createBanner(text: string, style: 'simple' | 'double' | 'rounded' = 'simple'): string {
    const styles = {
      simple: { h: '-', v: '|', tl: '+', tr: '+', bl: '+', br: '+' },
      double: { h: '═', v: '║', tl: '╔', tr: '╗', bl: '╚', br: '╝' },
      rounded: { h: '─', v: '│', tl: '╭', tr: '╮', bl: '╰', br: '╯' }
    };
    
    const chars = styles[style];
    const width = text.length + 4;
    
    let result = '';
    result += chars.tl + chars.h.repeat(width - 2) + chars.tr + '\r\n';
    result += chars.v + ` ${text} ` + chars.v + '\r\n';
    result += chars.bl + chars.h.repeat(width - 2) + chars.br;
    
    return this.colorize(result, 'cyan');
  }
}

interface ColorOptions {
  foreground?: keyof typeof TerminalThemeManager['THEME_COLORS'];
  background?: keyof typeof TerminalThemeManager['THEME_COLORS'];
  bold?: boolean;
  italic?: boolean;
  underline?: boolean;
}

最佳实践

终端生命周期管理

typescript
// 1. 终端资源管理
class TerminalResourceManager {
  private terminals: Set<Terminal> = new Set();
  private disposables: Disposable[] = [];
  
  createManagedTerminal(options: TerminalOptions): Terminal {
    const terminal = window.createTerminal(options);
    this.terminals.add(terminal);
    
    // 监听终端关闭事件
    const disposable = window.onDidCloseTerminal(closedTerminal => {
      if (closedTerminal === terminal) {
        this.terminals.delete(terminal);
        disposable.dispose();
      }
    });
    
    this.disposables.push(disposable);
    
    return terminal;
  }
  
  dispose(): void {
    // 关闭所有管理的终端
    this.terminals.forEach(terminal => terminal.dispose());
    this.terminals.clear();
    
    // 清理事件监听器
    this.disposables.forEach(d => d.dispose());
    this.disposables = [];
  }
}

// 2. 终端状态监控
class TerminalStatusMonitor {
  private statusBar: StatusBarItem;
  
  constructor() {
    this.statusBar = window.createStatusBarItem(StatusBarAlignment.Right, 100);
    this.statusBar.command = 'terminal.showAll';
    this.updateStatus();
    
    window.onDidOpenTerminal(() => this.updateStatus());
    window.onDidCloseTerminal(() => this.updateStatus());
    window.onDidChangeActiveTerminal(() => this.updateStatus());
  }
  
  private updateStatus(): void {
    const terminalCount = window.terminals.length;
    const activeTerminal = window.activeTerminal;
    
    if (terminalCount === 0) {
      this.statusBar.text = '$(terminal) No terminals';
      this.statusBar.tooltip = 'Click to create a new terminal';
    } else {
      this.statusBar.text = `$(terminal) ${terminalCount} terminal${terminalCount > 1 ? 's' : ''}`;
      this.statusBar.tooltip = activeTerminal 
        ? `Active: ${activeTerminal.name}\nClick to show all terminals`
        : 'Click to show all terminals';
    }
    
    this.statusBar.show();
  }
  
  dispose(): void {
    this.statusBar.dispose();
  }
}

错误处理和调试

typescript
// 终端错误处理
class TerminalErrorHandler {
  static handleTerminalError(error: Error, context: string): void {
    console.error(`Terminal error in ${context}:`, error);
    
    window.showErrorMessage(
      `Terminal error: ${error.message}`,
      'Show Details',
      'Retry'
    ).then(action => {
      if (action === 'Show Details') {
        this.showErrorDetails(error, context);
      } else if (action === 'Retry') {
        // 实现重试逻辑
      }
    });
  }
  
  private static showErrorDetails(error: Error, context: string): void {
    const channel = window.createOutputChannel('Terminal Errors');
    channel.appendLine(`Error in ${context}:`);
    channel.appendLine(`Message: ${error.message}`);
    channel.appendLine(`Stack: ${error.stack}`);
    channel.appendLine(`Timestamp: ${new Date().toISOString()}`);
    channel.show();
  }
  
  static wrapTerminalOperation<T>(
    operation: () => T,
    context: string
  ): T | undefined {
    try {
      return operation();
    } catch (error) {
      this.handleTerminalError(error as Error, context);
      return undefined;
    }
  }
  
  static async wrapAsyncTerminalOperation<T>(
    operation: () => Promise<T>,
    context: string
  ): Promise<T | undefined> {
    try {
      return await operation();
    } catch (error) {
      this.handleTerminalError(error as Error, context);
      return undefined;
    }
  }
}

相关 API

示例项目

查看 终端 API 示例 了解完整的实现示例。

您的终极 AI 驱动 IDE 学习指南