news 2026/9/20 5:57:21

Eclipse Theia 示例插件(Sample Plugins)完全指南:编写、打包与在 Theia 中验证 VS Code 扩展

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Eclipse Theia 示例插件(Sample Plugins)完全指南:编写、打包与在 Theia 中验证 VS Code 扩展

Eclipse Theia 示例插件(Sample Plugins)完全指南:编写、打包与在 Theia 中验证 VS Code 扩展

【免费下载链接】theiaEclipse Theia is a cloud & desktop IDE framework implemented in TypeScript.项目地址: https://gitcode.com/gh_mirrors/th/theia

sample-plugins/是 Eclipse Theia 仓库中一组最小化的 VS Code 扩展集合,用于在真实 Theia 例程应用(browser / electron 示例)中演练插件运行时(plugin runtime)。本文以 sample-plugins/README.md 为主干,结合每个插件的package.jsonextension.js源码,完整讲解插件的目录结构、两种安装验证方式(复制到plugins目录 / 从 Extensions 视图安装.vsix)、以及各插件所演示的运行时变体(CJS、浏览器限定、ESM、headless、LM 工具等)。读完本文,你将能够独立编写一个最小 VS Code 插件、将其打包并在 Theia 中安装、激活与验证。

一、Sample Plugins 是什么

按仓库文档 sample-plugins/README.md 的说明:

A small collection of minimal VS Code extensions used by Theia to exercise the plugin runtime.

这是一批刻意保持最小的 VS Code 扩展,其唯一目的就是让 Theia 的插件运行时(plugin runtime)有"练习对象"。每个插件都位于sample-namespace/命名空间下,并注册一条简单的Hello from <plugin-name>命令;其中一部分插件还专门演示特定的运行时变体(browser-only、ESM 等)。

从目录布局看,sample-plugins/sample-namespace 下目前包含 7 个插件:

插件目录演示重点
plugin-a最经典的 CommonJS 插件模板
plugin-b与 plugin-a 结构一致的第二个 CJS 插件
plugin-browser仅在浏览器端(browser worker)运行
plugin-esm以 ESM("type": "module")方式编写的插件
plugin-esm-mjs.mjs扩展名导出的 ESM 插件
plugin-gotdheadless 插件(后端无 UI 运行),并使用@theia/api-provider-sample
plugin-lm-tools通过vscode.lm.registerTool注册语言模型(LM)工具

二、最小的插件长什么样:以 plugin-a 为例

任何一个 "Hello" 插件都由两个文件构成核心逻辑:描述插件元数据的package.json,以及实现激活逻辑的extension.js

2.1 package.json:清单文件

以 sample-plugins/sample-namespace/plugin-a/package.json 为例:

{ "private": true, "name": "plugin-a", "version": "1.75.0", "main": "extension.js", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "publisher": "sample-namespace", "engines": { "vscode": "^1.125.0" }, "activationEvents": [ "onCommand:plugin-a.hello" ], "scripts": { "build": "vsce package --no-dependencies" }, "contributes": { "commands": [ { "command": "plugin-a.hello", "title": "Hello from plugin-a" } ] } }

几个关键字段的说明:

  • main:插件入口文件,Theia 插件宿主会加载该文件并调用其导出的activate
  • engines.vscode:声明兼容的 VS Code API 版本(此处为^1.125.0),Theia 的插件兼容层以此为基准;
  • activationEvents:声明激活事件。这里使用onCommand:plugin-a.hello,意味着只有当用户真正执行plugin-a.hello命令时插件才会被激活,属于懒加载(lazy activation)模式;
  • contributes.commands:把命令 ID 与命令面板中显示的中文/英文标题绑定起来,标题正是Hello from plugin-a
  • scripts.buildvsce package --no-dependencies,用于把插件目录打成.vsix安装包(--no-dependencies表示不打包 npm 依赖);
  • publishersample-namespace,与目录名sample-namespace/保持一致,这也是.vsix发布者标识;
  • private: true:避免该包被意外发布到 npm registry。

2.2 extension.js:激活逻辑

plugin-a/extension.js 的完整实现只有几行:

const vscode = require('vscode'); exports.activate = function (context) { context.subscriptions.push(vscode.commands.registerCommand('plugin-a.hello', () => { vscode.window.showInformationMessage('Hello from plugin-a!'); })); }

这里遵循 VS Code 扩展的标准生命周期约定:

  • activate(context)在满足activationEvents条件时被调用;
  • vscode.commands.registerCommand('plugin-a.hello', ...)注册命令回调;
  • 返回值通过context.subscriptions.push(...)登记,插件被禁用/卸载时 Theia 会自动执行对应的dispose()释放资源。

plugin-b 与 plugin-a 结构完全一致,仅命令 ID、标题与提示消息替换为plugin-b.hello/Hello from plugin-b!,可作为同一目录下多个插件并存时互相印证的最小样例。

三、运行时变体:每个插件演示什么

除了最基础的 CJS 模板,sample 集合还覆盖了 Theia 插件运行时支持的多种形态。

3.1 浏览器限定插件:plugin-browser

plugin-browser/package.json 的关键在于它没有main字段,而是使用:

"browser": "./extension.js"

plugin-browser/extension.js 在激活时打印日志:

exports.activate = function (context) { console.log('[plugin-browser] activated in browser worker!'); const disposable = vscode.commands.registerCommand('plugin-browser.hello', function () { vscode.window.showInformationMessage('Hello from a browser-only extension!'); }); context.subscriptions.push(disposable); };

browser字段声明该插件的入口只面向浏览器环境运行,Theia 会在前端(browser worker)中执行它,同时导出的deactivate钩子也被显式声明(此处为空实现)。这类插件对应 Theia 文档中 "browser-only" 的插件形态,适合演示前端插件不需要 Node.js 能力即可工作的场景。

3.2 ESM 插件:plugin-esm 与 plugin-esm-mjs

plugin-esm通过"type": "module"把整个包声明为 ESM:

"type": "module", "main": "extension.js"

其 extension.js 使用import/export语法:

import * as vscode from 'vscode'; export function activate(context) { context.subscriptions.push(vscode.commands.registerCommand('plugin-esm.hello', () => { vscode.window.showInformationMessage('Hello from plugin-esm (ESM)!'); })); }

plugin-esm-mjs则直接以.mjs扩展名作为main入口:

"main": "extension.mjs"

其 extension.mjs 采用命名导入的写法:

import { commands, window } from 'vscode'; export function activate(context) { context.subscriptions.push(commands.registerCommand('plugin-esm-mjs.hello', () => { window.showInformationMessage('Hello from plugin-esm-mjs (.mjs)!'); })); }

从源码结构看,这两个插件用于验证 Theia 插件宿主对 ESM 入口(无论是包级type: module还是文件级.mjs)的加载支持。

3.3 headless 插件:plugin-gotd

plugin-gotd(GotD,Greetings of the Day)演示的是headless 插件——即在后端进程(无 UI 的 headless 环境)中运行、不依赖任何界面组件的插件形态。其 package.json 中值得关注的是:

"main": "extension", "activationEvents": ["*"], "theiaPlugin": { "headless": "headless" }, "headless": { "activationEvents": ["*"], "contributes": {} }
  • "main": "extension":默认入口(后端/前端共用),对应的 extension.js 会读取vscode.versionvscode.env.shell,并通过vscode.extensions.getExtension('<unpublished>.plugin-gotd')查询自身extensionKind与安装路径,用于诊断插件运行环境;
  • "theiaPlugin": { "headless": "headless" }:Theia 专有扩展点,声明 headless 入口指向headless(即headless.js);
  • headless.js 引用@theia/api-provider-sample包:通过gotd.greeting.createGreeter()创建问候器,监听onGreetingKindsChanged,并依次把问候类型切换为DIRECTQUIRKYSNARKY,把问候语打印到日志中。

这个插件同时演示了两点:一是 Theia 对 headless 插件的支持(examples/api-provider-sample 提供了对应 API 的服务端实现);二是插件运行时对@theia/api-provider-sample这类"API 提供者"包的依赖能力。

3.4 LM 工具插件:plugin-lm-tools

plugin-lm-tools演示的是最新形态的Language Model Tools——通过 VS Code API 把函数暴露给 AI 语言模型调用。清单部分在 package.json 的contributes.languageModelTools中声明了三个工具:

  • sample-getCurrentTime:无参数,返回 ISO 格式的当前时间;
  • sample-calculateSum:接收numbers数组,返回求和结果(inputSchema定义了{"type":"object","properties":{"numbers":{"type":"array","items":{"type":"number"}},"required":["numbers"]}});
  • sample-getSystemInfo:无参数,返回平台、Node 版本与运行时长。

实现部分在 extension.js 中使用vscode.lm.registerTool(name, handler)注册,并通过context.subscriptions.push(...)登记生命周期:

const timeTool = vscode.lm.registerTool('sample-getCurrentTime', { invoke(_options, _token) { const now = new Date().toISOString(); return { content: [new vscode.LanguageModelTextPart(now)] }; }, prepareInvocation(_options, _token) { return { invocationMessage: 'Getting current time...' }; } });

其中sample-calculateSuminvoke还会检查token.isCancellationRequested并逐个数字延迟 2 秒求和,用于演示长耗时工具的取消(cancellation)机制sample-getSystemInfo则演示返回LanguageModelTextPart+LanguageModelDataPart.json混合内容

四、在 Theia 示例应用中测试示例插件

sample-plugins/README.md 给出了两种安装验证路径,下面逐一展开。

方法 A:把插件文件夹复制进已部署的 plugins 目录

这是最快、最直接的验证方式,不需要打包:

1.(可选)先下载 VS Code 内置扩展,让示例环境更完整:

npm run download:plugins

该命令在根目录 package.json 中定义为theia download:plugins,由 dev-packages 下的 Theia CLI 实现。

  1. 把插件目录整体复制到部署好的plugins目录下:
cp -r sample-plugins/sample-namespace/<plugin-name> plugins/

其中<plugin-name>替换为目标插件名(如plugin-aplugin-browserplugin-lm-tools)。

plugins目录的位置由示例应用的配置决定:在 examples/browser/package.json 中可以看到"theiaPluginsDir": "../../plugins",即浏览器示例从仓库根目录的plugins/目录加载插件;electron 示例的配置与之类似。

  1. 启动示例应用,例如浏览器版:
npm run start:browser

在 examples/browser 内部,start脚本实际执行的是theia start --plugins=local-dir:../../plugins --ovsx-router-config=../ovsx-router-config.json(见 examples/browser/package.json),即通过local-dir:前缀把../../plugins指定为本地插件目录。

  1. 打开命令面板(Ctrl+Shift+P / Cmd+Shift+P),运行Hello from <plugin-name>。此时由于activationEvents中声明了onCommand:<id>,插件会在命令执行时被激活,右下角弹出Hello from <plugin-name>!通知即表示验证成功。

提示:此方式依赖plugins目录已被示例应用加载。若在示例应用运行时新增插件目录,通常需要重启应用使其被扫描到。

方法 B:打包成 .vsix 并从 Extensions 视图安装

这种方式更接近真实用户安装插件的路径,适合验证打包产物:

  1. 在插件目录内执行打包:
cd sample-plugins/sample-namespace/<plugin-name> npm run build

该脚本即vsce package --no-dependencies,会在插件源文件旁生成<plugin-name>-<version>.vsix(例如plugin-a-1.75.0.vsix)。--no-dependencies表示不把 npm 依赖打进去——由于这些示例插件都只依赖vscodeAPI(由 Theia 提供),因此无需打包依赖。

  1. 启动 Theia:
npm run start:browser
  1. 打开 Extensions 视图(左侧扩展图标)。

  2. 点击 Extensions 视图右上角的...菜单,选择Install from VSIX...,然后选中上一步生成的.vsix文件。

  3. 打开命令面板,运行Hello from <plugin-name>验证插件已生效。

两种方式如何选择

维度方法 A(复制目录)方法 B(.vsix 安装)
是否打包否,直接拷贝源码目录是,需先npm run build
适用场景开发迭代期快速验证验证打包产物、模拟用户安装流程
产物plugins/下的插件目录<plugin-name>-<version>.vsix
触发激活命令面板执行命令同上,但需先经 Extensions 视图安装

五、验证要点与常见排查思路

  • 命令找不到:确认插件目录确实位于plugins/下(或.vsix已成功安装),且contributes.commands中的commandID 与activationEvents中的onCommand:ID 完全一致(例如plugin-a.hello);
  • 激活未触发:检查activationEvents。若使用onCommand:,必须真正执行该命令才会激活;plugin-gotdplugin-lm-tools分别使用"*""onStartupFinished",属于启动即激活的不同策略;
  • 插件类型不符:browser-only 插件(plugin-browser)应能在纯浏览器示例中运行;涉及 Node 能力的插件请确认运行环境满足要求;
  • headless 插件plugin-gotd的日志输出在后台进程,可从后端控制台查看[GOTD-BE]/[GOTD]前缀的日志(见 headless.js);
  • LM 工具plugin-lm-tools注册的三个工具需在具备 AI/LM 能力的 Theia 环境(如 ai-chat 相关扩展)中才会被调用,控制台会打印[plugin-lm-tools]前缀的调用日志。

六、总结

sample-plugins/为 Theia 插件开发者提供了一套"从零到验证"的最小闭环:每种插件形态(CJS、browser-only、ESM/.mjs、headless、LM tools)都配有可直接运行的package.jsonextension.js,而 sample-plugins/README.md 给出的两种安装方式覆盖了开发调试(复制目录)与真实分发(.vsix+ Extensions 视图)两个阶段。以此为基础,你可以把任意一个 sample 插件复制出来改名改造,快速搭建属于自己的 Theia 插件原型。

【免费下载链接】theiaEclipse Theia is a cloud & desktop IDE framework implemented in TypeScript.项目地址: https://gitcode.com/gh_mirrors/th/theia

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/20 5:55:06

AI生成内容识别:双引擎系统降低误判率至6%

1. 项目背景与核心挑战去年我们团队接手了一个棘手的项目——某内容平台的AI生成内容识别系统。初始版本上线后&#xff0c;系统误判率高达68%&#xff0c;这意味着每100篇人工创作的内容中&#xff0c;有68篇被错误标记为AI生成。这种误判直接影响了创作者收益和平台信誉&…

作者头像 李华
网站建设 2026/9/20 5:52:01

解决Windows下Python科学计算中的libiomp5md.dll冲突

1. 问题现象与背景解析当你在Windows系统上运行基于Python的科学计算程序时&#xff08;特别是使用NumPy、PyTorch等库时&#xff09;&#xff0c;可能会遇到这样的报错信息&#xff1a;OMP: Error #15: Initializing libiomp5md.dll, but found libiomp5md.dll already initia…

作者头像 李华
网站建设 2026/9/20 5:50:36

浏览器自动化利器Playwright:动态渲染处理与pytest实践

入行做浏览器自动化这些年&#xff0c;我一直有个很深的感触&#xff1a;很多人一提到爬虫或者自动化&#xff0c;第一反应还是“直接抓接口、解参数”&#xff0c;觉得这才是高效的正道。但真到了生产环境你会发现&#xff0c;页面上随便一个动态Token、一段JS加密、一层嵌套i…

作者头像 李华
网站建设 2026/9/20 5:50:17

AI辅助Web接口逆向解析实战:从抓包到结构化数据提取

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华