Editor API 
The Editor API provides comprehensive control over Trae IDE's text editors, allowing you to manipulate documents, handle selections, and customize editing behavior.
Overview 
The Editor API enables you to:
- Access and modify text documents
- Control cursor position and selections
- Handle text changes and edits
- Customize editor behavior
- Implement language-specific features
Core Interfaces 
TextEditor 
Represents an active text editor instance.
typescript
interface TextEditor {
  readonly document: TextDocument;
  readonly selection: Selection;
  readonly selections: readonly Selection[];
  readonly visibleRanges: readonly Range[];
  readonly options: TextEditorOptions;
  readonly viewColumn?: ViewColumn;
  edit(callback: (editBuilder: TextEditorEdit) => void): Thenable<boolean>;
  insertSnippet(snippet: SnippetString, location?: Position | Range | readonly Position[] | readonly Range[]): Thenable<boolean>;
  setDecorations(decorationType: TextEditorDecorationType, rangesOrOptions: readonly Range[] | readonly DecorationOptions[]): void;
  revealRange(range: Range, revealType?: TextEditorRevealType): void;
  show(column?: ViewColumn): void;
  hide(): void;
}TextDocument 
Represents a text document in the editor.
typescript
interface TextDocument {
  readonly uri: Uri;
  readonly fileName: string;
  readonly isUntitled: boolean;
  readonly languageId: string;
  readonly version: number;
  readonly isDirty: boolean;
  readonly isClosed: boolean;
  readonly save: () => Thenable<boolean>;
  readonly eol: EndOfLine;
  readonly lineCount: number;
  getText(range?: Range): string;
  getWordRangeAtPosition(position: Position, regex?: RegExp): Range | undefined;
  lineAt(line: number): TextLine;
  lineAt(position: Position): TextLine;
  offsetAt(position: Position): number;
  positionAt(offset: number): Position;
  validateRange(range: Range): Range;
  validatePosition(position: Position): Position;
}Basic Operations 
Getting the Active Editor 
typescript
import { TraeAPI } from '@trae/api';
// Get the currently active editor
const editor = TraeAPI.window.activeTextEditor;
if (editor) {
  console.log('Active file:', editor.document.fileName);
  console.log('Language:', editor.document.languageId);
  console.log('Line count:', editor.document.lineCount);
}Reading Text Content 
typescript
const editor = TraeAPI.window.activeTextEditor;
if (editor) {
  const document = editor.document;
  
  // Get all text
  const fullText = document.getText();
  
  // Get selected text
  const selectedText = document.getText(editor.selection);
  
  // Get text from a specific range
  const range = new TraeAPI.Range(0, 0, 5, 0); // First 5 lines
  const rangeText = document.getText(range);
  
  // Get a specific line
  const firstLine = document.lineAt(0);
  console.log('First line text:', firstLine.text);
}Modifying Text 
typescript
const editor = TraeAPI.window.activeTextEditor;
if (editor) {
  editor.edit(editBuilder => {
    // Insert text at cursor position
    editBuilder.insert(editor.selection.active, 'Hello, World!');
    
    // Replace selected text
    editBuilder.replace(editor.selection, 'New text');
    
    // Delete a range
    const range = new TraeAPI.Range(0, 0, 1, 0);
    editBuilder.delete(range);
    
    // Insert at the beginning of the document
    editBuilder.insert(new TraeAPI.Position(0, 0), '// Generated code\n');
  });
}Selections and Cursors 
Working with Selections 
typescript
const editor = TraeAPI.window.activeTextEditor;
if (editor) {
  // Get current selection
  const selection = editor.selection;
  console.log('Selection start:', selection.start);
  console.log('Selection end:', selection.end);
  console.log('Is empty:', selection.isEmpty);
  
  // Set a new selection
  const newSelection = new TraeAPI.Selection(0, 0, 5, 10);
  editor.selection = newSelection;
  
  // Work with multiple selections
  const selections = editor.selections;
  console.log('Number of cursors:', selections.length);
  
  // Set multiple selections
  editor.selections = [
    new TraeAPI.Selection(0, 0, 0, 5),
    new TraeAPI.Selection(1, 0, 1, 5),
    new TraeAPI.Selection(2, 0, 2, 5)
  ];
}Cursor Movement 
typescript
// Move cursor to a specific position
const position = new TraeAPI.Position(10, 5);
editor.selection = new TraeAPI.Selection(position, position);
// Move to the end of the document
const lastLine = editor.document.lineCount - 1;
const lastChar = editor.document.lineAt(lastLine).text.length;
const endPosition = new TraeAPI.Position(lastLine, lastChar);
editor.selection = new TraeAPI.Selection(endPosition, endPosition);
// Reveal the cursor position
editor.revealRange(editor.selection, TraeAPI.TextEditorRevealType.InCenter);Text Decorations 
Creating Decoration Types 
typescript
// Create a decoration type for highlighting errors
const errorDecorationType = TraeAPI.window.createTextEditorDecorationType({
  backgroundColor: 'rgba(255, 0, 0, 0.2)',
  border: '1px solid red',
  borderRadius: '3px',
  overviewRulerColor: 'red',
  overviewRulerLane: TraeAPI.OverviewRulerLane.Right,
  light: {
    backgroundColor: 'rgba(255, 0, 0, 0.1)'
  },
  dark: {
    backgroundColor: 'rgba(255, 0, 0, 0.3)'
  }
});
// Create a decoration type for warnings
const warningDecorationType = TraeAPI.window.createTextEditorDecorationType({
  backgroundColor: 'rgba(255, 165, 0, 0.2)',
  border: '1px solid orange',
  gutterIconPath: TraeAPI.Uri.file('/path/to/warning-icon.svg'),
  gutterIconSize: 'contain'
});Applying Decorations 
typescript
const editor = TraeAPI.window.activeTextEditor;
if (editor) {
  // Simple range decorations
  const errorRanges = [
    new TraeAPI.Range(5, 0, 5, 10),
    new TraeAPI.Range(10, 5, 10, 15)
  ];
  editor.setDecorations(errorDecorationType, errorRanges);
  
  // Decorations with hover messages
  const warningDecorations: TraeAPI.DecorationOptions[] = [
    {
      range: new TraeAPI.Range(3, 0, 3, 20),
      hoverMessage: 'This is a warning message',
      renderOptions: {
        after: {
          contentText: ' ⚠️',
          color: 'orange'
        }
      }
    }
  ];
  editor.setDecorations(warningDecorationType, warningDecorations);
}Snippets 
Inserting Snippets 
typescript
const editor = TraeAPI.window.activeTextEditor;
if (editor) {
  // Simple snippet
  const snippet = new TraeAPI.SnippetString('console.log("${1:message}");');
  editor.insertSnippet(snippet);
  
  // Complex snippet with multiple placeholders
  const functionSnippet = new TraeAPI.SnippetString([
    'function ${1:functionName}(${2:parameters}) {',
    '\t${3:// TODO: Implement}',
    '\treturn ${4:undefined};',
    '}'
  ].join('\n'));
  
  // Insert at specific position
  const position = new TraeAPI.Position(10, 0);
  editor.insertSnippet(functionSnippet, position);
  
  // Insert at multiple positions
  const positions = [
    new TraeAPI.Position(5, 0),
    new TraeAPI.Position(15, 0),
    new TraeAPI.Position(25, 0)
  ];
  editor.insertSnippet(snippet, positions);
}Dynamic Snippets 
typescript
// Create a snippet based on current context
function createGetterSetterSnippet(propertyName: string, propertyType: string): TraeAPI.SnippetString {
  return new TraeAPI.SnippetString([
    `private _${propertyName}: ${propertyType};`,
    '',
    `get ${propertyName}(): ${propertyType} {`,
    `\treturn this._${propertyName};`,
    '}',
    '',
    `set ${propertyName}(value: ${propertyType}) {`,
    `\tthis._${propertyName} = value;`,
    '}'
  ].join('\n'));
}
// Usage
const snippet = createGetterSetterSnippet('name', 'string');
editor.insertSnippet(snippet);Event Handling 
Document Events 
typescript
// Listen to document changes
TraeAPI.workspace.onDidChangeTextDocument(event => {
  console.log('Document changed:', event.document.fileName);
  console.log('Changes:', event.contentChanges);
  
  // Process each change
  event.contentChanges.forEach(change => {
    console.log('Range:', change.range);
    console.log('New text:', change.text);
    console.log('Range length:', change.rangeLength);
  });
});
// Listen to document saves
TraeAPI.workspace.onDidSaveTextDocument(document => {
  console.log('Document saved:', document.fileName);
});
// Listen to document opens
TraeAPI.workspace.onDidOpenTextDocument(document => {
  console.log('Document opened:', document.fileName);
});
// Listen to document closes
TraeAPI.workspace.onDidCloseTextDocument(document => {
  console.log('Document closed:', document.fileName);
});Editor Events 
typescript
// Listen to active editor changes
TraeAPI.window.onDidChangeActiveTextEditor(editor => {
  if (editor) {
    console.log('Active editor changed to:', editor.document.fileName);
  } else {
    console.log('No active editor');
  }
});
// Listen to selection changes
TraeAPI.window.onDidChangeTextEditorSelection(event => {
  console.log('Selection changed in:', event.textEditor.document.fileName);
  console.log('New selections:', event.selections);
});
// Listen to visible range changes
TraeAPI.window.onDidChangeTextEditorVisibleRanges(event => {
  console.log('Visible ranges changed:', event.visibleRanges);
});
// Listen to editor options changes
TraeAPI.window.onDidChangeTextEditorOptions(event => {
  console.log('Editor options changed:', event.options);
});Advanced Features 
Custom Language Support 
typescript
// Register a language
TraeAPI.languages.registerDocumentFormattingEditProvider('mylang', {
  provideDocumentFormattingEdits(document: TraeAPI.TextDocument): TraeAPI.TextEdit[] {
    // Implement formatting logic
    const edits: TraeAPI.TextEdit[] = [];
    
    for (let i = 0; i < document.lineCount; i++) {
      const line = document.lineAt(i);
      if (line.text.startsWith('  ')) {
        // Convert 2 spaces to 4 spaces
        const newText = line.text.replace(/^  /, '    ');
        edits.push(TraeAPI.TextEdit.replace(line.range, newText));
      }
    }
    
    return edits;
  }
});
// Register hover provider
TraeAPI.languages.registerHoverProvider('mylang', {
  provideHover(document: TraeAPI.TextDocument, position: TraeAPI.Position): TraeAPI.Hover | undefined {
    const range = document.getWordRangeAtPosition(position);
    if (range) {
      const word = document.getText(range);
      return new TraeAPI.Hover(`Information about: ${word}`);
    }
  }
});Code Actions 
typescript
// Register code action provider
TraeAPI.languages.registerCodeActionsProvider('typescript', {
  provideCodeActions(
    document: TraeAPI.TextDocument,
    range: TraeAPI.Range,
    context: TraeAPI.CodeActionContext
  ): TraeAPI.CodeAction[] {
    const actions: TraeAPI.CodeAction[] = [];
    
    // Add a quick fix for console.log statements
    const text = document.getText(range);
    if (text.includes('console.log')) {
      const action = new TraeAPI.CodeAction('Remove console.log', TraeAPI.CodeActionKind.QuickFix);
      action.edit = new TraeAPI.WorkspaceEdit();
      action.edit.delete(document.uri, range);
      actions.push(action);
    }
    
    // Add a refactor action
    const refactorAction = new TraeAPI.CodeAction('Extract to function', TraeAPI.CodeActionKind.Refactor);
    refactorAction.command = {
      title: 'Extract to function',
      command: 'myExtension.extractFunction',
      arguments: [document.uri, range]
    };
    actions.push(refactorAction);
    
    return actions;
  }
});Folding Ranges 
typescript
// Register folding range provider
TraeAPI.languages.registerFoldingRangeProvider('mylang', {
  provideFoldingRanges(document: TraeAPI.TextDocument): TraeAPI.FoldingRange[] {
    const ranges: TraeAPI.FoldingRange[] = [];
    
    for (let i = 0; i < document.lineCount; i++) {
      const line = document.lineAt(i);
      
      // Fold function blocks
      if (line.text.includes('function')) {
        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;
  }
});Utilities 
Text Manipulation Helpers 
typescript
class TextUtils {
  static getIndentation(line: string): string {
    const match = line.match(/^\s*/);
    return match ? match[0] : '';
  }
  
  static getWordAt(document: TraeAPI.TextDocument, position: TraeAPI.Position): string {
    const range = document.getWordRangeAtPosition(position);
    return range ? document.getText(range) : '';
  }
  
  static findAllOccurrences(document: TraeAPI.TextDocument, searchText: string): TraeAPI.Range[] {
    const ranges: TraeAPI.Range[] = [];
    const text = document.getText();
    let index = 0;
    
    while ((index = text.indexOf(searchText, index)) !== -1) {
      const startPos = document.positionAt(index);
      const endPos = document.positionAt(index + searchText.length);
      ranges.push(new TraeAPI.Range(startPos, endPos));
      index += searchText.length;
    }
    
    return ranges;
  }
  
  static insertAtLineEnd(editor: TraeAPI.TextEditor, lineNumber: number, text: string): void {
    const line = editor.document.lineAt(lineNumber);
    const position = new TraeAPI.Position(lineNumber, line.text.length);
    
    editor.edit(editBuilder => {
      editBuilder.insert(position, text);
    });
  }
}Editor State Management 
typescript
class EditorStateManager {
  private savedStates = new Map<string, EditorState>();
  
  saveState(editor: TraeAPI.TextEditor): void {
    const state: EditorState = {
      selections: [...editor.selections],
      visibleRanges: [...editor.visibleRanges],
      options: { ...editor.options }
    };
    
    this.savedStates.set(editor.document.uri.toString(), state);
  }
  
  restoreState(editor: TraeAPI.TextEditor): void {
    const state = this.savedStates.get(editor.document.uri.toString());
    if (state) {
      editor.selections = state.selections;
      if (state.visibleRanges.length > 0) {
        editor.revealRange(state.visibleRanges[0]);
      }
    }
  }
}
interface EditorState {
  selections: TraeAPI.Selection[];
  visibleRanges: TraeAPI.Range[];
  options: TraeAPI.TextEditorOptions;
}Best Practices 
Performance 
- Batch multiple edits into a single edit()call
- Use document.getText(range)instead of getting all text when possible
- Dispose of decorations when no longer needed
- Avoid frequent selection changes
User Experience 
- Preserve user selections when possible
- Provide undo/redo support for custom operations
- Show progress for long-running operations
- Use appropriate decoration types for different purposes
Error Handling 
typescript
try {
  await editor.edit(editBuilder => {
    // Your edit operations
  });
} catch (error) {
  TraeAPI.window.showErrorMessage(`Edit failed: ${error.message}`);
}Examples 
Auto-formatter Extension 
typescript
export function activate(context: TraeAPI.ExtensionContext) {
  const disposable = TraeAPI.commands.registerCommand('myExtension.formatDocument', async () => {
    const editor = TraeAPI.window.activeTextEditor;
    if (!editor) return;
    
    await editor.edit(editBuilder => {
      for (let i = 0; i < editor.document.lineCount; i++) {
        const line = editor.document.lineAt(i);
        const trimmed = line.text.trim();
        
        if (trimmed !== line.text) {
          editBuilder.replace(line.range, trimmed);
        }
      }
    });
    
    TraeAPI.window.showInformationMessage('Document formatted!');
  });
  
  context.subscriptions.push(disposable);
}Word Counter Extension 
typescript
class WordCounter {
  private statusBarItem: TraeAPI.StatusBarItem;
  
  constructor() {
    this.statusBarItem = TraeAPI.window.createStatusBarItem(TraeAPI.StatusBarAlignment.Left);
    this.updateWordCount();
    
    // Update on document changes
    TraeAPI.workspace.onDidChangeTextDocument(() => this.updateWordCount());
    TraeAPI.window.onDidChangeActiveTextEditor(() => this.updateWordCount());
  }
  
  private updateWordCount(): void {
    const editor = TraeAPI.window.activeTextEditor;
    if (!editor) {
      this.statusBarItem.hide();
      return;
    }
    
    const text = editor.document.getText();
    const wordCount = text.split(/\s+/).filter(word => word.length > 0).length;
    
    this.statusBarItem.text = `Words: ${wordCount}`;
    this.statusBarItem.show();
  }
  
  dispose(): void {
    this.statusBarItem.dispose();
  }
}For more examples and advanced usage patterns, see the Extension Samples repository.