ist jetzt verfügbar! Lesen Sie über die neuen Funktionen und Fehlerbehebungen vom November.

Notebook API

Die Notebook-API ermöglicht es Visual Studio Code-Erweiterungen, Dateien als Notebooks zu öffnen, Notebook-Codezellen auszuführen und Notebook-Ausgaben in einer Vielzahl von reichhaltigen und interaktiven Formaten zu rendern. Möglicherweise kennen Sie beliebte Notebook-Schnittstellen wie Jupyter Notebook oder Google Colab – die Notebook-API ermöglicht ähnliche Erfahrungen innerhalb von Visual Studio Code.

Teile eines Notebooks

Ein Notebook besteht aus einer Sequenz von Zellen und deren Ausgaben. Die Zellen eines Notebooks können entweder Markdown-Zellen oder Codezellen sein und werden im Kern von VS Code gerendert. Die Ausgaben können in verschiedenen Formaten vorliegen. Einige Ausgabeformate wie einfacher Text, JSON, Bilder und HTML werden vom VS Code-Kern gerendert. Andere, wie anwendungsspezifische Daten oder interaktive Applets, werden von Erweiterungen gerendert.

Zellen in einem Notebook werden von einem NotebookSerializer gelesen und in das Dateisystem geschrieben. Dieser ist dafür zuständig, Daten aus dem Dateisystem zu lesen und sie in eine Beschreibung von Zellen umzuwandeln, sowie Änderungen am Notebook zurück in das Dateisystem zu speichern. Die Codezellen eines Notebooks können von einem NotebookController ausgeführt werden, der den Inhalt einer Zelle nimmt und daraus null oder mehr Ausgaben in einer Vielzahl von Formaten erzeugt, die von einfachem Text bis zu formatierten Dokumenten oder interaktiven Applets reichen. Anwendungs spezifische Ausgabeformate und interaktive Applet-Ausgaben werden von einem NotebookRenderer gerendert.

Visuell

Overview of 3 components of notebooks: NotebookSerializer, NotebookController, and NotebookRenderer, and how they interact. Described textually above and in following sections.

Serializer

NotebookSerializer API-Referenz

Ein NotebookSerializer ist dafür verantwortlich, die serialisierten Bytes eines Notebooks zu nehmen und diese Bytes in NotebookData zu deserialisieren, welche eine Liste von Markdown- und Codezellen enthält. Er ist auch für die umgekehrte Konvertierung zuständig: NotebookData nehmen und die Daten in serialisierte Bytes umwandeln, die gespeichert werden sollen.

Beispiele

  • JSON Notebook Serializer: Einfaches Beispiel-Notebook, das JSON-Eingaben nimmt und aufbereitete JSON-Ausgaben in einem benutzerdefinierten NotebookRenderer liefert.
  • Markdown Serializer: Öffnen und Bearbeiten von Markdown-Dateien als Notebook.

Beispiel

In diesem Beispiel erstellen wir eine vereinfachte Notebook-Provider-Erweiterung zum Anzeigen von Dateien im Jupyter Notebook-Format mit der Erweiterung .notebook (anstelle seiner traditionellen Dateierweiterung .ipynb).

Ein Notebook-Serializer wird in package.json unter dem Abschnitt contributes.notebooks wie folgt deklariert

{
    ...
    "contributes": {
        ...
        "notebooks": [
            {
                "type": "my-notebook",
                "displayName": "My Notebook",
                "selector": [
                    {
                        "filenamePattern": "*.notebook"
                    }
                ]
            }
        ]
    }
}

Der Notebook-Serializer wird dann im Aktivierungsereignis der Erweiterung registriert

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));
  }
}

Versuchen Sie nun, Ihre Erweiterung auszuführen und eine im Jupyter Notebook-Format gespeicherte Datei mit der Erweiterung .notebook zu öffnen

Notebook showing contents of a Jupyter Notebook formatted file

Sie sollten in der Lage sein, Jupyter-formatierte Notebooks zu öffnen und ihre Zellen sowohl als einfachen Text als auch als gerendertes Markdown anzuzeigen und die Zellen zu bearbeiten. Ausgaben werden jedoch nicht auf der Festplatte gespeichert. Um Ausgaben zu speichern, müssten Sie auch die Ausgaben von Zellen aus NotebookData serialisieren und deserialisieren.

Um eine Zelle auszuführen, müssen Sie einen NotebookController implementieren.

Controller

NotebookController API-Referenz

Ein NotebookController ist dafür verantwortlich, eine Codezelle zu nehmen und den Code auszuführen, um Ausgaben zu erzeugen oder eben keine.

Ein Controller ist direkt mit einem Notebook-Serializer und einem Notebook-Typ verknüpft, indem die Eigenschaft NotebookController#notebookType bei der Erstellung des Controllers gesetzt wird. Anschließend wird der Controller global registriert, indem der Controller beim Aktivieren der Erweiterung zu den Erweiterungsabonnements hinzugefügt wird.

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()); // Keep track of elapsed time to execute cell.

    /* Do some execution here; not implemented */

    execution.replaceOutput([
      new vscode.NotebookCellOutput([
        vscode.NotebookCellOutputItem.text('Dummy output text!')
      ])
    ]);
    execution.end(true, Date.now());
  }
}

Wenn Sie eine NotebookController-bereitstellende Erweiterung separat von ihrem Serializer veröffentlichen, fügen Sie einen Eintrag wie notebookKernel<ViewTypeUpperCamelCased> zu den keywords in seiner package.json hinzu. Wenn Sie beispielsweise einen alternativen Kernel für den Notebook-Typ github-issues veröffentlichen, sollten Sie Ihrem Erweiterung ein Schlüsselwort notebookKernelGithubIssues hinzufügen. Dies verbessert die Auffindbarkeit der Erweiterung beim Öffnen von Notebooks des Typs <ViewTypeUpperCamelCased> innerhalb von Visual Studio Code.

Beispiele

Ausgabetypen

Ausgaben müssen eines von drei Formaten haben: Textausgabe, Fehlerausgabe oder Rich-Ausgabe. Ein Kernel kann mehrere Ausgaben für eine einzelne Ausführung einer Zelle liefern, in diesem Fall werden sie als Liste angezeigt.

Einfache Formate wie Textausgabe, Fehlerausgabe oder "einfache" Varianten von Rich-Ausgabe (HTML, Markdown, JSON usw.) werden vom VS Code-Kern gerendert, während anwendungsspezifische Rich-Ausgabe-Typen von einem NotebookRenderer gerendert werden. Eine Erweiterung kann optional "einfache" Rich-Ausgaben selbst rendern, um beispielsweise LaTeX-Unterstützung für Markdown-Ausgaben hinzuzufügen.

Diagram of the different output types described above

Textausgabe

Textausgaben sind das einfachste Ausgabeformat und funktionieren ähnlich wie viele REPLs, die Sie vielleicht kennen. Sie bestehen nur aus einem text-Feld, das als einfacher Text im Ausgabeelement der Zelle gerendert wird.

vscode.NotebookCellOutputItem.text('This is the output...');

Cell with simple text output

Fehlerausgabe

Fehlerausgaben sind hilfreich, um Laufzeitfehler konsistent und verständlich anzuzeigen. Sie unterstützen Standard-Error-Objekte.

try {
  /* Some code */
} catch (error) {
  vscode.NotebookCellOutputItem.error(error);
}

Cell with error output showing error name and message, as well as a stack trace with magenta text

Rich-Ausgabe

Rich-Ausgaben sind die fortschrittlichste Form der Anzeige von Zellenausgaben. Sie ermöglichen die Bereitstellung vieler verschiedener Darstellungen der Ausgabedaten, die durch Mimetypen indiziert werden. Wenn beispielsweise eine Zellenausgabe ein GitHub Issue darstellen soll, könnte der Kernel eine Rich-Ausgabe mit mehreren Eigenschaften im data-Feld erzeugen

  • Ein text/html-Feld, das eine formatierte Ansicht des Issues enthält.
  • Ein text/x-json-Feld, das eine maschinenlesbare Ansicht enthält.
  • Ein application/github-issue-Feld, das ein NotebookRenderer zur Erstellung einer vollständig interaktiven Ansicht des Issues verwenden könnte.

In diesem Fall werden die text/html- und text/x-json-Ansichten nativ von VS Code gerendert, aber die Ansicht application/github-issue zeigt einen Fehler an, wenn kein NotebookRenderer für diesen Mimetyp registriert wurde.

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'),
                        ])]);

Cell with rich output showing switching between formatted HTML, a JSON editor, and an error message showing no renderer is available (application/hello-world)

Standardmäßig kann VS Code die folgenden Mimetypen rendern

  • application/javascript
  • text/html
  • image/svg+xml
  • text/markdown
  • image/png
  • image/jpeg
  • text/plain

VS Code rendert diese Mimetypen als Code in einem integrierten Editor

  • text/x-json
  • text/x-javascript
  • text/x-html
  • text/x-rust
  • ... text/x-LANGUAGE_ID für alle anderen integrierten oder installierten Sprachen.

Dieses Notebook verwendet den integrierten Editor, um Rust-Code anzuzeigen: Notebook zeigt Rust-Code in einem integrierten Monaco-Editor

Um einen alternativen Mimetype zu rendern, muss ein NotebookRenderer für diesen Mimetype registriert werden.

Notebook Renderer

Ein Notebook-Renderer ist dafür verantwortlich, Ausgabedaten eines bestimmten Mimetype zu nehmen und eine gerenderte Ansicht dieser Daten bereitzustellen. Ein von Ausgabezellen geteilter Renderer kann einen globalen Zustand zwischen diesen Zellen aufrechterhalten. Die Komplexität der gerenderten Ansicht kann von einfachem statischem HTML bis zu dynamischen, vollständig interaktiven Applets reichen. In diesem Abschnitt werden wir verschiedene Techniken zum Rendern einer Ausgabe untersuchen, die ein GitHub Issue repräsentiert.

Sie können schnell mit Boilerplate aus unseren Yeoman-Generatoren beginnen. Installieren Sie dazu zunächst Yeoman und die VS Code-Generatoren mit

npm install -g yo generator-code

Führen Sie dann yo code aus und wählen Sie New Notebook Renderer (TypeScript).

Wenn Sie diese Vorlage nicht verwenden, stellen Sie sicher, dass Sie notebookRenderer zu den keywords in der package.json Ihrer Erweiterung hinzufügen und seinen Mimetype irgendwo im Erweiterungsnamen oder in der Beschreibung erwähnen, damit Benutzer Ihren Renderer finden können.

Ein einfacher, nicht interaktiver Renderer

Renderer werden für eine Reihe von Mimetypen deklariert, indem sie zur Eigenschaft contributes.notebookRenderer der package.json einer Erweiterung beitragen. Dieser Renderer funktioniert mit Eingaben im Format ms-vscode.github-issue-notebook/github-issue, das, wie wir annehmen, ein installierter Controller bereitstellen kann

{
  "activationEvents": ["...."],
  "contributes": {
    ...
    "notebookRenderer": [
      {
        "id": "github-issue-renderer",
        "displayName": "GitHub Issue Renderer",
        "entrypoint": "./out/renderer.js",
        "mimeTypes": [
          "ms-vscode.github-issue-notebook/github-issue"
        ]
      }
    ]
  }
}

Ausgaberenderer werden immer in einem einzigen iframe gerendert, getrennt vom Rest der VS Code-Benutzeroberfläche, um sicherzustellen, dass sie VS Code nicht versehentlich beeinträchtigen oder verlangsamen. Der Beitrag verweist auf ein "Einstiegspunkt"-Skript, das in den iframe des Notebooks geladen wird, kurz bevor eine Ausgabe gerendert werden muss. Ihr Einstiegspunkt muss eine einzelne Datei sein, die Sie selbst schreiben oder mit einem Bundler wie Webpack, Rollup oder Parcel erstellen können.

Wenn es geladen wird, sollte Ihr Einstiegspunkt-Skript ActivationFunction von vscode-notebook-renderer exportieren, um Ihre Benutzeroberfläche zu rendern, sobald VS Code bereit ist, Ihren Renderer zu rendern. Zum Beispiel wird dies alle Ihre GitHub Issue-Daten als JSON in die Zellenausgabe einfügen

import type { ActivationFunction } from 'vscode-notebook-renderer';

export const activate: ActivationFunction = context => ({
  renderOutputItem(data, element) {
    element.innerText = JSON.stringify(data.json());
  }
});

Sie können die vollständige API-Definition hier einsehen. Wenn Sie TypeScript verwenden, können Sie @types/vscode-notebook-renderer installieren und dann vscode-notebook-renderer zur types-Array in Ihrer tsconfig.json hinzufügen, um diese Typen in Ihrem Code verfügbar zu machen.

Um reichhaltigere Inhalte zu erstellen, könnten Sie DOM-Elemente manuell erstellen oder ein Framework wie Preact verwenden und es in das Ausgabeelement rendern, zum Beispiel

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);
    }
});

Wenn wir diesen Renderer auf eine Ausgabezelle mit einem ms-vscode.github-issue-notebook/github-issue-Datenfeld anwenden, erhalten wir die folgende statische HTML-Ansicht

Cell output showing rendered HTML view of issue

Wenn Sie Elemente außerhalb des Containers oder andere asynchrone Prozesse haben, können Sie disposeOutputItem verwenden, um diese abzubrechen. Dieses Ereignis wird ausgelöst, wenn die Ausgabe gelöscht wird, eine Zelle gelöscht wird und bevor neue Ausgaben für eine vorhandene Zelle gerendert werden. Zum Beispiel

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);
    }
});

Es ist wichtig zu bedenken, dass alle Ausgaben eines Notebooks in verschiedenen Elementen im selben iframe gerendert werden. Wenn Sie Funktionen wie document.querySelector verwenden, stellen Sie sicher, dass Sie sie auf die spezifische Ausgabe beschränken, an der Sie interessiert sind, um Konflikte mit anderen Ausgaben zu vermeiden. In diesem Beispiel verwenden wir element.querySelector, um dieses Problem zu vermeiden.

Interaktive Notebooks (Kommunikation mit dem Controller)

Stellen Sie sich vor, wir möchten die Möglichkeit hinzufügen, die Kommentare eines Issues nach dem Klicken auf eine Schaltfläche in der gerenderten Ausgabe anzuzeigen. Unter der Annahme, dass ein Controller Issue-Daten mit Kommentaren unter dem Mimetype ms-vscode.github-issue-notebook/github-issue-with-comments bereitstellen kann, könnten wir versuchen, alle Kommentare im Voraus zu laden und sie wie folgt zu implementieren

const Issue: FunctionComponent<{ issue: GithubIssueWithComments }> = ({ issue }) => {
  const [showComments, setShowComments] = useState(false);

  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>
      <button onClick={() => setShowComments(true)}>Show Comments</button>
      {showComments && issue.comments.map(comment => <div>{comment.text}</div>)}
    </div>
  );
};

Dies wirft sofort einige Bedenken auf. Zum einen laden wir die vollständigen Kommentardaten für alle Issues, noch bevor wir auf die Schaltfläche klicken. Außerdem benötigen wir Controller-Unterstützung für einen völlig anderen Mimetype, obwohl wir nur ein paar zusätzliche Daten anzeigen möchten.

Stattdessen kann der Controller dem Renderer zusätzliche Funktionalität zur Verfügung stellen, indem er ein Preload-Skript enthält, das VS Code ebenfalls in den iframe lädt. Dieses Skript hat Zugriff auf die globalen Funktionen postKernelMessage und onDidReceiveKernelMessage, die zur Kommunikation mit dem Controller verwendet werden können.

Diagram showing how controllers interact with renderers through the NotebookRendererScript

Zum Beispiel könnten Sie die rendererScripts Ihres Controllers ändern, um auf eine neue Datei zu verweisen, in der Sie eine Verbindung zurück zum Extension Host herstellen und ein globales Kommunikationsskript für den Renderer zur Verfügung stellen.

In Ihrem Controller

class Controller {
  // ...

  readonly rendererScriptId = 'my-renderer-script';

  constructor() {
    // ...

    this._controller.rendererScripts.push(
      new vscode.NotebookRendererScript(
        vscode.Uri.file(/* path to script */),
        rendererScriptId
      )
    );
  }
}

Geben Sie in Ihrer package.json Ihr Skript als Abhängigkeit Ihres Renderers an

{
  "activationEvents": ["...."],
  "contributes": {
    ...
    "notebookRenderer": [
      {
        "id": "github-issue-renderer",
        "displayName": "GitHub Issue Renderer",
        "entrypoint": "./out/renderer.js",
        "mimeTypes": [...],
        "dependencies": [
            "my-renderer-script"
        ]
      }
    ]
  }
}

In Ihrer Skriptdatei können Sie Kommunikationsfunktionen deklarieren, um mit dem Controller zu kommunizieren

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);
      }
    });
  }
};

Und dann können Sie dies im Renderer verbrauchen. Sie sollten sicherstellen, dass Sie prüfen, ob das vom Controller-Rendering-Skript bereitgestellte Global verfügbar ist, da andere Entwickler GitHub Issue-Ausgaben in anderen Notebooks und Controllern erstellen könnten, die den githubIssueCommentProvider nicht implementieren. In diesem Fall zeigen wir die Schaltfläche Kommentare laden nur an, wenn das Global verfügbar ist

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>
  );
};

Schließlich richten wir die Kommunikation mit dem Controller ein. Die Methode NotebookController.onDidReceiveMessage wird aufgerufen, wenn ein Renderer eine Nachricht über die globale Funktion postKernelMessage sendet. Um diese Methode zu implementieren, hängen Sie sich an onDidReceiveMessage, um auf Nachrichten zu warten

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
        );
      }
    });
  }
}

Interaktive Notebooks (Kommunikation mit dem Extension Host)

Stellen Sie sich vor, wir möchten die Möglichkeit hinzufügen, das Ausgabeelement in einem separaten Editor zu öffnen. Um dies zu ermöglichen, muss der Renderer eine Nachricht an den Extension Host senden können, der dann den Editor startet.

Dies wäre nützlich in Szenarien, in denen der Renderer und der Controller zwei separate Erweiterungen sind.

Geben Sie in der package.json der Renderer-Erweiterung den Wert für requiresMessaging als optional an. Dies ermöglicht Ihrem Renderer sowohl in Situationen, in denen er Zugriff auf den Extension Host hat, als auch wenn nicht.

{
  "activationEvents": ["...."],
  "contributes": {
    ...
    "notebookRenderer": [
      {
        "id": "output-editor-renderer",
        "displayName": "Output Editor Renderer",
        "entrypoint": "./out/renderer.js",
        "mimeTypes": [...],
        "requiresMessaging": "optional"
      }
    ]
  }
}

Die möglichen Werte für requiresMessaging sind

  • always : Messaging ist erforderlich. Der Renderer wird nur verwendet, wenn er Teil einer Erweiterung ist, die in einem Extension Host ausgeführt werden kann.
  • optional: Der Renderer ist mit Messaging besser, wenn der Extension Host verfügbar ist, aber die Installation und Ausführung des Renderers ist nicht erforderlich.
  • never : Der Renderer benötigt kein Messaging.

Die letzten beiden Optionen werden bevorzugt, da dies die Portabilität von Renderer-Erweiterungen in andere Kontexte gewährleistet, in denen der Extension Host möglicherweise nicht unbedingt verfügbar ist.

Die Renderer-Skriptdatei kann die Kommunikation wie folgt einrichten

import { ActivationFunction } from 'vscode-notebook-renderer';

export const activate: ActivationFunction = (context) => ({
  renderOutputItem(data, element) {
    // Render the output using the output `data`
    ....
    // The availability of messaging depends on the value in `requiresMessaging`
    if (!context.postMessage){
      return;
    }

    // Upon some user action in the output (such as clicking a button),
    // send a message to the extension host requesting the launch of the editor.
    document.querySelector('#openEditor').addEventListener('click', () => {
      context.postMessage({
        request: 'showEditor',
        data: '<custom data>'
      })
    });
  }
});

Und dann können Sie dies im Extension Host wie folgt verbrauchen

const messageChannel = notebooks.createRendererMessaging('output-editor-renderer');
messageChannel.onDidReceiveMessage(e => {
  if (e.message.request === 'showEditor') {
    // Launch the editor for the output identified by `e.message.data`
  }
});

Hinweis

  • Um sicherzustellen, dass Ihre Erweiterung im Extension Host ausgeführt wird, bevor Nachrichten zugestellt werden, fügen Sie onRenderer:<your renderer id> zu Ihren activationEvents hinzu und richten Sie die Kommunikation in der activate-Funktion Ihrer Erweiterung ein.
  • Nicht alle vom Renderer an den Extension Host gesendeten Nachrichten können garantiert zugestellt werden. Ein Benutzer könnte das Notebook schließen, bevor Nachrichten vom Renderer zugestellt werden.

Unterstützung für das Debugging

Für einige Controller, wie z. B. diejenigen, die eine Programmiersprache implementieren, kann es wünschenswert sein, das Debugging der Ausführung einer Zelle zu ermöglichen. Um Debugging-Unterstützung hinzuzufügen, kann ein Notebook-Kernel einen Debug-Adapter implementieren, entweder durch direkte Implementierung des Debug-Adapter-Protokolls (DAP) oder durch Delegation und Transformation des Protokolls an einen vorhandenen Notebook-Debugger (wie im 'vscode-simple-jupyter-notebook'-Sample). Ein viel einfacherer Ansatz ist die Verwendung einer vorhandenen, unveränderten Debug-Erweiterung und die Transformation des DAP im laufenden Betrieb für Notebook-Anforderungen (wie in 'vscode-nodebook').

Beispiele

  • vscode-nodebook: Node.js Notebook mit Debugging-Unterstützung durch den integrierten JavaScript-Debugger von VS Code und einfache Protokolltransformationen
  • vscode-simple-jupyter-notebook: Jupyter Notebook mit Debugging-Unterstützung durch den vorhandenen Xeus-Debugger
© . This site is unofficial and not affiliated with Microsoft.