Skip to content

Debugging API

Debugging APIは、さまざまなプログラミング言語とランタイム環境に対する包括的なデバッグ機能を提供します。

概要

Debugging APIを使用すると、以下のことができます:

  • デバッグ設定の作成と管理
  • デバッグセッションの開始、停止、制御
  • ブレークポイントの設定と管理
  • 変数とコールスタックの検査
  • デバッグコンテキストでの式の評価
  • デバッグイベントと状態変更の処理
  • 言語固有のデバッガーとの統合
  • カスタムデバッグアダプターの提供

基本的な使用方法

デバッグ設定

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

// デバッグ設定を定義
const debugConfig: TraeAPI.DebugConfiguration = {
  type: 'node',
  request: 'launch',
  name: 'Launch Program',
  program: '${workspaceFolder}/src/index.js',
  args: ['--verbose'],
  env: {
    NODE_ENV: 'development'
  },
  console: 'integratedTerminal',
  sourceMaps: true,
  outFiles: ['${workspaceFolder}/dist/**/*.js']
};

// デバッグセッションを開始
async function startDebugging() {
  const success = await TraeAPI.debug.startDebugging(
    TraeAPI.workspace.workspaceFolders?.[0],
    debugConfig
  );
  
  if (success) {
    console.log('デバッグセッションが正常に開始されました');
  } else {
    console.error('デバッグセッションの開始に失敗しました');
  }
}

// アクティブなデバッグセッションを停止
async function stopDebugging() {
  const activeSession = TraeAPI.debug.activeDebugSession;
  if (activeSession) {
    await activeSession.customRequest('disconnect');
    console.log('デバッグセッションが停止されました');
  }
}

ブレークポイント管理

typescript
class BreakpointManager {
  private breakpoints: Map<string, TraeAPI.Breakpoint[]> = new Map();

  constructor() {
    this.setupBreakpointListeners();
  }

  private setupBreakpointListeners() {
    // ブレークポイントの変更をリッスン
    TraeAPI.debug.onDidChangeBreakpoints(event => {
      console.log('ブレークポイントが変更されました:', {
        added: event.added,
        removed: event.removed,
        changed: event.changed
      });
      
      this.updateBreakpointCache(event);
    });
  }

  private updateBreakpointCache(event: TraeAPI.BreakpointsChangeEvent) {
    // 追加されたブレークポイントを更新
    for (const breakpoint of event.added) {
      if (breakpoint instanceof TraeAPI.SourceBreakpoint) {
        const uri = breakpoint.location.uri.toString();
        const existing = this.breakpoints.get(uri) || [];
        existing.push(breakpoint);
        this.breakpoints.set(uri, existing);
      }
    }

    // 削除されたブレークポイントを除去
    for (const breakpoint of event.removed) {
      if (breakpoint instanceof TraeAPI.SourceBreakpoint) {
        const uri = breakpoint.location.uri.toString();
        const existing = this.breakpoints.get(uri) || [];
        const filtered = existing.filter(bp => bp.id !== breakpoint.id);
        this.breakpoints.set(uri, filtered);
      }
    }

    // 変更されたブレークポイントを更新
    for (const breakpoint of event.changed) {
      if (breakpoint instanceof TraeAPI.SourceBreakpoint) {
        const uri = breakpoint.location.uri.toString();
        const existing = this.breakpoints.get(uri) || [];
        const index = existing.findIndex(bp => bp.id === breakpoint.id);
        if (index !== -1) {
          existing[index] = breakpoint;
        }
      }
    }
  }

  // プログラムでブレークポイントを追加
  addBreakpoint(uri: TraeAPI.Uri, line: number, condition?: string): TraeAPI.SourceBreakpoint {
    const location = new TraeAPI.Location(uri, new TraeAPI.Position(line, 0));
    const breakpoint = new TraeAPI.SourceBreakpoint(location, true, condition);
    
    // VS Codeのブレークポイントコレクションに追加
    const existing = TraeAPI.debug.breakpoints;
    TraeAPI.debug.breakpoints = [...existing, breakpoint];
    
    return breakpoint;
  }

  // ブレークポイントを削除
  removeBreakpoint(breakpoint: TraeAPI.Breakpoint): void {
    const existing = TraeAPI.debug.breakpoints;
    TraeAPI.debug.breakpoints = existing.filter(bp => bp.id !== breakpoint.id);
  }

  // ブレークポイントを切り替え
  toggleBreakpoint(uri: TraeAPI.Uri, line: number): void {
    const existing = this.findBreakpointAtLine(uri, line);
    
    if (existing) {
      this.removeBreakpoint(existing);
    } else {
      this.addBreakpoint(uri, line);
    }
  }

  // 特定の行のブレークポイントを検索
  findBreakpointAtLine(uri: TraeAPI.Uri, line: number): TraeAPI.SourceBreakpoint | undefined {
    const uriString = uri.toString();
    const breakpoints = this.breakpoints.get(uriString) || [];
    
    return breakpoints.find(bp => {
      if (bp instanceof TraeAPI.SourceBreakpoint) {
        return bp.location.range.start.line === line;
      }
      return false;
    }) as TraeAPI.SourceBreakpoint | undefined;
  }

  // ファイルのすべてのブレークポイントを取得
  getBreakpointsForFile(uri: TraeAPI.Uri): TraeAPI.Breakpoint[] {
    return this.breakpoints.get(uri.toString()) || [];
  }

  // 条件付きブレークポイントを設定
  setConditionalBreakpoint(uri: TraeAPI.Uri, line: number, condition: string): TraeAPI.SourceBreakpoint {
    // この行の既存のブレークポイントを削除
    const existing = this.findBreakpointAtLine(uri, line);
    if (existing) {
      this.removeBreakpoint(existing);
    }
    
    // 新しい条件付きブレークポイントを追加
    return this.addBreakpoint(uri, line, condition);
  }

  // ログポイントを設定(停止せずにログを出力するブレークポイント)
  setLogpoint(uri: TraeAPI.Uri, line: number, logMessage: string): TraeAPI.SourceBreakpoint {
    const location = new TraeAPI.Location(uri, new TraeAPI.Position(line, 0));
    const breakpoint = new TraeAPI.SourceBreakpoint(location, true);
    breakpoint.logMessage = logMessage;
    
    const existing = TraeAPI.debug.breakpoints;
    TraeAPI.debug.breakpoints = [...existing, breakpoint];
    
    return breakpoint;
  }
}

// ブレークポイントマネージャーを初期化
const breakpointManager = new BreakpointManager();

デバッグセッション管理

typescript
class DebugSessionManager {
  private activeSessions: Map<string, TraeAPI.DebugSession> = new Map();
  private sessionListeners: Map<string, TraeAPI.Disposable[]> = new Map();

  constructor() {
    this.setupSessionListeners();
  }

  private setupSessionListeners() {
    // セッション開始をリッスン
    TraeAPI.debug.onDidStartDebugSession(session => {
      console.log('デバッグセッションが開始されました:', session.name);
      this.activeSessions.set(session.id, session);
      this.setupSessionEventListeners(session);
    });

    // セッション終了をリッスン
    TraeAPI.debug.onDidTerminateDebugSession(session => {
      console.log('デバッグセッションが終了されました:', session.name);
      this.cleanupSession(session.id);
    });

    // アクティブセッションの変更をリッスン
    TraeAPI.debug.onDidChangeActiveDebugSession(session => {
      if (session) {
        console.log('アクティブなデバッグセッションが変更されました:', session.name);
      } else {
        console.log('アクティブなデバッグセッションがありません');
      }
    });
  }

  private setupSessionEventListeners(session: TraeAPI.DebugSession) {
    const listeners: TraeAPI.Disposable[] = [];

    // デバッグアダプターからのカスタムイベントをリッスン
    const customEventListener = session.onDidReceiveDebugSessionCustomEvent(event => {
      this.handleCustomEvent(session, event);
    });
    listeners.push(customEventListener);

    this.sessionListeners.set(session.id, listeners);
  }

  private handleCustomEvent(session: TraeAPI.DebugSession, event: TraeAPI.DebugSessionCustomEvent) {
    console.log('カスタムデバッグイベント:', {
      session: session.name,
      event: event.event,
      body: event.body
    });

    switch (event.event) {
      case 'output':
        this.handleOutputEvent(session, event.body);
        break;
      case 'stopped':
        this.handleStoppedEvent(session, event.body);
        break;
      case 'continued':
        this.handleContinuedEvent(session, event.body);
        break;
      case 'thread':
        this.handleThreadEvent(session, event.body);
        break;
      case 'breakpoint':
        this.handleBreakpointEvent(session, event.body);
        break;
      default:
        console.log('不明なデバッグイベント:', event.event);
    }
  }

  private handleOutputEvent(session: TraeAPI.DebugSession, body: any) {
    if (body.category === 'console') {
      console.log(`[${session.name}] コンソール:`, body.output);
    } else if (body.category === 'stderr') {
      console.error(`[${session.name}] エラー:`, body.output);
    } else {
      console.log(`[${session.name}] 出力:`, body.output);
    }
  }

  private handleStoppedEvent(session: TraeAPI.DebugSession, body: any) {
    console.log(`[${session.name}] 停止:`, {
      reason: body.reason,
      threadId: body.threadId,
      text: body.text
    });

    // 停止時に自動的にコールスタックを表示
    this.showCallStack(session, body.threadId);
  }

  private handleContinuedEvent(session: TraeAPI.DebugSession, body: any) {
    console.log(`[${session.name}] 継続:`, {
      threadId: body.threadId,
      allThreadsContinued: body.allThreadsContinued
    });
  }

  private handleThreadEvent(session: TraeAPI.DebugSession, body: any) {
    console.log(`[${session.name}] スレッドイベント:`, {
      reason: body.reason,
      threadId: body.threadId
    });
  }

  private handleBreakpointEvent(session: TraeAPI.DebugSession, body: any) {
    console.log(`[${session.name}] ブレークポイントイベント:`, {
      reason: body.reason,
      breakpoint: body.breakpoint
    });
  }

  private async showCallStack(session: TraeAPI.DebugSession, threadId: number) {
    try {
      const stackTrace = await session.customRequest('stackTrace', {
        threadId: threadId,
        startFrame: 0,
        levels: 20
      });

      console.log(`[${session.name}] コールスタック:`);
      for (const frame of stackTrace.stackFrames) {
        console.log(`  ${frame.name} (${frame.source?.name}:${frame.line})`);
      }
    } catch (error) {
      console.error('スタックトレースの取得に失敗しました:', error);
    }
  }

  private cleanupSession(sessionId: string) {
    // イベントリスナーを破棄
    const listeners = this.sessionListeners.get(sessionId);
    if (listeners) {
      listeners.forEach(listener => listener.dispose());
      this.sessionListeners.delete(sessionId);
    }

    // アクティブセッションから削除
    this.activeSessions.delete(sessionId);
  }

  // アクティブなデバッグセッションを取得
  getActiveSession(): TraeAPI.DebugSession | undefined {
    return TraeAPI.debug.activeDebugSession;
  }

  // すべてのアクティブセッションを取得
  getAllSessions(): TraeAPI.DebugSession[] {
    return Array.from(this.activeSessions.values());
  }

  // セッションにカスタムリクエストを送信
  async sendCustomRequest(sessionId: string, command: string, args?: any): Promise<any> {
    const session = this.activeSessions.get(sessionId);
    if (!session) {
      throw new Error(`ID ${sessionId} のアクティブセッションが見つかりません`);
    }

    return await session.customRequest(command, args);
  }

  // セッション実行の制御
  async continueExecution(sessionId: string, threadId?: number): Promise<void> {
    await this.sendCustomRequest(sessionId, 'continue', { threadId });
  }

  async stepOver(sessionId: string, threadId: number): Promise<void> {
    await this.sendCustomRequest(sessionId, 'next', { threadId });
  }

  async stepInto(sessionId: string, threadId: number): Promise<void> {
    await this.sendCustomRequest(sessionId, 'stepIn', { threadId });
  }

  async stepOut(sessionId: string, threadId: number): Promise<void> {
    await this.sendCustomRequest(sessionId, 'stepOut', { threadId });
  }

  async pause(sessionId: string, threadId: number): Promise<void> {
    await this.sendCustomRequest(sessionId, 'pause', { threadId });
  }

  async restart(sessionId: string): Promise<void> {
    await this.sendCustomRequest(sessionId, 'restart');
  }

  async terminate(sessionId: string): Promise<void> {
    await this.sendCustomRequest(sessionId, 'terminate');
  }
}

// デバッグセッションマネージャーを初期化
const debugSessionManager = new DebugSessionManager();

変数検査

typescript
class VariableInspector {
  private variableCache: Map<string, any> = new Map();

  constructor() {
    this.setupVariableListeners();
  }

  private setupVariableListeners() {
    TraeAPI.debug.onDidChangeActiveDebugSession(session => {
      if (session) {
        this.clearVariableCache();
      }
    });
  }

  private clearVariableCache() {
    this.variableCache.clear();
  }

  // 特定のスコープの変数を取得
  async getVariables(sessionId: string, frameId: number, scopeId?: number): Promise<any[]> {
    const session = debugSessionManager.getAllSessions().find(s => s.id === sessionId);
    if (!session) {
      throw new Error(`ID ${sessionId} のセッションが見つかりません`);
    }

    try {
      // フレームのスコープを取得
      const scopes = await session.customRequest('scopes', { frameId });
      
      if (scopeId !== undefined) {
        // 特定のスコープの変数を取得
        const scope = scopes.scopes.find((s: any) => s.variablesReference === scopeId);
        if (scope) {
          const variables = await session.customRequest('variables', {
            variablesReference: scope.variablesReference
          });
          return variables.variables;
        }
      } else {
        // すべてのスコープの変数を取得
        const allVariables = [];
        for (const scope of scopes.scopes) {
          const variables = await session.customRequest('variables', {
            variablesReference: scope.variablesReference
          });
          allVariables.push({
            scope: scope.name,
            variables: variables.variables
          });
        }
        return allVariables;
      }
    } catch (error) {
      console.error('変数の取得に失敗しました:', error);
      return [];
    }

    return [];
  }

  // 変数の詳細を取得
  async getVariableDetails(sessionId: string, variableReference: number): Promise<any> {
    const cacheKey = `${sessionId}-${variableReference}`;
    
    if (this.variableCache.has(cacheKey)) {
      return this.variableCache.get(cacheKey);
    }

    const session = debugSessionManager.getAllSessions().find(s => s.id === sessionId);
    if (!session) {
      throw new Error(`ID ${sessionId} のセッションが見つかりません`);
    }

    try {
      const variables = await session.customRequest('variables', {
        variablesReference
      });
      
      this.variableCache.set(cacheKey, variables.variables);
      return variables.variables;
    } catch (error) {
      console.error('変数の詳細取得に失敗しました:', error);
      return null;
    }
  }

  // 式を評価
  async evaluateExpression(
    sessionId: string,
    expression: string,
    frameId?: number,
    context: 'watch' | 'repl' | 'hover' = 'repl'
  ): Promise<any> {
    const session = debugSessionManager.getAllSessions().find(s => s.id === sessionId);
    if (!session) {
      throw new Error(`ID ${sessionId} のセッションが見つかりません`);
    }

    try {
      const result = await session.customRequest('evaluate', {
        expression,
        frameId,
        context
      });
      
      return {
        result: result.result,
        type: result.type,
        variablesReference: result.variablesReference,
        namedVariables: result.namedVariables,
        indexedVariables: result.indexedVariables
      };
    } catch (error) {
      console.error('式の評価に失敗しました:', error);
      throw error;
    }
  }

  // 変数の値を設定
  async setVariableValue(
    sessionId: string,
    variablesReference: number,
    name: string,
    value: string
  ): Promise<any> {
    const session = debugSessionManager.getAllSessions().find(s => s.id === sessionId);
    if (!session) {
      throw new Error(`ID ${sessionId} のセッションが見つかりません`);
    }

    try {
      const result = await session.customRequest('setVariable', {
        variablesReference,
        name,
        value
      });
      
      // この変数参照のキャッシュをクリア
      const cacheKey = `${sessionId}-${variablesReference}`;
      this.variableCache.delete(cacheKey);
      
      return result;
    } catch (error) {
      console.error('変数値の設定に失敗しました:', error);
      throw error;
    }
  }

  // ウォッチ式
  private watchExpressions: Map<string, string[]> = new Map();

  addWatchExpression(sessionId: string, expression: string): void {
    const existing = this.watchExpressions.get(sessionId) || [];
    if (!existing.includes(expression)) {
      existing.push(expression);
      this.watchExpressions.set(sessionId, existing);
    }
  }

  removeWatchExpression(sessionId: string, expression: string): void {
    const existing = this.watchExpressions.get(sessionId) || [];
    const filtered = existing.filter(expr => expr !== expression);
    this.watchExpressions.set(sessionId, filtered);
  }

  async evaluateWatchExpressions(sessionId: string, frameId?: number): Promise<Array<{ expression: string; result: any }>> {
    const expressions = this.watchExpressions.get(sessionId) || [];
    const results = [];

    for (const expression of expressions) {
      try {
        const result = await this.evaluateExpression(sessionId, expression, frameId, 'watch');
        results.push({ expression, result });
      } catch (error) {
        results.push({ 
          expression, 
          result: { error: error.message } 
        });
      }
    }

    return results;
  }
}

// 変数インスペクターを初期化
const variableInspector = new VariableInspector();

カスタムデバッグアダプター

typescript
import { DebugAdapterDescriptorFactory, DebugAdapterDescriptor, DebugSession, DebugAdapterExecutable } from 'vscode';

class CustomDebugAdapterFactory implements DebugAdapterDescriptorFactory {
  createDebugAdapterDescriptor(
    session: DebugSession,
    executable: DebugAdapterExecutable | undefined
  ): ProviderResult<DebugAdapterDescriptor> {
    // 外部デバッグアダプターの実行可能ファイルを返す
    if (session.configuration.type === 'myCustomDebugger') {
      return new DebugAdapterExecutable(
        'node',
        ['/path/to/debug-adapter.js'],
        {
          env: {
            ...process.env,
            DEBUG_MODE: 'true'
          }
        }
      );
    }

    // インラインデバッグアダプターを返す
    return new DebugAdapterInlineImplementation(session);
  }
}

class DebugAdapterInlineImplementation extends DebugAdapterDescriptor {
  constructor(private session: DebugSession) {
    super();
  }

  // デバッグアダプタープロトコルを実装
  async handleMessage(message: any): Promise<any> {
    switch (message.command) {
      case 'initialize':
        return this.handleInitialize(message);
      case 'launch':
        return this.handleLaunch(message);
      case 'setBreakpoints':
        return this.handleSetBreakpoints(message);
      case 'continue':
        return this.handleContinue(message);
      case 'next':
        return this.handleNext(message);
      case 'stepIn':
        return this.handleStepIn(message);
      case 'stepOut':
        return this.handleStepOut(message);
      case 'pause':
        return this.handlePause(message);
      case 'stackTrace':
        return this.handleStackTrace(message);
      case 'scopes':
        return this.handleScopes(message);
      case 'variables':
        return this.handleVariables(message);
      case 'evaluate':
        return this.handleEvaluate(message);
      case 'disconnect':
        return this.handleDisconnect(message);
      default:
        throw new Error(`不明なコマンド: ${message.command}`);
    }
  }

  private async handleInitialize(message: any): Promise<any> {
    return {
      seq: 0,
      type: 'response',
      request_seq: message.seq,
      command: 'initialize',
      success: true,
      body: {
        supportsConfigurationDoneRequest: true,
        supportsEvaluateForHovers: true,
        supportsStepBack: false,
        supportsSetVariable: true,
        supportsRestartFrame: false,
        supportsGotoTargetsRequest: false,
        supportsStepInTargetsRequest: false,
        supportsCompletionsRequest: true,
        supportsModulesRequest: false,
        additionalModuleColumns: [],
        supportedChecksumAlgorithms: [],
        supportsRestartRequest: true,
        supportsExceptionOptions: false,
        supportsValueFormattingOptions: true,
        supportsExceptionInfoRequest: false,
        supportTerminateDebuggee: true,
        supportsDelayedStackTraceLoading: false,
        supportsLoadedSourcesRequest: false,
        supportsLogPoints: true,
        supportsTerminateThreadsRequest: false,
        supportsSetExpression: false,
        supportsTerminateRequest: true,
        completionTriggerCharacters: ['.', '['],
        supportsBreakpointLocationsRequest: false
      }
    };
  }

  private async handleLaunch(message: any): Promise<any> {
    const config = message.arguments;
    
    // 設定でデバッガーを初期化
    console.log('設定でデバッグセッションを起動:', config);
    
    // 初期化イベントを送信
    this.sendEvent({
      seq: 0,
      type: 'event',
      event: 'initialized'
    });
    
    return {
      seq: 0,
      type: 'response',
      request_seq: message.seq,
      command: 'launch',
      success: true
    };
  }

  private async handleSetBreakpoints(message: any): Promise<any> {
    const args = message.arguments;
    const breakpoints = [];
    
    for (const bp of args.breakpoints) {
      breakpoints.push({
        verified: true,
        line: bp.line,
        column: bp.column,
        source: args.source
      });
    }
    
    return {
      seq: 0,
      type: 'response',
      request_seq: message.seq,
      command: 'setBreakpoints',
      success: true,
      body: {
        breakpoints
      }
    };
  }

  private async handleStackTrace(message: any): Promise<any> {
    const args = message.arguments;
    
    // モックスタックトレース
    const stackFrames = [
      {
        id: 1,
        name: 'main',
        source: {
          name: 'index.js',
          path: '/path/to/index.js'
        },
        line: 10,
        column: 5
      },
      {
        id: 2,
        name: 'helper',
        source: {
          name: 'helper.js',
          path: '/path/to/helper.js'
        },
        line: 25,
        column: 12
      }
    ];
    
    return {
      seq: 0,
      type: 'response',
      request_seq: message.seq,
      command: 'stackTrace',
      success: true,
      body: {
        stackFrames,
        totalFrames: stackFrames.length
      }
    };
  }

  private sendEvent(event: any): void {
    // デバッグセッションにイベントを送信
    console.log('デバッグイベントを送信:', event);
  }
}

// デバッグアダプターファクトリーを登録
TraeAPI.debug.registerDebugAdapterDescriptorFactory(
  'myCustomDebugger',
  new CustomDebugAdapterFactory()
);

デバッグ設定プロバイダー

typescript
class CustomDebugConfigurationProvider implements TraeAPI.DebugConfigurationProvider {
  // 初期デバッグ設定を提供
  provideDebugConfigurations(
    folder: TraeAPI.WorkspaceFolder | undefined,
    token?: TraeAPI.CancellationToken
  ): TraeAPI.ProviderResult<TraeAPI.DebugConfiguration[]> {
    return [
      {
        name: 'Launch Program',
        type: 'myCustomDebugger',
        request: 'launch',
        program: '${workspaceFolder}/src/main.js',
        args: [],
        console: 'integratedTerminal',
        internalConsoleOptions: 'neverOpen'
      },
      {
        name: 'Attach to Process',
        type: 'myCustomDebugger',
        request: 'attach',
        processId: '${command:pickProcess}',
        console: 'integratedTerminal'
      }
    ];
  }

  // 開始前にデバッグ設定を解決
  resolveDebugConfiguration(
    folder: TraeAPI.WorkspaceFolder | undefined,
    config: TraeAPI.DebugConfiguration,
    token?: TraeAPI.CancellationToken
  ): TraeAPI.ProviderResult<TraeAPI.DebugConfiguration> {
    // 設定を検証して解決
    if (!config.type && !config.request && !config.name) {
      // デバッグ設定ピッカーを表示するためにnullを返す
      return null;
    }

    // 設定内の変数を解決
    if (config.program) {
      config.program = this.resolveVariables(config.program, folder);
    }

    if (config.cwd) {
      config.cwd = this.resolveVariables(config.cwd, folder);
    }

    // デフォルト値を設定
    if (!config.console) {
      config.console = 'integratedTerminal';
    }

    return config;
  }

  // 動的に設定を解決
  resolveDebugConfigurationWithSubstitutedVariables(
    folder: TraeAPI.WorkspaceFolder | undefined,
    config: TraeAPI.DebugConfiguration,
    token?: TraeAPI.CancellationToken
  ): TraeAPI.ProviderResult<TraeAPI.DebugConfiguration> {
    // 変数置換後の最終解決
    
    // 必須フィールドを検証
    if (config.request === 'launch' && !config.program) {
      TraeAPI.window.showErrorMessage('デバッグ設定に"program"フィールドがありません');
      return null;
    }

    if (config.request === 'attach' && !config.processId) {
      TraeAPI.window.showErrorMessage('デバッグ設定に"processId"フィールドがありません');
      return null;
    }

    return config;
  }

  private resolveVariables(value: string, folder?: TraeAPI.WorkspaceFolder): string {
    if (!value) return value;

    // ワークスペースフォルダーを解決
    if (value.includes('${workspaceFolder}')) {
      const workspacePath = folder?.uri.fsPath || TraeAPI.workspace.workspaceFolders?.[0]?.uri.fsPath || '';
      value = value.replace(/\$\{workspaceFolder\}/g, workspacePath);
    }

    // ファイルパスを解決
    if (value.includes('${file}')) {
      const activeFile = TraeAPI.window.activeTextEditor?.document.fileName || '';
      value = value.replace(/\$\{file\}/g, activeFile);
    }

    // 必要に応じて他の変数を解決
    return value;
  }
}

// デバッグ設定プロバイダーを登録
TraeAPI.debug.registerDebugConfigurationProvider(
  'myCustomDebugger',
  new CustomDebugConfigurationProvider()
);

APIリファレンス

コアインターフェース

typescript
interface DebugSession {
  readonly id: string;
  readonly type: string;
  readonly name: string;
  readonly workspaceFolder: WorkspaceFolder | undefined;
  readonly configuration: DebugConfiguration;
  
  customRequest(command: string, args?: any): Thenable<any>;
  onDidReceiveDebugSessionCustomEvent: Event<DebugSessionCustomEvent>;
}

interface DebugConfiguration {
  type: string;
  name: string;
  request: string;
  [key: string]: any;
}

interface Breakpoint {
  readonly id: string;
  readonly enabled: boolean;
  readonly condition?: string;
  readonly hitCondition?: string;
  readonly logMessage?: string;
}

interface SourceBreakpoint extends Breakpoint {
  readonly location: Location;
}

interface DebugAdapterDescriptorFactory {
  createDebugAdapterDescriptor(
    session: DebugSession,
    executable: DebugAdapterExecutable | undefined
  ): ProviderResult<DebugAdapterDescriptor>;
}

interface DebugConfigurationProvider {
  provideDebugConfigurations?(
    folder: WorkspaceFolder | undefined,
    token?: CancellationToken
  ): ProviderResult<DebugConfiguration[]>;
  
  resolveDebugConfiguration?(
    folder: WorkspaceFolder | undefined,
    config: DebugConfiguration,
    token?: CancellationToken
  ): ProviderResult<DebugConfiguration>;
  
  resolveDebugConfigurationWithSubstitutedVariables?(
    folder: WorkspaceFolder | undefined,
    config: DebugConfiguration,
    token?: CancellationToken
  ): ProviderResult<DebugConfiguration>;
}

ベストプラクティス

  1. 設定: 適切なデフォルト値と明確なエラーメッセージを提供
  2. パフォーマンス: デバッグ情報をキャッシュし、遅延読み込みを使用
  3. ユーザーエクスペリエンス: 長時間の操作に対して進行状況を表示
  4. エラーハンドリング: デバッグアダプターの障害を適切に処理
  5. セキュリティ: すべてのデバッグ設定と入力を検証
  6. 互換性: 複数のデバッグアダプタープロトコルをサポート
  7. テスト: さまざまなデバッグシナリオでテスト
  8. ドキュメント: 明確なセットアップ手順を提供

関連API

究極の AI 駆動 IDE 学習ガイド