Skip to content

插件 API

概述

Trae 插件 API 提供了强大的扩展机制,允许开发者创建、管理和分发插件。通过插件系统,可以扩展 IDE 的功能,添加新的语言支持、工具集成、UI 组件等。

核心概念

插件结构

typescript
interface Plugin {
  readonly id: string;
  readonly name: string;
  readonly version: string;
  readonly description?: string;
  readonly author?: string;
  readonly license?: string;
  readonly homepage?: string;
  readonly repository?: string;
  readonly keywords?: string[];
  readonly engines: EngineRequirement;
  readonly dependencies?: Record<string, string>;
  readonly devDependencies?: Record<string, string>;
  readonly activationEvents?: string[];
  readonly main?: string;
  readonly contributes?: PluginContributions;
}

interface EngineRequirement {
  trae: string; // 例如: "^1.0.0"
  node?: string;
}

interface PluginContributions {
  commands?: CommandContribution[];
  menus?: MenuContribution[];
  keybindings?: KeybindingContribution[];
  languages?: LanguageContribution[];
  grammars?: GrammarContribution[];
  themes?: ThemeContribution[];
  iconThemes?: IconThemeContribution[];
  snippets?: SnippetContribution[];
  debuggers?: DebuggerContribution[];
  configuration?: ConfigurationContribution;
  views?: ViewContribution[];
  viewsContainers?: ViewContainerContribution[];
  problemMatchers?: ProblemMatcherContribution[];
  taskDefinitions?: TaskDefinitionContribution[];
}

插件生命周期

typescript
interface PluginContext {
  readonly subscriptions: Disposable[];
  readonly workspaceState: Memento;
  readonly globalState: Memento;
  readonly extensionPath: string;
  readonly extensionUri: Uri;
  readonly storageUri?: Uri;
  readonly globalStorageUri: Uri;
  readonly logUri: Uri;
  readonly extensionMode: ExtensionMode;
  readonly environmentVariableCollection: EnvironmentVariableCollection;
}

enum ExtensionMode {
  Production = 1,
  Development = 2,
  Test = 3
}

// 插件激活函数
export function activate(context: PluginContext): void | Thenable<void> {
  // 插件激活逻辑
}

// 插件停用函数
export function deactivate(): void | Thenable<void> {
  // 插件清理逻辑
}

API 参考

插件管理

插件注册

typescript
import { plugins } from '@trae/api';

// 注册插件
const registerPlugin = (plugin: Plugin): Disposable => {
  return plugins.registerPlugin(plugin);
};

// 获取插件信息
const getPlugin = (id: string): Plugin | undefined => {
  return plugins.getPlugin(id);
};

// 获取所有插件
const getAllPlugins = (): Plugin[] => {
  return plugins.getAllPlugins();
};

// 检查插件是否已激活
const isPluginActive = (id: string): boolean => {
  return plugins.isActive(id);
};

// 激活插件
const activatePlugin = async (id: string): Promise<void> => {
  await plugins.activate(id);
};

// 停用插件
const deactivatePlugin = async (id: string): Promise<void> => {
  await plugins.deactivate(id);
};

插件依赖管理

typescript
class PluginDependencyManager {
  private dependencyGraph: Map<string, Set<string>> = new Map();
  private reverseDependencyGraph: Map<string, Set<string>> = new Map();
  
  // 添加依赖关系
  addDependency(pluginId: string, dependencyId: string): void {
    if (!this.dependencyGraph.has(pluginId)) {
      this.dependencyGraph.set(pluginId, new Set());
    }
    this.dependencyGraph.get(pluginId)!.add(dependencyId);
    
    if (!this.reverseDependencyGraph.has(dependencyId)) {
      this.reverseDependencyGraph.set(dependencyId, new Set());
    }
    this.reverseDependencyGraph.get(dependencyId)!.add(pluginId);
  }
  
  // 获取插件依赖
  getDependencies(pluginId: string): string[] {
    return Array.from(this.dependencyGraph.get(pluginId) || []);
  }
  
  // 获取依赖此插件的插件
  getDependents(pluginId: string): string[] {
    return Array.from(this.reverseDependencyGraph.get(pluginId) || []);
  }
  
  // 检查循环依赖
  hasCyclicDependency(pluginId: string): boolean {
    const visited = new Set<string>();
    const recursionStack = new Set<string>();
    
    return this.detectCycle(pluginId, visited, recursionStack);
  }
  
  private detectCycle(
    pluginId: string,
    visited: Set<string>,
    recursionStack: Set<string>
  ): boolean {
    visited.add(pluginId);
    recursionStack.add(pluginId);
    
    const dependencies = this.dependencyGraph.get(pluginId) || new Set();
    
    for (const dependency of dependencies) {
      if (!visited.has(dependency)) {
        if (this.detectCycle(dependency, visited, recursionStack)) {
          return true;
        }
      } else if (recursionStack.has(dependency)) {
        return true;
      }
    }
    
    recursionStack.delete(pluginId);
    return false;
  }
  
  // 获取激活顺序
  getActivationOrder(pluginIds: string[]): string[] {
    const result: string[] = [];
    const visited = new Set<string>();
    const temp = new Set<string>();
    
    const visit = (pluginId: string): void => {
      if (temp.has(pluginId)) {
        throw new Error(`Circular dependency detected: ${pluginId}`);
      }
      
      if (!visited.has(pluginId)) {
        temp.add(pluginId);
        
        const dependencies = this.dependencyGraph.get(pluginId) || new Set();
        for (const dependency of dependencies) {
          visit(dependency);
        }
        
        temp.delete(pluginId);
        visited.add(pluginId);
        result.push(pluginId);
      }
    };
    
    for (const pluginId of pluginIds) {
      visit(pluginId);
    }
    
    return result;
  }
}

插件开发

基础插件模板

typescript
// package.json
{
  "name": "my-awesome-plugin",
  "displayName": "My Awesome Plugin",
  "description": "An awesome plugin for Trae",
  "version": "1.0.0",
  "engines": {
    "trae": "^1.0.0"
  },
  "categories": [
    "Other"
  ],
  "activationEvents": [
    "onCommand:myAwesomePlugin.helloWorld"
  ],
  "main": "./out/extension.js",
  "contributes": {
    "commands": [
      {
        "command": "myAwesomePlugin.helloWorld",
        "title": "Hello World",
        "category": "My Awesome Plugin"
      }
    ],
    "menus": {
      "commandPalette": [
        {
          "command": "myAwesomePlugin.helloWorld",
          "when": "true"
        }
      ]
    }
  },
  "scripts": {
    "compile": "tsc -p ./",
    "watch": "tsc -watch -p ./"
  },
  "devDependencies": {
    "@types/trae": "^1.0.0",
    "typescript": "^4.0.0"
  }
}

// src/extension.ts
import * as trae from '@trae/api';

export function activate(context: trae.PluginContext) {
  console.log('My Awesome Plugin is now active!');
  
  // 注册命令
  const disposable = trae.commands.registerCommand(
    'myAwesomePlugin.helloWorld',
    () => {
      trae.window.showInformationMessage('Hello World from My Awesome Plugin!');
    }
  );
  
  context.subscriptions.push(disposable);
}

export function deactivate() {
  console.log('My Awesome Plugin is now deactivated!');
}

高级插件示例

typescript
// 语言支持插件
class LanguageSupportPlugin {
  private disposables: trae.Disposable[] = [];
  
  activate(context: trae.PluginContext): void {
    // 注册语言
    this.disposables.push(
      trae.languages.registerLanguage({
        id: 'mylang',
        extensions: ['.mylang'],
        aliases: ['MyLang'],
        mimetypes: ['text/x-mylang']
      })
    );
    
    // 注册语言配置
    this.disposables.push(
      trae.languages.setLanguageConfiguration('mylang', {
        comments: {
          lineComment: '//',
          blockComment: ['/*', '*/']
        },
        brackets: [
          ['{', '}'],
          ['[', ']'],
          ['(', ')']
        ],
        autoClosingPairs: [
          { open: '{', close: '}' },
          { open: '[', close: ']' },
          { open: '(', close: ')' },
          { open: '"', close: '"' }
        ]
      })
    );
    
    // 注册补全提供者
    this.disposables.push(
      trae.languages.registerCompletionItemProvider(
        { language: 'mylang' },
        new MyLangCompletionProvider(),
        '.', '('
      )
    );
    
    // 注册诊断提供者
    const diagnosticCollection = trae.languages.createDiagnosticCollection('mylang');
    this.disposables.push(diagnosticCollection);
    
    // 监听文档变化
    this.disposables.push(
      trae.workspace.onDidChangeTextDocument(event => {
        if (event.document.languageId === 'mylang') {
          this.validateDocument(event.document, diagnosticCollection);
        }
      })
    );
    
    context.subscriptions.push(...this.disposables);
  }
  
  private async validateDocument(
    document: trae.TextDocument,
    diagnosticCollection: trae.DiagnosticCollection
  ): Promise<void> {
    const diagnostics: trae.Diagnostic[] = [];
    const text = document.getText();
    
    // 简单的语法检查
    const lines = text.split('\n');
    for (let i = 0; i < lines.length; i++) {
      const line = lines[i];
      
      // 检查未闭合的括号
      const openBrackets = (line.match(/\{/g) || []).length;
      const closeBrackets = (line.match(/\}/g) || []).length;
      
      if (openBrackets !== closeBrackets) {
        diagnostics.push({
          range: new trae.Range(
            new trae.Position(i, 0),
            new trae.Position(i, line.length)
          ),
          message: 'Unmatched brackets',
          severity: trae.DiagnosticSeverity.Error
        });
      }
    }
    
    diagnosticCollection.set(document.uri, diagnostics);
  }
  
  deactivate(): void {
    this.disposables.forEach(d => d.dispose());
  }
}

class MyLangCompletionProvider implements trae.CompletionItemProvider {
  provideCompletionItems(
    document: trae.TextDocument,
    position: trae.Position,
    token: trae.CancellationToken,
    context: trae.CompletionContext
  ): trae.CompletionItem[] {
    const items: trae.CompletionItem[] = [];
    
    // 添加关键字补全
    const keywords = ['function', 'class', 'if', 'else', 'for', 'while'];
    for (const keyword of keywords) {
      items.push({
        label: keyword,
        kind: trae.CompletionItemKind.Keyword,
        insertText: keyword
      });
    }
    
    // 添加代码片段
    items.push({
      label: 'function',
      kind: trae.CompletionItemKind.Snippet,
      insertText: new trae.SnippetString('function ${1:name}(${2:params}) {\n\t$0\n}'),
      documentation: 'Function declaration'
    });
    
    return items;
  }
}

插件配置

配置贡献

typescript
// package.json 中的配置贡献
{
  "contributes": {
    "configuration": {
      "title": "My Awesome Plugin",
      "properties": {
        "myAwesomePlugin.enable": {
          "type": "boolean",
          "default": true,
          "description": "Enable My Awesome Plugin"
        },
        "myAwesomePlugin.maxItems": {
          "type": "number",
          "default": 10,
          "minimum": 1,
          "maximum": 100,
          "description": "Maximum number of items to show"
        },
        "myAwesomePlugin.customPath": {
          "type": "string",
          "default": "",
          "description": "Custom path for plugin operations"
        },
        "myAwesomePlugin.features": {
          "type": "object",
          "properties": {
            "autoComplete": {
              "type": "boolean",
              "default": true,
              "description": "Enable auto completion"
            },
            "diagnostics": {
              "type": "boolean",
              "default": true,
              "description": "Enable diagnostics"
            }
          },
          "default": {
            "autoComplete": true,
            "diagnostics": true
          },
          "description": "Feature toggles"
        }
      }
    }
  }
}

// 在插件中使用配置
class PluginConfiguration {
  private static readonly SECTION = 'myAwesomePlugin';
  
  static get enabled(): boolean {
    return trae.workspace.getConfiguration(this.SECTION).get('enable', true);
  }
  
  static get maxItems(): number {
    return trae.workspace.getConfiguration(this.SECTION).get('maxItems', 10);
  }
  
  static get customPath(): string {
    return trae.workspace.getConfiguration(this.SECTION).get('customPath', '');
  }
  
  static get features(): { autoComplete: boolean; diagnostics: boolean } {
    return trae.workspace.getConfiguration(this.SECTION).get('features', {
      autoComplete: true,
      diagnostics: true
    });
  }
  
  static onDidChange(listener: () => void): trae.Disposable {
    return trae.workspace.onDidChangeConfiguration(event => {
      if (event.affectsConfiguration(this.SECTION)) {
        listener();
      }
    });
  }
}

// 使用配置
class ConfigurablePlugin {
  private disposables: trae.Disposable[] = [];
  
  activate(context: trae.PluginContext): void {
    // 监听配置变化
    this.disposables.push(
      PluginConfiguration.onDidChange(() => {
        this.updateFeatures();
      })
    );
    
    this.updateFeatures();
    context.subscriptions.push(...this.disposables);
  }
  
  private updateFeatures(): void {
    if (!PluginConfiguration.enabled) {
      this.disableAllFeatures();
      return;
    }
    
    const features = PluginConfiguration.features;
    
    if (features.autoComplete) {
      this.enableAutoComplete();
    } else {
      this.disableAutoComplete();
    }
    
    if (features.diagnostics) {
      this.enableDiagnostics();
    } else {
      this.disableDiagnostics();
    }
  }
  
  private enableAutoComplete(): void {
    // 启用自动补全功能
  }
  
  private disableAutoComplete(): void {
    // 禁用自动补全功能
  }
  
  private enableDiagnostics(): void {
    // 启用诊断功能
  }
  
  private disableDiagnostics(): void {
    // 禁用诊断功能
  }
  
  private disableAllFeatures(): void {
    this.disableAutoComplete();
    this.disableDiagnostics();
  }
}

插件通信

插件间通信

typescript
// 插件通信接口
interface PluginMessage {
  type: string;
  payload: any;
  sender: string;
  timestamp: number;
}

class PluginCommunicationManager {
  private messageHandlers: Map<string, Set<(message: PluginMessage) => void>> = new Map();
  private plugins: Map<string, Plugin> = new Map();
  
  // 注册插件
  registerPlugin(plugin: Plugin): void {
    this.plugins.set(plugin.id, plugin);
  }
  
  // 发送消息
  sendMessage(
    fromPluginId: string,
    toPluginId: string,
    type: string,
    payload: any
  ): void {
    const message: PluginMessage = {
      type,
      payload,
      sender: fromPluginId,
      timestamp: Date.now()
    };
    
    const handlers = this.messageHandlers.get(toPluginId);
    if (handlers) {
      handlers.forEach(handler => {
        try {
          handler(message);
        } catch (error) {
          console.error(`Error handling message in plugin ${toPluginId}:`, error);
        }
      });
    }
  }
  
  // 广播消息
  broadcastMessage(
    fromPluginId: string,
    type: string,
    payload: any
  ): void {
    const message: PluginMessage = {
      type,
      payload,
      sender: fromPluginId,
      timestamp: Date.now()
    };
    
    for (const [pluginId, handlers] of this.messageHandlers) {
      if (pluginId !== fromPluginId) {
        handlers.forEach(handler => {
          try {
            handler(message);
          } catch (error) {
            console.error(`Error handling broadcast in plugin ${pluginId}:`, error);
          }
        });
      }
    }
  }
  
  // 订阅消息
  subscribeToMessages(
    pluginId: string,
    handler: (message: PluginMessage) => void
  ): trae.Disposable {
    if (!this.messageHandlers.has(pluginId)) {
      this.messageHandlers.set(pluginId, new Set());
    }
    
    this.messageHandlers.get(pluginId)!.add(handler);
    
    return {
      dispose: () => {
        const handlers = this.messageHandlers.get(pluginId);
        if (handlers) {
          handlers.delete(handler);
          if (handlers.size === 0) {
            this.messageHandlers.delete(pluginId);
          }
        }
      }
    };
  }
  
  // 获取可用插件列表
  getAvailablePlugins(): Plugin[] {
    return Array.from(this.plugins.values());
  }
  
  // 检查插件是否存在
  hasPlugin(pluginId: string): boolean {
    return this.plugins.has(pluginId);
  }
}

// 使用插件通信
class CommunicatingPlugin {
  private communicationManager: PluginCommunicationManager;
  private disposables: trae.Disposable[] = [];
  
  constructor(
    private pluginId: string,
    communicationManager: PluginCommunicationManager
  ) {
    this.communicationManager = communicationManager;
  }
  
  activate(context: trae.PluginContext): void {
    // 订阅消息
    this.disposables.push(
      this.communicationManager.subscribeToMessages(
        this.pluginId,
        this.handleMessage.bind(this)
      )
    );
    
    // 注册命令
    this.disposables.push(
      trae.commands.registerCommand(
        `${this.pluginId}.sendMessage`,
        this.sendTestMessage.bind(this)
      )
    );
    
    context.subscriptions.push(...this.disposables);
  }
  
  private handleMessage(message: PluginMessage): void {
    switch (message.type) {
      case 'greeting':
        this.handleGreeting(message);
        break;
      case 'data-request':
        this.handleDataRequest(message);
        break;
      case 'notification':
        this.handleNotification(message);
        break;
      default:
        console.log(`Unknown message type: ${message.type}`);
    }
  }
  
  private handleGreeting(message: PluginMessage): void {
    console.log(`Received greeting from ${message.sender}: ${message.payload.text}`);
    
    // 回复问候
    this.communicationManager.sendMessage(
      this.pluginId,
      message.sender,
      'greeting-reply',
      { text: `Hello back from ${this.pluginId}!` }
    );
  }
  
  private handleDataRequest(message: PluginMessage): void {
    const requestedData = this.getRequestedData(message.payload.dataType);
    
    this.communicationManager.sendMessage(
      this.pluginId,
      message.sender,
      'data-response',
      { data: requestedData }
    );
  }
  
  private handleNotification(message: PluginMessage): void {
    trae.window.showInformationMessage(
      `Notification from ${message.sender}: ${message.payload.message}`
    );
  }
  
  private sendTestMessage(): void {
    // 发送测试消息给所有插件
    this.communicationManager.broadcastMessage(
      this.pluginId,
      'greeting',
      { text: 'Hello from test plugin!' }
    );
  }
  
  private getRequestedData(dataType: string): any {
    // 根据数据类型返回相应数据
    switch (dataType) {
      case 'config':
        return { setting1: 'value1', setting2: 'value2' };
      case 'stats':
        return { activeFiles: 5, totalLines: 1000 };
      default:
        return null;
    }
  }
}

插件测试

单元测试

typescript
// test/extension.test.ts
import * as assert from 'assert';
import * as trae from '@trae/api';
import { activate, deactivate } from '../src/extension';

suite('Extension Test Suite', () => {
  let context: trae.PluginContext;
  
  setup(() => {
    // 创建模拟上下文
    context = {
      subscriptions: [],
      workspaceState: new MockMemento(),
      globalState: new MockMemento(),
      extensionPath: '/mock/path',
      extensionUri: trae.Uri.file('/mock/path'),
      globalStorageUri: trae.Uri.file('/mock/global'),
      logUri: trae.Uri.file('/mock/log'),
      extensionMode: trae.ExtensionMode.Test,
      environmentVariableCollection: new MockEnvironmentVariableCollection()
    } as trae.PluginContext;
  });
  
  teardown(() => {
    // 清理
    context.subscriptions.forEach(d => d.dispose());
  });
  
  test('Extension should activate without errors', async () => {
    await activate(context);
    assert.ok(context.subscriptions.length > 0, 'Should register disposables');
  });
  
  test('Commands should be registered', async () => {
    await activate(context);
    
    const commands = await trae.commands.getCommands();
    assert.ok(
      commands.includes('myAwesomePlugin.helloWorld'),
      'Hello World command should be registered'
    );
  });
  
  test('Configuration should be accessible', () => {
    const config = trae.workspace.getConfiguration('myAwesomePlugin');
    assert.ok(config, 'Configuration should be accessible');
  });
  
  test('Extension should deactivate cleanly', async () => {
    await activate(context);
    await deactivate();
    // 验证清理是否正确
  });
});

// 模拟类
class MockMemento implements trae.Memento {
  private storage: Map<string, any> = new Map();
  
  get<T>(key: string): T | undefined;
  get<T>(key: string, defaultValue: T): T;
  get<T>(key: string, defaultValue?: T): T | undefined {
    return this.storage.get(key) ?? defaultValue;
  }
  
  async update(key: string, value: any): Promise<void> {
    this.storage.set(key, value);
  }
  
  keys(): readonly string[] {
    return Array.from(this.storage.keys());
  }
}

class MockEnvironmentVariableCollection implements trae.EnvironmentVariableCollection {
  private variables: Map<string, trae.EnvironmentVariableMutator> = new Map();
  
  replace(variable: string, value: string): void {
    this.variables.set(variable, { value, type: trae.EnvironmentVariableMutatorType.Replace });
  }
  
  append(variable: string, value: string): void {
    this.variables.set(variable, { value, type: trae.EnvironmentVariableMutatorType.Append });
  }
  
  prepend(variable: string, value: string): void {
    this.variables.set(variable, { value, type: trae.EnvironmentVariableMutatorType.Prepend });
  }
  
  get(variable: string): trae.EnvironmentVariableMutator | undefined {
    return this.variables.get(variable);
  }
  
  forEach(callback: (variable: string, mutator: trae.EnvironmentVariableMutator) => void): void {
    this.variables.forEach((mutator, variable) => callback(variable, mutator));
  }
  
  delete(variable: string): void {
    this.variables.delete(variable);
  }
  
  clear(): void {
    this.variables.clear();
  }
}

集成测试

typescript
// test/integration.test.ts
import * as assert from 'assert';
import * as trae from '@trae/api';
import * as path from 'path';

suite('Integration Test Suite', () => {
  let workspaceUri: trae.Uri;
  
  suiteSetup(async () => {
    // 创建测试工作区
    const testWorkspace = path.join(__dirname, '..', 'test-workspace');
    workspaceUri = trae.Uri.file(testWorkspace);
    
    // 打开工作区
    await trae.commands.executeCommand('trae.openFolder', workspaceUri);
  });
  
  test('Plugin should work with real files', async () => {
    // 创建测试文件
    const testFile = trae.Uri.joinPath(workspaceUri, 'test.mylang');
    const content = 'function test() {\n  console.log("Hello");\n}';
    
    await trae.workspace.fs.writeFile(testFile, Buffer.from(content));
    
    // 打开文件
    const document = await trae.workspace.openTextDocument(testFile);
    await trae.window.showTextDocument(document);
    
    // 等待插件处理
    await new Promise(resolve => setTimeout(resolve, 1000));
    
    // 验证诊断
    const diagnostics = trae.languages.getDiagnostics(testFile);
    assert.ok(Array.isArray(diagnostics), 'Diagnostics should be available');
  });
  
  test('Commands should work in real environment', async () => {
    // 执行插件命令
    await trae.commands.executeCommand('myAwesomePlugin.helloWorld');
    
    // 验证命令执行结果
    // 这里可能需要检查状态栏、通知等
  });
  
  test('Configuration changes should be handled', async () => {
    const config = trae.workspace.getConfiguration('myAwesomePlugin');
    
    // 更改配置
    await config.update('enable', false, trae.ConfigurationTarget.Workspace);
    
    // 等待配置变化处理
    await new Promise(resolve => setTimeout(resolve, 500));
    
    // 验证配置变化的影响
    const newConfig = trae.workspace.getConfiguration('myAwesomePlugin');
    assert.strictEqual(newConfig.get('enable'), false);
    
    // 恢复配置
    await config.update('enable', true, trae.ConfigurationTarget.Workspace);
  });
});

插件发布

打包配置

typescript
// webpack.config.js
const path = require('path');

module.exports = {
  target: 'node',
  entry: './src/extension.ts',
  output: {
    path: path.resolve(__dirname, 'out'),
    filename: 'extension.js',
    libraryTarget: 'commonjs2',
    devtoolModuleFilenameTemplate: '../[resource-path]'
  },
  devtool: 'source-map',
  externals: {
    '@trae/api': 'commonjs @trae/api'
  },
  resolve: {
    extensions: ['.ts', '.js']
  },
  module: {
    rules: [
      {
        test: /\.ts$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'ts-loader'
          }
        ]
      }
    ]
  }
};

// .vscodeignore
node_modules
src
test
.gitignore
.git
*.map
*.ts
.vscode-test
.nyc_output
coverage

// 发布脚本
{
  "scripts": {
    "package": "trae-cli package",
    "publish": "trae-cli publish",
    "prepublish": "npm run compile && npm run test"
  }
}

最佳实践

性能优化

typescript
// 1. 延迟加载
class LazyLoadingPlugin {
  private featureModules: Map<string, () => Promise<any>> = new Map();
  
  activate(context: trae.PluginContext): void {
    // 注册延迟加载的功能模块
    this.featureModules.set('completion', () => import('./features/completion'));
    this.featureModules.set('diagnostics', () => import('./features/diagnostics'));
    this.featureModules.set('formatting', () => import('./features/formatting'));
    
    // 只在需要时加载功能
    this.registerLazyCommands(context);
  }
  
  private registerLazyCommands(context: trae.PluginContext): void {
    context.subscriptions.push(
      trae.commands.registerCommand('plugin.enableCompletion', async () => {
        const module = await this.loadFeature('completion');
        module.activate(context);
      })
    );
  }
  
  private async loadFeature(name: string): Promise<any> {
    const loader = this.featureModules.get(name);
    if (!loader) {
      throw new Error(`Feature ${name} not found`);
    }
    
    return await loader();
  }
}

// 2. 资源管理
class ResourceManager {
  private disposables: trae.Disposable[] = [];
  private timers: Set<NodeJS.Timeout> = new Set();
  private watchers: Set<trae.FileSystemWatcher> = new Set();
  
  addDisposable(disposable: trae.Disposable): void {
    this.disposables.push(disposable);
  }
  
  addTimer(timer: NodeJS.Timeout): void {
    this.timers.add(timer);
  }
  
  addWatcher(watcher: trae.FileSystemWatcher): void {
    this.watchers.add(watcher);
    this.addDisposable(watcher);
  }
  
  dispose(): void {
    // 清理所有资源
    this.disposables.forEach(d => d.dispose());
    this.timers.forEach(t => clearTimeout(t));
    this.watchers.forEach(w => w.dispose());
    
    this.disposables = [];
    this.timers.clear();
    this.watchers.clear();
  }
}

// 3. 缓存策略
class CacheManager<T> {
  private cache: Map<string, { value: T; timestamp: number }> = new Map();
  private ttl: number;
  
  constructor(ttlMs: number = 300000) { // 5分钟默认TTL
    this.ttl = ttlMs;
  }
  
  get(key: string): T | undefined {
    const entry = this.cache.get(key);
    if (!entry) {
      return undefined;
    }
    
    if (Date.now() - entry.timestamp > this.ttl) {
      this.cache.delete(key);
      return undefined;
    }
    
    return entry.value;
  }
  
  set(key: string, value: T): void {
    this.cache.set(key, {
      value,
      timestamp: Date.now()
    });
  }
  
  clear(): void {
    this.cache.clear();
  }
  
  cleanup(): void {
    const now = Date.now();
    for (const [key, entry] of this.cache) {
      if (now - entry.timestamp > this.ttl) {
        this.cache.delete(key);
      }
    }
  }
}

错误处理

typescript
// 插件错误处理
class PluginErrorHandler {
  static handleError(error: Error, context: string): void {
    console.error(`Plugin error in ${context}:`, error);
    
    // 发送错误报告
    this.reportError(error, context);
    
    // 显示用户友好的错误消息
    trae.window.showErrorMessage(
      `插件错误: ${error.message}`,
      '报告问题',
      '忽略'
    ).then(action => {
      if (action === '报告问题') {
        this.openIssueReporter(error, context);
      }
    });
  }
  
  static wrapAsync<T>(
    operation: () => Promise<T>,
    context: string
  ): Promise<T | undefined> {
    return operation().catch(error => {
      this.handleError(error, context);
      return undefined;
    });
  }
  
  static wrapSync<T>(
    operation: () => T,
    context: string
  ): T | undefined {
    try {
      return operation();
    } catch (error) {
      this.handleError(error as Error, context);
      return undefined;
    }
  }
  
  private static reportError(error: Error, context: string): void {
    // 实现错误报告逻辑
    const errorReport = {
      message: error.message,
      stack: error.stack,
      context,
      timestamp: new Date().toISOString(),
      version: trae.version
    };
    
    // 发送到错误收集服务
    console.log('Error report:', errorReport);
  }
  
  private static openIssueReporter(error: Error, context: string): void {
    const issueBody = `
**Error Context:** ${context}
**Error Message:** ${error.message}
**Stack Trace:**
\`\`\`
${error.stack}
\`\`\`
**Trae Version:** ${trae.version}
    `.trim();
    
    const issueUrl = `https://github.com/your-plugin/issues/new?body=${encodeURIComponent(issueBody)}`;
    trae.env.openExternal(trae.Uri.parse(issueUrl));
  }
}

相关 API

示例项目

查看 插件 API 示例 了解完整的实现示例。

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