Skip to content

编辑器 API

编辑器 API 提供了对 Trae IDE 文本编辑器的完整控制,包括文档操作、选择管理、装饰、代码片段等功能。

核心接口

TextEditor

文本编辑器是编辑器 API 的核心接口,代表一个打开的文本文档的编辑器视图。

typescript
interface TextEditor {
  // 关联的文档
  readonly document: TextDocument;
  
  // 当前选择
  selection: Selection;
  selections: readonly Selection[];
  
  // 可见范围
  readonly visibleRanges: readonly Range[];
  
  // 编辑器选项
  options: TextEditorOptions;
  
  // 视图列
  readonly viewColumn?: ViewColumn;
}

基本使用

typescript
// 获取当前活动编辑器
const editor = TraeAPI.window.activeTextEditor;
if (editor) {
  console.log('当前文档:', editor.document.fileName);
  console.log('当前选择:', editor.selection);
  console.log('语言:', editor.document.languageId);
}

// 监听活动编辑器变化
TraeAPI.window.onDidChangeActiveTextEditor(editor => {
  if (editor) {
    console.log('切换到编辑器:', editor.document.fileName);
  } else {
    console.log('没有活动编辑器');
  }
});

TextDocument

文本文档表示编辑器中的文件内容。

typescript
interface TextDocument {
  // 文档标识
  readonly uri: Uri;
  readonly fileName: string;
  readonly languageId: string;
  
  // 文档内容
  readonly lineCount: number;
  readonly version: number;
  readonly isDirty: boolean;
  readonly isClosed: boolean;
  
  // 保存状态
  readonly isUntitled: boolean;
  
  // 文档操作方法
  getText(range?: Range): string;
  getWordRangeAtPosition(position: Position, regex?: RegExp): Range | undefined;
  lineAt(line: number | Position): TextLine;
  offsetAt(position: Position): number;
  positionAt(offset: number): Position;
  validateRange(range: Range): Range;
  validatePosition(position: Position): Position;
}

文档操作示例

typescript
// 读取文档内容
const document = editor.document;
const fullText = document.getText();
const firstLine = document.lineAt(0).text;
const selection = document.getText(editor.selection);

// 获取单词范围
const position = editor.selection.active;
const wordRange = document.getWordRangeAtPosition(position);
if (wordRange) {
  const word = document.getText(wordRange);
  console.log('当前单词:', word);
}

// 位置和偏移量转换
const position = new TraeAPI.Position(10, 5);
const offset = document.offsetAt(position);
const backToPosition = document.positionAt(offset);

文本操作

编辑操作

typescript
// 基本编辑
editor.edit(editBuilder => {
  // 插入文本
  editBuilder.insert(new TraeAPI.Position(0, 0), 'Hello, World!\n');
  
  // 替换文本
  const range = new TraeAPI.Range(1, 0, 1, 10);
  editBuilder.replace(range, '新文本');
  
  // 删除文本
  const deleteRange = new TraeAPI.Range(2, 0, 3, 0);
  editBuilder.delete(deleteRange);
});

// 带选项的编辑
editor.edit(editBuilder => {
  editBuilder.insert(editor.selection.active, '插入的文本');
}, {
  undoStopBefore: true,
  undoStopAfter: true
});

批量编辑

typescript
// 工作区编辑
const workspaceEdit = new TraeAPI.WorkspaceEdit();

// 编辑多个文件
workspaceEdit.replace(
  document.uri,
  new TraeAPI.Range(0, 0, 0, 5),
  '替换文本'
);

workspaceEdit.insert(
  document.uri,
  new TraeAPI.Position(1, 0),
  '插入文本\n'
);

// 应用编辑
const success = await TraeAPI.workspace.applyEdit(workspaceEdit);
if (success) {
  console.log('编辑应用成功');
}

格式化

typescript
// 格式化整个文档
const formatEdits = await TraeAPI.commands.executeCommand(
  'editor.action.formatDocument'
);

// 格式化选择
const formatSelectionEdits = await TraeAPI.commands.executeCommand(
  'editor.action.formatSelection'
);

// 自定义格式化
const formatProvider: TraeAPI.DocumentFormattingEditProvider = {
  provideDocumentFormattingEdits(document, options, token) {
    const edits: TraeAPI.TextEdit[] = [];
    
    // 添加格式化逻辑
    for (let i = 0; i < document.lineCount; i++) {
      const line = document.lineAt(i);
      if (line.text.startsWith('  ')) {
        // 将双空格替换为制表符
        edits.push(TraeAPI.TextEdit.replace(
          new TraeAPI.Range(i, 0, i, 2),
          '\t'
        ));
      }
    }
    
    return edits;
  }
};

// 注册格式化提供程序
TraeAPI.languages.registerDocumentFormattingEditProvider(
  'typescript',
  formatProvider
);

选择与光标

Selection 和 Range

typescript
// 创建选择
const start = new TraeAPI.Position(0, 0);
const end = new TraeAPI.Position(0, 10);
const selection = new TraeAPI.Selection(start, end);

// 设置选择
editor.selection = selection;

// 多选择
editor.selections = [
  new TraeAPI.Selection(0, 0, 0, 5),
  new TraeAPI.Selection(1, 0, 1, 5),
  new TraeAPI.Selection(2, 0, 2, 5)
];

// 选择操作
const currentSelection = editor.selection;
console.log('选择是否为空:', currentSelection.isEmpty);
console.log('选择是否为单个光标:', currentSelection.isSingleLine);
console.log('活动位置:', currentSelection.active);
console.log('锚点位置:', currentSelection.anchor);

光标操作

typescript
// 移动光标
const moveToPosition = new TraeAPI.Position(10, 0);
editor.selection = new TraeAPI.Selection(moveToPosition, moveToPosition);

// 选择到行尾
const currentPos = editor.selection.active;
const lineEnd = editor.document.lineAt(currentPos.line).range.end;
editor.selection = new TraeAPI.Selection(currentPos, lineEnd);

// 选择整行
function selectLine(lineNumber: number) {
  const line = editor.document.lineAt(lineNumber);
  editor.selection = new TraeAPI.Selection(line.range.start, line.range.end);
}

// 扩展选择
function expandSelection() {
  const currentSelection = editor.selection;
  const document = editor.document;
  
  // 扩展到单词边界
  const wordRange = document.getWordRangeAtPosition(currentSelection.active);
  if (wordRange) {
    editor.selection = new TraeAPI.Selection(wordRange.start, wordRange.end);
  }
}

多光标操作

typescript
// 添加多个光标
function addCursorsAtPositions(positions: TraeAPI.Position[]) {
  const selections = positions.map(pos => 
    new TraeAPI.Selection(pos, pos)
  );
  editor.selections = selections;
}

// 在每行末尾添加光标
function addCursorsAtLineEnds() {
  const selections: TraeAPI.Selection[] = [];
  for (let i = 0; i < editor.document.lineCount; i++) {
    const line = editor.document.lineAt(i);
    const endPos = line.range.end;
    selections.push(new TraeAPI.Selection(endPos, endPos));
  }
  editor.selections = selections;
}

// 查找并选择所有匹配项
function selectAllOccurrences(searchText: string) {
  const document = editor.document;
  const text = document.getText();
  const selections: TraeAPI.Selection[] = [];
  
  let index = 0;
  while ((index = text.indexOf(searchText, index)) !== -1) {
    const startPos = document.positionAt(index);
    const endPos = document.positionAt(index + searchText.length);
    selections.push(new TraeAPI.Selection(startPos, endPos));
    index += searchText.length;
  }
  
  editor.selections = selections;
}

装饰

文本装饰

typescript
// 创建装饰类型
const errorDecorationType = TraeAPI.window.createTextEditorDecorationType({
  backgroundColor: 'rgba(255, 0, 0, 0.1)',
  border: '1px solid red',
  borderRadius: '3px',
  overviewRulerColor: 'red',
  overviewRulerLane: TraeAPI.OverviewRulerLane.Right
});

const warningDecorationType = TraeAPI.window.createTextEditorDecorationType({
  backgroundColor: 'rgba(255, 255, 0, 0.1)',
  border: '1px solid orange',
  gutterIconPath: TraeAPI.Uri.file('/path/to/warning-icon.svg'),
  gutterIconSize: 'contain'
});

// 应用装饰
function highlightErrors(ranges: TraeAPI.Range[]) {
  const decorations: TraeAPI.DecorationOptions[] = ranges.map(range => ({
    range,
    hoverMessage: '发现错误'
  }));
  
  editor.setDecorations(errorDecorationType, decorations);
}

// 带详细信息的装饰
function highlightWithDetails(items: { range: TraeAPI.Range; message: string }[]) {
  const decorations: TraeAPI.DecorationOptions[] = items.map(item => ({
    range: item.range,
    hoverMessage: new TraeAPI.MarkdownString(item.message),
    renderOptions: {
      after: {
        contentText: ' ⚠️',
        color: 'orange'
      }
    }
  }));
  
  editor.setDecorations(warningDecorationType, decorations);
}

高级装饰

typescript
// 创建复杂装饰
const complexDecorationType = TraeAPI.window.createTextEditorDecorationType({
  // 文本样式
  color: '#ff0000',
  fontWeight: 'bold',
  fontStyle: 'italic',
  textDecoration: 'underline',
  
  // 背景和边框
  backgroundColor: 'rgba(255, 0, 0, 0.1)',
  border: '2px solid red',
  borderRadius: '5px',
  
  // 概览标尺
  overviewRulerColor: 'red',
  overviewRulerLane: TraeAPI.OverviewRulerLane.Full,
  
  // 装订线图标
  gutterIconPath: TraeAPI.Uri.file('/path/to/icon.svg'),
  gutterIconSize: 'auto',
  
  // 伪元素
  before: {
    contentText: '🔥',
    margin: '0 5px 0 0'
  },
  after: {
    contentText: ' (重要)',
    color: 'gray',
    fontStyle: 'italic'
  }
});

// 动态装饰
class DiagnosticDecorator {
  private decorationType: TraeAPI.TextEditorDecorationType;
  
  constructor() {
    this.decorationType = TraeAPI.window.createTextEditorDecorationType({
      backgroundColor: 'rgba(255, 0, 0, 0.1)',
      border: '1px solid red'
    });
  }
  
  updateDecorations(editor: TraeAPI.TextEditor, diagnostics: TraeAPI.Diagnostic[]) {
    const decorations: TraeAPI.DecorationOptions[] = diagnostics.map(diagnostic => ({
      range: diagnostic.range,
      hoverMessage: `${diagnostic.severity}: ${diagnostic.message}`,
      renderOptions: {
        after: {
          contentText: ` (${diagnostic.code})`,
          color: 'gray'
        }
      }
    }));
    
    editor.setDecorations(this.decorationType, decorations);
  }
  
  dispose() {
    this.decorationType.dispose();
  }
}

代码片段

基本代码片段

typescript
// 插入简单代码片段
const snippet = new TraeAPI.SnippetString('console.log("${1:message}");');
editor.insertSnippet(snippet);

// 带多个占位符的代码片段
const functionSnippet = new TraeAPI.SnippetString([
  'function ${1:functionName}(${2:parameters}) {',
  '\t${3:// 函数体}',
  '\treturn ${4:result};',
  '}'
].join('\n'));

editor.insertSnippet(functionSnippet, editor.selection.active);

高级代码片段

typescript
// React 组件代码片段
const reactComponentSnippet = new TraeAPI.SnippetString([
  'import React from \'react\';',
  '',
  'interface ${1:ComponentName}Props {',
  '\t${2:// props}',
  '}',
  '',
  'const ${1:ComponentName}: React.FC<${1:ComponentName}Props> = (${3:props}) => {',
  '\treturn (',
  '\t\t<div>',
  '\t\t\t${4:// JSX content}',
  '\t\t</div>',
  '\t);',
  '};',
  '',
  'export default ${1:ComponentName};'
].join('\n'));

// 条件代码片段
function insertConditionalSnippet(condition: string) {
  let snippet: TraeAPI.SnippetString;
  
  if (condition === 'if') {
    snippet = new TraeAPI.SnippetString([
      'if (${1:condition}) {',
      '\t${2:// 代码}',
      '}'
    ].join('\n'));
  } else if (condition === 'for') {
    snippet = new TraeAPI.SnippetString([
      'for (let ${1:i} = 0; ${1:i} < ${2:length}; ${1:i}++) {',
      '\t${3:// 循环体}',
      '}'
    ].join('\n'));
  } else {
    snippet = new TraeAPI.SnippetString('${1:// 代码}');
  }
  
  editor.insertSnippet(snippet);
}

// 动态代码片段
class SnippetProvider {
  private snippets = new Map<string, TraeAPI.SnippetString>();
  
  constructor() {
    this.initializeSnippets();
  }
  
  private initializeSnippets() {
    // 类代码片段
    this.snippets.set('class', new TraeAPI.SnippetString([
      'class ${1:ClassName} {',
      '\tconstructor(${2:parameters}) {',
      '\t\t${3:// 构造函数}',
      '\t}',
      '',
      '\t${4:// 方法}',
      '}'
    ].join('\n')));
    
    // 接口代码片段
    this.snippets.set('interface', new TraeAPI.SnippetString([
      'interface ${1:InterfaceName} {',
      '\t${2:// 属性}',
      '}'
    ].join('\n')));
    
    // 异步函数代码片段
    this.snippets.set('async', new TraeAPI.SnippetString([
      'async function ${1:functionName}(${2:parameters}) {',
      '\ttry {',
      '\t\t${3:// 异步操作}',
      '\t} catch (error) {',
      '\t\tconsole.error(error);',
      '\t}',
      '}'
    ].join('\n')));
  }
  
  insertSnippet(key: string, position?: TraeAPI.Position) {
    const snippet = this.snippets.get(key);
    if (snippet && editor) {
      editor.insertSnippet(snippet, position);
    }
  }
  
  getAvailableSnippets(): string[] {
    return Array.from(this.snippets.keys());
  }
}

事件处理

文档事件

typescript
// 文档变化事件
TraeAPI.workspace.onDidChangeTextDocument(event => {
  console.log('文档已更改:', event.document.fileName);
  console.log('变化数量:', event.contentChanges.length);
  
  event.contentChanges.forEach(change => {
    console.log('变化范围:', change.range);
    console.log('新文本:', change.text);
  });
});

// 文档保存事件
TraeAPI.workspace.onDidSaveTextDocument(document => {
  console.log('文档已保存:', document.fileName);
  
  // 保存后执行操作
  if (document.languageId === 'typescript') {
    // 运行 TypeScript 编译
    TraeAPI.commands.executeCommand('typescript.build');
  }
});

// 文档打开/关闭事件
TraeAPI.workspace.onDidOpenTextDocument(document => {
  console.log('文档已打开:', document.fileName);
});

TraeAPI.workspace.onDidCloseTextDocument(document => {
  console.log('文档已关闭:', document.fileName);
});

编辑器事件

typescript
// 选择变化事件
TraeAPI.window.onDidChangeTextEditorSelection(event => {
  console.log('选择已更改:', event.textEditor.document.fileName);
  console.log('新选择:', event.selections);
  
  // 根据选择更新状态栏
  updateStatusBar(event.selections[0]);
});

// 可见范围变化事件
TraeAPI.window.onDidChangeTextEditorVisibleRanges(event => {
  console.log('可见范围已更改:', event.visibleRanges);
});

// 编辑器选项变化事件
TraeAPI.window.onDidChangeTextEditorOptions(event => {
  console.log('编辑器选项已更改:', event.options);
});

// 活动编辑器变化事件
TraeAPI.window.onDidChangeActiveTextEditor(editor => {
  if (editor) {
    console.log('活动编辑器:', editor.document.fileName);
    // 更新语言特定的功能
    updateLanguageFeatures(editor.document.languageId);
  }
});

高级功能

代码折叠

typescript
// 注册折叠范围提供程序
const foldingProvider: TraeAPI.FoldingRangeProvider = {
  provideFoldingRanges(document, context, token) {
    const ranges: TraeAPI.FoldingRange[] = [];
    
    for (let i = 0; i < document.lineCount; i++) {
      const line = document.lineAt(i);
      
      // 检测函数开始
      if (line.text.includes('function') || line.text.includes('{')) {
        let endLine = i;
        let braceCount = 0;
        
        // 查找匹配的结束括号
        for (let j = i; j < document.lineCount; j++) {
          const currentLine = document.lineAt(j);
          braceCount += (currentLine.text.match(/{/g) || []).length;
          braceCount -= (currentLine.text.match(/}/g) || []).length;
          
          if (braceCount === 0 && j > i) {
            endLine = j;
            break;
          }
        }
        
        if (endLine > i) {
          ranges.push(new TraeAPI.FoldingRange(i, endLine, TraeAPI.FoldingRangeKind.Region));
        }
      }
    }
    
    return ranges;
  }
};

TraeAPI.languages.registerFoldingRangeProvider('typescript', foldingProvider);

代码镜头

typescript
// 注册代码镜头提供程序
const codeLensProvider: TraeAPI.CodeLensProvider = {
  provideCodeLenses(document, token) {
    const codeLenses: TraeAPI.CodeLens[] = [];
    
    for (let i = 0; i < document.lineCount; i++) {
      const line = document.lineAt(i);
      
      // 在函数上添加代码镜头
      if (line.text.includes('function')) {
        const range = new TraeAPI.Range(i, 0, i, line.text.length);
        const codeLens = new TraeAPI.CodeLens(range);
        codeLenses.push(codeLens);
      }
    }
    
    return codeLenses;
  },
  
  resolveCodeLens(codeLens, token) {
    // 解析代码镜头命令
    codeLens.command = {
      title: '运行函数',
      command: 'extension.runFunction',
      arguments: [codeLens.range]
    };
    
    return codeLens;
  }
};

TraeAPI.languages.registerCodeLensProvider('typescript', codeLensProvider);

内联提示

typescript
// 注册内联提示提供程序
const inlayHintsProvider: TraeAPI.InlayHintsProvider = {
  provideInlayHints(document, range, token) {
    const hints: TraeAPI.InlayHint[] = [];
    
    for (let i = range.start.line; i <= range.end.line; i++) {
      const line = document.lineAt(i);
      
      // 为函数参数添加类型提示
      const functionMatch = line.text.match(/function\s+(\w+)\s*\(([^)]*)\)/);
      if (functionMatch) {
        const params = functionMatch[2].split(',');
        params.forEach((param, index) => {
          const paramName = param.trim();
          if (paramName) {
            const position = new TraeAPI.Position(i, line.text.indexOf(paramName) + paramName.length);
            const hint = new TraeAPI.InlayHint(position, ': string', TraeAPI.InlayHintKind.Type);
            hints.push(hint);
          }
        });
      }
    }
    
    return hints;
  }
};

TraeAPI.languages.registerInlayHintsProvider('typescript', inlayHintsProvider);

实用工具

文本处理工具

typescript
class TextUtils {
  // 获取当前单词
  static getCurrentWord(editor: TraeAPI.TextEditor): string | undefined {
    const position = editor.selection.active;
    const wordRange = editor.document.getWordRangeAtPosition(position);
    return wordRange ? editor.document.getText(wordRange) : undefined;
  }
  
  // 获取当前行
  static getCurrentLine(editor: TraeAPI.TextEditor): string {
    const position = editor.selection.active;
    return editor.document.lineAt(position.line).text;
  }
  
  // 获取选中文本或当前单词
  static getSelectedTextOrWord(editor: TraeAPI.TextEditor): string {
    if (!editor.selection.isEmpty) {
      return editor.document.getText(editor.selection);
    }
    return this.getCurrentWord(editor) || '';
  }
  
  // 插入文本到指定位置
  static async insertTextAt(editor: TraeAPI.TextEditor, position: TraeAPI.Position, text: string): Promise<boolean> {
    return editor.edit(editBuilder => {
      editBuilder.insert(position, text);
    });
  }
  
  // 替换选中文本
  static async replaceSelection(editor: TraeAPI.TextEditor, newText: string): Promise<boolean> {
    return editor.edit(editBuilder => {
      editBuilder.replace(editor.selection, newText);
    });
  }
  
  // 在当前行后插入新行
  static async insertLineAfter(editor: TraeAPI.TextEditor, text: string): Promise<boolean> {
    const position = editor.selection.active;
    const lineEnd = editor.document.lineAt(position.line).range.end;
    return this.insertTextAt(editor, lineEnd, '\n' + text);
  }
  
  // 获取缩进字符串
  static getIndentation(editor: TraeAPI.TextEditor, line: number): string {
    const lineText = editor.document.lineAt(line).text;
    const match = lineText.match(/^\s*/);
    return match ? match[0] : '';
  }
  
  // 格式化代码块
  static formatCodeBlock(code: string, language: string): string {
    // 简单的代码格式化逻辑
    const lines = code.split('\n');
    let indentLevel = 0;
    const indentSize = 2;
    
    return lines.map(line => {
      const trimmed = line.trim();
      if (!trimmed) return '';
      
      // 减少缩进
      if (trimmed.startsWith('}') || trimmed.startsWith(']') || trimmed.startsWith(')'))
        indentLevel = Math.max(0, indentLevel - 1);
      
      const formatted = ' '.repeat(indentLevel * indentSize) + trimmed;
      
      // 增加缩进
      if (trimmed.endsWith('{') || trimmed.endsWith('[') || trimmed.endsWith('('))
        indentLevel++;
      
      return formatted;
    }).join('\n');
  }
}

位置和范围工具

typescript
class PositionUtils {
  // 创建位置
  static createPosition(line: number, character: number): TraeAPI.Position {
    return new TraeAPI.Position(line, character);
  }
  
  // 创建范围
  static createRange(startLine: number, startChar: number, endLine: number, endChar: number): TraeAPI.Range {
    return new TraeAPI.Range(startLine, startChar, endLine, endChar);
  }
  
  // 比较位置
  static comparePositions(a: TraeAPI.Position, b: TraeAPI.Position): number {
    if (a.line !== b.line) {
      return a.line - b.line;
    }
    return a.character - b.character;
  }
  
  // 检查位置是否在范围内
  static isPositionInRange(position: TraeAPI.Position, range: TraeAPI.Range): boolean {
    return range.contains(position);
  }
  
  // 获取范围的中心位置
  static getRangeCenter(range: TraeAPI.Range): TraeAPI.Position {
    const startLine = range.start.line;
    const endLine = range.end.line;
    const centerLine = Math.floor((startLine + endLine) / 2);
    
    if (startLine === endLine) {
      const centerChar = Math.floor((range.start.character + range.end.character) / 2);
      return new TraeAPI.Position(centerLine, centerChar);
    }
    
    return new TraeAPI.Position(centerLine, 0);
  }
  
  // 扩展范围
  static expandRange(range: TraeAPI.Range, lines: number, characters: number): TraeAPI.Range {
    const newStart = new TraeAPI.Position(
      Math.max(0, range.start.line - lines),
      Math.max(0, range.start.character - characters)
    );
    const newEnd = new TraeAPI.Position(
      range.end.line + lines,
      range.end.character + characters
    );
    return new TraeAPI.Range(newStart, newEnd);
  }
}

最佳实践

性能优化

  1. 批量操作

    typescript
    // 好的做法:批量编辑
    editor.edit(editBuilder => {
      changes.forEach(change => {
        editBuilder.replace(change.range, change.text);
      });
    });
    
    // 避免:多次单独编辑
    // changes.forEach(change => {
    //   editor.edit(editBuilder => {
    //     editBuilder.replace(change.range, change.text);
    //   });
    // });
  2. 事件处理优化

    typescript
    // 使用防抖处理频繁事件
    let timeout: NodeJS.Timeout;
    TraeAPI.workspace.onDidChangeTextDocument(event => {
      clearTimeout(timeout);
      timeout = setTimeout(() => {
        // 处理文档变化
        processDocumentChange(event);
      }, 300);
    });
  3. 装饰管理

    typescript
    // 及时清理装饰
    class DecorationManager {
      private decorations = new Map<string, TraeAPI.TextEditorDecorationType>();
      
      setDecoration(key: string, options: TraeAPI.DecorationRenderOptions) {
        // 清理旧装饰
        const old = this.decorations.get(key);
        if (old) {
          old.dispose();
        }
        
        // 创建新装饰
        const decoration = TraeAPI.window.createTextEditorDecorationType(options);
        this.decorations.set(key, decoration);
        return decoration;
      }
      
      dispose() {
        this.decorations.forEach(decoration => decoration.dispose());
        this.decorations.clear();
      }
    }

错误处理

typescript
// 安全的编辑器操作
async function safeEdit(editor: TraeAPI.TextEditor, editFunction: (editBuilder: TraeAPI.TextEditorEdit) => void): Promise<boolean> {
  try {
    return await editor.edit(editFunction);
  } catch (error) {
    console.error('编辑操作失败:', error);
    TraeAPI.window.showErrorMessage(`编辑失败: ${error.message}`);
    return false;
  }
}

// 验证文档状态
function validateDocument(document: TraeAPI.TextDocument): boolean {
  if (document.isClosed) {
    TraeAPI.window.showWarningMessage('文档已关闭');
    return false;
  }
  
  if (document.isDirty) {
    TraeAPI.window.showInformationMessage('文档有未保存的更改');
  }
  
  return true;
}

用户体验

typescript
// 提供进度反馈
async function processLargeFile(document: TraeAPI.TextDocument) {
  await TraeAPI.window.withProgress({
    location: TraeAPI.ProgressLocation.Notification,
    title: '处理文件中...',
    cancellable: true
  }, async (progress, token) => {
    const totalLines = document.lineCount;
    
    for (let i = 0; i < totalLines; i++) {
      if (token.isCancellationRequested) {
        break;
      }
      
      // 处理行
      await processLine(document.lineAt(i));
      
      // 更新进度
      progress.report({
        increment: (100 / totalLines),
        message: `处理第 ${i + 1}/${totalLines} 行`
      });
    }
  });
}

// 智能选择
function smartSelect(editor: TraeAPI.TextEditor) {
  const position = editor.selection.active;
  const document = editor.document;
  
  // 尝试选择单词
  let wordRange = document.getWordRangeAtPosition(position);
  if (wordRange) {
    editor.selection = new TraeAPI.Selection(wordRange.start, wordRange.end);
    return;
  }
  
  // 尝试选择行
  const line = document.lineAt(position.line);
  editor.selection = new TraeAPI.Selection(line.range.start, line.range.end);
}

示例:完整的编辑器扩展

typescript
// 文本处理扩展示例
class TextProcessorExtension {
  private decorationType: TraeAPI.TextEditorDecorationType;
  private statusBarItem: TraeAPI.StatusBarItem;
  
  constructor(private context: TraeAPI.ExtensionContext) {
    this.decorationType = TraeAPI.window.createTextEditorDecorationType({
      backgroundColor: 'rgba(255, 255, 0, 0.2)'
    });
    
    this.statusBarItem = TraeAPI.window.createStatusBarItem(
      TraeAPI.StatusBarAlignment.Right,
      100
    );
    
    this.initialize();
  }
  
  private initialize() {
    // 注册命令
    const commands = [
      TraeAPI.commands.registerCommand('textProcessor.highlightDuplicates', () => {
        this.highlightDuplicateWords();
      }),
      
      TraeAPI.commands.registerCommand('textProcessor.formatSelection', () => {
        this.formatSelection();
      }),
      
      TraeAPI.commands.registerCommand('textProcessor.insertTemplate', () => {
        this.insertTemplate();
      })
    ];
    
    // 注册事件监听器
    const eventListeners = [
      TraeAPI.window.onDidChangeActiveTextEditor(editor => {
        this.updateStatusBar(editor);
      }),
      
      TraeAPI.window.onDidChangeTextEditorSelection(event => {
        this.updateStatusBar(event.textEditor);
      })
    ];
    
    // 添加到订阅
    this.context.subscriptions.push(
      ...commands,
      ...eventListeners,
      this.decorationType,
      this.statusBarItem
    );
    
    // 显示状态栏
    this.statusBarItem.show();
  }
  
  private highlightDuplicateWords() {
    const editor = TraeAPI.window.activeTextEditor;
    if (!editor) return;
    
    const document = editor.document;
    const text = document.getText();
    const words = text.match(/\b\w+\b/g) || [];
    
    // 统计单词频率
    const wordCount = new Map<string, number>();
    words.forEach(word => {
      const count = wordCount.get(word.toLowerCase()) || 0;
      wordCount.set(word.toLowerCase(), count + 1);
    });
    
    // 查找重复单词的位置
    const duplicateRanges: TraeAPI.Range[] = [];
    const regex = /\b\w+\b/g;
    let match;
    
    while ((match = regex.exec(text)) !== null) {
      const word = match[0].toLowerCase();
      if (wordCount.get(word)! > 1) {
        const startPos = document.positionAt(match.index);
        const endPos = document.positionAt(match.index + match[0].length);
        duplicateRanges.push(new TraeAPI.Range(startPos, endPos));
      }
    }
    
    // 应用装饰
    editor.setDecorations(this.decorationType, duplicateRanges);
    
    TraeAPI.window.showInformationMessage(`高亮了 ${duplicateRanges.length} 个重复单词`);
  }
  
  private async formatSelection() {
    const editor = TraeAPI.window.activeTextEditor;
    if (!editor) return;
    
    const selection = editor.selection;
    const selectedText = editor.document.getText(selection);
    
    if (selectedText.trim() === '') {
      TraeAPI.window.showWarningMessage('请先选择要格式化的文本');
      return;
    }
    
    // 简单的格式化:移除多余空格,规范化换行
    const formatted = selectedText
      .replace(/\s+/g, ' ')  // 多个空格替换为单个空格
      .replace(/\s*\n\s*/g, '\n')  // 规范化换行
      .trim();
    
    const success = await editor.edit(editBuilder => {
      editBuilder.replace(selection, formatted);
    });
    
    if (success) {
      TraeAPI.window.showInformationMessage('文本格式化完成');
    }
  }
  
  private async insertTemplate() {
    const editor = TraeAPI.window.activeTextEditor;
    if (!editor) return;
    
    const templates = [
      { label: 'JavaScript 函数', value: 'function' },
      { label: 'TypeScript 接口', value: 'interface' },
      { label: 'React 组件', value: 'react' },
      { label: 'HTML 模板', value: 'html' }
    ];
    
    const selected = await TraeAPI.window.showQuickPick(templates, {
      placeHolder: '选择要插入的模板'
    });
    
    if (!selected) return;
    
    let snippet: TraeAPI.SnippetString;
    
    switch (selected.value) {
      case 'function':
        snippet = new TraeAPI.SnippetString([
          'function ${1:functionName}(${2:parameters}) {',
          '\t${3:// 函数体}',
          '\treturn ${4:result};',
          '}'
        ].join('\n'));
        break;
        
      case 'interface':
        snippet = new TraeAPI.SnippetString([
          'interface ${1:InterfaceName} {',
          '\t${2:property}: ${3:type};',
          '}'
        ].join('\n'));
        break;
        
      case 'react':
        snippet = new TraeAPI.SnippetString([
          'import React from \'react\';',
          '',
          'const ${1:ComponentName}: React.FC = () => {',
          '\treturn (',
          '\t\t<div>',
          '\t\t\t${2:// JSX content}',
          '\t\t</div>',
          '\t);',
          '};',
          '',
          'export default ${1:ComponentName};'
        ].join('\n'));
        break;
        
      case 'html':
        snippet = new TraeAPI.SnippetString([
          '<!DOCTYPE html>',
          '<html lang="zh-CN">',
          '<head>',
          '\t<meta charset="UTF-8">',
          '\t<meta name="viewport" content="width=device-width, initial-scale=1.0">',
          '\t<title>${1:页面标题}</title>',
          '</head>',
          '<body>',
          '\t${2:// 页面内容}',
          '</body>',
          '</html>'
        ].join('\n'));
        break;
        
      default:
        return;
    }
    
    await editor.insertSnippet(snippet);
  }
  
  private updateStatusBar(editor?: TraeAPI.TextEditor) {
    if (!editor) {
      this.statusBarItem.hide();
      return;
    }
    
    const document = editor.document;
    const selection = editor.selection;
    
    let text = `行 ${selection.active.line + 1}, 列 ${selection.active.character + 1}`;
    
    if (!selection.isEmpty) {
      const selectedText = document.getText(selection);
      const lines = selectedText.split('\n').length;
      const chars = selectedText.length;
      text += ` (已选择 ${lines} 行, ${chars} 字符)`;
    }
    
    this.statusBarItem.text = text;
    this.statusBarItem.show();
  }
}

// 激活扩展
export function activate(context: TraeAPI.ExtensionContext) {
  new TextProcessorExtension(context);
}

编辑器 API 为 Trae IDE 扩展开发提供了强大而灵活的文本编辑功能。通过合理使用这些 API,您可以创建出功能丰富、用户体验良好的编辑器扩展。

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