mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-28 12:33:35 +01:00
Test extension for notebook
This commit is contained in:
20
extensions/notebook-test/.vscode/launch.json
vendored
Normal file
20
extensions/notebook-test/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Run Extension",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/out/**/*.js"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
1
extensions/notebook-test/README.md
Normal file
1
extensions/notebook-test/README.md
Normal file
@@ -0,0 +1 @@
|
||||
# Notebook test
|
||||
20
extensions/notebook-test/extension.webpack.config.js
Normal file
20
extensions/notebook-test/extension.webpack.config.js
Normal file
@@ -0,0 +1,20 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
//@ts-check
|
||||
|
||||
'use strict';
|
||||
|
||||
const withDefaults = require('../shared.webpack.config');
|
||||
|
||||
module.exports = withDefaults({
|
||||
context: __dirname,
|
||||
resolve: {
|
||||
mainFields: ['module', 'main']
|
||||
},
|
||||
entry: {
|
||||
extension: './src/extension.ts',
|
||||
}
|
||||
});
|
||||
BIN
extensions/notebook-test/icon.png
Normal file
BIN
extensions/notebook-test/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
18
extensions/notebook-test/icon.svg
Normal file
18
extensions/notebook-test/icon.svg
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 23.0.6, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#C5C5C5;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M2,14.1l-1.1-1L0.9,3l1-1.1l12.1,0l1.1,1l0,10.1L14,14.1H2z M13.9,12.9V3.1H2.1v9.8H13.9z M8.9,12.1V7.9h4.2
|
||||
v4.2H8.9z M11.9,10.9V9.1h-1.8v1.8H11.9z M2.9,12.1v-1.2h4.2v1.2H2.9z M2.9,9.1V7.9h4.2v1.2H2.9z M2.9,7.1V3.9h10.2v3.2H2.9z
|
||||
M11.9,5.9V5.1H4.1v0.8H11.9z"/>
|
||||
<path d="M14,2l1,1v10l-1,1H2l-1-1V3l1-1H14 M2,13h12V3H2V13 M13,4v3H3V4H13 M4,6h8V5H4V6 M13,8v4H9V8H13 M10,11h2V9h-2V11 M7,8v1H3
|
||||
V8H7 M7,11v1H3v-1H7 M14.1,1.8L14.1,1.8H2H1.9L1.9,1.9l-1,1L0.8,2.9V3v10v0.1l0.1,0.1l1,1l0.1,0.1H2h12h0.1l0.1-0.1l1-1l0.1-0.1V13
|
||||
V3V2.9l-0.1-0.1L14.1,1.8L14.1,1.8L14.1,1.8z M2.2,3.2h11.6v9.6H2.2V3.2L2.2,3.2z M13.2,3.8H13H3H2.8V4v3v0.2H3h10h0.2V7V4V3.8
|
||||
L13.2,3.8z M4.2,5.2h7.6v0.6H4.2V5.2L4.2,5.2z M13.2,7.8H13H9H8.8V8v4v0.2H9h4h0.2V12V8V7.8L13.2,7.8z M10.2,9.2h1.6v1.6h-1.6V9.2
|
||||
L10.2,9.2z M7.2,7.8H7H3H2.8V8v1v0.2H3h4h0.2V9V8V7.8L7.2,7.8z M7.2,10.8H7H3H2.8V11v1v0.2H3h4h0.2V12v-1V10.8L7.2,10.8z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
46
extensions/notebook-test/package.json
Normal file
46
extensions/notebook-test/package.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"name": "notebook-test",
|
||||
"displayName": "Notebook Test",
|
||||
"description": "Notebook test for execution and outputs",
|
||||
"extensionKind": [
|
||||
"ui",
|
||||
"workspace"
|
||||
],
|
||||
"version": "1.0.0",
|
||||
"publisher": "vscode",
|
||||
"icon": "icon.png",
|
||||
"enableProposedApi": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"vscode": "^1.40.0"
|
||||
},
|
||||
"main": "./out/extension",
|
||||
"categories": [
|
||||
"Other"
|
||||
],
|
||||
"activationEvents": [
|
||||
"*"
|
||||
],
|
||||
"contributes": {
|
||||
"notebookProvider": [
|
||||
{
|
||||
"viewType": "jupyter",
|
||||
"displayName": "Jupyter",
|
||||
"selector": [
|
||||
{
|
||||
"filenamePattern": "*.test.ipynb"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"compile": "tsc -p ./",
|
||||
"watch": "tsc -watch -p ./"
|
||||
},
|
||||
"dependencies": {
|
||||
"vscode-extension-telemetry": "0.1.1",
|
||||
"vscode-nls": "^4.0.0",
|
||||
"typescript": "^3.6.4"
|
||||
}
|
||||
}
|
||||
14
extensions/notebook-test/src/extension.ts
Normal file
14
extensions/notebook-test/src/extension.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { NotebookProvider } from './notebookProvider';
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
console.log(context.extensionPath);
|
||||
|
||||
context.subscriptions.push(vscode.window.registerNotebookProvider('jupyter', new NotebookProvider()));
|
||||
}
|
||||
|
||||
156
extensions/notebook-test/src/notebookProvider.ts
Normal file
156
extensions/notebook-test/src/notebookProvider.ts
Normal file
@@ -0,0 +1,156 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export class Cell implements vscode.ICell {
|
||||
public outputs: any[] = [];
|
||||
|
||||
constructor(
|
||||
public source: string[],
|
||||
public cell_type: 'markdown' | 'code',
|
||||
private _outputs: any[]
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
fillInOutputs() {
|
||||
this.outputs = this._outputs;
|
||||
}
|
||||
}
|
||||
|
||||
export class JupyterNotebook implements vscode.INotebook {
|
||||
constructor(
|
||||
public metadata: vscode.IMetadata,
|
||||
public cells: Cell[]
|
||||
) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookProvider implements vscode.NotebookProvider {
|
||||
private _onDidChangeNotebook = new vscode.EventEmitter<{ resource: vscode.Uri; notebook: vscode.INotebook; }>();
|
||||
onDidChangeNotebook: vscode.Event<{ resource: vscode.Uri; notebook: vscode.INotebook; }> = this._onDidChangeNotebook.event;
|
||||
private _notebook: JupyterNotebook;
|
||||
private _notebooks: Map<vscode.Uri, JupyterNotebook> = new Map();
|
||||
|
||||
constructor() {
|
||||
this._notebook = new JupyterNotebook(
|
||||
{
|
||||
language_info: {
|
||||
file_extension: 'ipynb'
|
||||
}
|
||||
},
|
||||
[
|
||||
new Cell([
|
||||
'# header\n',
|
||||
'body\n'
|
||||
],
|
||||
'markdown',
|
||||
[]
|
||||
),
|
||||
new Cell([
|
||||
'print(a)',
|
||||
],
|
||||
'code',
|
||||
[
|
||||
{
|
||||
'output_type': 'stream',
|
||||
'name': 'stdout',
|
||||
'text': 'hi, stdout\n'
|
||||
}
|
||||
]
|
||||
),
|
||||
new Cell(
|
||||
[
|
||||
'import time, sys\n',
|
||||
'for i in range(8):\n',
|
||||
' print(i)\n',
|
||||
' time.sleep(0.5)'
|
||||
],
|
||||
'code',
|
||||
[
|
||||
{
|
||||
'name': 'stdout',
|
||||
'text': '0\n',
|
||||
'output_type': 'stream'
|
||||
},
|
||||
{
|
||||
'name': 'stdout',
|
||||
'text': '1\n',
|
||||
'output_type': 'stream'
|
||||
},
|
||||
{
|
||||
'name': 'stdout',
|
||||
'text': '2\n',
|
||||
'output_type': 'stream'
|
||||
},
|
||||
{
|
||||
'name': 'stdout',
|
||||
'text': '3\n',
|
||||
'output_type': 'stream'
|
||||
},
|
||||
{
|
||||
'name': 'stdout',
|
||||
'text': '4\n',
|
||||
'output_type': 'stream'
|
||||
},
|
||||
{
|
||||
'name': 'stdout',
|
||||
'text': '5\n',
|
||||
'output_type': 'stream'
|
||||
},
|
||||
{
|
||||
'name': 'stdout',
|
||||
'text': '6\n',
|
||||
'output_type': 'stream'
|
||||
},
|
||||
{
|
||||
'name': 'stdout',
|
||||
'text': '7\n',
|
||||
'output_type': 'stream'
|
||||
}
|
||||
]
|
||||
),
|
||||
new Cell(
|
||||
[
|
||||
'print(a + 4)'
|
||||
],
|
||||
'code',
|
||||
[
|
||||
{
|
||||
'output_type': 'error',
|
||||
'ename': 'NameError',
|
||||
'evalue': 'name \'a\' is not defined',
|
||||
'traceback': [
|
||||
'\u001b[0;31m---------------------------------------------------------------------------\u001b[0m',
|
||||
'\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)',
|
||||
'\u001b[0;32m<ipython-input-1-f270cadddfe4>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m4\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m',
|
||||
'\u001b[0;31mNameError\u001b[0m: name \'a\' is not defined'
|
||||
]
|
||||
}
|
||||
]
|
||||
)
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
async resolveNotebook(resource: vscode.Uri): Promise<vscode.INotebook | undefined> {
|
||||
if (this._notebooks.has(resource)) {
|
||||
return this._notebooks.get(resource);
|
||||
}
|
||||
|
||||
this._notebooks.set(resource, this._notebook);
|
||||
|
||||
return Promise.resolve(this._notebook);
|
||||
}
|
||||
|
||||
async executeNotebook(resource: vscode.Uri): Promise<void> {
|
||||
this._notebook.cells.forEach(cell => cell.fillInOutputs());
|
||||
|
||||
this._onDidChangeNotebook.fire({ resource, notebook: this._notebook });
|
||||
return;
|
||||
}
|
||||
}
|
||||
8
extensions/notebook-test/src/typings/ref.d.ts
vendored
Normal file
8
extensions/notebook-test/src/typings/ref.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/// <reference path='../../../../src/vs/vscode.d.ts'/>
|
||||
/// <reference path='../../../../src/vs/vscode.proposed.d.ts'/>
|
||||
/// <reference types='@types/node'/>
|
||||
10
extensions/notebook-test/tsconfig.json
Normal file
10
extensions/notebook-test/tsconfig.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "../shared.tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out",
|
||||
"experimentalDecorators": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
]
|
||||
}
|
||||
46
extensions/notebook-test/yarn.lock
Normal file
46
extensions/notebook-test/yarn.lock
Normal file
@@ -0,0 +1,46 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
applicationinsights@1.0.8:
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.8.tgz#db6e3d983cf9f9405fe1ee5ba30ac6e1914537b5"
|
||||
integrity sha512-KzOOGdphOS/lXWMFZe5440LUdFbrLpMvh2SaRxn7BmiI550KAoSb2gIhiq6kJZ9Ir3AxRRztjhzif+e5P5IXIg==
|
||||
dependencies:
|
||||
diagnostic-channel "0.2.0"
|
||||
diagnostic-channel-publishers "0.2.1"
|
||||
zone.js "0.7.6"
|
||||
|
||||
diagnostic-channel-publishers@0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz#8e2d607a8b6d79fe880b548bc58cc6beb288c4f3"
|
||||
integrity sha1-ji1geottef6IC1SLxYzGvrKIxPM=
|
||||
|
||||
diagnostic-channel@0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-0.2.0.tgz#cc99af9612c23fb1fff13612c72f2cbfaa8d5a17"
|
||||
integrity sha1-zJmvlhLCP7H/8TYSxy8sv6qNWhc=
|
||||
dependencies:
|
||||
semver "^5.3.0"
|
||||
|
||||
semver@^5.3.0:
|
||||
version "5.5.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
|
||||
integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==
|
||||
|
||||
vscode-extension-telemetry@0.1.1:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.1.tgz#91387e06b33400c57abd48979b0e790415ae110b"
|
||||
integrity sha512-TkKKG/B/J94DP5qf6xWB4YaqlhWDg6zbbqVx7Bz//stLQNnfE9XS1xm3f6fl24c5+bnEK0/wHgMgZYKIKxPeUA==
|
||||
dependencies:
|
||||
applicationinsights "1.0.8"
|
||||
|
||||
vscode-nls@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.0.0.tgz#4001c8a6caba5cedb23a9c5ce1090395c0e44002"
|
||||
integrity sha512-qCfdzcH+0LgQnBpZA53bA32kzp9rpq/f66Som577ObeuDlFIrtbEJ+A/+CCxjIh4G8dpJYNCKIsxpRAHIfsbNw==
|
||||
|
||||
zone.js@0.7.6:
|
||||
version "0.7.6"
|
||||
resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.7.6.tgz#fbbc39d3e0261d0986f1ba06306eb3aeb0d22009"
|
||||
integrity sha1-+7w50+AmHQmG8boGMG6zrrDSIAk=
|
||||
@@ -1447,6 +1447,85 @@ export interface IWebviewPanelOptions {
|
||||
readonly retainContextWhenHidden?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
||||
export interface INotebookSelectors {
|
||||
readonly filenamePattern?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface IStreamOutput {
|
||||
output_type: 'stream';
|
||||
text: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface IErrorOutput {
|
||||
output_type: 'error';
|
||||
evalue: string;
|
||||
traceback: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface IDisplayOutput {
|
||||
output_type: 'display_data';
|
||||
data: { string: string };
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface IGenericOutput {
|
||||
output_type: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export type IOutput = IStreamOutput | any;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface ICell {
|
||||
handle: number;
|
||||
source: string[];
|
||||
cell_type: 'markdown' | 'code';
|
||||
outputs: IOutput[];
|
||||
onDidChangeOutputs?: Event<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface LanguageInfo {
|
||||
file_extension: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface IMetadata {
|
||||
language_info: LanguageInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface INotebook {
|
||||
handle: number;
|
||||
metadata: IMetadata;
|
||||
cells: ICell[];
|
||||
onDidChangeCells?: Event<void>;
|
||||
}
|
||||
|
||||
export interface CodeLens {
|
||||
range: IRange;
|
||||
|
||||
58
src/vs/vscode.proposed.d.ts
vendored
58
src/vs/vscode.proposed.d.ts
vendored
@@ -1286,4 +1286,62 @@ declare module 'vscode' {
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Peng: Notebook
|
||||
|
||||
export interface IStreamOutput {
|
||||
output_type: 'stream';
|
||||
text: string;
|
||||
}
|
||||
|
||||
export interface IErrorOutput {
|
||||
output_type: 'error';
|
||||
evalue: string;
|
||||
traceback: string[];
|
||||
}
|
||||
|
||||
export interface IDisplayOutput {
|
||||
output_type: 'display_data';
|
||||
data: { string: string };
|
||||
}
|
||||
|
||||
export interface IGenericOutput {
|
||||
output_type: string;
|
||||
}
|
||||
|
||||
export type IOutput = IStreamOutput | any;
|
||||
|
||||
export interface ICell {
|
||||
source: string[];
|
||||
cell_type: 'markdown' | 'code';
|
||||
outputs: IOutput[];
|
||||
}
|
||||
|
||||
export interface LanguageInfo {
|
||||
file_extension: string;
|
||||
}
|
||||
|
||||
export interface IMetadata {
|
||||
language_info: LanguageInfo;
|
||||
}
|
||||
|
||||
export interface INotebook {
|
||||
metadata: IMetadata;
|
||||
cells: ICell[];
|
||||
}
|
||||
|
||||
export interface NotebookProvider {
|
||||
onDidChangeNotebook?: Event<{ resource: Uri, notebook: INotebook }>;
|
||||
resolveNotebook(resource: Uri): Promise<INotebook | undefined>;
|
||||
executeNotebook(resource: Uri): Promise<void>;
|
||||
}
|
||||
|
||||
namespace window {
|
||||
export function registerNotebookProvider(
|
||||
notebookType: string,
|
||||
provider: NotebookProvider
|
||||
): Disposable;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ import './mainThreadWindow';
|
||||
import './mainThreadWebview';
|
||||
import './mainThreadWorkspace';
|
||||
import './mainThreadComments';
|
||||
import './mainThreadNotebook';
|
||||
import './mainThreadTask';
|
||||
import './mainThreadLabelService';
|
||||
import 'vs/workbench/api/common/apiCommands';
|
||||
|
||||
142
src/vs/workbench/api/browser/mainThreadNotebook.ts
Normal file
142
src/vs/workbench/api/browser/mainThreadNotebook.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { MainContext, MainThreadNotebookShape, NotebookExtensionDescription, IExtHostContext, ExtHostNotebookShape, ExtHostContext } from '../common/extHost.protocol';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { INotebookService, IMainNotebookController } from 'vs/workbench/contrib/notebook/browser/notebookService';
|
||||
import { INotebook, IMetadata, ICell, IOutput } from 'vs/editor/common/modes';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
|
||||
export class MainThreadCell implements ICell {
|
||||
private _onDidChangeOutputs = new Emitter<void>();
|
||||
onDidChangeOutputs: Event<void> = this._onDidChangeOutputs.event;
|
||||
|
||||
private _outputs: IOutput[];
|
||||
|
||||
public get outputs(): IOutput[] {
|
||||
return this._outputs;
|
||||
}
|
||||
|
||||
public set outputs(newOutputs: IOutput[]) {
|
||||
this._outputs = newOutputs;
|
||||
this._onDidChangeOutputs.fire();
|
||||
}
|
||||
|
||||
constructor(
|
||||
public handle: number,
|
||||
public source: string[],
|
||||
public cell_type: 'markdown' | 'code',
|
||||
outputs: IOutput[]
|
||||
) {
|
||||
this._outputs = outputs;
|
||||
}
|
||||
}
|
||||
|
||||
export class MainThreadNotebook implements INotebook {
|
||||
|
||||
private readonly _onDidChangeCells = new Emitter<void>();
|
||||
get onDidChangeCells(): Event<void> { return this._onDidChangeCells.event; }
|
||||
private _mapping: Map<number, MainThreadCell> = new Map();
|
||||
public cells: MainThreadCell[];
|
||||
|
||||
constructor(
|
||||
public handle: number,
|
||||
public metadata: IMetadata,
|
||||
cells: ICell[]
|
||||
) {
|
||||
this.cells = [];
|
||||
cells.forEach(cell => {
|
||||
let mainCell = new MainThreadCell(cell.handle, cell.source, cell.cell_type, cell.outputs);
|
||||
this._mapping.set(cell.handle, mainCell);
|
||||
this.cells.push(mainCell);
|
||||
});
|
||||
}
|
||||
|
||||
updateCells(newCells: ICell[]) {
|
||||
// todo, handle cell insertion and deletion
|
||||
newCells.forEach(newCell => {
|
||||
let cell = this._mapping.get(newCell.handle);
|
||||
if (cell) {
|
||||
cell.outputs = newCell.outputs;
|
||||
}
|
||||
});
|
||||
|
||||
this._onDidChangeCells.fire();
|
||||
}
|
||||
}
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadNotebook)
|
||||
export class MainThreadNotebooks extends Disposable implements MainThreadNotebookShape {
|
||||
private readonly _notebookProviders = new Map<string, MainThreadNotebookController>();
|
||||
private readonly _proxy: ExtHostNotebookShape;
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@INotebookService private _notebookService: INotebookService
|
||||
) {
|
||||
super();
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostNotebook);
|
||||
}
|
||||
|
||||
$registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string): void {
|
||||
let controller = new MainThreadNotebookController(this);
|
||||
this._notebookProviders.set(viewType, controller);
|
||||
this._notebookService.registerNotebookController(viewType, controller);
|
||||
}
|
||||
|
||||
$unregisterNotebookProvider(viewType: string): void {
|
||||
this._notebookProviders.delete(viewType);
|
||||
this._notebookService.unregisterNotebookProvider(viewType);
|
||||
}
|
||||
|
||||
$updateNotebook(viewType: string, uri: URI, notebook: INotebook): void {
|
||||
let controller = this._notebookProviders.get(viewType);
|
||||
|
||||
if (controller) {
|
||||
controller.updateNotebook(uri, notebook);
|
||||
}
|
||||
}
|
||||
|
||||
resolveNotebook(viewType: string, uri: URI): Promise<INotebook | undefined> {
|
||||
return this._proxy.$resolveNotebook(viewType, uri);
|
||||
}
|
||||
|
||||
executeNotebook(viewType: string, uri: URI): Promise<void> {
|
||||
return this._proxy.$executeNotebook(viewType, uri);
|
||||
}
|
||||
}
|
||||
|
||||
export class MainThreadNotebookController implements IMainNotebookController {
|
||||
private _mapping: Map<string, MainThreadNotebook> = new Map();
|
||||
|
||||
constructor(
|
||||
private _mainThreadNotebook: MainThreadNotebooks
|
||||
) {
|
||||
}
|
||||
|
||||
async resolveNotebook(viewType: string, uri: URI): Promise<INotebook | undefined> {
|
||||
let notebook = await this._mainThreadNotebook.resolveNotebook(viewType, uri);
|
||||
if (notebook) {
|
||||
let mainthreadNotebook = new MainThreadNotebook(notebook.handle, notebook.metadata, notebook.cells);
|
||||
this._mapping.set(uri.toString(), mainthreadNotebook);
|
||||
return mainthreadNotebook;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async executeNotebook(viewType: string, uri: URI): Promise<void> {
|
||||
this._mainThreadNotebook.executeNotebook(viewType, uri);
|
||||
}
|
||||
|
||||
updateNotebook(uri: URI, notebook: INotebook): void {
|
||||
let mainthreadNotebook = this._mapping.get(URI.from(uri).toString());
|
||||
|
||||
if (mainthreadNotebook) {
|
||||
mainthreadNotebook.updateCells(notebook.cells);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,6 +67,7 @@ import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IURITransformerService } from 'vs/workbench/api/common/extHostUriTransformerService';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook';
|
||||
|
||||
export interface IExtensionApiFactory {
|
||||
(extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode;
|
||||
@@ -123,6 +124,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
const extHostWindow = rpcProtocol.set(ExtHostContext.ExtHostWindow, new ExtHostWindow(rpcProtocol));
|
||||
const extHostProgress = rpcProtocol.set(ExtHostContext.ExtHostProgress, new ExtHostProgress(rpcProtocol.getProxy(MainContext.MainThreadProgress)));
|
||||
const extHostLabelService = rpcProtocol.set(ExtHostContext.ExtHosLabelService, new ExtHostLabelService(rpcProtocol));
|
||||
const extHostNotebook = rpcProtocol.set(ExtHostContext.ExtHostNotebook, new ExtHostNotebookController(rpcProtocol));
|
||||
|
||||
// Check that no named customers are missing
|
||||
const expected: ProxyIdentifier<any>[] = values(ExtHostContext);
|
||||
@@ -550,6 +552,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
},
|
||||
createInputBox(): vscode.InputBox {
|
||||
return extHostQuickOpen.createInputBox(extension.identifier);
|
||||
},
|
||||
registerNotebookProvider: (viewType: string, provider: vscode.NotebookProvider) => {
|
||||
return extHostNotebook.registerNotebookProvider(extension, viewType, provider);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -556,6 +556,11 @@ export interface WebviewExtensionDescription {
|
||||
readonly location: UriComponents;
|
||||
}
|
||||
|
||||
export interface NotebookExtensionDescription {
|
||||
readonly id: ExtensionIdentifier;
|
||||
readonly location: UriComponents;
|
||||
}
|
||||
|
||||
export enum WebviewEditorCapabilities {
|
||||
Editable,
|
||||
}
|
||||
@@ -606,6 +611,12 @@ export interface ExtHostWebviewsShape {
|
||||
$onSaveAs(handle: WebviewPanelHandle, resource: UriComponents, targetResource: UriComponents): Promise<void>;
|
||||
}
|
||||
|
||||
export interface MainThreadNotebookShape extends IDisposable {
|
||||
$registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string): void;
|
||||
$unregisterNotebookProvider(viewType: string): void;
|
||||
$updateNotebook(viewType: string, resource: UriComponents, notebook: modes.INotebook): void;
|
||||
}
|
||||
|
||||
export interface MainThreadUrlsShape extends IDisposable {
|
||||
$registerUriHandler(handle: number, extensionId: ExtensionIdentifier): Promise<void>;
|
||||
$unregisterUriHandler(handle: number): Promise<void>;
|
||||
@@ -1387,6 +1398,11 @@ export interface ExtHostCommentsShape {
|
||||
$toggleReaction(commentControllerHandle: number, threadHandle: number, uri: UriComponents, comment: modes.Comment, reaction: modes.CommentReaction): Promise<void>;
|
||||
}
|
||||
|
||||
export interface ExtHostNotebookShape {
|
||||
$resolveNotebook(viewType: string, uri: URI): Promise<modes.INotebook | undefined>;
|
||||
$executeNotebook(viewType: string, uri: URI): Promise<void>;
|
||||
}
|
||||
|
||||
export interface ExtHostStorageShape {
|
||||
$acceptValue(shared: boolean, key: string, value: object | undefined): void;
|
||||
}
|
||||
@@ -1431,7 +1447,8 @@ export const MainContext = {
|
||||
MainThreadSearch: createMainId<MainThreadSearchShape>('MainThreadSearch'),
|
||||
MainThreadTask: createMainId<MainThreadTaskShape>('MainThreadTask'),
|
||||
MainThreadWindow: createMainId<MainThreadWindowShape>('MainThreadWindow'),
|
||||
MainThreadLabelService: createMainId<MainThreadLabelServiceShape>('MainThreadLabelService')
|
||||
MainThreadLabelService: createMainId<MainThreadLabelServiceShape>('MainThreadLabelService'),
|
||||
MainThreadNotebook: createMainId<MainThreadNotebookShape>('MainThreadNotebook')
|
||||
};
|
||||
|
||||
export const ExtHostContext = {
|
||||
@@ -1465,5 +1482,6 @@ export const ExtHostContext = {
|
||||
ExtHostStorage: createMainId<ExtHostStorageShape>('ExtHostStorage'),
|
||||
ExtHostUrls: createExtId<ExtHostUrlsShape>('ExtHostUrls'),
|
||||
ExtHostOutputService: createMainId<ExtHostOutputServiceShape>('ExtHostOutputService'),
|
||||
ExtHosLabelService: createMainId<ExtHostLabelServiceShape>('ExtHostLabelService')
|
||||
ExtHosLabelService: createMainId<ExtHostLabelServiceShape>('ExtHostLabelService'),
|
||||
ExtHostNotebook: createMainId<ExtHostNotebookShape>('ExtHostNotebook')
|
||||
};
|
||||
|
||||
127
src/vs/workbench/api/common/extHostNotebook.ts
Normal file
127
src/vs/workbench/api/common/extHostNotebook.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { ExtHostNotebookShape, IMainContext, MainThreadNotebookShape, MainContext } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { Disposable as VSCodeDisposable } from './extHostTypes';
|
||||
import { INotebook } from 'vs/editor/common/modes';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
|
||||
export class ExtHostCell implements vscode.ICell {
|
||||
private static _handlePool: number = 0;
|
||||
readonly handle = ExtHostCell._handlePool++;
|
||||
|
||||
source: string[];
|
||||
cell_type: 'markdown' | 'code';
|
||||
outputs: any[];
|
||||
|
||||
constructor(
|
||||
raw_cell: vscode.ICell
|
||||
) {
|
||||
this.source = raw_cell.source;
|
||||
this.cell_type = raw_cell.cell_type;
|
||||
this.outputs = raw_cell.outputs;
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtHostNotebook implements vscode.INotebook {
|
||||
private static _handlePool: number = 0;
|
||||
readonly handle = ExtHostNotebook._handlePool++;
|
||||
public cells: ExtHostCell[];
|
||||
public cellMapping: Map<vscode.ICell, ExtHostCell> = new Map();
|
||||
|
||||
constructor(
|
||||
public metadata: vscode.IMetadata,
|
||||
private raw_cells: vscode.ICell[]
|
||||
) {
|
||||
this.cells = raw_cells.map(cell => {
|
||||
let extHostCell = new ExtHostCell(cell);
|
||||
this.cellMapping.set(cell, extHostCell);
|
||||
return extHostCell;
|
||||
});
|
||||
}
|
||||
|
||||
updateCells(newCells: vscode.ICell[]) {
|
||||
newCells.forEach(cell => {
|
||||
let extHostCell = this.cellMapping.get(cell);
|
||||
|
||||
if (extHostCell) {
|
||||
extHostCell.outputs = cell.outputs;
|
||||
}
|
||||
});
|
||||
|
||||
// trigger update
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtHostNotebookController implements ExtHostNotebookShape {
|
||||
private readonly _proxy: MainThreadNotebookShape;
|
||||
private readonly _notebookProviders = new Map<string, { readonly provider: vscode.NotebookProvider, readonly extension: IExtensionDescription }>();
|
||||
private readonly _notebookCache = new Map<string, ExtHostNotebook>();
|
||||
private readonly _localStore = new DisposableStore();
|
||||
|
||||
constructor(mainContext: IMainContext) {
|
||||
this._proxy = mainContext.getProxy(MainContext.MainThreadNotebook);
|
||||
}
|
||||
|
||||
public registerNotebookProvider(
|
||||
extension: IExtensionDescription,
|
||||
viewType: string,
|
||||
provider: vscode.NotebookProvider,
|
||||
): vscode.Disposable {
|
||||
|
||||
if (this._notebookProviders.has(viewType)) {
|
||||
throw new Error(`Notebook provider for '${viewType}' already registered`);
|
||||
}
|
||||
|
||||
this._notebookProviders.set(viewType, { extension, provider });
|
||||
this._proxy.$registerNotebookProvider({ id: extension.identifier, location: extension.extensionLocation }, viewType);
|
||||
|
||||
if (provider.onDidChangeNotebook) {
|
||||
this._localStore.add(provider.onDidChangeNotebook!((e) => {
|
||||
let resource = e.resource;
|
||||
let notebook = e.notebook;
|
||||
|
||||
let cachedNotebook = this._notebookCache.get(resource.toString());
|
||||
|
||||
if (cachedNotebook) {
|
||||
cachedNotebook.updateCells(notebook.cells);
|
||||
|
||||
this._proxy.$updateNotebook(viewType, resource, cachedNotebook);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
return new VSCodeDisposable(() => {
|
||||
this._notebookProviders.delete(viewType);
|
||||
this._proxy.$unregisterNotebookProvider(viewType);
|
||||
});
|
||||
}
|
||||
|
||||
async $resolveNotebook(viewType: string, uri: URI): Promise<INotebook | undefined> {
|
||||
let provider = this._notebookProviders.get(viewType);
|
||||
|
||||
if (provider) {
|
||||
let notebook = await provider.provider.resolveNotebook(uri);
|
||||
if (notebook) {
|
||||
let extHostNotebook = new ExtHostNotebook(notebook.metadata, notebook.cells);
|
||||
this._notebookCache.set(uri.toString(), extHostNotebook);
|
||||
return extHostNotebook;
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
async $executeNotebook(viewType: string, uri: URI): Promise<void> {
|
||||
let provider = this._notebookProviders.get(viewType);
|
||||
|
||||
if (provider) {
|
||||
return provider.provider.executeNotebook(uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@
|
||||
import 'vs/css!./notebook';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { ICell } from 'vs/workbench/contrib/notebook/browser/notebookEditorInput';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget';
|
||||
@@ -34,6 +33,7 @@ import { Emitter } from 'vs/base/common/event';
|
||||
import { IWebviewService } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent';
|
||||
import * as UUID from 'vs/base/common/uuid';
|
||||
import { ICell } from 'vs/editor/common/modes';
|
||||
|
||||
export class ViewCell {
|
||||
private _textModel: ITextModel | null = null;
|
||||
@@ -47,6 +47,9 @@ export class ViewCell {
|
||||
protected readonly _onDidChangeEditingState = new Emitter<void>();
|
||||
readonly onDidChangeEditingState = this._onDidChangeEditingState.event;
|
||||
|
||||
protected readonly _onDidChangeOutputs = new Emitter<void>();
|
||||
readonly onDidChangeOutputs = this._onDidChangeOutputs.event;
|
||||
|
||||
get cellType() {
|
||||
return this.cell.cell_type;
|
||||
}
|
||||
@@ -77,6 +80,11 @@ export class ViewCell {
|
||||
private readonly modeService: IModeService
|
||||
) {
|
||||
this.id = UUID.generateUuid();
|
||||
if (this.cell.onDidChangeOutputs) {
|
||||
this.cell.onDidChangeOutputs(() => {
|
||||
this._onDidChangeOutputs.fire();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
hasDynamicHeight() {
|
||||
@@ -563,7 +571,54 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende
|
||||
this.showContextMenu(element, e.posx, top + height);
|
||||
});
|
||||
|
||||
this.disposables.set(templateData.cellContainer, listener);
|
||||
let rerenderOutput = element.onDidChangeOutputs(() => {
|
||||
if (element.outputs.length > 0) {
|
||||
let hasDynamicHeight = true;
|
||||
for (let i = 0; i < element.outputs.length; i++) {
|
||||
let result = MimeTypeRenderer.render(element.outputs[i], this.themeService, this.webviewService);
|
||||
if (result) {
|
||||
hasDynamicHeight = hasDynamicHeight || result?.hasDynamicHeight;
|
||||
templateData.outputContainer?.appendChild(result.element);
|
||||
if (result.shadowContent) {
|
||||
this.handler.createContentWidget(element, result.shadowContent, totalHeight + 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (height !== undefined && hasDynamicHeight) {
|
||||
let dimensions = DOM.getClientArea(templateData.outputContainer!);
|
||||
const elementSizeObserver = new ElementSizeObserver(templateData.outputContainer!, dimensions, () => {
|
||||
if (templateData.outputContainer && document.body.contains(templateData.outputContainer!)) {
|
||||
let height = elementSizeObserver.getHeight();
|
||||
if (dimensions.height !== height) {
|
||||
element.setDynamicHeight(totalHeight + 32 + height);
|
||||
this.handler.layoutElement(element, totalHeight + 32 + height);
|
||||
}
|
||||
|
||||
elementSizeObserver.dispose();
|
||||
}
|
||||
});
|
||||
elementSizeObserver.startObserving();
|
||||
if (!hasDynamicHeight && dimensions.height !== 0) {
|
||||
element.setDynamicHeight(totalHeight + 32 + dimensions.height);
|
||||
this.handler.layoutElement(element, totalHeight + 32 + dimensions.height);
|
||||
}
|
||||
|
||||
this.disposables.set(templateData.cellContainer, {
|
||||
dispose: () => {
|
||||
elementSizeObserver.dispose();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.disposables.set(templateData.cellContainer, {
|
||||
dispose: () => {
|
||||
listener.dispose();
|
||||
rerenderOutput.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
if (templateData.outputContainer) {
|
||||
templateData.outputContainer!.innerHTML = '';
|
||||
|
||||
66
src/vs/workbench/contrib/notebook/browser/extensionPoint.ts
Normal file
66
src/vs/workbench/contrib/notebook/browser/extensionPoint.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
import * as nls from 'vs/nls';
|
||||
import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry';
|
||||
import { languagesExtPoint } from 'vs/workbench/services/mode/common/workbenchModeService';
|
||||
import { NotebookSelector } from 'vs/workbench/contrib/notebook/common/notebookProvider';
|
||||
|
||||
namespace NotebookEditorContribution {
|
||||
export const viewType = 'viewType';
|
||||
export const displayName = 'displayName';
|
||||
export const selector = 'selector';
|
||||
}
|
||||
|
||||
|
||||
interface INotebookEditorContribution {
|
||||
readonly [NotebookEditorContribution.viewType]: string;
|
||||
readonly [NotebookEditorContribution.displayName]: string;
|
||||
readonly [NotebookEditorContribution.selector]?: readonly NotebookSelector[];
|
||||
}
|
||||
|
||||
const notebookContribution: IJSONSchema = {
|
||||
description: nls.localize('contributes.notebook', 'Contributes notebook.'),
|
||||
type: 'array',
|
||||
defaultSnippets: [{ body: [{ viewType: '', displayName: '' }] }],
|
||||
items: {
|
||||
type: 'object',
|
||||
required: [
|
||||
NotebookEditorContribution.viewType,
|
||||
NotebookEditorContribution.displayName,
|
||||
NotebookEditorContribution.selector,
|
||||
],
|
||||
properties: {
|
||||
[NotebookEditorContribution.viewType]: {
|
||||
type: 'string',
|
||||
description: nls.localize('contributes.notebook.viewType', 'Unique identifier of the notebook.'),
|
||||
},
|
||||
[NotebookEditorContribution.displayName]: {
|
||||
type: 'string',
|
||||
description: nls.localize('contributes.notebook.displayName', 'Human readable name of the notebook.'),
|
||||
},
|
||||
[NotebookEditorContribution.selector]: {
|
||||
type: 'array',
|
||||
description: nls.localize('contributes.notebook.selector', 'Set of globs that the notebook is for.'),
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
filenamePattern: {
|
||||
type: 'string',
|
||||
description: nls.localize('contributes.notebook.selector.filenamePattern', 'Glob that the notebook is enabled for.'),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const notebookExtensionPoint = ExtensionsRegistry.registerExtensionPoint<INotebookEditorContribution[]>({
|
||||
extensionPoint: 'notebookProvider',
|
||||
deps: [languagesExtPoint],
|
||||
jsonSchema: notebookContribution
|
||||
});
|
||||
@@ -16,6 +16,12 @@ import { IEditorOptions, ITextEditorOptions } from 'vs/platform/editor/common/ed
|
||||
import { endsWith } from 'vs/base/common/strings';
|
||||
import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { INotebookService, NotebookService } from 'vs/workbench/contrib/notebook/browser/notebookService';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions';
|
||||
import { IActiveCodeEditor, isDiffEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
|
||||
|
||||
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
|
||||
EditorDescriptor.create(
|
||||
@@ -28,39 +34,112 @@ Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
export class ExecuteNotebookAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.executeNotebook';
|
||||
static readonly LABEL = 'Execute Notebook';
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@INotebookService private readonly notebookService: INotebookService
|
||||
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
let resource = this.editorService.activeEditor?.getResource();
|
||||
|
||||
if (resource) {
|
||||
let notebookProviders = this.notebookService.getContributedNotebook(resource!);
|
||||
|
||||
if (notebookProviders.length > 0) {
|
||||
let viewType = notebookProviders[0].id;
|
||||
this.notebookService.executeNotebook(viewType, resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookContribution implements IWorkbenchContribution {
|
||||
private _resourceMapping: Map<string, NotebookEditorInput> = new Map<string, NotebookEditorInput>();
|
||||
|
||||
constructor(
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@INotebookService private readonly notebookService: INotebookService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService
|
||||
|
||||
) {
|
||||
this.editorService.overrideOpenEditor((editor, options, group) => this.onEditorOpening(editor, options, group));
|
||||
|
||||
this.registerCommands();
|
||||
}
|
||||
|
||||
private onEditorOpening(editor: IEditorInput, options: IEditorOptions | ITextEditorOptions | undefined, group: IEditorGroup): IOpenEditorOverride | undefined {
|
||||
const resource = editor.getResource();
|
||||
let viewType: string | undefined = undefined;
|
||||
|
||||
if (
|
||||
!resource ||
|
||||
!endsWith(resource.path, '.ipynb')
|
||||
) {
|
||||
return undefined;
|
||||
if (resource) {
|
||||
let notebookProviders = this.notebookService.getContributedNotebook(resource!);
|
||||
|
||||
if (notebookProviders.length > 0) {
|
||||
viewType = notebookProviders[0].id;
|
||||
}
|
||||
}
|
||||
|
||||
if (this._resourceMapping.has(resource.path)) {
|
||||
const input = this._resourceMapping.get(resource.path);
|
||||
if (viewType === undefined) {
|
||||
if (
|
||||
!resource ||
|
||||
!endsWith(resource.path, '.ipynb')
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
if (this._resourceMapping.has(resource!.path)) {
|
||||
const input = this._resourceMapping.get(resource!.path);
|
||||
|
||||
return { override: this.editorService.openEditor(input!, options, group) };
|
||||
}
|
||||
|
||||
const input = this.instantiationService.createInstance(NotebookEditorInput, editor);
|
||||
this._resourceMapping.set(resource.path, input);
|
||||
|
||||
const input = this.instantiationService.createInstance(NotebookEditorInput, editor, viewType);
|
||||
this._resourceMapping.set(resource!.path, input);
|
||||
|
||||
return { override: this.editorService.openEditor(input, { ...options, ignoreOverrides: true }, group) };
|
||||
}
|
||||
|
||||
private registerCommands() {
|
||||
const workbenchActionsRegistry = Registry.as<IWorkbenchActionRegistry>(WorkbenchActionExtensions.WorkbenchActions);
|
||||
|
||||
workbenchActionsRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ExecuteNotebookAction, ExecuteNotebookAction.ID, ExecuteNotebookAction.LABEL), 'Execute Notebook', 'Notebook');
|
||||
}
|
||||
}
|
||||
|
||||
const workbenchContributionsRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
|
||||
workbenchContributionsRegistry.registerWorkbenchContribution(NotebookContribution, LifecyclePhase.Starting);
|
||||
|
||||
|
||||
registerSingleton(INotebookService, NotebookService);
|
||||
|
||||
|
||||
export function getActiveEditor(editorService: IEditorService): IActiveCodeEditor | null {
|
||||
let activeTextEditorWidget = editorService.activeTextEditorWidget;
|
||||
|
||||
if (isDiffEditor(activeTextEditorWidget)) {
|
||||
if (activeTextEditorWidget.getOriginalEditor().hasTextFocus()) {
|
||||
activeTextEditorWidget = activeTextEditorWidget.getOriginalEditor();
|
||||
} else {
|
||||
activeTextEditorWidget = activeTextEditorWidget.getModifiedEditor();
|
||||
}
|
||||
}
|
||||
|
||||
if (!isCodeEditor(activeTextEditorWidget) || !activeTextEditorWidget.hasModel()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return activeTextEditorWidget;
|
||||
}
|
||||
|
||||
@@ -191,6 +191,10 @@ export class NotebookEditor extends BaseEditor implements NotebookHandler {
|
||||
}
|
||||
|
||||
this.model = model;
|
||||
this.localStore.add(this.model.onDidChangeCells(() => {
|
||||
this.updateViewCells();
|
||||
}));
|
||||
|
||||
this.viewCells = model.getNotebook().cells.map(cell => {
|
||||
return new ViewCell(cell, false, this.modelService, this.modeService);
|
||||
});
|
||||
@@ -243,8 +247,13 @@ export class NotebookEditor extends BaseEditor implements NotebookHandler {
|
||||
}, 0);
|
||||
}
|
||||
|
||||
updateViewCells() {
|
||||
this.list?.rerender();
|
||||
}
|
||||
|
||||
insertEmptyNotebookCell(cell: ViewCell, type: 'code' | 'markdown', direction: 'above' | 'below') {
|
||||
let newCell = new ViewCell({
|
||||
handle: -1,
|
||||
cell_type: type,
|
||||
source: [],
|
||||
outputs: []
|
||||
|
||||
@@ -9,59 +9,30 @@ import { ITextModel } from 'vs/editor/common/model';
|
||||
import { format } from 'vs/base/common/jsonFormatter';
|
||||
import { applyEdits } from 'vs/base/common/jsonEdit';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
|
||||
export interface IStreamOutput {
|
||||
output_type: 'stream';
|
||||
text: string;
|
||||
}
|
||||
|
||||
export interface IErrorOutput {
|
||||
output_type: 'error';
|
||||
evalue: string;
|
||||
traceback: string[];
|
||||
}
|
||||
|
||||
export interface IDisplayOutput {
|
||||
output_type: 'display_data';
|
||||
data: { string: string };
|
||||
}
|
||||
|
||||
export interface IGenericOutput {
|
||||
output_type: string;
|
||||
}
|
||||
|
||||
export type IOutput = IStreamOutput | any;
|
||||
|
||||
export interface ICell {
|
||||
source: string[];
|
||||
cell_type: 'markdown' | 'code';
|
||||
outputs: IOutput[];
|
||||
}
|
||||
|
||||
export interface LanguageInfo {
|
||||
file_extension: string;
|
||||
}
|
||||
export interface IMetadata {
|
||||
language_info: LanguageInfo;
|
||||
}
|
||||
export interface INotebook {
|
||||
metadata: IMetadata;
|
||||
cells: ICell[];
|
||||
}
|
||||
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { INotebook, ICell } from 'vs/editor/common/modes';
|
||||
import { INotebookService } from 'vs/workbench/contrib/notebook/browser/notebookService';
|
||||
|
||||
export class NotebookEditorModel extends EditorModel {
|
||||
private _notebook: INotebook | undefined;
|
||||
private _dirty = false;
|
||||
|
||||
protected readonly _onDidChangeDirty = this._register(new Emitter<void>());
|
||||
readonly onDidChangeDirty = this._onDidChangeDirty.event;
|
||||
|
||||
private readonly _onDidChangeCells = new Emitter<void>();
|
||||
get onDidChangeCells(): Event<void> { return this._onDidChangeCells.event; }
|
||||
|
||||
constructor(
|
||||
public readonly textModel: ITextModel
|
||||
public readonly textModel: ITextModel,
|
||||
private _notebook: INotebook | undefined
|
||||
) {
|
||||
super();
|
||||
|
||||
if (_notebook && _notebook.onDidChangeCells) {
|
||||
this._register(_notebook.onDidChangeCells(() => {
|
||||
this._onDidChangeCells.fire();
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
isDirty() {
|
||||
@@ -115,6 +86,8 @@ export class NotebookEditorInput extends EditorInput {
|
||||
|
||||
constructor(
|
||||
public readonly editorInput: IEditorInput,
|
||||
public readonly viewType: string | undefined,
|
||||
@INotebookService private readonly notebookService: INotebookService,
|
||||
@ITextFileService private readonly textFileService: ITextFileService,
|
||||
@ITextModelService private readonly textModelResolverService: ITextModelService
|
||||
) {
|
||||
@@ -142,13 +115,22 @@ export class NotebookEditorInput extends EditorInput {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
getResource() {
|
||||
return this.editorInput.getResource();
|
||||
}
|
||||
|
||||
resolve(): Promise<NotebookEditorModel> {
|
||||
if (!this.promise) {
|
||||
this.promise = this.textModelResolverService.createModelReference(this.editorInput.getResource()!)
|
||||
.then(ref => {
|
||||
.then(async ref => {
|
||||
const textModel = ref.object.textEditorModel;
|
||||
|
||||
this.textModel = new NotebookEditorModel(textModel);
|
||||
let notebook: INotebook | undefined = undefined;
|
||||
if (this.viewType !== undefined) {
|
||||
notebook = await this.notebookService.resolveNotebook(this.viewType, this.editorInput.getResource()!);
|
||||
}
|
||||
|
||||
this.textModel = new NotebookEditorModel(textModel, notebook);
|
||||
this.textModel.onDidChangeDirty(() => this._onDidChangeDirty.fire());
|
||||
return this.textModel;
|
||||
});
|
||||
|
||||
112
src/vs/workbench/contrib/notebook/browser/notebookService.ts
Normal file
112
src/vs/workbench/contrib/notebook/browser/notebookService.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { INotebook } from 'vs/editor/common/modes';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { notebookExtensionPoint } from 'vs/workbench/contrib/notebook/browser/extensionPoint';
|
||||
import { NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider';
|
||||
|
||||
export const INotebookService = createDecorator<INotebookService>('notebookService');
|
||||
|
||||
export interface IMainNotebookController {
|
||||
resolveNotebook(viewType: string, uri: URI): Promise<INotebook | undefined>;
|
||||
executeNotebook(viewType: string, uri: URI): Promise<void>;
|
||||
updateNotebook(uri: URI, notebook: INotebook): void;
|
||||
}
|
||||
|
||||
export interface INotebookService {
|
||||
_serviceBrand: undefined;
|
||||
registerNotebookController(viewType: string, controller: IMainNotebookController): void;
|
||||
unregisterNotebookProvider(viewType: string): void;
|
||||
resolveNotebook(viewType: string, uri: URI): Promise<INotebook | undefined>;
|
||||
executeNotebook(viewType: string, uri: URI): Promise<void>;
|
||||
getContributedNotebook(resource: URI): readonly NotebookProviderInfo[];
|
||||
}
|
||||
|
||||
export class NotebookInfoStore {
|
||||
private readonly contributedEditors = new Map<string, NotebookProviderInfo>();
|
||||
|
||||
public clear() {
|
||||
this.contributedEditors.clear();
|
||||
}
|
||||
|
||||
public get(viewType: string): NotebookProviderInfo | undefined {
|
||||
return this.contributedEditors.get(viewType);
|
||||
}
|
||||
|
||||
public add(info: NotebookProviderInfo): void {
|
||||
if (this.contributedEditors.has(info.id)) {
|
||||
console.log(`Custom editor with id '${info.id}' already registered`);
|
||||
return;
|
||||
}
|
||||
this.contributedEditors.set(info.id, info);
|
||||
}
|
||||
|
||||
public getContributedNotebook(resource: URI): readonly NotebookProviderInfo[] {
|
||||
return Array.from(this.contributedEditors.values()).filter(customEditor =>
|
||||
customEditor.matches(resource));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class NotebookService extends Disposable implements INotebookService {
|
||||
_serviceBrand: undefined;
|
||||
private readonly _notebookProviders = new Map<string, IMainNotebookController>();
|
||||
public notebookProviderInfoStore: NotebookInfoStore = new NotebookInfoStore();
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
notebookExtensionPoint.setHandler((extensions) => {
|
||||
this.notebookProviderInfoStore.clear();
|
||||
|
||||
for (const extension of extensions) {
|
||||
for (const notebookContribution of extension.value) {
|
||||
this.notebookProviderInfoStore.add(new NotebookProviderInfo({
|
||||
id: notebookContribution.viewType,
|
||||
displayName: notebookContribution.displayName,
|
||||
selector: notebookContribution.selector || [],
|
||||
}));
|
||||
}
|
||||
}
|
||||
// console.log(this._notebookProviderInfoStore);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
registerNotebookController(viewType: string, controller: IMainNotebookController) {
|
||||
this._notebookProviders.set(viewType, controller);
|
||||
}
|
||||
|
||||
unregisterNotebookProvider(viewType: string): void {
|
||||
this._notebookProviders.delete(viewType);
|
||||
}
|
||||
|
||||
resolveNotebook(viewType: string, uri: URI): Promise<INotebook | undefined> {
|
||||
let provider = this._notebookProviders.get(viewType);
|
||||
|
||||
if (provider) {
|
||||
return provider.resolveNotebook(viewType, uri);
|
||||
}
|
||||
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
async executeNotebook(viewType: string, uri: URI): Promise<void> {
|
||||
let provider = this._notebookProviders.get(viewType);
|
||||
|
||||
if (provider) {
|
||||
return provider.executeNotebook(viewType, uri);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
public getContributedNotebook(resource: URI): readonly NotebookProviderInfo[] {
|
||||
return this.notebookProviderInfoStore.getContributedNotebook(resource);
|
||||
}
|
||||
}
|
||||
@@ -7,10 +7,10 @@ import * as DOM from 'vs/base/browser/dom';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { RGBA, Color } from 'vs/base/common/color';
|
||||
import { ansiColorIdentifiers } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry';
|
||||
import { IOutput } from 'vs/workbench/contrib/notebook/browser/notebookEditorInput';
|
||||
import { IWebviewService } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
import { isArray } from 'vs/base/common/types';
|
||||
import { NotebookHandler } from 'vs/workbench/contrib/notebook/browser/cellRenderer';
|
||||
import { IOutput } from 'vs/editor/common/modes';
|
||||
|
||||
export function registerMineTypeRenderer(types: string[], renderer: IMimeRenderer) {
|
||||
types.forEach(type => {
|
||||
|
||||
42
src/vs/workbench/contrib/notebook/common/notebookProvider.ts
Normal file
42
src/vs/workbench/contrib/notebook/common/notebookProvider.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as glob from 'vs/base/common/glob';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { basename } from 'vs/base/common/resources';
|
||||
|
||||
export interface NotebookSelector {
|
||||
readonly filenamePattern?: string;
|
||||
}
|
||||
|
||||
export class NotebookProviderInfo {
|
||||
|
||||
public readonly id: string;
|
||||
public readonly displayName: string;
|
||||
public readonly selector: readonly NotebookSelector[];
|
||||
|
||||
constructor(descriptor: {
|
||||
readonly id: string;
|
||||
readonly displayName: string;
|
||||
readonly selector: readonly NotebookSelector[];
|
||||
}) {
|
||||
this.id = descriptor.id;
|
||||
this.displayName = descriptor.displayName;
|
||||
this.selector = descriptor.selector;
|
||||
}
|
||||
|
||||
matches(resource: URI): boolean {
|
||||
return this.selector.some(selector => NotebookProviderInfo.selectorMatches(selector, resource));
|
||||
}
|
||||
|
||||
static selectorMatches(selector: NotebookSelector, resource: URI): boolean {
|
||||
if (selector.filenamePattern) {
|
||||
if (glob.match(selector.filenamePattern.toLowerCase(), basename(resource).toLowerCase())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user