Skip to content

Snippets API

The Snippets API provides comprehensive functionality for creating, managing, and using code snippets within the development environment.

Overview

The Snippets API enables you to:

  • Create and manage custom code snippets
  • Support multiple programming languages
  • Use variables and placeholders in snippets
  • Provide snippet completion and insertion
  • Import and export snippet collections
  • Support snippet transformations and conditions
  • Implement snippet sharing and synchronization
  • Handle snippet conflicts and versioning

Basic Usage

Snippet Management

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

// Snippet manager implementation
class SnippetManager {
  private snippets: Map<string, SnippetCollection> = new Map();
  private globalSnippets: Map<string, Snippet> = new Map();
  private snippetProviders: Map<string, SnippetProvider> = new Map();
  private transformers: Map<string, SnippetTransformer> = new Map();
  private variableResolvers: Map<string, VariableResolver> = new Map();

  constructor() {
    this.initializeBuiltinSnippets();
    this.initializeVariableResolvers();
    this.initializeTransformers();
    this.loadUserSnippets();
    this.setupEventListeners();
  }

  private initializeBuiltinSnippets(): void {
    // JavaScript/TypeScript snippets
    const jsSnippets: SnippetDefinition[] = [
      {
        name: 'log',
        prefix: 'log',
        body: [
          'console.log(${1:message});'
        ],
        description: 'Log output to console',
        scope: 'javascript,typescript'
      },
      {
        name: 'function',
        prefix: 'func',
        body: [
          'function ${1:name}(${2:params}) {',
          '\t${3:// body}',
          '}'
        ],
        description: 'Function declaration',
        scope: 'javascript,typescript'
      },
      {
        name: 'arrow-function',
        prefix: 'af',
        body: [
          'const ${1:name} = (${2:params}) => {',
          '\t${3:// body}',
          '};'
        ],
        description: 'Arrow function',
        scope: 'javascript,typescript'
      },
      {
        name: 'async-function',
        prefix: 'afunc',
        body: [
          'async function ${1:name}(${2:params}) {',
          '\t${3:// body}',
          '}'
        ],
        description: 'Async function declaration',
        scope: 'javascript,typescript'
      },
      {
        name: 'class',
        prefix: 'class',
        body: [
          'class ${1:ClassName} {',
          '\tconstructor(${2:params}) {',
          '\t\t${3:// constructor body}',
          '\t}',
          '',
          '\t${4:// methods}',
          '}'
        ],
        description: 'Class declaration',
        scope: 'javascript,typescript'
      },
      {
        name: 'interface',
        prefix: 'interface',
        body: [
          'interface ${1:InterfaceName} {',
          '\t${2:// properties}',
          '}'
        ],
        description: 'Interface declaration',
        scope: 'typescript'
      },
      {
        name: 'type',
        prefix: 'type',
        body: [
          'type ${1:TypeName} = ${2:type};'
        ],
        description: 'Type alias',
        scope: 'typescript'
      },
      {
        name: 'import',
        prefix: 'imp',
        body: [
          'import { ${2:imports} } from \'${1:module}\';'
        ],
        description: 'Import statement',
        scope: 'javascript,typescript'
      },
      {
        name: 'export',
        prefix: 'exp',
        body: [
          'export { ${1:exports} };'
        ],
        description: 'Export statement',
        scope: 'javascript,typescript'
      },
      {
        name: 'try-catch',
        prefix: 'try',
        body: [
          'try {',
          '\t${1:// try block}',
          '} catch (${2:error}) {',
          '\t${3:// catch block}',
          '}'
        ],
        description: 'Try-catch block',
        scope: 'javascript,typescript'
      },
      {
        name: 'for-loop',
        prefix: 'for',
        body: [
          'for (let ${1:i} = 0; ${1:i} < ${2:array}.length; ${1:i}++) {',
          '\t${3:// loop body}',
          '}'
        ],
        description: 'For loop',
        scope: 'javascript,typescript'
      },
      {
        name: 'for-of',
        prefix: 'forof',
        body: [
          'for (const ${1:item} of ${2:array}) {',
          '\t${3:// loop body}',
          '}'
        ],
        description: 'For-of loop',
        scope: 'javascript,typescript'
      },
      {
        name: 'for-in',
        prefix: 'forin',
        body: [
          'for (const ${1:key} in ${2:object}) {',
          '\tif (${2:object}.hasOwnProperty(${1:key})) {',
          '\t\t${3:// loop body}',
          '\t}',
          '}'
        ],
        description: 'For-in loop',
        scope: 'javascript,typescript'
      },
      {
        name: 'if-statement',
        prefix: 'if',
        body: [
          'if (${1:condition}) {',
          '\t${2:// if body}',
          '}'
        ],
        description: 'If statement',
        scope: 'javascript,typescript'
      },
      {
        name: 'if-else',
        prefix: 'ifelse',
        body: [
          'if (${1:condition}) {',
          '\t${2:// if body}',
          '} else {',
          '\t${3:// else body}',
          '}'
        ],
        description: 'If-else statement',
        scope: 'javascript,typescript'
      },
      {
        name: 'switch',
        prefix: 'switch',
        body: [
          'switch (${1:expression}) {',
          '\tcase ${2:value}:',
          '\t\t${3:// case body}',
          '\t\tbreak;',
          '\tdefault:',
          '\t\t${4:// default body}',
          '\t\tbreak;',
          '}'
        ],
        description: 'Switch statement',
        scope: 'javascript,typescript'
      },
      {
        name: 'promise',
        prefix: 'promise',
        body: [
          'new Promise((resolve, reject) => {',
          '\t${1:// promise body}',
          '});'
        ],
        description: 'Promise constructor',
        scope: 'javascript,typescript'
      },
      {
        name: 'async-await',
        prefix: 'await',
        body: [
          'const ${1:result} = await ${2:promise};'
        ],
        description: 'Await expression',
        scope: 'javascript,typescript'
      },
      {
        name: 'timeout',
        prefix: 'timeout',
        body: [
          'setTimeout(() => {',
          '\t${2:// timeout body}',
          '}, ${1:1000});'
        ],
        description: 'Set timeout',
        scope: 'javascript,typescript'
      },
      {
        name: 'interval',
        prefix: 'interval',
        body: [
          'setInterval(() => {',
          '\t${2:// interval body}',
          '}, ${1:1000});'
        ],
        description: 'Set interval',
        scope: 'javascript,typescript'
      }
    ];

    // React snippets
    const reactSnippets: SnippetDefinition[] = [
      {
        name: 'react-component',
        prefix: 'rfc',
        body: [
          'import React from \'react\';',
          '',
          'interface ${1:ComponentName}Props {',
          '\t${2:// props}',
          '}',
          '',
          'const ${1:ComponentName}: React.FC<${1:ComponentName}Props> = (${3:props}) => {',
          '\treturn (',
          '\t\t<div>',
          '\t\t\t${4:// component content}',
          '\t\t</div>',
          '\t);',
          '};',
          '',
          'export default ${1:ComponentName};'
        ],
        description: 'React functional component',
        scope: 'typescriptreact,javascriptreact'
      },
      {
        name: 'react-hook',
        prefix: 'rhook',
        body: [
          'import { ${1:useState} } from \'react\';',
          '',
          'const use${2:HookName} = (${3:params}) => {',
          '\t${4:// hook logic}',
          '',
          '\treturn ${5:returnValue};',
          '};',
          '',
          'export default use${2:HookName};'
        ],
        description: 'Custom React hook',
        scope: 'typescriptreact,javascriptreact'
      },
      {
        name: 'use-state',
        prefix: 'useState',
        body: [
          'const [${1:state}, set${1/(.*)/${1:/capitalize}/}] = useState${2:<${3:type}>}(${4:initialValue});'
        ],
        description: 'useState hook',
        scope: 'typescriptreact,javascriptreact'
      },
      {
        name: 'use-effect',
        prefix: 'useEffect',
        body: [
          'useEffect(() => {',
          '\t${1:// effect logic}',
          '}, [${2:dependencies}]);'
        ],
        description: 'useEffect hook',
        scope: 'typescriptreact,javascriptreact'
      },
      {
        name: 'use-context',
        prefix: 'useContext',
        body: [
          'const ${1:contextValue} = useContext(${2:Context});'
        ],
        description: 'useContext hook',
        scope: 'typescriptreact,javascriptreact'
      },
      {
        name: 'use-reducer',
        prefix: 'useReducer',
        body: [
          'const [${1:state}, ${2:dispatch}] = useReducer(${3:reducer}, ${4:initialState});'
        ],
        description: 'useReducer hook',
        scope: 'typescriptreact,javascriptreact'
      }
    ];

    // HTML snippets
    const htmlSnippets: SnippetDefinition[] = [
      {
        name: 'html5',
        prefix: 'html5',
        body: [
          '<!DOCTYPE html>',
          '<html lang="${1:en}">',
          '<head>',
          '\t<meta charset="UTF-8">',
          '\t<meta name="viewport" content="width=device-width, initial-scale=1.0">',
          '\t<title>${2:Document}</title>',
          '</head>',
          '<body>',
          '\t${3:<!-- content -->}',
          '</body>',
          '</html>'
        ],
        description: 'HTML5 boilerplate',
        scope: 'html'
      },
      {
        name: 'div',
        prefix: 'div',
        body: [
          '<div${1: class="${2:className}"}>',
          '\t${3:content}',
          '</div>'
        ],
        description: 'Div element',
        scope: 'html'
      },
      {
        name: 'link',
        prefix: 'a',
        body: [
          '<a href="${1:url}"${2: target="${3:_blank}"}>${4:link text}</a>'
        ],
        description: 'Anchor link',
        scope: 'html'
      },
      {
        name: 'image',
        prefix: 'img',
        body: [
          '<img src="${1:src}" alt="${2:alt}"${3: width="${4:width}" height="${5:height}"}/>'
        ],
        description: 'Image element',
        scope: 'html'
      },
      {
        name: 'form',
        prefix: 'form',
        body: [
          '<form${1: action="${2:action}" method="${3:post}"}>',
          '\t${4:<!-- form content -->}',
          '</form>'
        ],
        description: 'Form element',
        scope: 'html'
      },
      {
        name: 'input',
        prefix: 'input',
        body: [
          '<input type="${1:text}" name="${2:name}" id="${3:id}"${4: placeholder="${5:placeholder}"}${6: required}/>'
        ],
        description: 'Input element',
        scope: 'html'
      },
      {
        name: 'button',
        prefix: 'btn',
        body: [
          '<button type="${1:button}"${2: class="${3:className}"}>${4:Button Text}</button>'
        ],
        description: 'Button element',
        scope: 'html'
      }
    ];

    // CSS snippets
    const cssSnippets: SnippetDefinition[] = [
      {
        name: 'flexbox',
        prefix: 'flex',
        body: [
          'display: flex;',
          'justify-content: ${1:center};',
          'align-items: ${2:center};'
        ],
        description: 'Flexbox layout',
        scope: 'css,scss,less'
      },
      {
        name: 'grid',
        prefix: 'grid',
        body: [
          'display: grid;',
          'grid-template-columns: ${1:repeat(auto-fit, minmax(250px, 1fr))};',
          'gap: ${2:1rem};'
        ],
        description: 'CSS Grid layout',
        scope: 'css,scss,less'
      },
      {
        name: 'media-query',
        prefix: 'media',
        body: [
          '@media (${1:max-width}: ${2:768px}) {',
          '\t${3:/* styles */}',
          '}'
        ],
        description: 'Media query',
        scope: 'css,scss,less'
      },
      {
        name: 'animation',
        prefix: 'animation',
        body: [
          'animation: ${1:name} ${2:duration} ${3:ease-in-out} ${4:infinite};',
          '',
          '@keyframes ${1:name} {',
          '\t0% {',
          '\t\t${5:/* start styles */}',
          '\t}',
          '\t100% {',
          '\t\t${6:/* end styles */}',
          '\t}',
          '}'
        ],
        description: 'CSS animation',
        scope: 'css,scss,less'
      },
      {
        name: 'transition',
        prefix: 'transition',
        body: [
          'transition: ${1:property} ${2:duration} ${3:ease-in-out};'
        ],
        description: 'CSS transition',
        scope: 'css,scss,less'
      }
    ];

    // Register snippet collections
    this.registerSnippetCollection('javascript', {
      name: 'JavaScript',
      description: 'JavaScript code snippets',
      snippets: new Map(jsSnippets.map(s => [s.name, this.createSnippet(s)]))
    });

    this.registerSnippetCollection('react', {
      name: 'React',
      description: 'React code snippets',
      snippets: new Map(reactSnippets.map(s => [s.name, this.createSnippet(s)]))
    });

    this.registerSnippetCollection('html', {
      name: 'HTML',
      description: 'HTML code snippets',
      snippets: new Map(htmlSnippets.map(s => [s.name, this.createSnippet(s)]))
    });

    this.registerSnippetCollection('css', {
      name: 'CSS',
      description: 'CSS code snippets',
      snippets: new Map(cssSnippets.map(s => [s.name, this.createSnippet(s)]))
    });

    console.log('Initialized builtin snippets');
  }

  private initializeVariableResolvers(): void {
    // Built-in variable resolvers
    this.variableResolvers.set('TM_SELECTED_TEXT', {
      resolve: () => {
        const editor = TraeAPI.window.activeTextEditor;
        if (editor && !editor.selection.isEmpty) {
          return editor.document.getText(editor.selection);
        }
        return '';
      }
    });

    this.variableResolvers.set('TM_CURRENT_LINE', {
      resolve: () => {
        const editor = TraeAPI.window.activeTextEditor;
        if (editor) {
          const line = editor.document.lineAt(editor.selection.active.line);
          return line.text;
        }
        return '';
      }
    });

    this.variableResolvers.set('TM_CURRENT_WORD', {
      resolve: () => {
        const editor = TraeAPI.window.activeTextEditor;
        if (editor) {
          const wordRange = editor.document.getWordRangeAtPosition(editor.selection.active);
          if (wordRange) {
            return editor.document.getText(wordRange);
          }
        }
        return '';
      }
    });

    this.variableResolvers.set('TM_FILENAME', {
      resolve: () => {
        const editor = TraeAPI.window.activeTextEditor;
        if (editor) {
          return TraeAPI.path.basename(editor.document.fileName);
        }
        return '';
      }
    });

    this.variableResolvers.set('TM_FILENAME_BASE', {
      resolve: () => {
        const editor = TraeAPI.window.activeTextEditor;
        if (editor) {
          const filename = TraeAPI.path.basename(editor.document.fileName);
          return TraeAPI.path.parse(filename).name;
        }
        return '';
      }
    });

    this.variableResolvers.set('TM_DIRECTORY', {
      resolve: () => {
        const editor = TraeAPI.window.activeTextEditor;
        if (editor) {
          return TraeAPI.path.dirname(editor.document.fileName);
        }
        return '';
      }
    });

    this.variableResolvers.set('TM_FILEPATH', {
      resolve: () => {
        const editor = TraeAPI.window.activeTextEditor;
        if (editor) {
          return editor.document.fileName;
        }
        return '';
      }
    });

    this.variableResolvers.set('CLIPBOARD', {
      resolve: async () => {
        try {
          return await TraeAPI.env.clipboard.readText();
        } catch {
          return '';
        }
      }
    });

    this.variableResolvers.set('WORKSPACE_NAME', {
      resolve: () => {
        const workspaceFolder = TraeAPI.workspace.workspaceFolders?.[0];
        if (workspaceFolder) {
          return TraeAPI.path.basename(workspaceFolder.uri.fsPath);
        }
        return '';
      }
    });

    this.variableResolvers.set('WORKSPACE_FOLDER', {
      resolve: () => {
        const workspaceFolder = TraeAPI.workspace.workspaceFolders?.[0];
        if (workspaceFolder) {
          return workspaceFolder.uri.fsPath;
        }
        return '';
      }
    });

    // Date/time variables
    this.variableResolvers.set('CURRENT_YEAR', {
      resolve: () => new Date().getFullYear().toString()
    });

    this.variableResolvers.set('CURRENT_YEAR_SHORT', {
      resolve: () => new Date().getFullYear().toString().slice(-2)
    });

    this.variableResolvers.set('CURRENT_MONTH', {
      resolve: () => (new Date().getMonth() + 1).toString().padStart(2, '0')
    });

    this.variableResolvers.set('CURRENT_MONTH_NAME', {
      resolve: () => new Date().toLocaleString('default', { month: 'long' })
    });

    this.variableResolvers.set('CURRENT_MONTH_NAME_SHORT', {
      resolve: () => new Date().toLocaleString('default', { month: 'short' })
    });

    this.variableResolvers.set('CURRENT_DATE', {
      resolve: () => new Date().getDate().toString().padStart(2, '0')
    });

    this.variableResolvers.set('CURRENT_DAY_NAME', {
      resolve: () => new Date().toLocaleString('default', { weekday: 'long' })
    });

    this.variableResolvers.set('CURRENT_DAY_NAME_SHORT', {
      resolve: () => new Date().toLocaleString('default', { weekday: 'short' })
    });

    this.variableResolvers.set('CURRENT_HOUR', {
      resolve: () => new Date().getHours().toString().padStart(2, '0')
    });

    this.variableResolvers.set('CURRENT_MINUTE', {
      resolve: () => new Date().getMinutes().toString().padStart(2, '0')
    });

    this.variableResolvers.set('CURRENT_SECOND', {
      resolve: () => new Date().getSeconds().toString().padStart(2, '0')
    });

    this.variableResolvers.set('CURRENT_SECONDS_UNIX', {
      resolve: () => Math.floor(Date.now() / 1000).toString()
    });

    // Random values
    this.variableResolvers.set('RANDOM', {
      resolve: () => Math.random().toString(36).substring(2, 8)
    });

    this.variableResolvers.set('RANDOM_HEX', {
      resolve: () => Math.floor(Math.random() * 0xffffff).toString(16).padStart(6, '0')
    });

    this.variableResolvers.set('UUID', {
      resolve: () => {
        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
          const r = Math.random() * 16 | 0;
          const v = c === 'x' ? r : (r & 0x3 | 0x8);
          return v.toString(16);
        });
      }
    });

    console.log('Initialized variable resolvers');
  }

  private initializeTransformers(): void {
    // Text case transformers
    this.transformers.set('upcase', {
      transform: (text: string) => text.toUpperCase()
    });

    this.transformers.set('downcase', {
      transform: (text: string) => text.toLowerCase()
    });

    this.transformers.set('capitalize', {
      transform: (text: string) => {
        return text.charAt(0).toUpperCase() + text.slice(1).toLowerCase();
      }
    });

    this.transformers.set('camelcase', {
      transform: (text: string) => {
        return text.replace(/(?:^\w|[A-Z]|\b\w)/g, (word, index) => {
          return index === 0 ? word.toLowerCase() : word.toUpperCase();
        }).replace(/\s+/g, '');
      }
    });

    this.transformers.set('pascalcase', {
      transform: (text: string) => {
        return text.replace(/(?:^\w|[A-Z]|\b\w)/g, (word) => {
          return word.toUpperCase();
        }).replace(/\s+/g, '');
      }
    });

    this.transformers.set('snakecase', {
      transform: (text: string) => {
        return text.replace(/\W+/g, ' ')
          .split(/ |\s/)
          .map(word => word.toLowerCase())
          .join('_');
      }
    });

    this.transformers.set('kebabcase', {
      transform: (text: string) => {
        return text.replace(/\W+/g, ' ')
          .split(/ |\s/)
          .map(word => word.toLowerCase())
          .join('-');
      }
    });

    console.log('Initialized transformers');
  }

  private setupEventListeners(): void {
    // Listen for language changes
    TraeAPI.window.onDidChangeActiveTextEditor(editor => {
      if (editor) {
        this.updateLanguageContext(editor.document.languageId);
      }
    });

    // Listen for snippet provider registrations
    TraeAPI.languages.onDidChangeLanguages(() => {
      this.refreshSnippetProviders();
    });
  }

  private async loadUserSnippets(): Promise<void> {
    try {
      // Load user snippets from workspace settings
      const userSnippets = TraeAPI.workspace.getConfiguration('snippets').get<{ [key: string]: SnippetDefinition[] }>('user', {});
      
      for (const [language, snippets] of Object.entries(userSnippets)) {
        const collection = this.snippets.get(language) || {
          name: language,
          description: `User ${language} snippets`,
          snippets: new Map()
        };
        
        snippets.forEach(snippetDef => {
          const snippet = this.createSnippet(snippetDef);
          collection.snippets.set(snippet.name, snippet);
        });
        
        this.snippets.set(language, collection);
      }
      
      console.log('Loaded user snippets');
    } catch (error) {
      console.error('Failed to load user snippets:', error);
    }
  }

  // Core snippet operations
  createSnippet(definition: SnippetDefinition): Snippet {
    return {
      name: definition.name,
      prefix: definition.prefix,
      body: definition.body,
      description: definition.description,
      scope: definition.scope ? definition.scope.split(',').map(s => s.trim()) : [],
      isFileTemplate: definition.isFileTemplate || false,
      when: definition.when,
      sortText: definition.sortText,
      insertText: definition.insertText || definition.body.join('\n'),
      createdAt: Date.now(),
      usageCount: 0
    };
  }

  registerSnippet(snippet: Snippet, language?: string): void {
    if (language) {
      // Register to specific language collection
      let collection = this.snippets.get(language);
      if (!collection) {
        collection = {
          name: language,
          description: `${language} snippets`,
          snippets: new Map()
        };
        this.snippets.set(language, collection);
      }
      collection.snippets.set(snippet.name, snippet);
    } else {
      // Register as global snippet
      this.globalSnippets.set(snippet.name, snippet);
    }
    
    console.log(`Registered snippet: ${snippet.name}`);
  }

  unregisterSnippet(name: string, language?: string): boolean {
    if (language) {
      const collection = this.snippets.get(language);
      if (collection) {
        return collection.snippets.delete(name);
      }
    } else {
      return this.globalSnippets.delete(name);
    }
    return false;
  }

  registerSnippetCollection(language: string, collection: SnippetCollection): void {
    this.snippets.set(language, collection);
    console.log(`Registered snippet collection: ${collection.name}`);
  }

  getSnippetsForLanguage(language: string): Snippet[] {
    const snippets: Snippet[] = [];
    
    // Add global snippets
    snippets.push(...Array.from(this.globalSnippets.values()));
    
    // Add language-specific snippets
    const collection = this.snippets.get(language);
    if (collection) {
      snippets.push(...Array.from(collection.snippets.values()));
    }
    
    // Add snippets from other collections that support this language
    for (const [, coll] of this.snippets) {
      for (const snippet of coll.snippets.values()) {
        if (snippet.scope.length === 0 || snippet.scope.includes(language)) {
          snippets.push(snippet);
        }
      }
    }
    
    // Remove duplicates and sort
    const uniqueSnippets = Array.from(new Map(snippets.map(s => [s.name, s])).values());
    return uniqueSnippets.sort((a, b) => {
      // Sort by usage count (descending) then by name
      if (a.usageCount !== b.usageCount) {
        return (b.usageCount || 0) - (a.usageCount || 0);
      }
      return a.name.localeCompare(b.name);
    });
  }

  searchSnippets(query: string, language?: string): Snippet[] {
    const lowercaseQuery = query.toLowerCase();
    let snippets: Snippet[];
    
    if (language) {
      snippets = this.getSnippetsForLanguage(language);
    } else {
      snippets = this.getAllSnippets();
    }
    
    return snippets.filter(snippet => {
      return (
        snippet.name.toLowerCase().includes(lowercaseQuery) ||
        snippet.prefix.toLowerCase().includes(lowercaseQuery) ||
        (snippet.description && snippet.description.toLowerCase().includes(lowercaseQuery)) ||
        snippet.body.some(line => line.toLowerCase().includes(lowercaseQuery))
      );
    });
  }

  getAllSnippets(): Snippet[] {
    const snippets: Snippet[] = [];
    
    // Add global snippets
    snippets.push(...Array.from(this.globalSnippets.values()));
    
    // Add all collection snippets
    for (const collection of this.snippets.values()) {
      snippets.push(...Array.from(collection.snippets.values()));
    }
    
    return snippets;
  }

  // Snippet insertion and expansion
  async insertSnippet(snippet: Snippet, editor?: TraeAPI.TextEditor): Promise<boolean> {
    try {
      const targetEditor = editor || TraeAPI.window.activeTextEditor;
      if (!targetEditor) {
        return false;
      }

      // Resolve variables and transform text
      const expandedText = await this.expandSnippetText(snippet.insertText);
      
      // Create snippet string with placeholders
      const snippetString = new TraeAPI.SnippetString(expandedText);
      
      // Insert snippet
      const success = await targetEditor.insertSnippet(snippetString);
      
      if (success) {
        // Update usage statistics
        snippet.usageCount = (snippet.usageCount || 0) + 1;
        snippet.lastUsed = Date.now();
        
        console.log(`Inserted snippet: ${snippet.name}`);
      }
      
      return success;
    } catch (error) {
      console.error(`Failed to insert snippet ${snippet.name}:`, error);
      return false;
    }
  }

  async expandSnippetText(text: string): Promise<string> {
    let expandedText = text;
    
    // Replace variables
    const variableRegex = /\$\{([^}]+)\}/g;
    const matches = Array.from(text.matchAll(variableRegex));
    
    for (const match of matches) {
      const [fullMatch, variableExpr] = match;
      const expandedValue = await this.expandVariable(variableExpr);
      expandedText = expandedText.replace(fullMatch, expandedValue);
    }
    
    return expandedText;
  }

  private async expandVariable(variableExpr: string): Promise<string> {
    // Parse variable expression
    const parts = variableExpr.split(':');
    const variableName = parts[0];
    const defaultValue = parts[1] || '';
    
    // Check for transformations
    const transformMatch = variableName.match(/^(.+)\/(.+)\/$/);
    if (transformMatch) {
      const [, baseVar, transformName] = transformMatch;
      const baseValue = await this.resolveVariable(baseVar) || defaultValue;
      const transformer = this.transformers.get(transformName);
      if (transformer) {
        return transformer.transform(baseValue);
      }
      return baseValue;
    }
    
    // Resolve variable
    const value = await this.resolveVariable(variableName);
    return value !== undefined ? value : defaultValue;
  }

  private async resolveVariable(variableName: string): Promise<string | undefined> {
    const resolver = this.variableResolvers.get(variableName);
    if (resolver) {
      try {
        return await resolver.resolve();
      } catch (error) {
        console.error(`Error resolving variable ${variableName}:`, error);
        return undefined;
      }
    }
    
    // Check for numbered placeholders
    if (/^\d+$/.test(variableName)) {
      return `\${${variableName}}`; // Keep as placeholder
    }
    
    return undefined;
  }

  // Snippet providers
  registerSnippetProvider(language: string, provider: SnippetProvider): TraeAPI.Disposable {
    this.snippetProviders.set(`${language}:${Date.now()}`, provider);
    
    return {
      dispose: () => {
        // Remove provider
        for (const [key, p] of this.snippetProviders) {
          if (p === provider) {
            this.snippetProviders.delete(key);
            break;
          }
        }
      }
    };
  }

  async getSnippetCompletions(document: TraeAPI.TextDocument, position: TraeAPI.Position): Promise<TraeAPI.CompletionItem[]> {
    const completions: TraeAPI.CompletionItem[] = [];
    const language = document.languageId;
    
    // Get snippets for current language
    const snippets = this.getSnippetsForLanguage(language);
    
    // Get word at position for prefix matching
    const wordRange = document.getWordRangeAtPosition(position);
    const prefix = wordRange ? document.getText(wordRange) : '';
    
    // Filter snippets by prefix
    const matchingSnippets = snippets.filter(snippet => {
      return snippet.prefix.startsWith(prefix) || prefix === '';
    });
    
    // Convert to completion items
    for (const snippet of matchingSnippets) {
      const item = new TraeAPI.CompletionItem(snippet.prefix, TraeAPI.CompletionItemKind.Snippet);
      item.detail = snippet.description || snippet.name;
      item.documentation = new TraeAPI.MarkdownString().appendCodeblock(snippet.body.join('\n'), language);
      item.insertText = new TraeAPI.SnippetString(snippet.insertText);
      item.sortText = snippet.sortText || snippet.prefix;
      
      completions.push(item);
    }
    
    // Get completions from providers
    for (const provider of this.snippetProviders.values()) {
      try {
        const providerCompletions = await provider.provideCompletionItems(document, position);
        if (providerCompletions) {
          completions.push(...providerCompletions);
        }
      } catch (error) {
        console.error('Error getting snippets from provider:', error);
      }
    }
    
    return completions;
  }

  // Import/Export
  exportSnippets(language?: string): SnippetExport {
    const snippets: { [key: string]: SnippetDefinition[] } = {};
    
    if (language) {
      const collection = this.snippets.get(language);
      if (collection) {
        snippets[language] = Array.from(collection.snippets.values()).map(this.snippetToDefinition);
      }
    } else {
      // Export all snippets
      for (const [lang, collection] of this.snippets) {
        snippets[lang] = Array.from(collection.snippets.values()).map(this.snippetToDefinition);
      }
      
      // Add global snippets
      if (this.globalSnippets.size > 0) {
        snippets['global'] = Array.from(this.globalSnippets.values()).map(this.snippetToDefinition);
      }
    }
    
    return {
      version: '1.0.0',
      exportedAt: new Date().toISOString(),
      snippets
    };
  }

  async importSnippets(exportData: SnippetExport): Promise<boolean> {
    try {
      for (const [language, snippetDefs] of Object.entries(exportData.snippets)) {
        if (language === 'global') {
          // Import as global snippets
          snippetDefs.forEach(def => {
            const snippet = this.createSnippet(def);
            this.globalSnippets.set(snippet.name, snippet);
          });
        } else {
          // Import to language collection
          let collection = this.snippets.get(language);
          if (!collection) {
            collection = {
              name: language,
              description: `Imported ${language} snippets`,
              snippets: new Map()
            };
            this.snippets.set(language, collection);
          }
          
          snippetDefs.forEach(def => {
            const snippet = this.createSnippet(def);
            collection!.snippets.set(snippet.name, snippet);
          });
        }
      }
      
      console.log('Imported snippets successfully');
      return true;
    } catch (error) {
      console.error('Failed to import snippets:', error);
      return false;
    }
  }

  private snippetToDefinition(snippet: Snippet): SnippetDefinition {
    return {
      name: snippet.name,
      prefix: snippet.prefix,
      body: snippet.body,
      description: snippet.description,
      scope: snippet.scope.join(','),
      isFileTemplate: snippet.isFileTemplate,
      when: snippet.when,
      sortText: snippet.sortText,
      insertText: snippet.insertText
    };
  }

  // Utility methods
  private updateLanguageContext(languageId: string): void {
    // Update context for snippet filtering
    console.log(`Language context updated: ${languageId}`);
  }

  private refreshSnippetProviders(): void {
    // Refresh snippet providers when languages change
    console.log('Refreshing snippet providers');
  }

  dispose(): void {
    this.snippets.clear();
    this.globalSnippets.clear();
    this.snippetProviders.clear();
    this.transformers.clear();
    this.variableResolvers.clear();
  }
}

// Interfaces
interface SnippetDefinition {
  name: string;
  prefix: string;
  body: string[];
  description?: string;
  scope?: string;
  isFileTemplate?: boolean;
  when?: string;
  sortText?: string;
  insertText?: string;
}

interface Snippet {
  name: string;
  prefix: string;
  body: string[];
  description?: string;
  scope: string[];
  isFileTemplate: boolean;
  when?: string;
  sortText?: string;
  insertText: string;
  createdAt: number;
  lastUsed?: number;
  usageCount?: number;
}

interface SnippetCollection {
  name: string;
  description: string;
  snippets: Map<string, Snippet>;
}

interface SnippetProvider {
  provideCompletionItems(document: TraeAPI.TextDocument, position: TraeAPI.Position): Promise<TraeAPI.CompletionItem[] | undefined>;
}

interface VariableResolver {
  resolve(): Promise<string> | string;
}

interface SnippetTransformer {
  transform(text: string): string;
}

interface SnippetExport {
  version: string;
  exportedAt: string;
  snippets: { [language: string]: SnippetDefinition[] };
}

// Initialize snippet manager
const snippetManager = new SnippetManager();

File Templates

typescript
// File template functionality
class FileTemplateManager {
  private templates: Map<string, FileTemplate> = new Map();

  constructor(private snippetManager: SnippetManager) {
    this.initializeDefaultTemplates();
  }

  private initializeDefaultTemplates(): void {
    const templates: FileTemplateDefinition[] = [
      {
        name: 'React Component',
        extension: 'tsx',
        content: [
          'import React from \'react\';',
          '',
          'interface ${TM_FILENAME_BASE}Props {',
          '\t// Props definition',
          '}',
          '',
          'const ${TM_FILENAME_BASE}: React.FC<${TM_FILENAME_BASE}Props> = (props) => {',
          '\treturn (',
          '\t\t<div>',
          '\t\t\t{/* Component content */}',
          '\t\t</div>',
          '\t);',
          '};',
          '',
          'export default ${TM_FILENAME_BASE};'
        ],
        description: 'React functional component template'
      },
      {
        name: 'Node.js Module',
        extension: 'js',
        content: [
          '\'use strict\';',
          '',
          '/**',
          ' * ${TM_FILENAME_BASE} module',
          ' * @author ${USER}',
          ' * @date ${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE}',
          ' */',
          '',
          'module.exports = {',
          '\t// Module exports',
          '};'
        ],
        description: 'Node.js module template'
      },
      {
        name: 'HTML5 Document',
        extension: 'html',
        content: [
          '<!DOCTYPE html>',
          '<html lang="en">',
          '<head>',
          '\t<meta charset="UTF-8">',
          '\t<meta name="viewport" content="width=device-width, initial-scale=1.0">',
          '\t<title>${TM_FILENAME_BASE}</title>',
          '</head>',
          '<body>',
          '\t<!-- Content -->',
          '</body>',
          '</html>'
        ],
        description: 'HTML5 document template'
      },
      {
        name: 'CSS Stylesheet',
        extension: 'css',
        content: [
          '/**',
          ' * ${TM_FILENAME_BASE} stylesheet',
          ' * @author ${USER}',
          ' * @date ${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE}',
          ' */',
          '',
          '/* Reset and base styles */',
          '* {',
          '\tbox-sizing: border-box;',
          '}',
          '',
          '/* Component styles */'
        ],
        description: 'CSS stylesheet template'
      }
    ];

    templates.forEach(template => {
      this.registerTemplate(this.createTemplate(template));
    });
  }

  createTemplate(definition: FileTemplateDefinition): FileTemplate {
    return {
      name: definition.name,
      extension: definition.extension,
      content: definition.content.join('\n'),
      description: definition.description,
      createdAt: Date.now(),
      usageCount: 0
    };
  }

  registerTemplate(template: FileTemplate): void {
    this.templates.set(template.name, template);
    console.log(`Registered file template: ${template.name}`);
  }

  getTemplatesForExtension(extension: string): FileTemplate[] {
    return Array.from(this.templates.values())
      .filter(template => template.extension === extension)
      .sort((a, b) => (b.usageCount || 0) - (a.usageCount || 0));
  }

  async createFileFromTemplate(template: FileTemplate, filePath: string): Promise<boolean> {
    try {
      // Expand template content
      const expandedContent = await this.snippetManager.expandSnippetText(template.content);
      
      // Write file
      await TraeAPI.workspace.fs.writeFile(
        TraeAPI.Uri.file(filePath),
        Buffer.from(expandedContent, 'utf8')
      );
      
      // Update usage statistics
      template.usageCount = (template.usageCount || 0) + 1;
      template.lastUsed = Date.now();
      
      console.log(`Created file from template: ${template.name}`);
      return true;
    } catch (error) {
      console.error(`Failed to create file from template ${template.name}:`, error);
      return false;
    }
  }

  getAllTemplates(): FileTemplate[] {
    return Array.from(this.templates.values());
  }
}

interface FileTemplateDefinition {
  name: string;
  extension: string;
  content: string[];
  description?: string;
}

interface FileTemplate {
  name: string;
  extension: string;
  content: string;
  description?: string;
  createdAt: number;
  lastUsed?: number;
  usageCount?: number;
}

// Initialize file template manager
const fileTemplateManager = new FileTemplateManager(snippetManager);

API Reference

Core Interfaces

typescript
interface SnippetsAPI {
  // Snippet management
  createSnippet(definition: SnippetDefinition): Snippet;
  registerSnippet(snippet: Snippet, language?: string): void;
  unregisterSnippet(name: string, language?: string): boolean;
  
  // Collections
  registerSnippetCollection(language: string, collection: SnippetCollection): void;
  getSnippetsForLanguage(language: string): Snippet[];
  getAllSnippets(): Snippet[];
  
  // Search and query
  searchSnippets(query: string, language?: string): Snippet[];
  
  // Insertion
  insertSnippet(snippet: Snippet, editor?: TraeAPI.TextEditor): Promise<boolean>;
  expandSnippetText(text: string): Promise<string>;
  
  // Providers
  registerSnippetProvider(language: string, provider: SnippetProvider): TraeAPI.Disposable;
  getSnippetCompletions(document: TraeAPI.TextDocument, position: TraeAPI.Position): Promise<TraeAPI.CompletionItem[]>;
  
  // Import/Export
  exportSnippets(language?: string): SnippetExport;
  importSnippets(exportData: SnippetExport): Promise<boolean>;
  
  // File templates
  registerTemplate(template: FileTemplate): void;
  getTemplatesForExtension(extension: string): FileTemplate[];
  createFileFromTemplate(template: FileTemplate, filePath: string): Promise<boolean>;
}

Best Practices

  1. Snippet Organization: Group snippets by language and functionality
  2. Variable Usage: Use built-in variables for dynamic content
  3. Placeholder Design: Create logical tab stops for user input
  4. Scope Definition: Define appropriate scopes for snippet availability
  5. Performance: Optimize snippet loading and expansion
  6. User Experience: Provide clear descriptions and examples
  7. Customization: Allow users to create and modify snippets
  8. Sharing: Support snippet import/export for team collaboration

Your Ultimate AI-Powered IDE Learning Guide