- 文档
- 教程
【免费下载链接】vscode-docs
Public documentation for Visual Studio Code
Notebook 扩展开发实战指南:本文基于 VS Code 官方博客,系统讲解笔记本(Notebook)这一融合 Markdown、可执行代码与富输出的文档形式如何在 Visual Studio Code 中成为核心功能,并通过 Notebook API 让扩展作者为任意领域打造属于自己的自定义笔记本体验。阅读本文后,你将理解 NotebookSerializer、NotebookController、NotebookRenderer 三大组件的分工与协作,掌握从声明笔记本类型、注册序列化器、实现执行控制器到渲染富输出的完整扩展开发流程,并能够参照 GitHub Issues Notebook 与 REST Book 两个官方示例起步自己的笔记本扩展。
什么是 Notebook:可执行代码与叙事文本的交织
Notebook(笔记本)是一类特殊文档,其中混合了丰富的 Markdown、可执行的代码片段,以及随之产生的富输出(rich output)。这些内容被分隔为一个个独立的单元格(cell),并且可以按任意顺序交错排列。
如果你不熟悉 Notebook,那么很可能熟悉 REPL(read-eval-print loop,读取-求值-打印循环)——一种交互式应用,你可以写几行代码、立即执行并看到输出。Notebook 可以说是 REPL 的极致形态,让你能快速建立一个环境,对小块代码进行迭代与打磨。
但 Notebook 的价值不止于 REPL:它同样是绝佳的叙事工具(storytelling device)。你可以在代码之间穿插图片、数学公式、解释性文字等 Markdown 元素,从而把想法优雅地分享给同事或公众社区。
当前最流行的 Notebook 形态是 Jupyter Notebook,在数据科学社区中配合丰富的 Python 支持被广泛使用。Jupyter Notebook 还通过 **Jupyter kernel(内核)**支持 Julia、R 等其他语言——内核是遵循特定协议来运行笔记本代码的可执行程序。VS Code 已支持 Jupyter Notebook 多年,并在 2021 年 8 月宣布将原生的笔记本支持直接内置进 VS Code 核心(详见姊妹篇博客 The Coming of Age of Notebooks),这一演进也正是自定义笔记本生态的起点。
Notebook 支持进入 VS Code 核心
没错,Notebook 如今已是 VS Code 的核心功能之一。这意味着 VS Code 中提供了 Notebook API,让扩展作者可以创建自己的笔记本体验。任何人都可以开发支持自定义语言与富输出的 VS Code Notebook 扩展,而创建一个 Notebook 扩展与创建其他任何扩展并无本质区别。
在 Notebook API 出现之前,VS Code 中的 Jupyter Notebook 支持完全由 Jupyter 扩展单独贡献。该扩展把笔记本体验构建在一个隔离的 webview中——有点像 VS Code 内部的一个独立网页,无法与你安装的其他扩展通信。
而如今,借助核心 Notebook API,笔记本支持直接来自 VS Code 本身,不再被隔离在 webview 里。这意味着:
- 笔记本扩展可以与 VS Code 其余部分以及其他扩展交互;
- 编辑器类扩展(如 Rainbow Indent 缩进高亮)可以直接作用在笔记本的代码单元格中;
- 笔记本的代码单元格体验与完整代码编辑器趋于一致。
更重要的一点是:Notebook API并不局限于 Jupyter Notebook。VS Code 团队相信,还有许多其他领域可以从“迭代 + 叙述代码”的工具中受益,因此将新的 Notebook API 公开,供任何扩展作者打造属于自己的自定义笔记本。
相关阅读:Notebook API 的完整参考位于 api/extension-guides/notebook.md,其中详细拆解了笔记本的三大组成部件——序列化器(Serializer)、控制器(Controller)与渲染器(Renderer)。
笔记本扩展生态的雏形:两个官方示例
在打磨 Notebook API 的过程中,VS Code 团队自己开发了两个自定义笔记本,用以验证 API 的可用性。它们成为社区中最早、也最具参考价值的 Notebook 扩展样例。
GitHub Issues Notebook:用笔记本管理上万 Issue
VS Code 团队创建的第一个笔记本体验是 GitHub Issues Notebook。它是一本用于分诊(triage)与整理GitHub 上数千个 Issue 的笔记本:可以同时查看多个仓库,用类似“找出所有标记为bug且指派给我的 issue”的查询来定位问题。VS Code 团队每天用它来处理横跨多个仓库的 issue 流。
团队用于分诊的具体笔记本文件存放在 VS Code 主仓库的.vscode/notebooks目录下,其中inbox.github-issues用于把新 issue 分诊到合适的领域与负责人。
任何人都可以从 VS Code Marketplace 安装 GitHub Issues Notebook 扩展并试用:
- 安装扩展;
- 创建以
.github-issues结尾的笔记本文件,例如my-notebook.github-issues; - 在其中创建查询,例如:
$repo=repo:microsoft/vscode-github-issue-notebooks $repo is:open no:assignee笔记本语言github-issues的查询语法与 GitHub.com 上的搜索语法几乎一致;唯一的重要差异是:GitHub Issues Notebook 语言允许你定义变量,并在任意其他单元格中复用——上面的$repo就是这种变量的典型用法。
GitHub Issues Notebook 的完整源码以开源形式发布,可以作为学习 Notebook 扩展实现细节(尤其是 NotebookController 如何执行 GitHub 查询)的一手材料;在 api/extension-guides/notebook.md 的 Controller 小节中也将其notebookProvider.ts列为官方样例。
REST Book:在笔记本里发起 HTTP 请求
受 GitHub Issues Notebook 查询体验的启发,作者 Tanha Kabir 在加入 VS Code 团队之初,把 REST Book 作为学习练习创建了出来。REST Book 允许你在笔记本中发起 HTTP 调用,非常适合在带有服务器的项目上迭代:可以在一页上持续发起多次调用、随时间推移比较结果;作者还用它把文档与测试用例交错编排,为项目搭建手工测试环境。
REST Book 同样已上架 Marketplace,使用方式为:
- 安装 REST Book 扩展;
- 创建以
.restbook结尾的文件; - 执行任意 HTTP 查询,例如
GET github.com。
作者坦承,起初以为开发 REST Book 会很复杂,但得益于 VS Code 丰富的 API,实际开发比预想简单得多:他大量使用语言 API为自定义的 REST 查询语言提供语法高亮与自动补全;而有了 Notebook API,他只需要填上“用户运行查询时应该做什么”这一部分逻辑即可。
这两个示例共同印证了一个核心观点:借助 VS Code 提供的 Notebook UI,开发者无需操心创建和维护自己的 UI,只需专注于功能本身,就能获得 REPL 式的完整体验。
打造自己的自定义笔记本扩展
三大组件:Serializer、Controller 与 Renderer
要理解自定义笔记本扩展,首先需要建立对 Notebook API 三大组件的整体认知(详见 api/extension-guides/notebook.md):
| 组件 | 职责 | 对应 API 类型 |
|---|---|---|
| NotebookSerializer | 负责把笔记本文件的字节反序列化为NotebookData(即单元格列表),以及把NotebookData序列化回字节以便保存 | vscode.NotebookSerializer |
| NotebookController | 负责接收代码单元格并执行其中的代码,产出零个或多个输出 | vscode.NotebookController |
| NotebookRenderer | 负责把特定 mimetype 的输出数据渲染为视图,复杂度可从静态 HTML 到完全交互的小程序 | notebookRenderercontribution |
一个笔记本由一系列单元格及其输出组成。单元格分为Markdown 单元格与代码单元格两种,都渲染在 VS Code 核心内部。输出则有多种格式:纯文本、JSON、图片、HTML 等由 VS Code 核心渲染;应用特定的数据或交互式小程序则由扩展渲染。
声明笔记本类型与序列化器
一个 Notebook 扩展首先要在package.json的contributes.notebooks中声明笔记本类型与文件关联。以官方文档中“用.notebook扩展名查看 Jupyter Notebook 格式文件”的示例为例:
{ ... "contributes": { ... "notebooks": [ { "type": "my-notebook", "displayName": "My Notebook", "selector": [ { "filenamePattern": "*.notebook" } ] } ] } }随后在扩展的激活事件中注册序列化器。注意registerNotebookSerializer的第一个参数必须与package.json中的type一致:
import { TextDecoder, TextEncoder } from "util"; import * as vscode from 'vscode'; export function activate(context: vscode.ExtensionContext) { context.subscriptions.push( vscode.workspace.registerNotebookSerializer( "my-notebook", new SampleSerializer() ) ); } interface RawNotebook { cells: RawNotebookCell[]; } interface RawNotebookCell { source: string[]; cell_type: 'code' | 'markdown'; } class SampleSerializer implements vscode.NotebookSerializer { async deserializeNotebook(content: Uint8Array, _token: vscode.CancellationToken): Promise<vscode.NotebookData> { var contents = new TextDecoder().decode(content); let raw: RawNotebookCell[]; try { raw = (<RawNotebook>JSON.parse(contents)).cells; } catch { raw = []; } const cells = raw.map(item => new vscode.NotebookCellData( item.cell_type === 'code' ? vscode.NotebookCellKind.Code : vscode.NotebookCellKind.Markup, item.source.join('\n'), item.cell_type === 'code' ? 'python' : 'markdown' )); return new vscode.NotebookData(cells); } async serializeNotebook(data: vscode.NotebookData, _token: vscode.CancellationToken): Promise<Uint8Array> { let contents: RawNotebookCell[] = []; for (const cell of data.cells) { contents.push({ cell_type: cell.kind === vscode.NotebookCellKind.Code ? 'code' : 'markdown', source: cell.value.split(/\r?\n/g) }); } return new TextEncoder().encode(JSON.stringify(contents)); } }序列化器只需实现两个方法:deserializeNotebook负责“字节 → 单元格”,serializeNotebook负责“单元格 → 字节”。运行该扩展后,即可打开以.notebook结尾的 Jupyter 格式文件,以纯文本或渲染后的 Markdown 查看单元格并编辑。需要说明的局限是:示例中的输出(output)不会持久化到磁盘——若要保存输出,还需在NotebookData上同时序列化/反序列化单元格的输出。
提示:同一笔记本类型(
type)激活时还可搭配onNotebook:type激活事件,在打开指定笔记本文档类型时触发扩展激活,详见 api/references/activation-events.md 中的onNotebook:jupyter-notebook与onNotebook:interactive示例。
实现控制器执行代码
要真正“运行”单元格,需要实现NotebookController。控制器通过notebookType属性与特定笔记本类型(及其序列化器)直接关联,并在扩展激活时全局注册:
export function activate(context: vscode.ExtensionContext) { context.subscriptions.push(new Controller()); } class Controller { readonly controllerId = 'my-notebook-controller-id' readonly notebookType = 'my-notebook'; readonly label = 'My Notebook'; readonly supportedLanguages = ['python']; private readonly _controller: vscode.NotebookController; private _executionOrder = 0; constructor() { this._controller = vscode.notebooks.createNotebookController(this.controllerId, this.notebookType, this.label); this._controller.supportedLanguages = this.supportedLanguages; this._controller.supportsExecutionOrder = true; this._controller.executeHandler = this._execute.bind(this); } private _execute(cells: vscode.NotebookCell[], _notebook: vscode.NotebookDocument, _controller: vscode.NotebookController): void { for (let cell of cells) { this._doExecution(cell); } } private async _doExecution(cell: vscode.NotebookCell): Promise<void> { const execution = this._controller.createNotebookCellExecution(cell); execution.executionOrder = ++this._executionOrder; execution.start(Date.now()); // 记录执行单元格的耗时 /* 在此实现真正的执行逻辑 */ execution.replaceOutput([new vscode.NotebookCellOutput([vscode.NotebookCellOutputItem.text('Dummy output text!')])]) execution.end(true, Date.now()); } }控制器开发要点:
supportedLanguages声明控制器支持的语言,只有匹配语言的代码单元格才会显示“运行”按钮;supportsExecutionOrder = true启用执行顺序跟踪;createNotebookCellExecution创建一次单元格执行,用start/end记录耗时、用replaceOutput写入输出,最后以execution.end(true, ...)标记成功结束;executeHandler是控制器唯一需要“填上”的核心逻辑入口。
如果你的控制器扩展与序列化器扩展分开发布,建议在package.json的keywords中加入形如notebookKernel<ViewTypeUpperCamelCased>的关键字——例如为github-issues笔记本类型提供替代内核时,添加notebookKernelGithubIssues。这能显著提升扩展在 VS Code 内打开对应类型笔记本时的可发现性。官方在 api/extension-guides/notebook.md 中给出的控制器样例包括:GitHub Issues Notebook 的查询执行控制器、REST Book 的 REST 查询控制器,以及 Regexper 笔记本的正则可视化控制器。
理解三种输出类型
执行单元格后,内核(kernel)通过输出把结果交还 UI。输出必须是以下三种格式之一,一次执行可产出多个输出并以列表展示:
文本输出(Text Output)——最简单的格式,与常见 REPL 体验一致,只有一个text字段,以纯文本渲染在单元格输出区:
vscode.NotebookCellOutputItem.text('This is the output...')错误输出(Error Output)——以一致、可读的方式展示运行时错误,直接支持标准Error对象:
try { /* Some code */ } catch (error) { vscode.NotebookCellOutputItem.error(error) }富输出(Rich Output)——最先进的输出形式,按 mimetype 提供同一数据的多种表示。例如一个表示 GitHub Issue 的单元格输出,其data字段可以同时包含:
text/html:格式化后的 Issue 视图;text/x-json:机器可读的视图;application/github-issue:可由NotebookRenderer消费、用于构建完全交互式 Issue 视图的字段。
其中text/html与text/x-json由 VS Code 原生渲染;而若没有注册对应 mimetype 的NotebookRenderer,application/github-issue视图将显示错误。构造富输出的示例:
execution.replaceOutput([new vscode.NotebookCellOutput([ vscode.NotebookCellOutputItem.text('<b>Hello</b> World', 'text/html'), vscode.NotebookCellOutputItem.json({ hello: 'world' }), vscode.NotebookCellOutputItem.json({ custom-data-for-custom-renderer: 'data' }, 'application/custom'), ])]);VS Code 核心默认能渲染的 mimetype 包括:application/javascript、text/html、image/svg+xml、text/markdown、image/png、image/jpeg、text/plain。此外,text/x-json、text/x-javascript、text/x-html、text/x-rust以及任何其他内置或已安装语言的text/x-LANGUAGE_ID会以内置代码编辑器方式渲染(例如在 Monaco 编辑器中展示 Rust 代码)。要渲染其他 mimetype,就必须为该 mimetype 注册NotebookRenderer。
编写渲染器:从静态 HTML 到交互式小程序
渲染器通过package.json的contributes.notebookRenderer为一系列 mimetype 声明。下面的渲染器处理ms-vscode.github-issue-notebook/github-issue格式的输入(假设某个已安装的控制器能提供该数据):
{ "activationEvents": ["...."], "contributes": { ... "notebookRenderer": [ { "id": "github-issue-renderer", "displayName": "GitHub Issue Renderer", "entrypoint": "./out/renderer.js", "mimeTypes": [ "ms-vscode.github-issue-notebook/github-issue" ] } ] } }运行机制:所有输出渲染器都运行在独立的iframe中,与 VS Code 其余 UI 隔离,避免意外干扰或拖慢 VS Code。entrypoint指向一个会被加载进笔记本iframe的脚本,它必须是单个文件——可以手写,也可以用 Webpack、Rollup 或 Parcel 等打包器生成。加载后,脚本从vscode-notebook-renderer导出ActivationFunction来渲染 UI,例如把所有 GitHub Issue 数据以 JSON 形式放进单元格输出:
import type { ActivationFunction } from 'vscode-notebook-renderer'; export const activate: ActivationFunction = (context) => ({ renderOutputItem(data, element) { element.innerText = JSON.stringify(data.json()) } })如果使用 TypeScript,可安装@types/vscode-notebook-renderer并在tsconfig.json的types数组中加入vscode-notebook-renderer以获得类型支持。
快速起步:可以使用 Yeoman 生成器的样板代码,先全局安装yo与 VS Code 生成器:
npm install -g yo generator-code然后运行yo code并选择New Notebook Renderer (TypeScript)。若不使用该模板,请记得在扩展package.json的keywords中添加notebookRenderer,并在扩展名或描述中提及 mimetype,便于用户发现。
渲染更丰富的内容:可以手动创建 DOM 元素,或使用 Preact 之类的框架渲染到输出元素中,例如渲染一个 GitHub Issue 卡片列表:
import type { ActivationFunction } from 'vscode-notebook-renderer'; import { h, render } from 'preact'; const Issue: FunctionComponent<{ issue: GithubIssue }> = ({ issue }) => ( <div key={issue.number}> <h2> {issue.title} (<a href={`https://github.com/${issue.repo}/issues/${issue.number}`}>#{issue.number}</a>) </h2> <img src={issue.user.avatar_url} style={{ float: 'left', width: 32, borderRadius: '50%', marginRight: 20 }} /> <i>@{issue.user.login}</i> Opened: <div style="margin-top: 10px">{issue.body}</div> </div> ); const GithubIssues: FunctionComponent<{ issues: GithubIssue[]; }> = ({ issues }) => ( <div>{issues.map(issue => <Issue key={issue.number} issue={issue} />)}</div> ); export const activate: ActivationFunction = (context) => ({ renderOutputItem(data, element) { render(<GithubIssues issues={data.json()} />, element); } });清理资源:如果渲染涉及容器外的元素或异步进程,应实现disposeOutputItem进行拆除——它在输出被清空、单元格被删除、以及为既有单元格渲染新输出之前触发:
const intervals = new Map(); export const activate: ActivationFunction = (context) => ({ renderOutputItem(data, element) { render(<GithubIssues issues={data.json()} />, element); intervals.set(data.mime, setInterval(() => { if(element.querySelector('h2')) { element.querySelector('h2')!.style.color = `hsl(${Math.random() * 360}, 100%, 50%)`; } }, 1000)); }, disposeOutputItem(id) { clearInterval(intervals.get(id)); intervals.delete(id); } });务必注意作用域:笔记本的所有输出都渲染在同一个iframe的不同元素中。若使用document.querySelector之类的全局查询,请务必限定到你关心的具体输出(如上例用element.querySelector),避免与其他输出互相干扰。
交互式渲染器:与控制器通信
设想要在渲染输出中点击按钮后查看一个 Issue 的评论。如果控制器在ms-vscode.github-issue-notebook/github-issue-with-commentsmimetype 下提供带评论的数据,一个直观但低效的做法是预先加载所有评论、用本地状态切换显示。这有两个问题:一是未点击按钮也加载了全部评论数据;二是为展示更多数据而要求控制器支持一个全新的 mimetype。
更优的方案是预加载脚本(preload script):控制器可以提供一段 preload 脚本,VS Code 会将其一并加载进iframe。该脚本可访问全局函数postKernelMessage与onDidReceiveKernelMessage,用于与控制器通信。
首先在控制器中把脚本推入rendererScripts:
class Controller { // ... readonly rendererScriptId = 'my-renderer-script'; constructor() { // ... this._controller.rendererScripts.push(new vscode.NotebookRendererScript(vscode.Uri.file(/* path to script */), rendererScriptId)); } }在package.json中把脚本声明为渲染器的依赖:
{ "activationEvents": ["...."], "contributes": { ... "notebookRenderer": [ { "id": "github-issue-renderer", "displayName": "GitHub Issue Renderer", "entrypoint": "./out/renderer.js", "mimeTypes": [...], "dependencies": [ "my-renderer-script" ] } ] } }在脚本文件中声明与控制器通信的函数:
import "vscode-notebook-renderer/preload"; globalThis.githubIssueCommentProvider = { loadComments(issueId: string, callback: (comments: GithubComment[]) => void) { postKernelMessage({ command: 'comments', issueId }); onDidReceiveKernelMessage(event => { if (event.data.type === 'comments' && event.data.issueId === issueId) { callback(event.data.comments); } }) } };随后在渲染器中消费该全局。注意应先检查控制器渲染脚本暴露的全局是否可用——其他开发者可能在其他笔记本/控制器中创建 GitHub Issue 输出,而这些控制器未必实现githubIssueCommentProvider。本例中只有全局可用时才显示Load Comments按钮:
const canLoadComments = globalThis.githubIssueCommentProvider !== undefined; const Issue: FunctionComponent<{ issue: GithubIssue }> = ({ issue }) => { const [comments, setComments] = useState([]); const loadComments = () => globalThis.githubIssueCommentProvider.loadComments(issue.id, setComments); return ( <div key={issue.number}> <h2> {issue.title} (<a href={`https://github.com/${issue.repo}/issues/${issue.number}`}>#{issue.number}</a>) </h2> <img src={issue.user.avatar_url} style={{ float: 'left', width: 32, borderRadius: '50%', marginRight: 20 }} /> <i>@{issue.user.login}</i> Opened: <div style="margin-top: 10px">{issue.body}</div> {canLoadComments && <button onClick={loadComments}>Load Comments</button>} {comments.map(comment => <div>{comment.text}</div>)} </div> ); };最后,控制器侧监听消息:NotebookController.onDidReceiveMessage在渲染器通过全局postKernelMessage发送消息时被调用:
class Controller { // ... constructor() { // ... this._controller.onDidReceiveMessage(event => { if (event.message.command === 'comments') { _getCommentsForIssue(event.message.issueId).then(comments => this._controller.postMessage({ type: 'comments', issueId: event.message.issueId, comments, }), event.editor); } }) } }交互式渲染器:与扩展宿主通信
另一种交互场景是让渲染器把输出项发送到扩展宿主(extension host),由扩展宿主打开独立编辑器——这在渲染器与控制器是两个独立扩展时尤其有用。
在渲染器扩展的package.json中,把requiresMessaging设为optional,使渲染器在“有无扩展宿主”两种场景下都能工作:
{ "activationEvents": ["...."], "contributes": { ... "notebookRenderer": [ { "id": "output-editor-renderer", "displayName": "Output Editor Renderer", "entrypoint": "./out/renderer.js", "mimeTypes": [...], "requiresMessaging": "optional" } ] } }requiresMessaging的取值与语义如下:
| 取值 | 含义 |
|---|---|
always | 必须使用消息机制;渲染器仅在所属扩展可运行于扩展宿主时才会被使用 |
optional | 有扩展宿主时体验更佳,但不强制要求即可安装运行渲染器 |
never | 渲染器不需要消息机制 |
后两个取值更受推荐,因为它们保证渲染器扩展可以移植到扩展宿主不一定可用的其他上下文(如 Web 场景)。
渲染器脚本中按如下方式建立通信(通信可用性取决于requiresMessaging的值):
import { ActivationFunction } from 'vscode-notebook-renderer'; export const activate: ActivationFunction = (context) => ({ renderOutputItem(data, element) { // 使用输出 data 渲染输出 .... // 消息机制是否可用取决于 requiresMessaging 的值 if (!context.postMessage){ return; } // 在输出中的某用户操作(如点击按钮)时,向扩展宿主发送打开编辑器的请求 document.querySelector('#openEditor').addEventListener('click', () => { context.postMessage({ request: 'showEditor', data: '<custom data>' }) }); } });扩展宿主侧消费消息:
const messageChannel = notebooks.createRendererMessaging('output-editor-renderer'); messageChannel.onDidReceiveMessage((e) => { if (e.message.request === 'showEditor'){ // 为 e.message.data 标识的输出启动编辑器 } });两点注意事项:
- 为确保消息投递前扩展已运行于扩展宿主,请把
onRenderer:<你的渲染器 id>加入activationEvents,并在扩展的activate函数中建立通信; - 并非所有从渲染器发往扩展宿主的消息都能保证送达——用户可能在消息送达前就关闭了笔记本。
为笔记本控制器补充调试能力
对于实现了编程语言的控制器,通常还希望支持调试单元格执行。Notebook 内核可以通过实现 debug adapter 来支持调试,途径有三:
- 直接实现 调试适配器协议(DAP);
- 委托并转换协议给现有笔记本调试器(如
vscode-simple-jupyter-notebook示例的做法); - 更简单的方式:复用现有未修改的调试扩展,在运行时把 DAP 转换为笔记本所需(如
vscode-nodebook的做法)。
官方提供的两个参考样例:vscode-nodebook(Node.js 笔记本,调试能力由 VS Code 内置 JavaScript 调试器配合简单协议转换提供)与vscode-simple-jupyter-notebook(Jupyter 笔记本,调试能力由现有 Xeus 调试器提供),均可在 api/extension-guides/notebook.md 末尾找到链接。
动手实践与社区参与
观看官方编码教程
VS Code 团队数月前录制了一场关于自定义笔记本的直播,作者在直播中以 live coding 演示了创建自定义笔记本扩展的全过程,视频题为“VS Code Notebooks: A Deep Dive”(可搜索该标题观看)。注意:视频发布后部分 Notebook API 已有变化,但核心原则保持一致。
当你准备构建自己的笔记本扩展时,最完整、最新的信息以 Notebook 扩展作者指南为准,其中包含完整的 Serializer、Controller、输出类型与 Renderer 代码示例。
向社区分享你的想法
如果你对创建笔记本扩展本身不感兴趣,但有实用的应用点子,欢迎在 VS Code 官方 Twitter 账号 @code 上发推讨论,或在 VS Code 的 GitHub 仓库中创建 issue。这样 VS Code 社区就能读到并讨论你的笔记本创意,并有望启发开发者把你的想法变成现实。
结语:自定义笔记本只是开始
本文介绍的 GitHub Issues Notebook 与 REST Book 只是自定义笔记本生态的开端。核心要点可以总结为三条:
- Notebook 已成为 VS Code 核心能力,通过公开的 Notebook API,任何扩展作者都能为任意领域打造“迭代 + 叙事”的笔记本体验,且无需自建 UI;
- 三大组件各司其职:Serializer 负责文件格式读写、Controller 负责执行代码、Renderer 负责渲染富输出;三者通过
contributes.notebooks、contributes.notebookRenderer声明与vscode.workspace.registerNotebookSerializer、vscode.notebooks.createNotebookController注册串联; - 两条官方样例可复刻:GitHub Issues Notebook 展示“查询语言 + 变量 + 富输出”的完整闭环,REST Book 展示“语言 API 做高亮补全、Notebook API 做执行”的极简组合。
无论你想为数据科学、API 测试、Issue 管理还是其他领域构建专属笔记本,都可以从 Notebook 扩展作者指南与本文的两个示例起步。Happy Coding!
- 文档
- 教程
【免费下载链接】vscode-docs
Public documentation for Visual Studio Code
相关推荐
VS Code Notebook API 扩展开发指南:Serializer、Controller 与 Renderer 全解析
VS Code Notebook API 扩展开发指南:Serializer、Controller 与 Renderer 全解析 Notebook API 允许
文档教程Apache Zeppelin Notebook REST API 完全指南
Apache Zeppelin Notebook REST API 完全指南 概述 Apache Zeppelin 提供了一个功能强大的 REST API,允许
数据分析数据可视化大数据后端前端任务调度如何永久保存微信聊天记录?这款免费工具让你真正拥有自己的数字记忆
如何永久保存微信聊天记录?这款免费工具让你真正拥有自己的数字记忆 你是否曾有过这样的遗憾:那些与家人朋友的珍贵对话、工作中的重要讨论、生活中的美好瞬间,随着手机
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考