Skip to content

How can I load the vscode-cpptools extension? #719

@coloz

Description

@coloz

I'm trying to use monaco-vscode-api and vscode-cpptools to make a c++ IDE.

Through electron, I read the corresponding file and want to load this extension, but it seems that it does not work.

How can I completely load an external vscode extension??

This is my partial loading code:

const cppToolsPath = 'C:\\Users\\coloz\\AppData\\Local\\aily-project\\extensions\\cpptools';

      await this.extensionLoader.loadExternalExtension(cppToolsPath, {
        hostKind: ExtensionHostKind.LocalProcess,
        system: false
      });
  async loadExternalExtension(
    extensionPath: string,
    options?: {
      /** 扩展主机类型,默认为 LocalWebWorker */
      hostKind?: ExtensionHostKind
      /** 是否为系统扩展,默认为 false */
      system?: boolean
    }
  ): Promise<ExtensionLoadResult> {
    try {
      console.log(`Loading external extension from: ${extensionPath}`)

      // 检查是否在Electron环境中
      if (!window['fs'] || !window['fs'].readFileSync) {
        throw new Error('External extension loading requires Electron environment')
      }

      // 通过Electron IPC加载扩展清单
      const manifest = await this.loadExternalManifest(extensionPath)
      const extensionId = `${manifest.publisher}.${manifest.name}`

      // 检查是否已加载
      if (this.loadedExtensions.has(extensionId)) {
        console.log(`External extension ${extensionId} already loaded`)
        return this.loadedExtensions.get(extensionId)!
      }

      console.log(`Registering external extension: ${manifest.name} v${manifest.version}`)

      // 注册扩展
      const hostKind = options?.hostKind ?? ExtensionHostKind.LocalWebWorker
      
      // 为cpptools扩展启用所需的API提案
      const extensionOptions: any = {
        system: options?.system ?? false
      }
      
      if (manifest.publisher === 'ms-vscode' && manifest.name === 'cpptools') {
        extensionOptions.enableProposedApi = ['terminalDataWriteEvent', 'chatParticipantAdditions']
      }
      
      const registrationResult = registerExtension(
        manifest,
        hostKind,
        extensionOptions
      )

      // 注册扩展文件(从外部目录)
      await this.registerExternalExtensionFiles(extensionPath, manifest, registrationResult)

      // 等待扩展准备就绪
      await registrationResult.whenReady()
      console.log(`External extension ${manifest.name} loaded successfully`)

      const result: ExtensionLoadResult = {
        id: registrationResult.id,
        manifest,
        dispose: registrationResult.dispose,
        whenReady: registrationResult.whenReady,
        getApi: 'getApi' in registrationResult ? (registrationResult.getApi as () => Promise<typeof vscode>) : undefined
      }

      this.loadedExtensions.set(extensionId, result)

      return result
    } catch (error) {
      console.error(`Failed to load external extension from ${extensionPath}:`, error)
      throw error
    }
  }

private async registerExternalFile(
    extensionPath: string,
    relativePath: string,
    registrationResult: any,
    mimeType?: string
  ): Promise<void> {
    try {
      const fullPath = `${extensionPath}/${relativePath}`
      const fileContent = window['fs'].readFileSync(fullPath, 'utf8')
      
      // 创建Blob URL
      const blob = new Blob([fileContent], { type: mimeType || 'text/plain' })
      const url = URL.createObjectURL(blob)
      
      registrationResult.registerFileUrl(relativePath, url, mimeType)
      console.log(`Registered external file: ${relativePath} -> ${url}`)
    } catch (error) {
      console.warn(`Failed to register external file ${relativePath}:`, error)
    }
  }

  /**
   * 收集扩展中需要注册的文件
   */
  private collectExtensionFiles(
    manifest: IExtensionManifest,
    baseUrl: string
  ): ExtensionFileInfo[] {
    const files: ExtensionFileInfo[] = []
    const contributes = manifest.contributes

    if (!contributes) {
      return files
    }

    // 处理语言配置文件
    if (contributes.languages) {
      for (const language of contributes.languages) {
        if (language.configuration) {
          files.push({
            path: language.configuration,
            url: `${baseUrl}${language.configuration}`,
            mimeType: 'application/json'
          })
        }
      }
    }

    // 处理语法文件(TextMate grammars)
    if (contributes.grammars) {
      for (const grammar of contributes.grammars) {
        if (grammar.path) {
          files.push({
            path: grammar.path,
            url: `${baseUrl}${grammar.path}`,
            mimeType: 'application/json'
          })
        }
      }
    }

    // 处理主题文件
    if (contributes.themes) {
      for (const theme of contributes.themes) {
        if (theme.path) {
          files.push({
            path: theme.path,
            url: `${baseUrl}${theme.path}`,
            mimeType: 'application/json'
          })
        }
      }
    }

    // 处理图标主题
    if (contributes.iconThemes) {
      for (const iconTheme of contributes.iconThemes) {
        if (iconTheme.path) {
          files.push({
            path: iconTheme.path,
            url: `${baseUrl}${iconTheme.path}`,
            mimeType: 'application/json'
          })
        }
      }
    }

    // 处理产品图标主题
    if (contributes.productIconThemes) {
      for (const productIconTheme of contributes.productIconThemes) {
        if (productIconTheme.path) {
          files.push({
            path: productIconTheme.path,
            url: `${baseUrl}${productIconTheme.path}`,
            mimeType: 'application/json'
          })
        }
      }
    }

    // 处理代码片段
    if (contributes.snippets) {
      for (const snippet of contributes.snippets) {
        if (snippet.path) {
          files.push({
            path: snippet.path,
            url: `${baseUrl}${snippet.path}`,
            mimeType: 'application/json'
          })
        }
      }
    }

    // 处理扩展图标
    if (manifest.icon) {
      const iconExt = manifest.icon.split('.').pop()?.toLowerCase()
      const mimeType = iconExt === 'png' ? 'image/png' :
        iconExt === 'svg' ? 'image/svg+xml' :
          iconExt === 'jpg' || iconExt === 'jpeg' ? 'image/jpeg' :
            undefined

      files.push({
        path: manifest.icon,
        url: `${baseUrl}${manifest.icon}`,
        mimeType
      })
    }

    return files
  }

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions