Search API 
The Search API provides comprehensive search functionality across files, symbols, and content within the development environment.
Overview 
The Search API enables you to:
- Search for files by name and path
- Search for text content across files
- Search for symbols (functions, classes, variables)
- Perform regex-based searches
- Search within specific scopes and directories
- Provide search suggestions and autocomplete
- Index and cache search results
- Support fuzzy matching and ranking
Basic Usage 
Search Manager 
import { TraeAPI } from '@trae/api';
// Search manager
class SearchManager {
  private fileIndex: Map<string, FileIndexEntry> = new Map();
  private symbolIndex: Map<string, SymbolIndexEntry[]> = new Map();
  private contentIndex: Map<string, ContentIndexEntry[]> = new Map();
  private searchHistory: SearchHistoryEntry[] = [];
  private eventEmitter = new TraeAPI.EventEmitter<SearchEvent>();
  private indexingInProgress = false;
  private watchers: TraeAPI.FileSystemWatcher[] = [];
  constructor() {
    this.initializeIndexing();
    this.setupFileWatchers();
  }
  // Initialize indexing
  private async initializeIndexing(): Promise<void> {
    if (this.indexingInProgress) return;
    
    this.indexingInProgress = true;
    console.log('Starting search indexing...');
    this.eventEmitter.fire({ type: 'indexingStarted' });
    
    try {
      await this.buildFileIndex();
      await this.buildSymbolIndex();
      await this.buildContentIndex();
      
      console.log('Search indexing completed');
      this.eventEmitter.fire({ type: 'indexingCompleted' });
    } catch (error) {
      console.error('Search indexing failed:', error);
      this.eventEmitter.fire({ type: 'indexingFailed', error: error as Error });
    } finally {
      this.indexingInProgress = false;
    }
  }
  // Build file index
  private async buildFileIndex(): Promise<void> {
    const workspaceFolders = TraeAPI.workspace.workspaceFolders;
    if (!workspaceFolders) return;
    for (const folder of workspaceFolders) {
      await this.indexDirectory(folder.uri.fsPath);
    }
  }
  private async indexDirectory(dirPath: string): Promise<void> {
    try {
      const entries = await TraeAPI.workspace.fs.readDirectory(TraeAPI.Uri.file(dirPath));
      
      for (const [name, type] of entries) {
        const fullPath = TraeAPI.path.join(dirPath, name);
        
        if (type === TraeAPI.FileType.Directory) {
          // Skip common directories that shouldn't be indexed
          if (this.shouldSkipDirectory(name)) {
            continue;
          }
          await this.indexDirectory(fullPath);
        } else if (type === TraeAPI.FileType.File) {
          await this.indexFile(fullPath);
        }
      }
    } catch (error) {
      console.error(`Failed to index directory ${dirPath}:`, error);
    }
  }
  private shouldSkipDirectory(name: string): boolean {
    const skipDirs = [
      'node_modules', '.git', '.svn', '.hg',
      'dist', 'build', 'out', 'target',
      '.vscode', '.idea', '__pycache__',
      'coverage', '.nyc_output'
    ];
    return skipDirs.includes(name) || name.startsWith('.');
  }
  private async indexFile(filePath: string): Promise<void> {
    try {
      const stat = await TraeAPI.workspace.fs.stat(TraeAPI.Uri.file(filePath));
      const fileName = TraeAPI.path.basename(filePath);
      const extension = TraeAPI.path.extname(filePath);
      const relativePath = TraeAPI.workspace.asRelativePath(filePath);
      
      const entry: FileIndexEntry = {
        path: filePath,
        relativePath,
        name: fileName,
        extension,
        size: stat.size,
        lastModified: stat.mtime,
        language: this.getLanguageFromExtension(extension),
        searchTerms: this.generateFileSearchTerms(fileName, relativePath)
      };
      
      this.fileIndex.set(filePath, entry);
    } catch (error) {
      console.error(`Failed to index file ${filePath}:`, error);
    }
  }
  private getLanguageFromExtension(extension: string): string {
    const languageMap: { [key: string]: string } = {
      '.js': 'javascript',
      '.ts': 'typescript',
      '.jsx': 'javascriptreact',
      '.tsx': 'typescriptreact',
      '.py': 'python',
      '.java': 'java',
      '.cpp': 'cpp',
      '.c': 'c',
      '.cs': 'csharp',
      '.php': 'php',
      '.rb': 'ruby',
      '.go': 'go',
      '.rs': 'rust',
      '.swift': 'swift',
      '.kt': 'kotlin',
      '.scala': 'scala',
      '.html': 'html',
      '.css': 'css',
      '.scss': 'scss',
      '.less': 'less',
      '.json': 'json',
      '.xml': 'xml',
      '.yaml': 'yaml',
      '.yml': 'yaml',
      '.md': 'markdown',
      '.txt': 'plaintext'
    };
    
    return languageMap[extension.toLowerCase()] || 'plaintext';
  }
  private generateFileSearchTerms(fileName: string, relativePath: string): string[] {
    const terms = new Set<string>();
    
    // Add filename without extension
    const nameWithoutExt = TraeAPI.path.parse(fileName).name;
    terms.add(nameWithoutExt.toLowerCase());
    
    // Add full filename
    terms.add(fileName.toLowerCase());
    
    // Add path segments
    const pathSegments = relativePath.split(TraeAPI.path.sep);
    pathSegments.forEach(segment => {
      if (segment) {
        terms.add(segment.toLowerCase());
      }
    });
    
    // Add camelCase and snake_case splits
    const camelCaseSplit = nameWithoutExt.split(/(?=[A-Z])/);
    camelCaseSplit.forEach(part => {
      if (part) {
        terms.add(part.toLowerCase());
      }
    });
    
    const snakeCaseSplit = nameWithoutExt.split(/[_-]/);
    snakeCaseSplit.forEach(part => {
      if (part) {
        terms.add(part.toLowerCase());
      }
    });
    
    return Array.from(terms);
  }
  // Build symbol index
  private async buildSymbolIndex(): Promise<void> {
    for (const [filePath, fileEntry] of this.fileIndex) {
      if (this.isCodeFile(fileEntry.language)) {
        await this.indexFileSymbols(filePath);
      }
    }
  }
  private isCodeFile(language: string): boolean {
    const codeLanguages = [
      'javascript', 'typescript', 'javascriptreact', 'typescriptreact',
      'python', 'java', 'cpp', 'c', 'csharp', 'php', 'ruby',
      'go', 'rust', 'swift', 'kotlin', 'scala'
    ];
    return codeLanguages.includes(language);
  }
  private async indexFileSymbols(filePath: string): Promise<void> {
    try {
      const document = await TraeAPI.workspace.openTextDocument(TraeAPI.Uri.file(filePath));
      const symbols = await TraeAPI.commands.executeCommand<TraeAPI.DocumentSymbol[]>(
        'vscode.executeDocumentSymbolProvider',
        document.uri
      );
      
      if (symbols) {
        const symbolEntries = this.flattenSymbols(symbols, filePath);
        this.symbolIndex.set(filePath, symbolEntries);
      }
    } catch (error) {
      console.error(`Failed to index symbols for ${filePath}:`, error);
    }
  }
  private flattenSymbols(symbols: TraeAPI.DocumentSymbol[], filePath: string, parent?: string): SymbolIndexEntry[] {
    const entries: SymbolIndexEntry[] = [];
    
    for (const symbol of symbols) {
      const fullName = parent ? `${parent}.${symbol.name}` : symbol.name;
      
      entries.push({
        name: symbol.name,
        fullName,
        kind: symbol.kind,
        filePath,
        range: symbol.range,
        selectionRange: symbol.selectionRange,
        searchTerms: this.generateSymbolSearchTerms(symbol.name, fullName)
      });
      
      // Recursively process children
      if (symbol.children) {
        entries.push(...this.flattenSymbols(symbol.children, filePath, fullName));
      }
    }
    
    return entries;
  }
  private generateSymbolSearchTerms(name: string, fullName: string): string[] {
    const terms = new Set<string>();
    
    terms.add(name.toLowerCase());
    terms.add(fullName.toLowerCase());
    
    // Add camelCase splits
    const camelCaseSplit = name.split(/(?=[A-Z])/);
    camelCaseSplit.forEach(part => {
      if (part) {
        terms.add(part.toLowerCase());
      }
    });
    
    // Add snake_case splits
    const snakeCaseSplit = name.split(/[_-]/);
    snakeCaseSplit.forEach(part => {
      if (part) {
        terms.add(part.toLowerCase());
      }
    });
    
    return Array.from(terms);
  }
  // Build content index
  private async buildContentIndex(): Promise<void> {
    for (const [filePath, fileEntry] of this.fileIndex) {
      if (this.shouldIndexContent(fileEntry)) {
        await this.indexFileContent(filePath);
      }
    }
  }
  private shouldIndexContent(fileEntry: FileIndexEntry): boolean {
    // Skip binary files and very large files
    if (fileEntry.size > 1024 * 1024) { // 1MB limit
      return false;
    }
    
    const textExtensions = [
      '.js', '.ts', '.jsx', '.tsx', '.py', '.java', '.cpp', '.c',
      '.cs', '.php', '.rb', '.go', '.rs', '.swift', '.kt', '.scala',
      '.html', '.css', '.scss', '.less', '.json', '.xml', '.yaml',
      '.yml', '.md', '.txt', '.log', '.config', '.ini'
    ];
    
    return textExtensions.includes(fileEntry.extension.toLowerCase());
  }
  private async indexFileContent(filePath: string): Promise<void> {
    try {
      const document = await TraeAPI.workspace.openTextDocument(TraeAPI.Uri.file(filePath));
      const content = document.getText();
      const lines = content.split('\n');
      
      const entries: ContentIndexEntry[] = [];
      
      lines.forEach((line, lineNumber) => {
        const trimmedLine = line.trim();
        if (trimmedLine.length > 0) {
          entries.push({
            filePath,
            lineNumber: lineNumber + 1,
            content: line,
            trimmedContent: trimmedLine,
            searchTerms: this.generateContentSearchTerms(trimmedLine)
          });
        }
      });
      
      this.contentIndex.set(filePath, entries);
    } catch (error) {
      console.error(`Failed to index content for ${filePath}:`, error);
    }
  }
  private generateContentSearchTerms(content: string): string[] {
    const terms = new Set<string>();
    
    // Split by common delimiters
    const words = content.toLowerCase().split(/[\s\W]+/);
    words.forEach(word => {
      if (word.length > 2) { // Skip very short words
        terms.add(word);
      }
    });
    
    return Array.from(terms);
  }
  // Search files
  async searchFiles(query: string, options: FileSearchOptions = {}): Promise<FileSearchResult[]> {
    const results: FileSearchResult[] = [];
    const queryLower = query.toLowerCase();
    const maxResults = options.maxResults || 100;
    
    for (const [filePath, entry] of this.fileIndex) {
      if (results.length >= maxResults) break;
      
      // Apply filters
      if (options.includePatterns && !this.matchesPatterns(entry.relativePath, options.includePatterns)) {
        continue;
      }
      
      if (options.excludePatterns && this.matchesPatterns(entry.relativePath, options.excludePatterns)) {
        continue;
      }
      
      if (options.fileTypes && !options.fileTypes.includes(entry.extension)) {
        continue;
      }
      
      // Calculate match score
      const score = this.calculateFileMatchScore(entry, queryLower);
      if (score > 0) {
        results.push({
          file: entry,
          score,
          matches: this.getFileMatches(entry, queryLower)
        });
      }
    }
    
    // Sort by score (descending)
    results.sort((a, b) => b.score - a.score);
    
    // Add to search history
    this.addToSearchHistory({
      type: 'file',
      query,
      timestamp: Date.now(),
      resultCount: results.length
    });
    
    return results;
  }
  private calculateFileMatchScore(entry: FileIndexEntry, query: string): number {
    let score = 0;
    
    // Exact filename match (highest score)
    if (entry.name.toLowerCase() === query) {
      score += 100;
    }
    
    // Filename starts with query
    if (entry.name.toLowerCase().startsWith(query)) {
      score += 80;
    }
    
    // Filename contains query
    if (entry.name.toLowerCase().includes(query)) {
      score += 60;
    }
    
    // Path contains query
    if (entry.relativePath.toLowerCase().includes(query)) {
      score += 40;
    }
    
    // Search terms match
    for (const term of entry.searchTerms) {
      if (term === query) {
        score += 50;
      } else if (term.startsWith(query)) {
        score += 30;
      } else if (term.includes(query)) {
        score += 20;
      }
    }
    
    // Fuzzy match bonus
    const fuzzyScore = this.calculateFuzzyScore(entry.name.toLowerCase(), query);
    score += fuzzyScore * 10;
    
    return score;
  }
  private calculateFuzzyScore(text: string, query: string): number {
    if (query.length === 0) return 0;
    if (text.length === 0) return 0;
    
    let score = 0;
    let queryIndex = 0;
    let lastMatchIndex = -1;
    
    for (let i = 0; i < text.length && queryIndex < query.length; i++) {
      if (text[i] === query[queryIndex]) {
        score += 1;
        
        // Bonus for consecutive matches
        if (i === lastMatchIndex + 1) {
          score += 0.5;
        }
        
        lastMatchIndex = i;
        queryIndex++;
      }
    }
    
    // Normalize score
    return queryIndex === query.length ? score / query.length : 0;
  }
  private getFileMatches(entry: FileIndexEntry, query: string): FileMatch[] {
    const matches: FileMatch[] = [];
    
    // Check filename match
    const nameIndex = entry.name.toLowerCase().indexOf(query);
    if (nameIndex !== -1) {
      matches.push({
        type: 'filename',
        text: entry.name,
        startIndex: nameIndex,
        length: query.length
      });
    }
    
    // Check path match
    const pathIndex = entry.relativePath.toLowerCase().indexOf(query);
    if (pathIndex !== -1) {
      matches.push({
        type: 'path',
        text: entry.relativePath,
        startIndex: pathIndex,
        length: query.length
      });
    }
    
    return matches;
  }
  private matchesPatterns(path: string, patterns: string[]): boolean {
    return patterns.some(pattern => {
      // Simple glob pattern matching
      const regex = new RegExp(pattern.replace(/\*/g, '.*').replace(/\?/g, '.'));
      return regex.test(path);
    });
  }
  // Search symbols
  async searchSymbols(query: string, options: SymbolSearchOptions = {}): Promise<SymbolSearchResult[]> {
    const results: SymbolSearchResult[] = [];
    const queryLower = query.toLowerCase();
    const maxResults = options.maxResults || 100;
    
    for (const [filePath, symbols] of this.symbolIndex) {
      if (results.length >= maxResults) break;
      
      // Apply file filters
      const fileEntry = this.fileIndex.get(filePath);
      if (!fileEntry) continue;
      
      if (options.fileTypes && !options.fileTypes.includes(fileEntry.extension)) {
        continue;
      }
      
      for (const symbol of symbols) {
        if (results.length >= maxResults) break;
        
        // Apply symbol kind filter
        if (options.symbolKinds && !options.symbolKinds.includes(symbol.kind)) {
          continue;
        }
        
        // Calculate match score
        const score = this.calculateSymbolMatchScore(symbol, queryLower);
        if (score > 0) {
          results.push({
            symbol,
            file: fileEntry,
            score,
            matches: this.getSymbolMatches(symbol, queryLower)
          });
        }
      }
    }
    
    // Sort by score (descending)
    results.sort((a, b) => b.score - a.score);
    
    // Add to search history
    this.addToSearchHistory({
      type: 'symbol',
      query,
      timestamp: Date.now(),
      resultCount: results.length
    });
    
    return results;
  }
  private calculateSymbolMatchScore(symbol: SymbolIndexEntry, query: string): number {
    let score = 0;
    
    // Exact name match
    if (symbol.name.toLowerCase() === query) {
      score += 100;
    }
    
    // Name starts with query
    if (symbol.name.toLowerCase().startsWith(query)) {
      score += 80;
    }
    
    // Name contains query
    if (symbol.name.toLowerCase().includes(query)) {
      score += 60;
    }
    
    // Full name contains query
    if (symbol.fullName.toLowerCase().includes(query)) {
      score += 40;
    }
    
    // Search terms match
    for (const term of symbol.searchTerms) {
      if (term === query) {
        score += 50;
      } else if (term.startsWith(query)) {
        score += 30;
      } else if (term.includes(query)) {
        score += 20;
      }
    }
    
    // Symbol kind bonus (functions and classes get higher scores)
    if (symbol.kind === TraeAPI.SymbolKind.Function || symbol.kind === TraeAPI.SymbolKind.Method) {
      score += 10;
    } else if (symbol.kind === TraeAPI.SymbolKind.Class) {
      score += 15;
    }
    
    return score;
  }
  private getSymbolMatches(symbol: SymbolIndexEntry, query: string): SymbolMatch[] {
    const matches: SymbolMatch[] = [];
    
    // Check name match
    const nameIndex = symbol.name.toLowerCase().indexOf(query);
    if (nameIndex !== -1) {
      matches.push({
        type: 'name',
        text: symbol.name,
        startIndex: nameIndex,
        length: query.length
      });
    }
    
    // Check full name match
    const fullNameIndex = symbol.fullName.toLowerCase().indexOf(query);
    if (fullNameIndex !== -1 && symbol.fullName !== symbol.name) {
      matches.push({
        type: 'fullName',
        text: symbol.fullName,
        startIndex: fullNameIndex,
        length: query.length
      });
    }
    
    return matches;
  }
  // Search content
  async searchContent(query: string, options: ContentSearchOptions = {}): Promise<ContentSearchResult[]> {
    const results: ContentSearchResult[] = [];
    const maxResults = options.maxResults || 1000;
    const isRegex = options.isRegex || false;
    const isCaseSensitive = options.isCaseSensitive || false;
    
    let searchRegex: RegExp;
    try {
      if (isRegex) {
        searchRegex = new RegExp(query, isCaseSensitive ? 'g' : 'gi');
      } else {
        const escapedQuery = query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
        searchRegex = new RegExp(escapedQuery, isCaseSensitive ? 'g' : 'gi');
      }
    } catch (error) {
      throw new Error(`Invalid search pattern: ${error}`);
    }
    
    for (const [filePath, contentEntries] of this.contentIndex) {
      if (results.length >= maxResults) break;
      
      // Apply file filters
      const fileEntry = this.fileIndex.get(filePath);
      if (!fileEntry) continue;
      
      if (options.includePatterns && !this.matchesPatterns(fileEntry.relativePath, options.includePatterns)) {
        continue;
      }
      
      if (options.excludePatterns && this.matchesPatterns(fileEntry.relativePath, options.excludePatterns)) {
        continue;
      }
      
      if (options.fileTypes && !options.fileTypes.includes(fileEntry.extension)) {
        continue;
      }
      
      for (const contentEntry of contentEntries) {
        if (results.length >= maxResults) break;
        
        const matches = Array.from(contentEntry.content.matchAll(searchRegex));
        if (matches.length > 0) {
          results.push({
            file: fileEntry,
            lineNumber: contentEntry.lineNumber,
            content: contentEntry.content,
            matches: matches.map(match => ({
              startIndex: match.index || 0,
              length: match[0].length,
              text: match[0]
            }))
          });
        }
      }
    }
    
    // Add to search history
    this.addToSearchHistory({
      type: 'content',
      query,
      timestamp: Date.now(),
      resultCount: results.length
    });
    
    return results;
  }
  // Search suggestions
  async getSearchSuggestions(query: string, type: SearchType): Promise<string[]> {
    const suggestions = new Set<string>();
    const queryLower = query.toLowerCase();
    
    // Get suggestions from search history
    const historySuggestions = this.searchHistory
      .filter(entry => entry.type === type && entry.query.toLowerCase().startsWith(queryLower))
      .map(entry => entry.query)
      .slice(0, 5);
    
    historySuggestions.forEach(suggestion => suggestions.add(suggestion));
    
    // Get suggestions based on type
    switch (type) {
      case 'file':
        // Suggest file names and paths
        for (const entry of this.fileIndex.values()) {
          if (suggestions.size >= 10) break;
          
          if (entry.name.toLowerCase().startsWith(queryLower)) {
            suggestions.add(entry.name);
          }
          
          for (const term of entry.searchTerms) {
            if (term.startsWith(queryLower)) {
              suggestions.add(term);
            }
          }
        }
        break;
        
      case 'symbol':
        // Suggest symbol names
        for (const symbols of this.symbolIndex.values()) {
          if (suggestions.size >= 10) break;
          
          for (const symbol of symbols) {
            if (symbol.name.toLowerCase().startsWith(queryLower)) {
              suggestions.add(symbol.name);
            }
            
            for (const term of symbol.searchTerms) {
              if (term.startsWith(queryLower)) {
                suggestions.add(term);
              }
            }
          }
        }
        break;
        
      case 'content':
        // Suggest common words from content
        for (const contentEntries of this.contentIndex.values()) {
          if (suggestions.size >= 10) break;
          
          for (const entry of contentEntries) {
            for (const term of entry.searchTerms) {
              if (term.startsWith(queryLower)) {
                suggestions.add(term);
              }
            }
          }
        }
        break;
    }
    
    return Array.from(suggestions).slice(0, 10);
  }
  // Search history management
  private addToSearchHistory(entry: SearchHistoryEntry): void {
    // Remove duplicate entries
    this.searchHistory = this.searchHistory.filter(
      existing => !(existing.type === entry.type && existing.query === entry.query)
    );
    
    // Add new entry at the beginning
    this.searchHistory.unshift(entry);
    
    // Limit history size
    if (this.searchHistory.length > 100) {
      this.searchHistory = this.searchHistory.slice(0, 100);
    }
  }
  getSearchHistory(type?: SearchType): SearchHistoryEntry[] {
    if (type) {
      return this.searchHistory.filter(entry => entry.type === type);
    }
    return [...this.searchHistory];
  }
  clearSearchHistory(type?: SearchType): void {
    if (type) {
      this.searchHistory = this.searchHistory.filter(entry => entry.type !== type);
    } else {
      this.searchHistory = [];
    }
  }
  // File system watchers
  private setupFileWatchers(): void {
    const workspaceFolders = TraeAPI.workspace.workspaceFolders;
    if (!workspaceFolders) return;
    
    for (const folder of workspaceFolders) {
      const pattern = new TraeAPI.RelativePattern(folder, '**/*');
      const watcher = TraeAPI.workspace.createFileSystemWatcher(pattern);
      
      watcher.onDidCreate(uri => this.onFileCreated(uri.fsPath));
      watcher.onDidChange(uri => this.onFileChanged(uri.fsPath));
      watcher.onDidDelete(uri => this.onFileDeleted(uri.fsPath));
      
      this.watchers.push(watcher);
    }
  }
  private async onFileCreated(filePath: string): Promise<void> {
    await this.indexFile(filePath);
    
    const fileEntry = this.fileIndex.get(filePath);
    if (fileEntry) {
      if (this.isCodeFile(fileEntry.language)) {
        await this.indexFileSymbols(filePath);
      }
      
      if (this.shouldIndexContent(fileEntry)) {
        await this.indexFileContent(filePath);
      }
    }
    
    this.eventEmitter.fire({ type: 'fileIndexed', filePath });
  }
  private async onFileChanged(filePath: string): Promise<void> {
    const fileEntry = this.fileIndex.get(filePath);
    if (!fileEntry) return;
    
    // Update file entry
    await this.indexFile(filePath);
    
    // Update symbols if it's a code file
    if (this.isCodeFile(fileEntry.language)) {
      await this.indexFileSymbols(filePath);
    }
    
    // Update content if applicable
    if (this.shouldIndexContent(fileEntry)) {
      await this.indexFileContent(filePath);
    }
    
    this.eventEmitter.fire({ type: 'fileUpdated', filePath });
  }
  private onFileDeleted(filePath: string): void {
    this.fileIndex.delete(filePath);
    this.symbolIndex.delete(filePath);
    this.contentIndex.delete(filePath);
    
    this.eventEmitter.fire({ type: 'fileRemoved', filePath });
  }
  // Utility methods
  getIndexStats(): IndexStats {
    const symbolCount = Array.from(this.symbolIndex.values())
      .reduce((total, symbols) => total + symbols.length, 0);
    
    const contentLineCount = Array.from(this.contentIndex.values())
      .reduce((total, entries) => total + entries.length, 0);
    
    return {
      fileCount: this.fileIndex.size,
      symbolCount,
      contentLineCount,
      isIndexing: this.indexingInProgress
    };
  }
  async refreshIndex(): Promise<void> {
    this.fileIndex.clear();
    this.symbolIndex.clear();
    this.contentIndex.clear();
    
    await this.initializeIndexing();
  }
  // Event handling
  onDidChangeIndex(listener: (event: SearchEvent) => void): TraeAPI.Disposable {
    return this.eventEmitter.event(listener);
  }
  dispose(): void {
    this.watchers.forEach(watcher => watcher.dispose());
    this.watchers = [];
    
    this.fileIndex.clear();
    this.symbolIndex.clear();
    this.contentIndex.clear();
    this.searchHistory = [];
    
    this.eventEmitter.dispose();
  }
}
// Initialize search manager
const searchManager = new SearchManager();Search UI Integration 
// Search UI provider
class SearchUIProvider {
  private searchPanel: TraeAPI.WebviewPanel | null = null;
  private currentSearchType: SearchType = 'file';
  private currentQuery = '';
  private searchResults: any[] = [];
  constructor(private searchManager: SearchManager) {
    this.setupCommands();
    this.setupEventListeners();
  }
  private setupCommands(): void {
    // Register search commands
    TraeAPI.commands.registerCommand('search.showPanel', () => {
      this.showSearchPanel();
    });
    
    TraeAPI.commands.registerCommand('search.searchFiles', async () => {
      const query = await TraeAPI.window.showInputBox({
        prompt: 'Search for files',
        placeHolder: 'Enter file name or pattern...'
      });
      
      if (query) {
        await this.performSearch(query, 'file');
      }
    });
    
    TraeAPI.commands.registerCommand('search.searchSymbols', async () => {
      const query = await TraeAPI.window.showInputBox({
        prompt: 'Search for symbols',
        placeHolder: 'Enter symbol name...'
      });
      
      if (query) {
        await this.performSearch(query, 'symbol');
      }
    });
    
    TraeAPI.commands.registerCommand('search.searchContent', async () => {
      const query = await TraeAPI.window.showInputBox({
        prompt: 'Search in files',
        placeHolder: 'Enter search text...'
      });
      
      if (query) {
        await this.performSearch(query, 'content');
      }
    });
  }
  private async showSearchPanel(): Promise<void> {
    if (this.searchPanel) {
      this.searchPanel.reveal();
      return;
    }
    this.searchPanel = TraeAPI.window.createWebviewPanel(
      'search',
      'Search',
      TraeAPI.ViewColumn.One,
      {
        enableScripts: true,
        retainContextWhenHidden: true
      }
    );
    this.searchPanel.webview.html = this.getSearchHTML();
    this.setupWebviewMessageHandling();
    this.searchPanel.onDidDispose(() => {
      this.searchPanel = null;
    });
  }
  private getSearchHTML(): string {
    return `
      <!DOCTYPE html>
      <html>
      <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Search</title>
        <style>
          body {
            margin: 0;
            padding: 20px;
            background: #1e1e1e;
            color: #d4d4d4;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            font-size: 13px;
          }
          
          .search-container {
            max-width: 800px;
            margin: 0 auto;
          }
          
          .search-header {
            margin-bottom: 20px;
          }
          
          .search-tabs {
            display: flex;
            margin-bottom: 15px;
            border-bottom: 1px solid #3e3e42;
          }
          
          .search-tab {
            padding: 8px 16px;
            background: none;
            border: none;
            color: #cccccc;
            cursor: pointer;
            border-bottom: 2px solid transparent;
          }
          
          .search-tab.active {
            color: #ffffff;
            border-bottom-color: #007acc;
          }
          
          .search-input-container {
            display: flex;
            margin-bottom: 15px;
          }
          
          .search-input {
            flex: 1;
            padding: 8px 12px;
            background: #2d2d30;
            border: 1px solid #3e3e42;
            color: #d4d4d4;
            border-radius: 3px;
            font-size: 14px;
          }
          
          .search-input:focus {
            outline: none;
            border-color: #007acc;
          }
          
          .search-button {
            margin-left: 8px;
            padding: 8px 16px;
            background: #0e639c;
            border: none;
            color: #ffffff;
            border-radius: 3px;
            cursor: pointer;
          }
          
          .search-button:hover {
            background: #1177bb;
          }
          
          .search-options {
            display: flex;
            gap: 15px;
            margin-bottom: 20px;
            font-size: 12px;
          }
          
          .search-option {
            display: flex;
            align-items: center;
            gap: 5px;
          }
          
          .search-option input[type="checkbox"] {
            margin: 0;
          }
          
          .search-results {
            border-top: 1px solid #3e3e42;
            padding-top: 15px;
          }
          
          .search-stats {
            margin-bottom: 15px;
            color: #cccccc;
            font-size: 12px;
          }
          
          .search-result {
            margin-bottom: 10px;
            padding: 10px;
            background: #2d2d30;
            border-radius: 3px;
            cursor: pointer;
          }
          
          .search-result:hover {
            background: #37373d;
          }
          
          .result-title {
            font-weight: 500;
            margin-bottom: 5px;
          }
          
          .result-path {
            color: #cccccc;
            font-size: 11px;
            margin-bottom: 5px;
          }
          
          .result-content {
            font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
            font-size: 12px;
            color: #d4d4d4;
            white-space: pre-wrap;
          }
          
          .result-match {
            background: #515c6a;
            color: #ffffff;
            padding: 1px 2px;
            border-radius: 2px;
          }
          
          .no-results {
            text-align: center;
            color: #cccccc;
            padding: 40px;
          }
          
          .loading {
            text-align: center;
            color: #cccccc;
            padding: 20px;
          }
          
          .suggestions {
            position: absolute;
            top: 100%;
            left: 0;
            right: 0;
            background: #2d2d30;
            border: 1px solid #3e3e42;
            border-top: none;
            border-radius: 0 0 3px 3px;
            max-height: 200px;
            overflow-y: auto;
            z-index: 1000;
          }
          
          .suggestion {
            padding: 8px 12px;
            cursor: pointer;
            border-bottom: 1px solid #3e3e42;
          }
          
          .suggestion:hover {
            background: #37373d;
          }
          
          .suggestion:last-child {
            border-bottom: none;
          }
        </style>
      </head>
      <body>
        <div class="search-container">
          <div class="search-header">
            <div class="search-tabs">
              <button class="search-tab active" data-type="file">Files</button>
              <button class="search-tab" data-type="symbol">Symbols</button>
              <button class="search-tab" data-type="content">Content</button>
            </div>
            
            <div class="search-input-container" style="position: relative;">
              <input type="text" class="search-input" id="searchInput" placeholder="Enter search query...">
              <button class="search-button" onclick="performSearch()">Search</button>
              <div class="suggestions" id="suggestions" style="display: none;"></div>
            </div>
            
            <div class="search-options">
              <div class="search-option">
                <input type="checkbox" id="caseSensitive">
                <label for="caseSensitive">Case sensitive</label>
              </div>
              <div class="search-option">
                <input type="checkbox" id="useRegex">
                <label for="useRegex">Use regex</label>
              </div>
              <div class="search-option">
                <input type="checkbox" id="wholeWord">
                <label for="wholeWord">Whole word</label>
              </div>
            </div>
          </div>
          
          <div class="search-results">
            <div class="search-stats" id="searchStats"></div>
            <div id="resultsContainer">
              <div class="no-results">Enter a search query to begin</div>
            </div>
          </div>
        </div>
        
        <script>
          const vscode = acquireVsCodeApi();
          let currentSearchType = 'file';
          let searchTimeout = null;
          
          // Setup event listeners
          document.addEventListener('DOMContentLoaded', () => {
            const searchInput = document.getElementById('searchInput');
            const tabs = document.querySelectorAll('.search-tab');
            
            // Tab switching
            tabs.forEach(tab => {
              tab.addEventListener('click', () => {
                tabs.forEach(t => t.classList.remove('active'));
                tab.classList.add('active');
                currentSearchType = tab.dataset.type;
                updatePlaceholder();
                clearResults();
              });
            });
            
            // Search input
            searchInput.addEventListener('input', (e) => {
              const query = e.target.value;
              
              // Debounce search
              clearTimeout(searchTimeout);
              searchTimeout = setTimeout(() => {
                if (query.length > 0) {
                  getSuggestions(query);
                  if (query.length >= 2) {
                    performSearch();
                  }
                } else {
                  hideSuggestions();
                  clearResults();
                }
              }, 300);
            });
            
            searchInput.addEventListener('keydown', (e) => {
              if (e.key === 'Enter') {
                performSearch();
                hideSuggestions();
              } else if (e.key === 'Escape') {
                hideSuggestions();
              }
            });
            
            // Hide suggestions when clicking outside
            document.addEventListener('click', (e) => {
              if (!e.target.closest('.search-input-container')) {
                hideSuggestions();
              }
            });
            
            updatePlaceholder();
          });
          
          function updatePlaceholder() {
            const input = document.getElementById('searchInput');
            const placeholders = {
              file: 'Search for files by name...',
              symbol: 'Search for symbols (functions, classes, etc.)...',
              content: 'Search for text in files...'
            };
            input.placeholder = placeholders[currentSearchType];
          }
          
          function performSearch() {
            const query = document.getElementById('searchInput').value.trim();
            if (!query) return;
            
            showLoading();
            
            const options = {
              isCaseSensitive: document.getElementById('caseSensitive').checked,
              isRegex: document.getElementById('useRegex').checked,
              wholeWord: document.getElementById('wholeWord').checked
            };
            
            vscode.postMessage({
              type: 'search',
              searchType: currentSearchType,
              query,
              options
            });
          }
          
          function getSuggestions(query) {
            vscode.postMessage({
              type: 'getSuggestions',
              searchType: currentSearchType,
              query
            });
          }
          
          function showSuggestions(suggestions) {
            const suggestionsEl = document.getElementById('suggestions');
            
            if (suggestions.length === 0) {
              hideSuggestions();
              return;
            }
            
            suggestionsEl.innerHTML = suggestions.map(suggestion => 
              `<div class="suggestion" onclick="selectSuggestion('${suggestion}')">${suggestion}</div>`
            ).join('');
            
            suggestionsEl.style.display = 'block';
          }
          
          function hideSuggestions() {
            document.getElementById('suggestions').style.display = 'none';
          }
          
          function selectSuggestion(suggestion) {
            document.getElementById('searchInput').value = suggestion;
            hideSuggestions();
            performSearch();
          }
          
          function showLoading() {
            document.getElementById('resultsContainer').innerHTML = 
              '<div class="loading">Searching...</div>';
          }
          
          function clearResults() {
            document.getElementById('resultsContainer').innerHTML = 
              '<div class="no-results">Enter a search query to begin</div>';
            document.getElementById('searchStats').textContent = '';
          }
          
          function showResults(results, stats) {
            const container = document.getElementById('resultsContainer');
            const statsEl = document.getElementById('searchStats');
            
            statsEl.textContent = `${results.length} results found in ${stats.searchTime}ms`;
            
            if (results.length === 0) {
              container.innerHTML = '<div class="no-results">No results found</div>';
              return;
            }
            
            container.innerHTML = results.map(result => {
              switch (currentSearchType) {
                case 'file':
                  return createFileResult(result);
                case 'symbol':
                  return createSymbolResult(result);
                case 'content':
                  return createContentResult(result);
                default:
                  return '';
              }
            }).join('');
          }
          
          function createFileResult(result) {
            return `
              <div class="search-result" onclick="openFile('${result.file.path}')">
                <div class="result-title">${result.file.name}</div>
                <div class="result-path">${result.file.relativePath}</div>
              </div>
            `;
          }
          
          function createSymbolResult(result) {
            return `
              <div class="search-result" onclick="openSymbol('${result.file.path}', ${result.symbol.range.start.line})">
                <div class="result-title">${result.symbol.name}</div>
                <div class="result-path">${result.file.relativePath}:${result.symbol.range.start.line + 1}</div>
              </div>
            `;
          }
          
          function createContentResult(result) {
            let content = result.content;
            
            // Highlight matches
            result.matches.forEach(match => {
              const before = content.substring(0, match.startIndex);
              const matchText = content.substring(match.startIndex, match.startIndex + match.length);
              const after = content.substring(match.startIndex + match.length);
              content = before + `<span class="result-match">${matchText}</span>` + after;
            });
            
            return `
              <div class="search-result" onclick="openFile('${result.file.path}', ${result.lineNumber})">
                <div class="result-path">${result.file.relativePath}:${result.lineNumber}</div>
                <div class="result-content">${content}</div>
              </div>
            `;
          }
          
          function openFile(filePath, lineNumber) {
            vscode.postMessage({
              type: 'openFile',
              filePath,
              lineNumber
            });
          }
          
          function openSymbol(filePath, lineNumber) {
            vscode.postMessage({
              type: 'openFile',
              filePath,
              lineNumber
            });
          }
          
          // Handle messages from extension
          window.addEventListener('message', event => {
            const message = event.data;
            
            switch (message.type) {
              case 'searchResults':
                showResults(message.results, message.stats);
                break;
              case 'suggestions':
                showSuggestions(message.suggestions);
                break;
            }
          });
        </script>
      </body>
      </html>
    `;
  }
  private setupWebviewMessageHandling(): void {
    if (!this.searchPanel) return;
    this.searchPanel.webview.onDidReceiveMessage(async message => {
      switch (message.type) {
        case 'search':
          await this.handleSearchRequest(message);
          break;
        case 'getSuggestions':
          await this.handleSuggestionsRequest(message);
          break;
        case 'openFile':
          await this.handleOpenFile(message);
          break;
      }
    });
  }
  private async handleSearchRequest(message: any): Promise<void> {
    const startTime = Date.now();
    let results: any[] = [];
    try {
      switch (message.searchType) {
        case 'file':
          results = await this.searchManager.searchFiles(message.query, message.options);
          break;
        case 'symbol':
          results = await this.searchManager.searchSymbols(message.query, message.options);
          break;
        case 'content':
          results = await this.searchManager.searchContent(message.query, message.options);
          break;
      }
    } catch (error) {
      console.error('Search failed:', error);
      results = [];
    }
    const searchTime = Date.now() - startTime;
    this.sendMessage({
      type: 'searchResults',
      results,
      stats: { searchTime }
    });
  }
  private async handleSuggestionsRequest(message: any): Promise<void> {
    try {
      const suggestions = await this.searchManager.getSearchSuggestions(
        message.query,
        message.searchType
      );
      this.sendMessage({
        type: 'suggestions',
        suggestions
      });
    } catch (error) {
      console.error('Failed to get suggestions:', error);
    }
  }
  private async handleOpenFile(message: any): Promise<void> {
    try {
      const uri = TraeAPI.Uri.file(message.filePath);
      const document = await TraeAPI.workspace.openTextDocument(uri);
      const editor = await TraeAPI.window.showTextDocument(document);
      if (message.lineNumber) {
        const line = message.lineNumber - 1; // Convert to 0-based
        const position = new TraeAPI.Position(line, 0);
        editor.selection = new TraeAPI.Selection(position, position);
        editor.revealRange(new TraeAPI.Range(position, position));
      }
    } catch (error) {
      console.error('Failed to open file:', error);
      TraeAPI.window.showErrorMessage(`Failed to open file: ${message.filePath}`);
    }
  }
  private async performSearch(query: string, type: SearchType): Promise<void> {
    this.currentQuery = query;
    this.currentSearchType = type;
    try {
      let results: any[] = [];
      switch (type) {
        case 'file':
          results = await this.searchManager.searchFiles(query);
          break;
        case 'symbol':
          results = await this.searchManager.searchSymbols(query);
          break;
        case 'content':
          results = await this.searchManager.searchContent(query);
          break;
      }
      this.searchResults = results;
      await this.showSearchResults(results, type);
    } catch (error) {
      console.error('Search failed:', error);
      TraeAPI.window.showErrorMessage(`Search failed: ${error}`);
    }
  }
  private async showSearchResults(results: any[], type: SearchType): Promise<void> {
    if (results.length === 0) {
      TraeAPI.window.showInformationMessage('No results found');
      return;
    }
    // Show quick pick for results
    const items = results.slice(0, 50).map(result => {
      switch (type) {
        case 'file':
          return {
            label: result.file.name,
            description: result.file.relativePath,
            detail: `Score: ${result.score}`,
            result
          };
        case 'symbol':
          return {
            label: result.symbol.name,
            description: `${result.file.relativePath}:${result.symbol.range.start.line + 1}`,
            detail: `${this.getSymbolKindName(result.symbol.kind)} - Score: ${result.score}`,
            result
          };
        case 'content':
          return {
            label: result.content.trim(),
            description: `${result.file.relativePath}:${result.lineNumber}`,
            detail: `${result.matches.length} match(es)`,
            result
          };
        default:
          return { label: '', description: '', result };
      }
    });
    const selected = await TraeAPI.window.showQuickPick(items, {
      placeHolder: `Select from ${results.length} search results`,
      matchOnDescription: true,
      matchOnDetail: true
    });
    if (selected) {
      await this.openSearchResult(selected.result, type);
    }
  }
  private async openSearchResult(result: any, type: SearchType): Promise<void> {
    let filePath: string;
    let lineNumber: number | undefined;
    switch (type) {
      case 'file':
        filePath = result.file.path;
        break;
      case 'symbol':
        filePath = result.file.path;
        lineNumber = result.symbol.range.start.line;
        break;
      case 'content':
        filePath = result.file.path;
        lineNumber = result.lineNumber - 1; // Convert to 0-based
        break;
      default:
        return;
    }
    try {
      const uri = TraeAPI.Uri.file(filePath);
      const document = await TraeAPI.workspace.openTextDocument(uri);
      const editor = await TraeAPI.window.showTextDocument(document);
      if (lineNumber !== undefined) {
        const position = new TraeAPI.Position(lineNumber, 0);
        editor.selection = new TraeAPI.Selection(position, position);
        editor.revealRange(new TraeAPI.Range(position, position));
      }
    } catch (error) {
      console.error('Failed to open search result:', error);
      TraeAPI.window.showErrorMessage(`Failed to open file: ${filePath}`);
    }
  }
  private getSymbolKindName(kind: TraeAPI.SymbolKind): string {
    const kindNames: { [key: number]: string } = {
      [TraeAPI.SymbolKind.File]: 'File',
      [TraeAPI.SymbolKind.Module]: 'Module',
      [TraeAPI.SymbolKind.Namespace]: 'Namespace',
      [TraeAPI.SymbolKind.Package]: 'Package',
      [TraeAPI.SymbolKind.Class]: 'Class',
      [TraeAPI.SymbolKind.Method]: 'Method',
      [TraeAPI.SymbolKind.Property]: 'Property',
      [TraeAPI.SymbolKind.Field]: 'Field',
      [TraeAPI.SymbolKind.Constructor]: 'Constructor',
      [TraeAPI.SymbolKind.Enum]: 'Enum',
      [TraeAPI.SymbolKind.Interface]: 'Interface',
      [TraeAPI.SymbolKind.Function]: 'Function',
      [TraeAPI.SymbolKind.Variable]: 'Variable',
      [TraeAPI.SymbolKind.Constant]: 'Constant',
      [TraeAPI.SymbolKind.String]: 'String',
      [TraeAPI.SymbolKind.Number]: 'Number',
      [TraeAPI.SymbolKind.Boolean]: 'Boolean',
      [TraeAPI.SymbolKind.Array]: 'Array',
      [TraeAPI.SymbolKind.Object]: 'Object',
      [TraeAPI.SymbolKind.Key]: 'Key',
      [TraeAPI.SymbolKind.Null]: 'Null',
      [TraeAPI.SymbolKind.EnumMember]: 'EnumMember',
      [TraeAPI.SymbolKind.Struct]: 'Struct',
      [TraeAPI.SymbolKind.Event]: 'Event',
      [TraeAPI.SymbolKind.Operator]: 'Operator',
      [TraeAPI.SymbolKind.TypeParameter]: 'TypeParameter'
    };
    
    return kindNames[kind] || 'Unknown';
  }
  private sendMessage(message: any): void {
    if (this.searchPanel) {
      this.searchPanel.webview.postMessage(message);
    }
  }
  private setupEventListeners(): void {
    this.searchManager.onDidChangeIndex(event => {
      // Update search panel if open
      if (this.searchPanel && this.currentQuery) {
        // Re-run current search to get updated results
        this.performSearch(this.currentQuery, this.currentSearchType);
      }
    });
  }
}
// Initialize search UI
const searchUIProvider = new SearchUIProvider(searchManager);Interfaces 
interface FileIndexEntry {
  path: string;
  relativePath: string;
  name: string;
  extension: string;
  size: number;
  lastModified: number;
  language: string;
  searchTerms: string[];
}
interface SymbolIndexEntry {
  name: string;
  fullName: string;
  kind: TraeAPI.SymbolKind;
  filePath: string;
  range: TraeAPI.Range;
  selectionRange: TraeAPI.Range;
  searchTerms: string[];
}
interface ContentIndexEntry {
  filePath: string;
  lineNumber: number;
  content: string;
  trimmedContent: string;
  searchTerms: string[];
}
interface SearchHistoryEntry {
  type: SearchType;
  query: string;
  timestamp: number;
  resultCount: number;
}
interface FileSearchOptions {
  maxResults?: number;
  includePatterns?: string[];
  excludePatterns?: string[];
  fileTypes?: string[];
}
interface SymbolSearchOptions {
  maxResults?: number;
  fileTypes?: string[];
  symbolKinds?: TraeAPI.SymbolKind[];
}
interface ContentSearchOptions {
  maxResults?: number;
  includePatterns?: string[];
  excludePatterns?: string[];
  fileTypes?: string[];
  isCaseSensitive?: boolean;
  isRegex?: boolean;
  wholeWord?: boolean;
}
interface FileSearchResult {
  file: FileIndexEntry;
  score: number;
  matches: FileMatch[];
}
interface SymbolSearchResult {
  symbol: SymbolIndexEntry;
  file: FileIndexEntry;
  score: number;
  matches: SymbolMatch[];
}
interface ContentSearchResult {
  file: FileIndexEntry;
  lineNumber: number;
  content: string;
  matches: ContentMatch[];
}
interface FileMatch {
  type: 'filename' | 'path';
  text: string;
  startIndex: number;
  length: number;
}
interface SymbolMatch {
  type: 'name' | 'fullName';
  text: string;
  startIndex: number;
  length: number;
}
interface ContentMatch {
  startIndex: number;
  length: number;
  text: string;
}
interface IndexStats {
  fileCount: number;
  symbolCount: number;
  contentLineCount: number;
  isIndexing: boolean;
}
interface SearchEvent {
  type: 'indexingStarted' | 'indexingCompleted' | 'indexingFailed' | 'fileIndexed' | 'fileUpdated' | 'fileRemoved';
  filePath?: string;
  error?: Error;
}
type SearchType = 'file' | 'symbol' | 'content';API Reference 
SearchManager 
Methods 
- searchFiles(query: string, options?: FileSearchOptions): Promise<FileSearchResult[]>- Search for files by name and path
- Returns ranked results with match information
 
- searchSymbols(query: string, options?: SymbolSearchOptions): Promise<SymbolSearchResult[]>- Search for symbols (functions, classes, variables)
- Supports filtering by symbol kind and file type
 
- searchContent(query: string, options?: ContentSearchOptions): Promise<ContentSearchResult[]>- Search for text content within files
- Supports regex patterns and case sensitivity
 
- getSearchSuggestions(query: string, type: SearchType): Promise<string[]>- Get search suggestions based on query and type
- Returns relevant suggestions from history and index
 
- getSearchHistory(type?: SearchType): SearchHistoryEntry[]- Get search history entries
- Optionally filter by search type
 
- clearSearchHistory(type?: SearchType): void- Clear search history
- Optionally clear only specific type
 
- getIndexStats(): IndexStats- Get current indexing statistics
- Returns file, symbol, and content counts
 
- refreshIndex(): Promise<void>- Rebuild the entire search index
- Useful after major file system changes
 
- onDidChangeIndex(listener: (event: SearchEvent) => void): TraeAPI.Disposable- Listen for index change events
- Returns disposable to stop listening
 
- dispose(): void- Clean up resources and stop file watchers
 
SearchUIProvider 
Methods 
- showSearchPanel(): Promise<void>- Show the search webview panel
- Creates panel if it doesn't exist
 
- performSearch(query: string, type: SearchType): Promise<void>- Perform search and show results
- Updates UI with search results
 
Best Practices 
Performance Optimization 
// Limit search results for better performance
const results = await searchManager.searchFiles(query, {
  maxResults: 50
});
// Use specific file type filters
const jsResults = await searchManager.searchSymbols(query, {
  fileTypes: ['.js', '.ts'],
  symbolKinds: [TraeAPI.SymbolKind.Function, TraeAPI.SymbolKind.Class]
});
// Use exclude patterns for better performance
const contentResults = await searchManager.searchContent(query, {
  excludePatterns: ['node_modules/**', 'dist/**', '*.min.js']
});Search Query Optimization 
// Use specific queries for better results
const specificResults = await searchManager.searchFiles('UserService.ts');
// Use fuzzy matching for partial matches
const fuzzyResults = await searchManager.searchFiles('usrserv');
// Use regex for complex patterns
const regexResults = await searchManager.searchContent('function\\s+\\w+\\(', {
  isRegex: true
});Index Management 
// Monitor index status
searchManager.onDidChangeIndex(event => {
  switch (event.type) {
    case 'indexingStarted':
      console.log('Search indexing started');
      break;
    case 'indexingCompleted':
      console.log('Search indexing completed');
      break;
    case 'indexingFailed':
      console.error('Search indexing failed:', event.error);
      break;
  }
});
// Check index stats
const stats = searchManager.getIndexStats();
console.log(`Indexed ${stats.fileCount} files, ${stats.symbolCount} symbols`);
// Refresh index when needed
if (majorChangesDetected) {
  await searchManager.refreshIndex();
}Error Handling 
try {
  const results = await searchManager.searchContent(userQuery, {
    isRegex: true
  });
  displayResults(results);
} catch (error) {
  if (error.message.includes('Invalid search pattern')) {
    TraeAPI.window.showErrorMessage('Invalid regex pattern. Please check your search query.');
  } else {
    TraeAPI.window.showErrorMessage(`Search failed: ${error.message}`);
  }
}Memory Management 
// Dispose search manager when no longer needed
class MyExtension {
  private searchManager: SearchManager;
  
  activate() {
    this.searchManager = new SearchManager();
  }
  
  deactivate() {
    this.searchManager.dispose();
  }
}
// Limit content indexing for large files
const shouldIndex = (fileEntry: FileIndexEntry) => {
  return fileEntry.size < 1024 * 1024; // 1MB limit
};Related APIs 
- Workspace API - File system operations
- Editor API - Text editor integration
- Commands API - Search command registration
- UI API - Search UI components
- Language Services API - Symbol providers