Enable VS Code notebooks with a built-in SQL kernel. (#21995)

This commit is contained in:
Cory Rivera 2023-02-23 16:22:46 -08:00 коммит произвёл GitHub
Родитель 290687a207
Коммит f53119c2a6
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
66 изменённых файлов: 4962 добавлений и 318 удалений

Просмотреть файл

@ -47,6 +47,7 @@ module.exports.unicodeFilter = [
'!build/win32/**',
'!extensions/markdown-language-features/notebook-out/*.js',
'!extensions/markdown-math/notebook-out/**',
'!extensions/notebook-renderers/renderer-out/**',
'!extensions/php-language-features/src/features/phpGlobalFunctions.ts',
'!extensions/typescript-language-features/test-workspace/**',
'!extensions/vscode-api-tests/testWorkspace/**',
@ -130,6 +131,7 @@ module.exports.indentationFilter = [
'!extensions/markdown-language-features/media/*.js',
'!extensions/markdown-language-features/notebook-out/*.js',
'!extensions/markdown-math/notebook-out/*.js',
'!extensions/notebook-renderers/renderer-out/*.js',
'!extensions/simple-browser/media/*.js',
// {{SQL CARBON EDIT}} Except for our stuff

Просмотреть файл

@ -69,7 +69,6 @@ const compilations = [
'vscode-api-tests/tsconfig.json',
'vscode-colorize-tests/tsconfig.json',
'vscode-custom-editor-tests/tsconfig.json',
'vscode-notebook-tests/tsconfig.json',
'vscode-test-resolver/tsconfig.json'
];
*/

Просмотреть файл

@ -474,7 +474,7 @@ const esbuildMediaScripts = [
'markdown-language-features/esbuild-notebook.js',
'markdown-language-features/esbuild-preview.js',
'markdown-math/esbuild.js',
// 'notebook-renderers/esbuild.js', {{SQL CARBON EDIT}} We don't have this extension
'notebook-renderers/esbuild.js',
'simple-browser/esbuild-preview.js',
];
async function webpackExtensions(taskName, isWatch, webpackConfigLocations) {

Просмотреть файл

@ -285,7 +285,6 @@ const excludedExtensions = [
'vscode-test-resolver',
'ms-vscode.node-debug',
'ms-vscode.node-debug2',
'vscode-notebook-tests',
'vscode-custom-editor-tests',
'integration-tests', // {{SQL CARBON EDIT}}
];
@ -572,7 +571,7 @@ const esbuildMediaScripts = [
'markdown-language-features/esbuild-notebook.js',
'markdown-language-features/esbuild-preview.js',
'markdown-math/esbuild.js',
// 'notebook-renderers/esbuild.js', {{SQL CARBON EDIT}} We don't have this extension
'notebook-renderers/esbuild.js',
'simple-browser/esbuild-preview.js',
];

Просмотреть файл

@ -188,6 +188,7 @@ const VSCODEExtensions = [
"markdown-math",
"merge-conflict",
"microsoft-authentication",
"notebook-renderers",
"powershell",
"python",
"r",

Просмотреть файл

@ -206,6 +206,7 @@ const VSCODEExtensions = [
"markdown-math",
"merge-conflict",
"microsoft-authentication",
"notebook-renderers",
"powershell",
"python",
"r",

Просмотреть файл

@ -29,6 +29,7 @@ exports.dirs = [
'extensions/image-preview',
'extensions/import',
'extensions/integration-tests',
'extensions/ipynb',
'extensions/json-language-features',
'extensions/json-language-features/server',
'extensions/kusto',
@ -39,6 +40,7 @@ exports.dirs = [
'extensions/microsoft-authentication',
'extensions/mssql',
'extensions/notebook',
'extensions/notebook-renderers',
'extensions/profiler',
'extensions/python',
'extensions/query-history',

4
extensions/ipynb/.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1,4 @@
out
dist
node_modules
*.vsix

Просмотреть файл

@ -0,0 +1,9 @@
.vscode/**
src/**
out/**
tsconfig.json
extension.webpack.config.js
extension-browser.webpack.config.js
yarn.lock
.gitignore

Просмотреть файл

@ -0,0 +1,9 @@
# Jupyter for Azure Data Studio
**Notice:** This extension is bundled with Azure Data Studio. It can be disabled but not uninstalled.
## Features
This extension provides the following Jupyter-related features for Azure Data Studio:
- Open, edit and save .ipynb files

Просмотреть файл

@ -0,0 +1,22 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//@ts-check
'use strict';
const withBrowserDefaults = require('../shared.webpack.config').browser;
const config = withBrowserDefaults({
context: __dirname,
entry: {
extension: './src/ipynbMain.ts'
},
output: {
filename: 'ipynbMain.js'
}
});
module.exports = config;

Просмотреть файл

@ -0,0 +1,20 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. 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,
entry: {
extension: './src/ipynbMain.ts',
},
output: {
filename: 'ipynbMain.js'
}
});

Просмотреть файл

@ -0,0 +1,90 @@
{
"name": "ipynb",
"displayName": "%displayName%",
"description": "%description%",
"publisher": "vscode",
"version": "1.0.0",
"license": "MIT",
"engines": {
"vscode": "^1.57.0"
},
"enabledApiProposals": [
"notebookEditor",
"notebookEditorEdit"
],
"activationEvents": [
"*"
],
"extensionKind": [
"workspace",
"ui"
],
"main": "./out/ipynbMain.js",
"browser": "./dist/browser/ipynbMain.js",
"capabilities": {
"virtualWorkspaces": true,
"untrustedWorkspaces": {
"supported": true
}
},
"contributes": {
"commands": [
{
"command": "ipynb.newUntitledIpynb",
"title": "New Jupyter Notebook",
"shortTitle": "Jupyter Notebook",
"category": "Create"
},
{
"command": "ipynb.openIpynbInNotebookEditor",
"title": "Open ipynb file in notebook editor"
}
],
"notebooks": [
{
"type": "jupyter-notebook",
"displayName": "Jupyter Notebook",
"selector": [
{
"filenamePattern": "*.ipynb"
}
],
"priority": "default"
}
],
"menus": {
"file/newFile": [
{
"command": "ipynb.newUntitledIpynb",
"group": "notebook"
}
],
"commandPalette": [
{
"command": "ipynb.newUntitledIpynb"
},
{
"command": "ipynb.openIpynbInNotebookEditor",
"when": "false"
}
]
}
},
"scripts": {
"compile": "npx gulp compile-extension:ipynb",
"watch": "npx gulp watch-extension:ipynb"
},
"dependencies": {
"@enonic/fnv-plus": "^1.3.0",
"detect-indent": "^6.0.0",
"uuid": "^8.3.2"
},
"devDependencies": {
"@jupyterlab/nbformat": "^3.2.9",
"@types/uuid": "^8.3.1"
},
"repository": {
"type": "git",
"url": "https://github.com/microsoft/vscode.git"
}
}

Просмотреть файл

@ -0,0 +1,4 @@
{
"displayName": ".ipynb support",
"description": "Provides basic support for opening and reading Jupyter's .ipynb notebook files"
}

Просмотреть файл

@ -0,0 +1,77 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ExtensionContext, NotebookDocument, NotebookDocumentChangeEvent, workspace, WorkspaceEdit } from 'vscode';
import { v4 as uuid } from 'uuid';
import { getCellMetadata } from './serializers';
import { CellMetadata } from './common';
import { getNotebookMetadata } from './notebookSerializer';
import * as nbformat from '@jupyterlab/nbformat';
/**
* Ensure all new cells in notebooks with nbformat >= 4.5 have an id.
* Details of the spec can be found here https://jupyter.org/enhancement-proposals/62-cell-id/cell-id.html#
*/
export function ensureAllNewCellsHaveCellIds(context: ExtensionContext) {
workspace.onDidChangeNotebookDocument(onDidChangeNotebookCells, undefined, context.subscriptions);
}
function onDidChangeNotebookCells(e: NotebookDocumentChangeEvent) {
const nbMetadata = getNotebookMetadata(e.notebook);
if (!isCellIdRequired(nbMetadata)) {
return;
}
e.contentChanges.forEach(change => {
change.addedCells.forEach(cell => {
const cellMetadata = getCellMetadata(cell);
if (cellMetadata?.id) {
return;
}
const id = generateCellId(e.notebook);
const edit = new WorkspaceEdit();
// Don't edit the metadata directly, always get a clone (prevents accidental singletons and directly editing the objects).
const updatedMetadata: CellMetadata = { ...JSON.parse(JSON.stringify(cellMetadata || {})) };
updatedMetadata.id = id;
edit.replaceNotebookCellMetadata(cell.notebook.uri, cell.index, { ...(cell.metadata), custom: updatedMetadata });
workspace.applyEdit(edit);
});
});
}
/**
* Cell ids are required in notebooks only in notebooks with nbformat >= 4.5
*/
function isCellIdRequired(metadata: Pick<Partial<nbformat.INotebookContent>, 'nbformat' | 'nbformat_minor'>) {
if ((metadata.nbformat || 0) >= 5) {
return true;
}
if ((metadata.nbformat || 0) === 4 && (metadata.nbformat_minor || 0) >= 5) {
return true;
}
return false;
}
function generateCellId(notebook: NotebookDocument) {
while (true) {
// Details of the id can be found here https://jupyter.org/enhancement-proposals/62-cell-id/cell-id.html#adding-an-id-field,
// & here https://jupyter.org/enhancement-proposals/62-cell-id/cell-id.html#updating-older-formats
const id = uuid().replace(/-/g, '').substring(0, 8);
let duplicate = false;
for (let index = 0; index < notebook.cellCount; index++) {
const cell = notebook.cellAt(index);
const existingId = getCellMetadata(cell)?.id;
if (!existingId) {
continue;
}
if (existingId === id) {
duplicate = true;
break;
}
}
if (!duplicate) {
return id;
}
}
}

Просмотреть файл

@ -0,0 +1,62 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nbformat from '@jupyterlab/nbformat';
/**
* Metadata we store in VS Code cell output items.
* This contains the original metadata from the Jupyter outputs.
*/
export interface CellOutputMetadata {
/**
* Cell output metadata.
*/
metadata?: any;
/**
* Transient data from Jupyter.
*/
transient?: {
/**
* This is used for updating the output in other cells.
* We don't know of other properties, but this is definitely used.
*/
display_id?: string;
} & any;
/**
* Original cell output type
*/
outputType: nbformat.OutputType | string;
executionCount?: nbformat.IExecuteResult['ExecutionCount'];
/**
* Whether the original Mime data is JSON or not.
* This properly only exists in metadata for NotebookCellOutputItems
* (this is something we have added)
*/
__isJson?: boolean;
}
/**
* Metadata we store in VS Code cells.
* This contains the original metadata from the Jupyuter cells.
*/
export interface CellMetadata {
/**
* Cell id for notebooks created with the new 4.5 version of nbformat.
*/
id?: string;
/**
* Stores attachments for cells.
*/
attachments?: nbformat.IAttachments;
/**
* Stores cell metadata.
*/
metadata?: Partial<nbformat.ICellMetadata>;
}

Просмотреть файл

@ -0,0 +1,6 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export const defaultNotebookFormat = { major: 4, minor: 2 };

Просмотреть файл

@ -0,0 +1,359 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nbformat from '@jupyterlab/nbformat';
import { extensions, NotebookCellData, NotebookCellExecutionSummary, NotebookCellKind, NotebookCellOutput, NotebookCellOutputItem, NotebookData } from 'vscode';
import { CellMetadata, CellOutputMetadata } from './common';
const jupyterLanguageToMonacoLanguageMapping = new Map([
['c#', 'csharp'],
['f#', 'fsharp'],
['q#', 'qsharp'],
['c++11', 'c++'],
['c++12', 'c++'],
['c++14', 'c++']
]);
export function getPreferredLanguage(metadata?: nbformat.INotebookMetadata) {
const jupyterLanguage =
metadata?.language_info?.name ||
(metadata?.kernelspec as any)?.language;
// Default to python language only if the Python extension is installed.
const defaultLanguage =
extensions.getExtension('ms-python.python')
? 'python'
: (extensions.getExtension('ms-dotnettools.dotnet-interactive-vscode') ? 'csharp' : 'python');
// Note, whatever language is returned here, when the user selects a kernel, the cells (of blank documents) get updated based on that kernel selection.
return translateKernelLanguageToMonaco(jupyterLanguage || defaultLanguage);
}
function translateKernelLanguageToMonaco(language: string): string {
language = language.toLowerCase();
if (language.length === 2 && language.endsWith('#')) {
return `${language.substring(0, 1)}sharp`;
}
return jupyterLanguageToMonacoLanguageMapping.get(language) || language;
}
const orderOfMimeTypes = [
'application/vnd.*',
'application/vdom.*',
'application/geo+json',
'application/x-nteract-model-debug+json',
'text/html',
'application/javascript',
'image/gif',
'text/latex',
'text/markdown',
'image/png',
'image/svg+xml',
'image/jpeg',
'application/json',
'text/plain'
];
function isEmptyVendoredMimeType(outputItem: NotebookCellOutputItem) {
if (outputItem.mime.startsWith('application/vnd.')) {
try {
return outputItem.data.byteLength === 0 || Buffer.from(outputItem.data).toString().length === 0;
} catch { }
}
return false;
}
function isMimeTypeMatch(value: string, compareWith: string) {
if (value.endsWith('.*')) {
value = value.substr(0, value.indexOf('.*'));
}
return compareWith.startsWith(value);
}
function sortOutputItemsBasedOnDisplayOrder(outputItems: NotebookCellOutputItem[]): NotebookCellOutputItem[] {
return outputItems
.map(item => {
let index = orderOfMimeTypes.findIndex((mime) => isMimeTypeMatch(mime, item.mime));
// Sometimes we can have mime types with empty data, e.g. when using holoview we can have `application/vnd.holoviews_load.v0+json` with empty value.
// & in these cases we have HTML/JS and those take precedence.
// https://github.com/microsoft/vscode-jupyter/issues/6109
if (isEmptyVendoredMimeType(item)) {
index = -1;
}
index = index === -1 ? 100 : index;
return {
item, index
};
})
.sort((outputItemA, outputItemB) => outputItemA.index - outputItemB.index).map(item => item.item);
}
enum CellOutputMimeTypes {
error = 'application/vnd.code.notebook.error',
stderr = 'application/vnd.code.notebook.stderr',
stdout = 'application/vnd.code.notebook.stdout'
}
export const textMimeTypes = ['text/plain', 'text/markdown', 'text/latex', CellOutputMimeTypes.stderr, CellOutputMimeTypes.stdout];
function concatMultilineString(str: string | string[], trim?: boolean): string {
const nonLineFeedWhiteSpaceTrim = /(^[\t\f\v\r ]+|[\t\f\v\r ]+$)/g;
if (Array.isArray(str)) {
let result = '';
for (let i = 0; i < str.length; i += 1) {
const s = str[i];
if (i < str.length - 1 && !s.endsWith('\n')) {
result = result.concat(`${s}\n`);
} else {
result = result.concat(s);
}
}
// Just trim whitespace. Leave \n in place
return trim ? result.replace(nonLineFeedWhiteSpaceTrim, '') : result;
}
return trim ? str.toString().replace(nonLineFeedWhiteSpaceTrim, '') : str.toString();
}
function convertJupyterOutputToBuffer(mime: string, value: unknown): NotebookCellOutputItem {
if (!value) {
return NotebookCellOutputItem.text('', mime);
}
try {
if (
(mime.startsWith('text/') || textMimeTypes.includes(mime)) &&
(Array.isArray(value) || typeof value === 'string')
) {
const stringValue = Array.isArray(value) ? concatMultilineString(value) : value;
return NotebookCellOutputItem.text(stringValue, mime);
} else if (mime.startsWith('image/') && typeof value === 'string' && mime !== 'image/svg+xml') {
// Images in Jupyter are stored in base64 encoded format.
// VS Code expects bytes when rendering images.
if (typeof Buffer !== 'undefined' && typeof Buffer.from === 'function') {
return new NotebookCellOutputItem(Buffer.from(value, 'base64'), mime);
} else {
const data = Uint8Array.from(atob(value), c => c.charCodeAt(0));
return new NotebookCellOutputItem(data, mime);
}
} else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
return NotebookCellOutputItem.text(JSON.stringify(value), mime);
} else {
// For everything else, treat the data as strings (or multi-line strings).
value = Array.isArray(value) ? concatMultilineString(value) : value;
return NotebookCellOutputItem.text(value as string, mime);
}
} catch (ex) {
return NotebookCellOutputItem.error(ex);
}
}
function getNotebookCellMetadata(cell: nbformat.IBaseCell): CellMetadata {
// We put this only for VSC to display in diff view.
// Else we don't use this.
const propertiesToClone: (keyof CellMetadata)[] = ['metadata', 'attachments'];
const custom: CellMetadata = {};
propertiesToClone.forEach((propertyToClone) => {
if (cell[propertyToClone]) {
custom[propertyToClone] = JSON.parse(JSON.stringify(cell[propertyToClone]));
}
});
if ('id' in cell && typeof cell.id === 'string') {
custom.id = cell.id;
}
return custom;
}
function getOutputMetadata(output: nbformat.IOutput): CellOutputMetadata {
// Add on transient data if we have any. This should be removed by our save functions elsewhere.
const metadata: CellOutputMetadata = {
outputType: output.output_type
};
if (output.transient) {
metadata.transient = output.transient;
}
switch (output.output_type as nbformat.OutputType) {
case 'display_data':
case 'execute_result':
case 'update_display_data': {
metadata.executionCount = output.execution_count;
metadata.metadata = output.metadata ? JSON.parse(JSON.stringify(output.metadata)) : {};
break;
}
default:
break;
}
return metadata;
}
function translateDisplayDataOutput(
output: nbformat.IDisplayData | nbformat.IDisplayUpdate | nbformat.IExecuteResult
): NotebookCellOutput {
// Metadata could be as follows:
// We'll have metadata specific to each mime type as well as generic metadata.
/*
IDisplayData = {
output_type: 'display_data',
data: {
'image/jpg': '/////'
'image/png': '/////'
'text/plain': '/////'
},
metadata: {
'image/png': '/////',
'background': true,
'xyz': '///
}
}
*/
const metadata = getOutputMetadata(output);
const items: NotebookCellOutputItem[] = [];
if (output.data) {
for (const key in output.data) {
items.push(convertJupyterOutputToBuffer(key, output.data[key]));
}
}
return new NotebookCellOutput(sortOutputItemsBasedOnDisplayOrder(items), metadata);
}
function translateErrorOutput(output?: nbformat.IError): NotebookCellOutput {
output = output || { output_type: 'error', ename: '', evalue: '', traceback: [] };
return new NotebookCellOutput(
[
NotebookCellOutputItem.error({
name: output?.ename || '',
message: output?.evalue || '',
stack: (output?.traceback || []).join('\n')
})
],
{ ...getOutputMetadata(output), originalError: output }
);
}
function translateStreamOutput(output: nbformat.IStream): NotebookCellOutput {
const value = concatMultilineString(output.text);
const item = output.name === 'stderr' ? NotebookCellOutputItem.stderr(value) : NotebookCellOutputItem.stdout(value);
return new NotebookCellOutput([item], getOutputMetadata(output));
}
const cellOutputMappers = new Map<nbformat.OutputType, (output: any) => NotebookCellOutput>();
cellOutputMappers.set('display_data', translateDisplayDataOutput);
cellOutputMappers.set('execute_result', translateDisplayDataOutput);
cellOutputMappers.set('update_display_data', translateDisplayDataOutput);
cellOutputMappers.set('error', translateErrorOutput);
cellOutputMappers.set('stream', translateStreamOutput);
export function jupyterCellOutputToCellOutput(output: nbformat.IOutput): NotebookCellOutput {
/**
* Stream, `application/x.notebook.stream`
* Error, `application/x.notebook.error-traceback`
* Rich, { mime: value }
*
* outputs: [
new vscode.NotebookCellOutput([
new vscode.NotebookCellOutputItem('application/x.notebook.stream', 2),
new vscode.NotebookCellOutputItem('application/x.notebook.stream', 3),
]),
new vscode.NotebookCellOutput([
new vscode.NotebookCellOutputItem('text/markdown', '## header 2'),
new vscode.NotebookCellOutputItem('image/svg+xml', [
"<svg baseProfile=\"full\" height=\"200\" version=\"1.1\" width=\"300\" xmlns=\"http://www.w3.org/2000/svg\">\n",
" <rect fill=\"blue\" height=\"100%\" width=\"100%\"/>\n",
" <circle cx=\"150\" cy=\"100\" fill=\"green\" r=\"80\"/>\n",
" <text fill=\"white\" font-size=\"60\" text-anchor=\"middle\" x=\"150\" y=\"125\">SVG</text>\n",
"</svg>"
]),
]),
]
*
*/
const fn = cellOutputMappers.get(output.output_type as nbformat.OutputType);
let result: NotebookCellOutput;
if (fn) {
result = fn(output);
} else {
result = translateDisplayDataOutput(output as any);
}
return result;
}
function createNotebookCellDataFromRawCell(cell: nbformat.IRawCell): NotebookCellData {
const cellData = new NotebookCellData(NotebookCellKind.Code, concatMultilineString(cell.source), 'raw');
cellData.outputs = [];
cellData.metadata = { custom: getNotebookCellMetadata(cell) };
return cellData;
}
function createNotebookCellDataFromMarkdownCell(cell: nbformat.IMarkdownCell): NotebookCellData {
const cellData = new NotebookCellData(
NotebookCellKind.Markup,
concatMultilineString(cell.source),
'markdown'
);
cellData.outputs = [];
cellData.metadata = { custom: getNotebookCellMetadata(cell) };
return cellData;
}
function createNotebookCellDataFromCodeCell(cell: nbformat.ICodeCell, cellLanguage: string): NotebookCellData {
const cellOutputs = Array.isArray(cell.outputs) ? cell.outputs : [];
const outputs = cellOutputs.map(jupyterCellOutputToCellOutput);
const hasExecutionCount = typeof cell.execution_count === 'number' && cell.execution_count > 0;
const source = concatMultilineString(cell.source);
const executionSummary: NotebookCellExecutionSummary = hasExecutionCount
? { executionOrder: cell.execution_count as number }
: {};
const vscodeCustomMetadata = cell.metadata['vscode'] as { [key: string]: any } | undefined;
const cellLanguageId = vscodeCustomMetadata && vscodeCustomMetadata.languageId && typeof vscodeCustomMetadata.languageId === 'string' ? vscodeCustomMetadata.languageId : cellLanguage;
const cellData = new NotebookCellData(NotebookCellKind.Code, source, cellLanguageId);
cellData.outputs = outputs;
cellData.metadata = { custom: getNotebookCellMetadata(cell) };
cellData.executionSummary = executionSummary;
return cellData;
}
function createNotebookCellDataFromJupyterCell(
cellLanguage: string,
cell: nbformat.IBaseCell
): NotebookCellData | undefined {
switch (cell.cell_type) {
case 'raw': {
return createNotebookCellDataFromRawCell(cell as nbformat.IRawCell);
}
case 'markdown': {
return createNotebookCellDataFromMarkdownCell(cell as nbformat.IMarkdownCell);
}
case 'code': {
return createNotebookCellDataFromCodeCell(cell as nbformat.ICodeCell, cellLanguage);
}
}
return;
}
/**
* Converts a NotebookModel into VS Code format.
*/
export function jupyterNotebookModelToNotebookData(
notebookContent: Partial<nbformat.INotebookContent>,
preferredLanguage: string
): NotebookData {
const notebookContentWithoutCells = { ...notebookContent, cells: [] };
if (!notebookContent.cells || notebookContent.cells.length === 0) {
throw new Error('Notebook content is missing cells');
}
const cells = notebookContent.cells
.map(cell => createNotebookCellDataFromJupyterCell(preferredLanguage, cell))
.filter((item): item is NotebookCellData => !!item);
const notebookData = new NotebookData(cells);
notebookData.metadata = { custom: notebookContentWithoutCells };
return notebookData;
}

Просмотреть файл

@ -0,0 +1,116 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import { ensureAllNewCellsHaveCellIds } from './cellIdService';
import { NotebookSerializer } from './notebookSerializer';
// From {nbformat.INotebookMetadata} in @jupyterlab/coreutils
type NotebookMetadata = {
kernelspec?: {
name: string;
display_name: string;
[propName: string]: unknown;
};
language_info?: {
name: string;
codemirror_mode?: string | {};
file_extension?: string;
mimetype?: string;
pygments_lexer?: string;
[propName: string]: unknown;
};
orig_nbformat: number;
[propName: string]: unknown;
};
export function activate(context: vscode.ExtensionContext) {
const serializer = new NotebookSerializer(context);
ensureAllNewCellsHaveCellIds(context);
context.subscriptions.push(vscode.workspace.registerNotebookSerializer('jupyter-notebook', serializer, {
transientOutputs: false,
transientCellMetadata: {
breakpointMargin: true,
custom: false
}
}));
vscode.languages.registerCodeLensProvider({ pattern: '**/*.ipynb' }, {
provideCodeLenses: (document) => {
if (
document.uri.scheme === 'vscode-notebook-cell' ||
document.uri.scheme === 'vscode-notebook-cell-metadata' ||
document.uri.scheme === 'vscode-notebook-cell-output'
) {
return [];
}
const codelens = new vscode.CodeLens(new vscode.Range(0, 0, 0, 0), { title: 'Open in Notebook Editor', command: 'ipynb.openIpynbInNotebookEditor', arguments: [document.uri] });
return [codelens];
}
});
context.subscriptions.push(vscode.commands.registerCommand('ipynb.newUntitledIpynb', async () => {
const language = 'python';
const cell = new vscode.NotebookCellData(vscode.NotebookCellKind.Code, '', language);
const data = new vscode.NotebookData([cell]);
data.metadata = {
custom: {
cells: [],
metadata: {
orig_nbformat: 4
},
nbformat: 4,
nbformat_minor: 2
}
};
const doc = await vscode.workspace.openNotebookDocument('jupyter-notebook', data);
await vscode.window.showNotebookDocument(doc);
}));
context.subscriptions.push(vscode.commands.registerCommand('ipynb.openIpynbInNotebookEditor', async (uri: vscode.Uri) => {
if (vscode.window.activeTextEditor?.document.uri.toString() === uri.toString()) {
await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
}
const document = await vscode.workspace.openNotebookDocument(uri);
await vscode.window.showNotebookDocument(document);
}));
// Update new file contribution
vscode.extensions.onDidChange(() => {
vscode.commands.executeCommand('setContext', 'jupyterEnabled', vscode.extensions.getExtension('ms-toolsai.jupyter'));
});
vscode.commands.executeCommand('setContext', 'jupyterEnabled', vscode.extensions.getExtension('ms-toolsai.jupyter'));
return {
exportNotebook: (notebook: vscode.NotebookData): string => {
return exportNotebook(notebook, serializer);
},
setNotebookMetadata: async (resource: vscode.Uri, metadata: Partial<NotebookMetadata>): Promise<boolean> => {
const document = vscode.workspace.notebookDocuments.find(doc => doc.uri.toString() === resource.toString());
if (!document) {
return false;
}
const edit = new vscode.WorkspaceEdit();
edit.replaceNotebookMetadata(resource, {
...document.metadata,
custom: {
...(document.metadata.custom ?? {}),
metadata: <NotebookMetadata>{
...(document.metadata.custom?.metadata ?? {}),
...metadata
},
}
});
return vscode.workspace.applyEdit(edit);
},
};
}
function exportNotebook(notebook: vscode.NotebookData, serializer: NotebookSerializer): string {
return serializer.serializeNotebookToString(notebook);
}
export function deactivate() { }

Просмотреть файл

@ -0,0 +1,108 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nbformat from '@jupyterlab/nbformat';
import * as detectIndent from 'detect-indent';
import * as vscode from 'vscode';
import { defaultNotebookFormat } from './constants';
import { getPreferredLanguage, jupyterNotebookModelToNotebookData } from './deserializers';
import { createJupyterCellFromNotebookCell, pruneCell, sortObjectPropertiesRecursively } from './serializers';
import * as fnv from '@enonic/fnv-plus';
export class NotebookSerializer implements vscode.NotebookSerializer {
constructor(readonly context: vscode.ExtensionContext) {
}
public async deserializeNotebook(content: Uint8Array, _token: vscode.CancellationToken): Promise<vscode.NotebookData> {
let contents = '';
try {
contents = new TextDecoder().decode(content);
} catch {
}
let json = contents && /\S/.test(contents) ? (JSON.parse(contents) as Partial<nbformat.INotebookContent>) : {};
if (json.__webview_backup) {
const backupId = json.__webview_backup;
const uri = this.context.globalStorageUri;
const folder = uri.with({ path: this.context.globalStorageUri.path.replace('vscode.ipynb', 'ms-toolsai.jupyter') });
const fileHash = fnv.fast1a32hex(backupId) as string;
const fileName = `${fileHash}.ipynb`;
const file = vscode.Uri.joinPath(folder, fileName);
const data = await vscode.workspace.fs.readFile(file);
json = data ? JSON.parse(data.toString()) : {};
if (json.contents && typeof json.contents === 'string') {
contents = json.contents;
json = JSON.parse(contents) as Partial<nbformat.INotebookContent>;
}
}
if (json.nbformat && json.nbformat < 4) {
throw new Error('Only Jupyter notebooks version 4+ are supported');
}
// Then compute indent from the contents (only use first 1K characters as a perf optimization)
const indentAmount = contents ? detectIndent(contents.substring(0, 1_000)).indent : ' ';
const preferredCellLanguage = getPreferredLanguage(json.metadata);
// Ensure we always have a blank cell.
if ((json.cells || []).length === 0) {
json.cells = [
{
cell_type: 'code',
execution_count: null,
metadata: {},
outputs: [],
source: ''
}
];
}
// For notebooks without metadata default the language in metadata to the preferred language.
if (!json.metadata || (!json.metadata.kernelspec && !json.metadata.language_info)) {
json.metadata = json.metadata || { orig_nbformat: defaultNotebookFormat.major };
json.metadata.language_info = json.metadata.language_info || { name: preferredCellLanguage };
}
const data = jupyterNotebookModelToNotebookData(
json,
preferredCellLanguage
);
data.metadata = data.metadata || {};
data.metadata.indentAmount = indentAmount;
return data;
}
public serializeNotebook(data: vscode.NotebookData, _token: vscode.CancellationToken): Uint8Array {
return new TextEncoder().encode(this.serializeNotebookToString(data));
}
public serializeNotebookToString(data: vscode.NotebookData): string {
const notebookContent = getNotebookMetadata(data);
// use the preferred language from document metadata or the first cell language as the notebook preferred cell language
const preferredCellLanguage = notebookContent.metadata?.language_info?.name ?? data.cells[0].languageId;
notebookContent.cells = data.cells
.map(cell => createJupyterCellFromNotebookCell(cell, preferredCellLanguage))
.map(pruneCell);
const indentAmount = data.metadata && 'indentAmount' in data.metadata && typeof data.metadata.indentAmount === 'string' ?
data.metadata.indentAmount :
' ';
// ipynb always ends with a trailing new line (we add this so that SCMs do not show unnecesary changes, resulting from a missing trailing new line).
return JSON.stringify(sortObjectPropertiesRecursively(notebookContent), undefined, indentAmount) + '\n';
}
}
export function getNotebookMetadata(document: vscode.NotebookDocument | vscode.NotebookData) {
const notebookContent: Partial<nbformat.INotebookContent> = document.metadata?.custom || {};
notebookContent.cells = notebookContent.cells || [];
notebookContent.nbformat = notebookContent.nbformat || 4;
notebookContent.nbformat_minor = notebookContent.nbformat_minor ?? 2;
notebookContent.metadata = notebookContent.metadata || { orig_nbformat: 4 };
return notebookContent;
}

Просмотреть файл

@ -0,0 +1,421 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nbformat from '@jupyterlab/nbformat';
import { NotebookCell, NotebookCellData, NotebookCellKind, NotebookCellOutput } from 'vscode';
import { CellMetadata, CellOutputMetadata } from './common';
import { textMimeTypes } from './deserializers';
const textDecoder = new TextDecoder();
enum CellOutputMimeTypes {
error = 'application/vnd.code.notebook.error',
stderr = 'application/vnd.code.notebook.stderr',
stdout = 'application/vnd.code.notebook.stdout'
}
export function createJupyterCellFromNotebookCell(
vscCell: NotebookCellData,
preferredLanguage: string | undefined
): nbformat.IRawCell | nbformat.IMarkdownCell | nbformat.ICodeCell {
let cell: nbformat.IRawCell | nbformat.IMarkdownCell | nbformat.ICodeCell;
if (vscCell.kind === NotebookCellKind.Markup) {
cell = createMarkdownCellFromNotebookCell(vscCell);
} else if (vscCell.languageId === 'raw') {
cell = createRawCellFromNotebookCell(vscCell);
} else {
cell = createCodeCellFromNotebookCell(vscCell, preferredLanguage);
}
return cell;
}
/**
* Sort the JSON to minimize unnecessary SCM changes.
* Jupyter notbeooks/labs sorts the JSON keys in alphabetical order.
* https://github.com/microsoft/vscode-python/issues/13155
*/
export function sortObjectPropertiesRecursively(obj: any): any {
if (Array.isArray(obj)) {
return obj.map(sortObjectPropertiesRecursively);
}
if (obj !== undefined && obj !== null && typeof obj === 'object' && Object.keys(obj).length > 0) {
return (
Object.keys(obj)
.sort()
.reduce<Record<string, any>>((sortedObj, prop) => {
sortedObj[prop] = sortObjectPropertiesRecursively(obj[prop]);
return sortedObj;
}, {}) as any
);
}
return obj;
}
export function getCellMetadata(cell: NotebookCell | NotebookCellData) {
return cell.metadata?.custom as CellMetadata | undefined;
}
function createCodeCellFromNotebookCell(cell: NotebookCellData, preferredLanguage: string | undefined): nbformat.ICodeCell {
const cellMetadata = getCellMetadata(cell);
let metadata = cellMetadata?.metadata || {}; // This cannot be empty.
if (cell.languageId !== preferredLanguage) {
metadata = {
...metadata,
vscode: {
languageId: cell.languageId
}
};
} else {
// cell current language is the same as the preferred cell language in the document, flush the vscode custom language id metadata
metadata.vscode = undefined;
}
const codeCell: any = {
cell_type: 'code',
execution_count: cell.executionSummary?.executionOrder ?? null,
source: splitMultilineString(cell.value.replace(/\r\n/g, '\n')),
outputs: (cell.outputs || []).map(translateCellDisplayOutput),
metadata: metadata
};
if (cellMetadata?.id) {
codeCell.id = cellMetadata.id;
}
return codeCell;
}
function createRawCellFromNotebookCell(cell: NotebookCellData): nbformat.IRawCell {
const cellMetadata = getCellMetadata(cell);
const rawCell: any = {
cell_type: 'raw',
source: splitMultilineString(cell.value.replace(/\r\n/g, '\n')),
metadata: cellMetadata?.metadata || {} // This cannot be empty.
};
if (cellMetadata?.attachments) {
rawCell.attachments = cellMetadata.attachments;
}
if (cellMetadata?.id) {
rawCell.id = cellMetadata.id;
}
return rawCell;
}
function splitMultilineString(source: nbformat.MultilineString): string[] {
if (Array.isArray(source)) {
return source as string[];
}
const str = source.toString();
if (str.length > 0) {
// Each line should be a separate entry, but end with a \n if not last entry
const arr = str.split('\n');
return arr
.map((s, i) => {
if (i < arr.length - 1) {
return `${s}\n`;
}
return s;
})
.filter(s => s.length > 0); // Skip last one if empty (it's the only one that could be length 0)
}
return [];
}
function translateCellDisplayOutput(output: NotebookCellOutput): JupyterOutput {
const customMetadata = output.metadata as CellOutputMetadata | undefined;
let result: JupyterOutput;
// Possible some other extension added some output (do best effort to translate & save in ipynb).
// In which case metadata might not contain `outputType`.
const outputType = customMetadata?.outputType as nbformat.OutputType;
switch (outputType) {
case 'error': {
result = translateCellErrorOutput(output);
break;
}
case 'stream': {
result = convertStreamOutput(output);
break;
}
case 'display_data': {
result = {
output_type: 'display_data',
data: output.items.reduce((prev: any, curr) => {
prev[curr.mime] = convertOutputMimeToJupyterOutput(curr.mime, curr.data as Uint8Array);
return prev;
}, {}),
metadata: customMetadata?.metadata || {} // This can never be undefined.
};
break;
}
case 'execute_result': {
result = {
output_type: 'execute_result',
data: output.items.reduce((prev: any, curr) => {
prev[curr.mime] = convertOutputMimeToJupyterOutput(curr.mime, curr.data as Uint8Array);
return prev;
}, {}),
metadata: customMetadata?.metadata || {}, // This can never be undefined.
execution_count:
typeof customMetadata?.executionCount === 'number' ? customMetadata?.executionCount : null // This can never be undefined, only a number or `null`.
};
break;
}
case 'update_display_data': {
result = {
output_type: 'update_display_data',
data: output.items.reduce((prev: any, curr) => {
prev[curr.mime] = convertOutputMimeToJupyterOutput(curr.mime, curr.data as Uint8Array);
return prev;
}, {}),
metadata: customMetadata?.metadata || {} // This can never be undefined.
};
break;
}
default: {
const isError =
output.items.length === 1 && output.items.every((item) => item.mime === CellOutputMimeTypes.error);
const isStream = output.items.every(
(item) => item.mime === CellOutputMimeTypes.stderr || item.mime === CellOutputMimeTypes.stdout
);
if (isError) {
return translateCellErrorOutput(output);
}
// In the case of .NET & other kernels, we need to ensure we save ipynb correctly.
// Hence if we have stream output, save the output as Jupyter `stream` else `display_data`
// Unless we already know its an unknown output type.
const outputType: nbformat.OutputType =
<nbformat.OutputType>customMetadata?.outputType || (isStream ? 'stream' : 'display_data');
let unknownOutput: nbformat.IUnrecognizedOutput | nbformat.IDisplayData | nbformat.IStream;
if (outputType === 'stream') {
// If saving as `stream` ensure the mandatory properties are set.
unknownOutput = convertStreamOutput(output);
} else if (outputType === 'display_data') {
// If saving as `display_data` ensure the mandatory properties are set.
const displayData: nbformat.IDisplayData = {
data: {},
metadata: {},
output_type: 'display_data'
};
unknownOutput = displayData;
} else {
unknownOutput = {
output_type: outputType
};
}
if (customMetadata?.metadata) {
unknownOutput.metadata = customMetadata.metadata;
}
if (output.items.length > 0) {
unknownOutput.data = output.items.reduce((prev: any, curr) => {
prev[curr.mime] = convertOutputMimeToJupyterOutput(curr.mime, curr.data as Uint8Array);
return prev;
}, {});
}
result = unknownOutput;
break;
}
}
// Account for transient data as well
// `transient.display_id` is used to update cell output in other cells, at least thats one use case we know of.
if (result && customMetadata && customMetadata.transient) {
result.transient = customMetadata.transient;
}
return result;
}
function translateCellErrorOutput(output: NotebookCellOutput): nbformat.IError {
// it should have at least one output item
const firstItem = output.items[0];
// Bug in VS Code.
if (!firstItem.data) {
return {
output_type: 'error',
ename: '',
evalue: '',
traceback: []
};
}
const originalError: undefined | nbformat.IError = output.metadata?.originalError;
const value: Error = JSON.parse(textDecoder.decode(firstItem.data));
return {
output_type: 'error',
ename: value.name,
evalue: value.message,
// VS Code needs an `Error` object which requires a `stack` property as a string.
// Its possible the format could change when converting from `traceback` to `string` and back again to `string`
// When .NET stores errors in output (with their .NET kernel),
// stack is empty, hence store the message instead of stack (so that somethign gets displayed in ipynb).
traceback: originalError?.traceback || splitMultilineString(value.stack || value.message || '')
};
}
function getOutputStreamType(output: NotebookCellOutput): string | undefined {
if (output.items.length > 0) {
return output.items[0].mime === CellOutputMimeTypes.stderr ? 'stderr' : 'stdout';
}
return;
}
type JupyterOutput =
| nbformat.IUnrecognizedOutput
| nbformat.IExecuteResult
| nbformat.IDisplayData
| nbformat.IStream
| nbformat.IError;
function convertStreamOutput(output: NotebookCellOutput): JupyterOutput {
const outputs: string[] = [];
output.items
.filter((opit) => opit.mime === CellOutputMimeTypes.stderr || opit.mime === CellOutputMimeTypes.stdout)
.map((opit) => textDecoder.decode(opit.data))
.forEach(value => {
// Ensure each line is a seprate entry in an array (ending with \n).
const lines = value.split('\n');
// If the last item in `outputs` is not empty and the first item in `lines` is not empty, then concate them.
// As they are part of the same line.
if (outputs.length && lines.length && lines[0].length > 0) {
outputs[outputs.length - 1] = `${outputs[outputs.length - 1]}${lines.shift()!}`;
}
for (const line of lines) {
outputs.push(line);
}
});
for (let index = 0; index < (outputs.length - 1); index++) {
outputs[index] = `${outputs[index]}\n`;
}
// Skip last one if empty (it's the only one that could be length 0)
if (outputs.length && outputs[outputs.length - 1].length === 0) {
outputs.pop();
}
const streamType = getOutputStreamType(output) || 'stdout';
return {
output_type: 'stream',
name: streamType,
text: outputs
};
}
function convertOutputMimeToJupyterOutput(mime: string, value: Uint8Array) {
if (!value) {
return '';
}
try {
if (mime === CellOutputMimeTypes.error) {
const stringValue = textDecoder.decode(value);
return JSON.parse(stringValue);
} else if (mime.startsWith('text/') || textMimeTypes.includes(mime)) {
const stringValue = textDecoder.decode(value);
return splitMultilineString(stringValue);
} else if (mime.startsWith('image/') && mime !== 'image/svg+xml') {
// Images in Jupyter are stored in base64 encoded format.
// VS Code expects bytes when rendering images.
if (typeof Buffer !== 'undefined' && typeof Buffer.from === 'function') {
return Buffer.from(value).toString('base64');
} else {
return btoa(value.reduce((s: string, b: number) => s + String.fromCharCode(b), ''));
}
} else if (mime.toLowerCase().includes('json')) {
const stringValue = textDecoder.decode(value);
return stringValue.length > 0 ? JSON.parse(stringValue) : stringValue;
} else {
const stringValue = textDecoder.decode(value);
return stringValue;
}
} catch (ex) {
return '';
}
}
function createMarkdownCellFromNotebookCell(cell: NotebookCellData): nbformat.IMarkdownCell {
const cellMetadata = getCellMetadata(cell);
const markdownCell: any = {
cell_type: 'markdown',
source: splitMultilineString(cell.value.replace(/\r\n/g, '\n')),
metadata: cellMetadata?.metadata || {} // This cannot be empty.
};
if (cellMetadata?.attachments) {
markdownCell.attachments = cellMetadata.attachments;
}
if (cellMetadata?.id) {
markdownCell.id = cellMetadata.id;
}
return markdownCell;
}
export function pruneCell(cell: nbformat.ICell): nbformat.ICell {
// Source is usually a single string on input. Convert back to an array
const result = {
...cell,
source: splitMultilineString(cell.source)
} as nbformat.ICell;
// Remove outputs and execution_count from non code cells
if (result.cell_type !== 'code') {
delete (<any>result).outputs;
delete (<any>result).execution_count;
} else {
// Clean outputs from code cells
result.outputs = result.outputs ? (result.outputs as nbformat.IOutput[]).map(fixupOutput) : [];
}
return result;
}
const dummyStreamObj: nbformat.IStream = {
output_type: 'stream',
name: 'stdout',
text: ''
};
const dummyErrorObj: nbformat.IError = {
output_type: 'error',
ename: '',
evalue: '',
traceback: ['']
};
const dummyDisplayObj: nbformat.IDisplayData = {
output_type: 'display_data',
data: {},
metadata: {}
};
const dummyExecuteResultObj: nbformat.IExecuteResult = {
output_type: 'execute_result',
name: '',
execution_count: 0,
data: {},
metadata: {}
};
const AllowedCellOutputKeys = {
['stream']: new Set(Object.keys(dummyStreamObj)),
['error']: new Set(Object.keys(dummyErrorObj)),
['display_data']: new Set(Object.keys(dummyDisplayObj)),
['execute_result']: new Set(Object.keys(dummyExecuteResultObj))
};
function fixupOutput(output: nbformat.IOutput): nbformat.IOutput {
let allowedKeys: Set<string>;
switch (output.output_type) {
case 'stream':
case 'error':
case 'execute_result':
case 'display_data':
allowedKeys = AllowedCellOutputKeys[output.output_type];
break;
default:
return output;
}
const result = { ...output };
for (const k of Object.keys(output)) {
if (!allowedKeys.has(k)) {
delete result[k];
}
}
return result;
}

Просмотреть файл

@ -0,0 +1,40 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
const path = require('path');
const testRunner = require('../../../../test/integration/electron/testrunner');
const options: any = {
ui: 'tdd',
color: true,
timeout: 60000
};
// These integration tests is being run in multiple environments (electron, web, remote)
// so we need to set the suite name based on the environment as the suite name is used
// for the test results file name
let suite = '';
if (process.env.VSCODE_BROWSER) {
suite = `${process.env.VSCODE_BROWSER} Browser Integration .ipynb Tests`;
} else if (process.env.REMOTE_VSCODE) {
suite = 'Remote Integration .ipynb Tests';
} else {
suite = 'Integration .ipynb Tests';
}
if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) {
options.reporter = 'mocha-multi-reporters';
options.reporterOptions = {
reporterEnabled: 'spec, mocha-junit-reporter',
mochaJunitReporterReporterOptions: {
testsuitesTitle: `${suite} ${process.platform}`,
mochaFile: path.join(process.env.BUILD_ARTIFACTSTAGINGDIRECTORY, `test-results/${process.platform}-${process.arch}-${suite.toLowerCase().replace(/[^\w]/g, '-')}-results.xml`)
}
};
}
testRunner.configure(options);
export = testRunner;

Просмотреть файл

@ -0,0 +1,637 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nbformat from '@jupyterlab/nbformat';
import * as assert from 'assert';
import * as vscode from 'vscode';
import { jupyterCellOutputToCellOutput, jupyterNotebookModelToNotebookData } from '../deserializers';
function deepStripProperties(obj: any, props: string[]) {
for (let prop in obj) {
if (obj[prop]) {
delete obj[prop];
} else if (typeof obj[prop] === 'object') {
deepStripProperties(obj[prop], props);
}
}
}
suite('ipynb serializer', () => {
const base64EncodedImage =
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mOUlZL6DwAB/wFSU1jVmgAAAABJRU5ErkJggg==';
test('Deserialize', async () => {
const cells: nbformat.ICell[] = [
{
cell_type: 'code',
execution_count: 10,
outputs: [],
source: 'print(1)',
metadata: {}
},
{
cell_type: 'markdown',
source: '# HEAD',
metadata: {}
}
];
const notebook = jupyterNotebookModelToNotebookData({ cells }, 'python');
assert.ok(notebook);
const expectedCodeCell = new vscode.NotebookCellData(vscode.NotebookCellKind.Code, 'print(1)', 'python');
expectedCodeCell.outputs = [];
expectedCodeCell.metadata = { custom: { metadata: {} } };
expectedCodeCell.executionSummary = { executionOrder: 10 };
const expectedMarkdownCell = new vscode.NotebookCellData(vscode.NotebookCellKind.Markup, '# HEAD', 'markdown');
expectedMarkdownCell.outputs = [];
expectedMarkdownCell.metadata = {
custom: { metadata: {} }
};
assert.deepStrictEqual(notebook.cells, [expectedCodeCell, expectedMarkdownCell]);
});
suite('Outputs', () => {
function validateCellOutputTranslation(
outputs: nbformat.IOutput[],
expectedOutputs: vscode.NotebookCellOutput[],
propertiesToExcludeFromComparison: string[] = []
) {
const cells: nbformat.ICell[] = [
{
cell_type: 'code',
execution_count: 10,
outputs,
source: 'print(1)',
metadata: {}
}
];
const notebook = jupyterNotebookModelToNotebookData({ cells }, 'python');
// OutputItems contain an `id` property generated by VSC.
// Exclude that property when comparing.
const propertiesToExclude = propertiesToExcludeFromComparison.concat(['id']);
const actualOuts = notebook.cells[0].outputs;
deepStripProperties(actualOuts, propertiesToExclude);
deepStripProperties(expectedOutputs, propertiesToExclude);
assert.deepStrictEqual(actualOuts, expectedOutputs);
}
test('Empty output', () => {
validateCellOutputTranslation([], []);
});
test('Stream output', () => {
validateCellOutputTranslation(
[
{
output_type: 'stream',
name: 'stderr',
text: 'Error'
},
{
output_type: 'stream',
name: 'stdout',
text: 'NoError'
}
],
[
new vscode.NotebookCellOutput([vscode.NotebookCellOutputItem.stderr('Error')], {
outputType: 'stream'
}),
new vscode.NotebookCellOutput([vscode.NotebookCellOutputItem.stdout('NoError')], {
outputType: 'stream'
})
]
);
});
test('Stream output and line endings', () => {
validateCellOutputTranslation(
[
{
output_type: 'stream',
name: 'stdout',
text: [
'Line1\n',
'\n',
'Line3\n',
'Line4'
]
}
],
[
new vscode.NotebookCellOutput([vscode.NotebookCellOutputItem.stdout('Line1\n\nLine3\nLine4')], {
outputType: 'stream'
})
]
);
validateCellOutputTranslation(
[
{
output_type: 'stream',
name: 'stdout',
text: [
'Hello\n',
'Hello\n',
'Hello\n',
'Hello\n',
'Hello\n',
'Hello\n'
]
}
],
[
new vscode.NotebookCellOutput([vscode.NotebookCellOutputItem.stdout('Hello\nHello\nHello\nHello\nHello\nHello\n')], {
outputType: 'stream'
})
]
);
});
test('Multi-line Stream output', () => {
validateCellOutputTranslation(
[
{
name: 'stdout',
output_type: 'stream',
text: [
'Epoch 1/5\n',
'...\n',
'Epoch 2/5\n',
'...\n',
'Epoch 3/5\n',
'...\n',
'Epoch 4/5\n',
'...\n',
'Epoch 5/5\n',
'...\n'
]
}
],
[
new vscode.NotebookCellOutput([vscode.NotebookCellOutputItem.stdout(['Epoch 1/5\n',
'...\n',
'Epoch 2/5\n',
'...\n',
'Epoch 3/5\n',
'...\n',
'Epoch 4/5\n',
'...\n',
'Epoch 5/5\n',
'...\n'].join(''))], {
outputType: 'stream'
})
]
);
});
test('Multi-line Stream output (last empty line should not be saved in ipynb)', () => {
validateCellOutputTranslation(
[
{
name: 'stderr',
output_type: 'stream',
text: [
'Epoch 1/5\n',
'...\n',
'Epoch 2/5\n',
'...\n',
'Epoch 3/5\n',
'...\n',
'Epoch 4/5\n',
'...\n',
'Epoch 5/5\n',
'...\n'
]
}
],
[
new vscode.NotebookCellOutput([vscode.NotebookCellOutputItem.stderr(['Epoch 1/5\n',
'...\n',
'Epoch 2/5\n',
'...\n',
'Epoch 3/5\n',
'...\n',
'Epoch 4/5\n',
'...\n',
'Epoch 5/5\n',
'...\n',
// This last empty line should not be saved in ipynb.
'\n'].join(''))], {
outputType: 'stream'
})
]
);
});
test('Streamed text with Ansi characters', async () => {
validateCellOutputTranslation(
[
{
name: 'stderr',
text: '\u001b[K\u001b[33m✅ \u001b[0m Loading\n',
output_type: 'stream'
}
],
[
new vscode.NotebookCellOutput(
[vscode.NotebookCellOutputItem.stderr('\u001b[K\u001b[33m✅ \u001b[0m Loading\n')],
{
outputType: 'stream'
}
)
]
);
});
test('Streamed text with angle bracket characters', async () => {
validateCellOutputTranslation(
[
{
name: 'stderr',
text: '1 is < 2',
output_type: 'stream'
}
],
[
new vscode.NotebookCellOutput([vscode.NotebookCellOutputItem.stderr('1 is < 2')], {
outputType: 'stream'
})
]
);
});
test('Streamed text with angle bracket characters and ansi chars', async () => {
validateCellOutputTranslation(
[
{
name: 'stderr',
text: '1 is < 2\u001b[K\u001b[33m✅ \u001b[0m Loading\n',
output_type: 'stream'
}
],
[
new vscode.NotebookCellOutput(
[vscode.NotebookCellOutputItem.stderr('1 is < 2\u001b[K\u001b[33m✅ \u001b[0m Loading\n')],
{
outputType: 'stream'
}
)
]
);
});
test('Error', async () => {
validateCellOutputTranslation(
[
{
ename: 'Error Name',
evalue: 'Error Value',
traceback: ['stack1', 'stack2', 'stack3'],
output_type: 'error'
}
],
[
new vscode.NotebookCellOutput(
[
vscode.NotebookCellOutputItem.error({
name: 'Error Name',
message: 'Error Value',
stack: ['stack1', 'stack2', 'stack3'].join('\n')
})
],
{
outputType: 'error',
originalError: {
ename: 'Error Name',
evalue: 'Error Value',
traceback: ['stack1', 'stack2', 'stack3'],
output_type: 'error'
}
}
)
]
);
});
['display_data', 'execute_result'].forEach(output_type => {
suite(`Rich output for output_type = ${output_type}`, () => {
// Properties to exclude when comparing.
let propertiesToExcludeFromComparison: string[] = [];
setup(() => {
if (output_type === 'display_data') {
// With display_data the execution_count property will never exist in the output.
// We can ignore that (as it will never exist).
// But we leave it in the case of `output_type === 'execute_result'`
propertiesToExcludeFromComparison = ['execution_count', 'executionCount'];
}
});
test('Text mimeType output', async () => {
validateCellOutputTranslation(
[
{
data: {
'text/plain': 'Hello World!'
},
output_type,
metadata: {},
execution_count: 1
}
],
[
new vscode.NotebookCellOutput(
[new vscode.NotebookCellOutputItem(Buffer.from('Hello World!', 'utf8'), 'text/plain')],
{
outputType: output_type,
metadata: {}, // display_data & execute_result always have metadata.
executionCount: 1
}
)
],
propertiesToExcludeFromComparison
);
});
test('png,jpeg images', async () => {
validateCellOutputTranslation(
[
{
execution_count: 1,
data: {
'image/png': base64EncodedImage,
'image/jpeg': base64EncodedImage
},
metadata: {},
output_type
}
],
[
new vscode.NotebookCellOutput(
[
new vscode.NotebookCellOutputItem(Buffer.from(base64EncodedImage, 'base64'), 'image/png'),
new vscode.NotebookCellOutputItem(Buffer.from(base64EncodedImage, 'base64'), 'image/jpeg')
],
{
executionCount: 1,
outputType: output_type,
metadata: {} // display_data & execute_result always have metadata.
}
)
],
propertiesToExcludeFromComparison
);
});
test('png image with a light background', async () => {
validateCellOutputTranslation(
[
{
execution_count: 1,
data: {
'image/png': base64EncodedImage
},
metadata: {
needs_background: 'light'
},
output_type
}
],
[
new vscode.NotebookCellOutput(
[new vscode.NotebookCellOutputItem(Buffer.from(base64EncodedImage, 'base64'), 'image/png')],
{
executionCount: 1,
metadata: {
needs_background: 'light'
},
outputType: output_type
}
)
],
propertiesToExcludeFromComparison
);
});
test('png image with a dark background', async () => {
validateCellOutputTranslation(
[
{
execution_count: 1,
data: {
'image/png': base64EncodedImage
},
metadata: {
needs_background: 'dark'
},
output_type
}
],
[
new vscode.NotebookCellOutput(
[new vscode.NotebookCellOutputItem(Buffer.from(base64EncodedImage, 'base64'), 'image/png')],
{
executionCount: 1,
metadata: {
needs_background: 'dark'
},
outputType: output_type
}
)
],
propertiesToExcludeFromComparison
);
});
test('png image with custom dimensions', async () => {
validateCellOutputTranslation(
[
{
execution_count: 1,
data: {
'image/png': base64EncodedImage
},
metadata: {
'image/png': { height: '111px', width: '999px' }
},
output_type
}
],
[
new vscode.NotebookCellOutput(
[new vscode.NotebookCellOutputItem(Buffer.from(base64EncodedImage, 'base64'), 'image/png')],
{
executionCount: 1,
metadata: {
'image/png': { height: '111px', width: '999px' }
},
outputType: output_type
}
)
],
propertiesToExcludeFromComparison
);
});
test('png allowed to scroll', async () => {
validateCellOutputTranslation(
[
{
execution_count: 1,
data: {
'image/png': base64EncodedImage
},
metadata: {
unconfined: true,
'image/png': { width: '999px' }
},
output_type
}
],
[
new vscode.NotebookCellOutput(
[new vscode.NotebookCellOutputItem(Buffer.from(base64EncodedImage, 'base64'), 'image/png')],
{
executionCount: 1,
metadata: {
unconfined: true,
'image/png': { width: '999px' }
},
outputType: output_type
}
)
],
propertiesToExcludeFromComparison
);
});
});
});
});
suite('Output Order', () => {
test('Verify order of outputs', async () => {
const dataAndExpectedOrder: { output: nbformat.IDisplayData; expectedMimeTypesOrder: string[] }[] = [
{
output: {
data: {
'application/vnd.vegalite.v4+json': 'some json',
'text/html': '<a>Hello</a>'
},
metadata: {},
output_type: 'display_data'
},
expectedMimeTypesOrder: ['application/vnd.vegalite.v4+json', 'text/html']
},
{
output: {
data: {
'application/vnd.vegalite.v4+json': 'some json',
'application/javascript': 'some js',
'text/plain': 'some text',
'text/html': '<a>Hello</a>'
},
metadata: {},
output_type: 'display_data'
},
expectedMimeTypesOrder: [
'application/vnd.vegalite.v4+json',
'text/html',
'application/javascript',
'text/plain'
]
},
{
output: {
data: {
'application/vnd.vegalite.v4+json': '', // Empty, should give preference to other mimetypes.
'application/javascript': 'some js',
'text/plain': 'some text',
'text/html': '<a>Hello</a>'
},
metadata: {},
output_type: 'display_data'
},
expectedMimeTypesOrder: [
'text/html',
'application/javascript',
'text/plain',
'application/vnd.vegalite.v4+json'
]
},
{
output: {
data: {
'text/plain': 'some text',
'text/html': '<a>Hello</a>'
},
metadata: {},
output_type: 'display_data'
},
expectedMimeTypesOrder: ['text/html', 'text/plain']
},
{
output: {
data: {
'application/javascript': 'some js',
'text/plain': 'some text'
},
metadata: {},
output_type: 'display_data'
},
expectedMimeTypesOrder: ['application/javascript', 'text/plain']
},
{
output: {
data: {
'image/svg+xml': 'some svg',
'text/plain': 'some text'
},
metadata: {},
output_type: 'display_data'
},
expectedMimeTypesOrder: ['image/svg+xml', 'text/plain']
},
{
output: {
data: {
'text/latex': 'some latex',
'text/plain': 'some text'
},
metadata: {},
output_type: 'display_data'
},
expectedMimeTypesOrder: ['text/latex', 'text/plain']
},
{
output: {
data: {
'application/vnd.jupyter.widget-view+json': 'some widget',
'text/plain': 'some text'
},
metadata: {},
output_type: 'display_data'
},
expectedMimeTypesOrder: ['application/vnd.jupyter.widget-view+json', 'text/plain']
},
{
output: {
data: {
'text/plain': 'some text',
'image/svg+xml': 'some svg',
'image/png': 'some png'
},
metadata: {},
output_type: 'display_data'
},
expectedMimeTypesOrder: ['image/png', 'image/svg+xml', 'text/plain']
}
];
dataAndExpectedOrder.forEach(({ output, expectedMimeTypesOrder }) => {
const sortedOutputs = jupyterCellOutputToCellOutput(output);
const mimeTypes = sortedOutputs.items.map((item) => item.mime).join(',');
assert.equal(mimeTypes, expectedMimeTypesOrder.join(','));
});
});
});
});

6
extensions/ipynb/src/types.d.ts поставляемый Normal file
Просмотреть файл

@ -0,0 +1,6 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
declare module '@enonic/fnv-plus';

Просмотреть файл

@ -0,0 +1,15 @@
{
"extends": "../tsconfig.base.json",
"compilerOptions": {
"outDir": "./out",
"lib": [
"dom"
]
},
"include": [
"src/**/*",
"../../src/vscode-dts/vscode.d.ts",
"../../src/vscode-dts/vscode.proposed.notebookEditor.d.ts",
"../../src/vscode-dts/vscode.proposed.notebookEditorEdit.d.ts"
]
}

Просмотреть файл

@ -0,0 +1,35 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@enonic/fnv-plus@^1.3.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@enonic/fnv-plus/-/fnv-plus-1.3.0.tgz#be65a7b128a3b544f60aea3ef978d938e85869f3"
integrity sha512-BCN9uNWH8AmiP7BXBJqEinUY9KXalmRzo+L0cB/mQsmFfzODxwQrbvxCHXUNH2iP+qKkWYtB4vyy8N62PViMFw==
"@jupyterlab/nbformat@^3.2.9":
version "3.2.9"
resolved "https://registry.yarnpkg.com/@jupyterlab/nbformat/-/nbformat-3.2.9.tgz#e7d854719612133498af4280d9a8caa0873205b0"
integrity sha512-WSf9OQo8yfFjyodbXRdFoaNwMkaAL5jFZiD6V2f8HqI380ipansWrrV7R9CGzPfgKHpUGZMO1tYKmUwzMhvZ4w==
dependencies:
"@lumino/coreutils" "^1.5.3"
"@lumino/coreutils@^1.5.3":
version "1.12.0"
resolved "https://registry.yarnpkg.com/@lumino/coreutils/-/coreutils-1.12.0.tgz#fbdef760f736eaf2bd396a5c6fc3a68a4b449b15"
integrity sha512-DSglh4ylmLi820CNx9soJmDJCpUgymckdWeGWuN0Ash5g60oQvrQDfosVxEhzmNvtvXv45WZEqSBzDP6E5SEmQ==
"@types/uuid@^8.3.1":
version "8.3.1"
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.1.tgz#1a32969cf8f0364b3d8c8af9cc3555b7805df14f"
integrity sha512-Y2mHTRAbqfFkpjldbkHGY8JIzRN6XqYRliG8/24FcHm2D2PwW24fl5xMRTVGdrb7iMrwCaIEbLWerGIkXuFWVg==
detect-indent@^6.0.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6"
integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==
uuid@^8.3.2:
version "8.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==

Просмотреть файл

@ -67,6 +67,11 @@
"category": "MSSQL",
"title": "%title.designTable%"
},
{
"command": "mssql.changeNotebookConnection",
"category": "MSSQL",
"title": "%title.changeNotebookConnection%"
},
{
"command": "mssql.newLogin",
"category": "MSSQL",
@ -418,6 +423,10 @@
"command": "mssql.designTable",
"when": "false"
},
{
"command": "mssql.changeNotebookConnection",
"when": "false"
},
{
"command": "mssql.newServerRole",
"when": "false"

Просмотреть файл

@ -176,6 +176,7 @@
"objectsListProperties.name": "Name",
"title.newTable": "New Table",
"title.designTable": "Design",
"title.changeNotebookConnection": "Change SQL Notebook Connection",
"mssql.parallelMessageProcessing" : "[Experimental] Whether the requests to the SQL Tools Service should be handled in parallel. This is introduced to discover the issues there might be when handling all requests in parallel. The default value is false. Relaunch of ADS is required when the value is changed.",
"mssql.tableDesigner.preloadDatabaseModel": "Whether to preload the database model when the database node in the object explorer is expanded. When enabled, the loading time of table designer can be reduced. Note: You might see higher than normal memory usage if you need to expand a lot of database nodes.",
"mssql.objectExplorer.groupBySchema": "When enabled, the database objects in Object Explorer will be categorized by schema.",

Просмотреть файл

@ -22,6 +22,7 @@ import { IconPathHelper } from './iconHelper';
import * as nls from 'vscode-nls';
import { INotebookConvertService } from './notebookConvert/notebookConvertService';
import { registerTableDesignerCommands } from './tableDesigner/tableDesigner';
import { SqlNotebookController } from './sqlNotebook/sqlNotebookController';
import { registerObjectManagementCommands } from './objectManagement/commands';
import { TelemetryActions, TelemetryReporter, TelemetryViews } from './telemetry';
@ -114,7 +115,10 @@ export async function activate(context: vscode.ExtensionContext): Promise<IExten
registerTableDesignerCommands(appContext);
registerObjectManagementCommands(appContext);
context.subscriptions.push(new SqlNotebookController());
context.subscriptions.push(TelemetryReporter);
return createMssqlApi(appContext, server);
}

Просмотреть файл

@ -0,0 +1,329 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import * as azdata from 'azdata';
import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle();
interface QueryCompletionHandler {
ownerUri: string;
handler: (results: azdata.BatchSummary[]) => void
}
interface QueryMessageHandler {
ownerUri: string;
handler: (results: azdata.QueryExecuteMessageParams) => void
}
export class SqlNotebookController implements vscode.Disposable {
private readonly _cellUriScheme = 'vscode-notebook-cell';
private readonly _connectionLabel = (serverName: string) => localize('notebookConnection', 'Connected to: {0}', serverName);
private readonly _disconnectedLabel = localize('notebookDisconnected', 'Disconnected');
private readonly _disposables = new Array<vscode.Disposable>();
private readonly _controller: vscode.NotebookController;
private readonly _connectionsMap = new Map<vscode.Uri, azdata.connection.Connection>();
private readonly _executionOrderMap = new Map<vscode.Uri, number>();
private readonly _queryProvider: azdata.QueryProvider;
private readonly _connProvider: azdata.ConnectionProvider;
private readonly _connectionLabelItem: vscode.StatusBarItem;
private _queryCompleteHandler: QueryCompletionHandler;
private _queryMessageHandler: QueryMessageHandler;
private _activeCellUri: string;
constructor() {
this._controller = vscode.notebooks.createNotebookController('sql-controller-id', 'jupyter-notebook', 'SQL');
this._controller.supportedLanguages = ['sql'];
this._controller.supportsExecutionOrder = true;
this._controller.executeHandler = this.execute.bind(this);
const sqlProvider = 'MSSQL';
this._queryProvider = azdata.dataprotocol.getProvider<azdata.QueryProvider>(sqlProvider, azdata.DataProviderType.QueryProvider);
this._queryProvider.registerOnQueryComplete(result => this.handleQueryComplete(result));
this._queryProvider.registerOnMessage(message => this.handleQueryMessage(message));
this._connProvider = azdata.dataprotocol.getProvider<azdata.ConnectionProvider>(sqlProvider, azdata.DataProviderType.ConnectionProvider);
const commandName = 'mssql.changeNotebookConnection';
let changeConnectionCommand = vscode.commands.registerCommand(commandName, async () => await this.changeConnection());
this._disposables.push(changeConnectionCommand);
this._connectionLabelItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right);
this._connectionLabelItem.text = this._disconnectedLabel;
this._connectionLabelItem.tooltip = localize('changeNotebookConnection', 'Change SQL Notebook Connection');
this._connectionLabelItem.command = commandName;
this._disposables.push(this._connectionLabelItem);
// Show connection status if there's a notebook already open when ADS starts
if (vscode.window.activeTextEditor?.document.notebook) {
this._connectionLabelItem.show();
}
let editorChangedEvent = vscode.window.onDidChangeActiveTextEditor(async editor => await this.handleActiveEditorChanged(editor));
this._disposables.push(editorChangedEvent);
let docClosedEvent = vscode.workspace.onDidCloseTextDocument(document => this.handleDocumentClosed(document));
this._disposables.push(docClosedEvent);
}
private handleQueryComplete(result: azdata.QueryExecuteCompleteNotificationResult): void {
if (this._queryCompleteHandler && this._queryCompleteHandler.ownerUri === result.ownerUri) { // Check if handler is undefined separately in case the result URI is also undefined
this._queryCompleteHandler.handler(result.batchSummaries);
}
}
private handleQueryMessage(message: azdata.QueryExecuteMessageParams): void {
if (this._queryMessageHandler && this._queryMessageHandler.ownerUri === message.ownerUri) { // Check if handler is undefined separately in case the result URI is also undefined
this._queryMessageHandler.handler(message);
}
}
private async handleActiveEditorChanged(editor: vscode.TextEditor): Promise<void> {
let notebook = editor?.document.notebook;
if (!notebook) {
// Hide status bar item if the current editor isn't a notebook
this._connectionLabelItem.hide();
} else {
let connection = this._connectionsMap.get(notebook.uri);
if (connection) {
this._connectionLabelItem.text = this._connectionLabel(connection.options['server']);
// If this editor is for a cell, then update the connection for it
this.updateCellConnection(notebook.uri, connection);
} else {
this._connectionLabelItem.text = this._disconnectedLabel;
}
this._connectionLabelItem.show();
}
}
public getConnectionProfile(connection: azdata.connection.Connection): azdata.IConnectionProfile {
let connectionProfile: azdata.IConnectionProfile = {
connectionName: connection.options.connectionName,
serverName: connection.options.server,
databaseName: connection.options.database,
userName: connection.options.user,
password: connection.options.password,
authenticationType: connection.options.authenticationType,
savePassword: connection.options.savePassword,
groupFullName: undefined,
groupId: undefined,
providerName: connection.providerName,
saveProfile: false,
id: connection.connectionId,
options: connection.options
};
return connectionProfile;
}
private handleDocumentClosed(editor: vscode.TextDocument): void {
// Have to check isClosed here since this event is also emitted on doc language changes
if (editor.notebook && editor.isClosed) {
// Remove the connection & execution associations if the doc is closed, but don't close the connection since it might be re-used elsewhere
this._connectionsMap.delete(editor.notebook.uri);
this._executionOrderMap.delete(editor.notebook.uri);
}
}
private updateCellConnection(notebookUri: vscode.Uri, connection: azdata.connection.Connection): void {
let docUri = vscode.window.activeTextEditor?.document.uri;
if (docUri?.scheme === this._cellUriScheme && docUri?.path === notebookUri.path) {
if (this._activeCellUri) {
this._connProvider.disconnect(this._activeCellUri).then(() => undefined, error => console.log(error));
}
this._activeCellUri = docUri.toString();
// Delay connecting in case user is clicking between cells a lot
setTimeout(() => {
if (this._activeCellUri === docUri.toString()) {
let profile = this.getConnectionProfile(connection);
this._connProvider.connect(docUri.toString(), profile).then(
connected => {
if (!connected) {
console.log(`Failed to update cell connection for cell: ${docUri.toString()}`);
}
},
error => {
console.log(error);
});
}
}, 200);
}
}
private async changeConnection(notebook?: vscode.NotebookDocument): Promise<azdata.connection.Connection | undefined> {
let connection: azdata.connection.Connection;
let notebookUri = notebook?.uri ?? vscode.window.activeTextEditor?.document.notebook?.uri;
if (notebookUri) {
connection = await azdata.connection.openConnectionDialog(['MSSQL']);
if (connection) {
this._connectionsMap.set(notebookUri, connection);
this._connectionLabelItem.text = this._connectionLabel(connection.options['server']);
// Connect current notebook cell, if there is one
this.updateCellConnection(notebookUri, connection);
} else {
this._connectionLabelItem.text = this._disconnectedLabel;
}
this._connectionLabelItem.show();
}
return connection;
}
private async execute(cells: vscode.NotebookCell[], notebook: vscode.NotebookDocument, controller: vscode.NotebookController): Promise<void> {
if (this._queryCompleteHandler) {
throw new Error(localize('queryInProgressError', 'Another query is currently in progress. Please wait for that query to complete before running these cells.'));
}
let connection = this._connectionsMap.get(notebook.uri);
if (!connection) {
connection = await this.changeConnection(notebook);
}
let executionOrder = this._executionOrderMap.get(notebook.uri) ?? 0;
for (let cell of cells) {
await this.doExecution(cell, connection, ++executionOrder);
}
this._executionOrderMap.set(notebook.uri, executionOrder);
}
private async doExecution(cell: vscode.NotebookCell, connection: azdata.connection.Connection | undefined, executionOrder: number): Promise<void> {
const execution = this._controller.createNotebookCellExecution(cell);
execution.executionOrder = executionOrder;
execution.start(Date.now());
await execution.clearOutput();
if (!connection) {
await execution.appendOutput([
new vscode.NotebookCellOutput([
vscode.NotebookCellOutputItem.text(localize('noConnectionError', 'No connection provided.'))
])
]);
execution.end(false, Date.now());
return;
}
let cancelHandler: vscode.Disposable;
try {
const ownerUri = await azdata.connection.getUriForConnection(connection.connectionId);
await this._queryProvider.runQueryString(ownerUri, cell.document.getText());
cancelHandler = execution.token.onCancellationRequested(async () => await this._queryProvider.cancelQuery(ownerUri));
let queryComplete = new Promise<void>(resolve => {
let queryCompleteHandler = async (batchSummaries: azdata.BatchSummary[]) => {
let tableHtmlEntries: string[] = [];
for (let batchSummary of batchSummaries) {
if (execution.token.isCancellationRequested) {
break;
}
for (let resultSummary of batchSummary.resultSetSummaries) {
if (execution.token.isCancellationRequested) {
break;
}
if (resultSummary.rowCount > 0) {
// Add column headers
let tableHtml =
`<style>
.output_container .sqlNotebookResults td, .output_container .sqlNotebookResults th {
text-align: left;
}
</style>
<table class="sqlNotebookResults"><thead><tr>`;
for (let column of resultSummary.columnInfo) {
tableHtml += `<th>${htmlEscape(column.columnName)}</th>`;
}
tableHtml += '</tr></thead>';
// Add rows and cells
let subsetResult = await this._queryProvider.getQueryRows({
ownerUri: ownerUri,
batchIndex: batchSummary.id,
resultSetIndex: resultSummary.id,
rowsStartIndex: 0,
rowsCount: resultSummary.rowCount
});
tableHtml += '<tbody>';
for (let row of subsetResult.resultSubset.rows) {
tableHtml += '<tr>';
for (let cell of row) {
tableHtml += `<td>${htmlEscape(cell.displayValue)}</td>`;
}
tableHtml += '</tr>';
}
tableHtml += '</tbody></table>';
tableHtmlEntries.push(tableHtml);
}
}
}
if (execution.token.isCancellationRequested) {
await execution.appendOutput([
new vscode.NotebookCellOutput([
vscode.NotebookCellOutputItem.text(localize('cellExecutionCancelled', 'Cell execution cancelled.'))
])
]);
execution.end(false, Date.now());
} else {
await execution.appendOutput([
new vscode.NotebookCellOutput([
vscode.NotebookCellOutputItem.text(tableHtmlEntries.join('<br><br>'), 'text/html')
])
]);
execution.end(true, Date.now());
}
resolve();
};
this._queryCompleteHandler = { ownerUri: ownerUri, handler: queryCompleteHandler };
});
this._queryMessageHandler = {
ownerUri: ownerUri,
handler: async message => {
await execution.appendOutput([
new vscode.NotebookCellOutput([
vscode.NotebookCellOutputItem.text(message.message.message)
])
]);
}
};
await queryComplete;
} catch (error) {
await execution.appendOutput([
new vscode.NotebookCellOutput([
vscode.NotebookCellOutputItem.error(error)
])
]);
execution.end(false, Date.now());
} finally {
if (cancelHandler) {
cancelHandler.dispose();
}
this._queryCompleteHandler = undefined;
this._queryMessageHandler = undefined;
}
}
dispose() {
this._disposables.forEach(d => d.dispose());
}
}
function htmlEscape(html: string): string {
return html.replace(/[<|>|&|"]/g, function (match) {
switch (match) {
case '<': return '&lt;';
case '>': return '&gt;';
case '&': return '&amp;';
case '"': return '&quot;';
case '\'': return '&#39';
default: return match;
}
});
}

1
extensions/mssql/src/typings/refs.d.ts поставляемый
Просмотреть файл

@ -6,4 +6,5 @@
/// <reference path='../../../../src/sql/azdata.d.ts'/>
/// <reference path='../../../../src/sql/azdata.proposed.d.ts'/>
/// <reference path='../../../../src/vscode-dts/vscode.d.ts'/>
/// <reference path='../../../../src/vscode-dts/vscode.proposed.textDocumentNotebook.d.ts' />
/// <reference path='../../../azurecore/src/azurecore.d.ts' />

1
extensions/notebook-renderers/.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
renderer-out

Просмотреть файл

@ -0,0 +1,6 @@
src/**
notebook/**
tsconfig.json
.gitignore
esbuild.js
src/**

Просмотреть файл

@ -0,0 +1,9 @@
# Builtin Notebook Output Renderers for Azure Data Studio
**Notice:** This extension is bundled with Azure Data Studio. It can be disabled but not uninstalled.
## Features
This extension provides the following notebook renderers for Azure Data Studio:
- Image renderer for png, jpeg and gif

Просмотреть файл

@ -0,0 +1,44 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// @ts-check
const path = require('path');
const esbuild = require('esbuild');
const args = process.argv.slice(2);
const isWatch = args.indexOf('--watch') >= 0;
let outputRoot = __dirname;
const outputRootIndex = args.indexOf('--outputRoot');
if (outputRootIndex >= 0) {
outputRoot = args[outputRootIndex + 1];
}
const srcDir = path.join(__dirname, 'src');
const outDir = path.join(outputRoot, 'renderer-out');
function build() {
return esbuild.build({
entryPoints: [
path.join(srcDir, 'index.ts'),
],
bundle: true,
minify: false,
sourcemap: false,
format: 'esm',
outdir: outDir,
platform: 'browser',
target: ['es2020'],
});
}
build().catch(() => process.exit(1));
if (isWatch) {
const watcher = require('@parcel/watcher');
watcher.subscribe(srcDir, () => {
return build();
});
}

Просмотреть файл

@ -0,0 +1,57 @@
{
"name": "builtin-notebook-renderers",
"displayName": "%displayName%",
"description": "%description%",
"publisher": "vscode",
"version": "1.0.0",
"license": "MIT",
"engines": {
"vscode": "^1.57.0"
},
"capabilities": {
"virtualWorkspaces": true,
"untrustedWorkspaces": {
"supported": true
}
},
"contributes": {
"notebookRenderer": [
{
"id": "vscode-builtin-notebook-renderer",
"entrypoint": "./renderer-out/index.js",
"displayName": "VS Code Builtin Notebook Output Renderer",
"requiresMessaging": "never",
"mimeTypes": [
"image/gif",
"image/png",
"image/jpeg",
"image/git",
"image/svg+xml",
"text/html",
"application/javascript",
"application/vnd.code.notebook.error",
"application/vnd.code.notebook.stdout",
"application/x.notebook.stdout",
"application/x.notebook.stream",
"application/vnd.code.notebook.stderr",
"application/x.notebook.stderr",
"text/plain"
]
}
]
},
"scripts": {
"compile": "npm run build-notebook",
"watch": "node ./esbuild --watch",
"build-notebook": "node ./esbuild"
},
"dependencies": {
},
"devDependencies": {
"@types/vscode-notebook-renderer": "^1.60.0"
},
"repository": {
"type": "git",
"url": "https://github.com/microsoft/vscode.git"
}
}

Просмотреть файл

@ -0,0 +1,4 @@
{
"displayName": "Builtin Notebook Output Renderers",
"description": "Provides basic output renderers for notebooks"
}

Просмотреть файл

@ -0,0 +1,447 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { RGBA, Color } from './color';
import { ansiColorIdentifiers } from './colorMap';
import { linkify } from './linkify';
export function handleANSIOutput(text: string): HTMLSpanElement {
let workspaceFolder = undefined;
const root: HTMLSpanElement = document.createElement('span');
const textLength: number = text.length;
let styleNames: string[] = [];
let customFgColor: RGBA | string | undefined;
let customBgColor: RGBA | string | undefined;
let customUnderlineColor: RGBA | string | undefined;
let colorsInverted: boolean = false;
let currentPos: number = 0;
let buffer: string = '';
while (currentPos < textLength) {
let sequenceFound: boolean = false;
// Potentially an ANSI escape sequence.
// See http://ascii-table.com/ansi-escape-sequences.php & https://en.wikipedia.org/wiki/ANSI_escape_code
if (text.charCodeAt(currentPos) === 27 && text.charAt(currentPos + 1) === '[') {
const startPos: number = currentPos;
currentPos += 2; // Ignore 'Esc[' as it's in every sequence.
let ansiSequence: string = '';
while (currentPos < textLength) {
const char: string = text.charAt(currentPos);
ansiSequence += char;
currentPos++;
// Look for a known sequence terminating character.
if (char.match(/^[ABCDHIJKfhmpsu]$/)) {
sequenceFound = true;
break;
}
}
if (sequenceFound) {
// Flush buffer with previous styles.
appendStylizedStringToContainer(root, buffer, styleNames, workspaceFolder, customFgColor, customBgColor, customUnderlineColor);
buffer = '';
/*
* Certain ranges that are matched here do not contain real graphics rendition sequences. For
* the sake of having a simpler expression, they have been included anyway.
*/
if (ansiSequence.match(/^(?:[34][0-8]|9[0-7]|10[0-7]|[0-9]|2[1-5,7-9]|[34]9|5[8,9]|1[0-9])(?:;[349][0-7]|10[0-7]|[013]|[245]|[34]9)?(?:;[012]?[0-9]?[0-9])*;?m$/)) {
const styleCodes: number[] = ansiSequence.slice(0, -1) // Remove final 'm' character.
.split(';') // Separate style codes.
.filter(elem => elem !== '') // Filter empty elems as '34;m' -> ['34', ''].
.map(elem => parseInt(elem, 10)); // Convert to numbers.
if (styleCodes[0] === 38 || styleCodes[0] === 48 || styleCodes[0] === 58) {
// Advanced color code - can't be combined with formatting codes like simple colors can
// Ignores invalid colors and additional info beyond what is necessary
const colorType = (styleCodes[0] === 38) ? 'foreground' : ((styleCodes[0] === 48) ? 'background' : 'underline');
if (styleCodes[1] === 5) {
set8BitColor(styleCodes, colorType);
} else if (styleCodes[1] === 2) {
set24BitColor(styleCodes, colorType);
}
} else {
setBasicFormatters(styleCodes);
}
} else {
// Unsupported sequence so simply hide it.
}
} else {
currentPos = startPos;
}
}
if (sequenceFound === false) {
buffer += text.charAt(currentPos);
currentPos++;
}
}
// Flush remaining text buffer if not empty.
if (buffer) {
appendStylizedStringToContainer(root, buffer, styleNames, workspaceFolder, customFgColor, customBgColor, customUnderlineColor);
}
return root;
/**
* Change the foreground or background color by clearing the current color
* and adding the new one.
* @param colorType If `'foreground'`, will change the foreground color, if
* `'background'`, will change the background color, and if `'underline'`
* will set the underline color.
* @param color Color to change to. If `undefined` or not provided,
* will clear current color without adding a new one.
*/
function changeColor(colorType: 'foreground' | 'background' | 'underline', color?: RGBA | string | undefined): void {
if (colorType === 'foreground') {
customFgColor = color;
} else if (colorType === 'background') {
customBgColor = color;
} else if (colorType === 'underline') {
customUnderlineColor = color;
}
styleNames = styleNames.filter(style => style !== `code-${colorType}-colored`);
if (color !== undefined) {
styleNames.push(`code-${colorType}-colored`);
}
}
/**
* Swap foreground and background colors. Used for color inversion. Caller should check
* [] flag to make sure it is appropriate to turn ON or OFF (if it is already inverted don't call
*/
function reverseForegroundAndBackgroundColors(): void {
const oldFgColor: RGBA | string | undefined = customFgColor;
changeColor('foreground', customBgColor);
changeColor('background', oldFgColor);
}
/**
* Calculate and set basic ANSI formatting. Supports ON/OFF of bold, italic, underline,
* double underline, crossed-out/strikethrough, overline, dim, blink, rapid blink,
* reverse/invert video, hidden, superscript, subscript and alternate font codes,
* clearing/resetting of foreground, background and underline colors,
* setting normal foreground and background colors, and bright foreground and
* background colors. Not to be used for codes containing advanced colors.
* Will ignore invalid codes.
* @param styleCodes Array of ANSI basic styling numbers, which will be
* applied in order. New colors and backgrounds clear old ones; new formatting
* does not.
* @see {@link https://en.wikipedia.org/wiki/ANSI_escape_code#SGR }
*/
function setBasicFormatters(styleCodes: number[]): void {
for (const code of styleCodes) {
switch (code) {
case 0: { // reset (everything)
styleNames = [];
customFgColor = undefined;
customBgColor = undefined;
break;
}
case 1: { // bold
styleNames = styleNames.filter(style => style !== `code-bold`);
styleNames.push('code-bold');
break;
}
case 2: { // dim
styleNames = styleNames.filter(style => style !== `code-dim`);
styleNames.push('code-dim');
break;
}
case 3: { // italic
styleNames = styleNames.filter(style => style !== `code-italic`);
styleNames.push('code-italic');
break;
}
case 4: { // underline
styleNames = styleNames.filter(style => (style !== `code-underline` && style !== `code-double-underline`));
styleNames.push('code-underline');
break;
}
case 5: { // blink
styleNames = styleNames.filter(style => style !== `code-blink`);
styleNames.push('code-blink');
break;
}
case 6: { // rapid blink
styleNames = styleNames.filter(style => style !== `code-rapid-blink`);
styleNames.push('code-rapid-blink');
break;
}
case 7: { // invert foreground and background
if (!colorsInverted) {
colorsInverted = true;
reverseForegroundAndBackgroundColors();
}
break;
}
case 8: { // hidden
styleNames = styleNames.filter(style => style !== `code-hidden`);
styleNames.push('code-hidden');
break;
}
case 9: { // strike-through/crossed-out
styleNames = styleNames.filter(style => style !== `code-strike-through`);
styleNames.push('code-strike-through');
break;
}
case 10: { // normal default font
styleNames = styleNames.filter(style => !style.startsWith('code-font'));
break;
}
case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20: { // font codes (and 20 is 'blackletter' font code)
styleNames = styleNames.filter(style => !style.startsWith('code-font'));
styleNames.push(`code-font-${code - 10}`);
break;
}
case 21: { // double underline
styleNames = styleNames.filter(style => (style !== `code-underline` && style !== `code-double-underline`));
styleNames.push('code-double-underline');
break;
}
case 22: { // normal intensity (bold off and dim off)
styleNames = styleNames.filter(style => (style !== `code-bold` && style !== `code-dim`));
break;
}
case 23: { // Neither italic or blackletter (font 10)
styleNames = styleNames.filter(style => (style !== `code-italic` && style !== `code-font-10`));
break;
}
case 24: { // not underlined (Neither singly nor doubly underlined)
styleNames = styleNames.filter(style => (style !== `code-underline` && style !== `code-double-underline`));
break;
}
case 25: { // not blinking
styleNames = styleNames.filter(style => (style !== `code-blink` && style !== `code-rapid-blink`));
break;
}
case 27: { // not reversed/inverted
if (colorsInverted) {
colorsInverted = false;
reverseForegroundAndBackgroundColors();
}
break;
}
case 28: { // not hidden (reveal)
styleNames = styleNames.filter(style => style !== `code-hidden`);
break;
}
case 29: { // not crossed-out
styleNames = styleNames.filter(style => style !== `code-strike-through`);
break;
}
case 53: { // overlined
styleNames = styleNames.filter(style => style !== `code-overline`);
styleNames.push('code-overline');
break;
}
case 55: { // not overlined
styleNames = styleNames.filter(style => style !== `code-overline`);
break;
}
case 39: { // default foreground color
changeColor('foreground', undefined);
break;
}
case 49: { // default background color
changeColor('background', undefined);
break;
}
case 59: { // default underline color
changeColor('underline', undefined);
break;
}
case 73: { // superscript
styleNames = styleNames.filter(style => (style !== `code-superscript` && style !== `code-subscript`));
styleNames.push('code-superscript');
break;
}
case 74: { // subscript
styleNames = styleNames.filter(style => (style !== `code-superscript` && style !== `code-subscript`));
styleNames.push('code-subscript');
break;
}
case 75: { // neither superscript or subscript
styleNames = styleNames.filter(style => (style !== `code-superscript` && style !== `code-subscript`));
break;
}
default: {
setBasicColor(code);
break;
}
}
}
}
/**
* Calculate and set styling for complicated 24-bit ANSI color codes.
* @param styleCodes Full list of integer codes that make up the full ANSI
* sequence, including the two defining codes and the three RGB codes.
* @param colorType If `'foreground'`, will set foreground color, if
* `'background'`, will set background color, and if it is `'underline'`
* will set the underline color.
* @see {@link https://en.wikipedia.org/wiki/ANSI_escape_code#24-bit }
*/
function set24BitColor(styleCodes: number[], colorType: 'foreground' | 'background' | 'underline'): void {
if (styleCodes.length >= 5 &&
styleCodes[2] >= 0 && styleCodes[2] <= 255 &&
styleCodes[3] >= 0 && styleCodes[3] <= 255 &&
styleCodes[4] >= 0 && styleCodes[4] <= 255) {
const customColor = new RGBA(styleCodes[2], styleCodes[3], styleCodes[4]);
changeColor(colorType, customColor);
}
}
/**
* Calculate and set styling for advanced 8-bit ANSI color codes.
* @param styleCodes Full list of integer codes that make up the ANSI
* sequence, including the two defining codes and the one color code.
* @param colorType If `'foreground'`, will set foreground color, if
* `'background'`, will set background color and if it is `'underline'`
* will set the underline color.
* @see {@link https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit }
*/
function set8BitColor(styleCodes: number[], colorType: 'foreground' | 'background' | 'underline'): void {
let colorNumber = styleCodes[2];
const color = calcANSI8bitColor(colorNumber);
if (color) {
changeColor(colorType, color);
} else if (colorNumber >= 0 && colorNumber <= 15) {
if (colorType === 'underline') {
// for underline colors we just decode the 0-15 color number to theme color, set and return
changeColor(colorType, ansiColorIdentifiers[colorNumber].colorValue);
return;
}
// Need to map to one of the four basic color ranges (30-37, 90-97, 40-47, 100-107)
colorNumber += 30;
if (colorNumber >= 38) {
// Bright colors
colorNumber += 52;
}
if (colorType === 'background') {
colorNumber += 10;
}
setBasicColor(colorNumber);
}
}
/**
* Calculate and set styling for basic bright and dark ANSI color codes. Uses
* theme colors if available. Automatically distinguishes between foreground
* and background colors; does not support color-clearing codes 39 and 49.
* @param styleCode Integer color code on one of the following ranges:
* [30-37, 90-97, 40-47, 100-107]. If not on one of these ranges, will do
* nothing.
*/
function setBasicColor(styleCode: number): void {
// const theme = themeService.getColorTheme();
let colorType: 'foreground' | 'background' | undefined;
let colorIndex: number | undefined;
if (styleCode >= 30 && styleCode <= 37) {
colorIndex = styleCode - 30;
colorType = 'foreground';
} else if (styleCode >= 90 && styleCode <= 97) {
colorIndex = (styleCode - 90) + 8; // High-intensity (bright)
colorType = 'foreground';
} else if (styleCode >= 40 && styleCode <= 47) {
colorIndex = styleCode - 40;
colorType = 'background';
} else if (styleCode >= 100 && styleCode <= 107) {
colorIndex = (styleCode - 100) + 8; // High-intensity (bright)
colorType = 'background';
}
if (colorIndex !== undefined && colorType) {
changeColor(colorType, ansiColorIdentifiers[colorIndex]?.colorValue);
}
}
}
export function appendStylizedStringToContainer(
root: HTMLElement,
stringContent: string,
cssClasses: string[],
workspaceFolder: string | undefined,
customTextColor?: RGBA | string,
customBackgroundColor?: RGBA | string,
customUnderlineColor?: RGBA | string
): void {
if (!root || !stringContent) {
return;
}
const container = linkify(stringContent, true, workspaceFolder);
container.className = cssClasses.join(' ');
if (customTextColor) {
container.style.color = typeof customTextColor === 'string' ? customTextColor : Color.Format.CSS.formatRGB(new Color(customTextColor));
}
if (customBackgroundColor) {
container.style.backgroundColor = typeof customBackgroundColor === 'string' ? customBackgroundColor : Color.Format.CSS.formatRGB(new Color(customBackgroundColor));
}
if (customUnderlineColor) {
container.style.textDecorationColor = typeof customUnderlineColor === 'string' ? customUnderlineColor : Color.Format.CSS.formatRGB(new Color(customUnderlineColor));
}
root.appendChild(container);
}
/**
* Calculate the color from the color set defined in the ANSI 8-bit standard.
* Standard and high intensity colors are not defined in the standard as specific
* colors, so these and invalid colors return `undefined`.
* @see {@link https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit } for info.
* @param colorNumber The number (ranging from 16 to 255) referring to the color
* desired.
*/
export function calcANSI8bitColor(colorNumber: number): RGBA | undefined {
if (colorNumber % 1 !== 0) {
// Should be integer
return;
} if (colorNumber >= 16 && colorNumber <= 231) {
// Converts to one of 216 RGB colors
colorNumber -= 16;
let blue: number = colorNumber % 6;
colorNumber = (colorNumber - blue) / 6;
let green: number = colorNumber % 6;
colorNumber = (colorNumber - green) / 6;
let red: number = colorNumber;
// red, green, blue now range on [0, 5], need to map to [0,255]
const convFactor: number = 255 / 5;
blue = Math.round(blue * convFactor);
green = Math.round(green * convFactor);
red = Math.round(red * convFactor);
return new RGBA(red, green, blue);
} else if (colorNumber >= 232 && colorNumber <= 255) {
// Converts to a grayscale value
colorNumber -= 232;
const colorLevel: number = Math.round(colorNumber / 23 * 255);
return new RGBA(colorLevel, colorLevel, colorLevel);
} else {
return;
}
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,62 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export const ansiColorIdentifiers: { colorName: string; colorValue: string }[] = [];
export const ansiColorMap: { [key: string]: { index: number } } = {
'terminal.ansiBlack': {
index: 0,
},
'terminal.ansiRed': {
index: 1,
},
'terminal.ansiGreen': {
index: 2,
},
'terminal.ansiYellow': {
index: 3,
},
'terminal.ansiBlue': {
index: 4,
},
'terminal.ansiMagenta': {
index: 5,
},
'terminal.ansiCyan': {
index: 6,
},
'terminal.ansiWhite': {
index: 7,
},
'terminal.ansiBrightBlack': {
index: 8,
},
'terminal.ansiBrightRed': {
index: 9,
},
'terminal.ansiBrightGreen': {
index: 10,
},
'terminal.ansiBrightYellow': {
index: 11,
},
'terminal.ansiBrightBlue': {
index: 12,
},
'terminal.ansiBrightMagenta': {
index: 13,
},
'terminal.ansiBrightCyan': {
index: 14,
},
'terminal.ansiBrightWhite': {
index: 15,
}
};
for (const id in ansiColorMap) {
const entry = ansiColorMap[id];
const colorName = id.substring(13);
ansiColorIdentifiers[entry.index] = { colorName, colorValue: 'var(--vscode-' + id.replace('.', '-') + ')' };
}

Просмотреть файл

@ -0,0 +1,281 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import type { ActivationFunction, OutputItem, RendererContext } from 'vscode-notebook-renderer';
import { truncatedArrayOfString } from './textHelper';
interface IDisposable {
dispose(): void;
}
function clearContainer(container: HTMLElement) {
while (container.firstChild) {
container.removeChild(container.firstChild);
}
}
function renderImage(outputInfo: OutputItem, element: HTMLElement): IDisposable {
const blob = new Blob([outputInfo.data()], { type: outputInfo.mime });
const src = URL.createObjectURL(blob);
const disposable = {
dispose: () => {
URL.revokeObjectURL(src);
}
};
const image = document.createElement('img');
image.src = src;
const display = document.createElement('div');
display.classList.add('display');
display.appendChild(image);
element.appendChild(display);
return disposable;
}
const ttPolicy = window.trustedTypes?.createPolicy('notebookRenderer', {
createHTML: value => value,
createScript: value => value,
});
const preservedScriptAttributes: (keyof HTMLScriptElement)[] = [
'type', 'src', 'nonce', 'noModule', 'async',
];
const domEval = (container: Element) => {
const arr = Array.from(container.getElementsByTagName('script'));
for (let n = 0; n < arr.length; n++) {
const node = arr[n];
const scriptTag = document.createElement('script');
const trustedScript = ttPolicy?.createScript(node.innerText) ?? node.innerText;
scriptTag.text = trustedScript as string;
for (const key of preservedScriptAttributes) {
const val = node[key] || node.getAttribute && node.getAttribute(key);
if (val) {
scriptTag.setAttribute(key, val as any);
}
}
// TODO@connor4312: should script with src not be removed?
container.appendChild(scriptTag).parentNode!.removeChild(scriptTag);
}
};
function renderHTML(outputInfo: OutputItem, container: HTMLElement): void {
clearContainer(container);
const htmlContent = outputInfo.text();
const element = document.createElement('div');
const trustedHtml = ttPolicy?.createHTML(htmlContent) ?? htmlContent;
element.innerHTML = trustedHtml as string;
container.appendChild(element);
domEval(element);
}
function renderJavascript(outputInfo: OutputItem, container: HTMLElement): void {
const str = outputInfo.text();
const scriptVal = `<script type="application/javascript">${str}</script>`;
const element = document.createElement('div');
const trustedHtml = ttPolicy?.createHTML(scriptVal) ?? scriptVal;
element.innerHTML = trustedHtml as string;
container.appendChild(element);
domEval(element);
}
function renderError(outputInfo: OutputItem, container: HTMLElement, ctx: RendererContext<void> & { readonly settings: { readonly lineLimit: number } }): void {
const element = document.createElement('div');
container.appendChild(element);
type ErrorLike = Partial<Error>;
let err: ErrorLike;
try {
err = <ErrorLike>JSON.parse(outputInfo.text());
} catch (e) {
console.log(e);
return;
}
if (err.stack) {
const stack = document.createElement('pre');
stack.classList.add('traceback');
stack.style.margin = '8px 0';
const element = document.createElement('span');
truncatedArrayOfString(outputInfo.id, [err.stack ?? ''], ctx.settings.lineLimit, element);
stack.appendChild(element);
container.appendChild(stack);
} else {
const header = document.createElement('div');
const headerMessage = err.name && err.message ? `${err.name}: ${err.message}` : err.name || err.message;
if (headerMessage) {
header.innerText = headerMessage;
container.appendChild(header);
}
}
container.classList.add('error');
}
function renderStream(outputInfo: OutputItem, container: HTMLElement, error: boolean, ctx: RendererContext<void> & { readonly settings: { readonly lineLimit: number } }): void {
const outputContainer = container.parentElement;
if (!outputContainer) {
// should never happen
return;
}
const prev = outputContainer.previousSibling;
if (prev) {
// OutputItem in the same cell
// check if the previous item is a stream
const outputElement = (prev.firstChild as HTMLElement | null);
if (outputElement && outputElement.getAttribute('output-mime-type') === outputInfo.mime) {
// same stream
const text = outputInfo.text();
const element = document.createElement('span');
truncatedArrayOfString(outputInfo.id, [text], ctx.settings.lineLimit, element);
outputElement.appendChild(element);
return;
}
}
const element = document.createElement('span');
element.classList.add('output-stream');
const text = outputInfo.text();
truncatedArrayOfString(outputInfo.id, [text], ctx.settings.lineLimit, element);
while (container.firstChild) {
container.removeChild(container.firstChild);
}
container.appendChild(element);
container.setAttribute('output-mime-type', outputInfo.mime);
if (error) {
container.classList.add('error');
}
}
function renderText(outputInfo: OutputItem, container: HTMLElement, ctx: RendererContext<void> & { readonly settings: { readonly lineLimit: number } }): void {
clearContainer(container);
const contentNode = document.createElement('div');
contentNode.classList.add('output-plaintext');
const text = outputInfo.text();
truncatedArrayOfString(outputInfo.id, [text], ctx.settings.lineLimit, contentNode);
container.appendChild(contentNode);
}
export const activate: ActivationFunction<void> = (ctx) => {
const disposables = new Map<string, IDisposable>();
const latestContext = ctx as (RendererContext<void> & { readonly settings: { readonly lineLimit: number } });
const style = document.createElement('style');
style.textContent = `
.output-plaintext,
.output-stream,
.traceback {
line-height: var(--notebook-cell-output-line-height);
font-family: var(--notebook-cell-output-font-family);
white-space: pre-wrap;
word-wrap: break-word;
font-size: var(--notebook-cell-output-font-size);
user-select: text;
-webkit-user-select: text;
-ms-user-select: text;
cursor: auto;
}
span.output-stream {
display: inline-block;
}
.output-plaintext .code-bold,
.output-stream .code-bold,
.traceback .code-bold {
font-weight: bold;
}
.output-plaintext .code-italic,
.output-stream .code-italic,
.traceback .code-italic {
font-style: italic;
}
.output-plaintext .code-strike-through,
.output-stream .code-strike-through,
.traceback .code-strike-through {
text-decoration: line-through;
}
.output-plaintext .code-underline,
.output-stream .code-underline,
.traceback .code-underline {
text-decoration: underline;
}
`;
document.body.appendChild(style);
return {
renderOutputItem: (outputInfo, element) => {
switch (outputInfo.mime) {
case 'text/html':
case 'image/svg+xml':
{
if (!ctx.workspace.isTrusted) {
return;
}
renderHTML(outputInfo, element);
}
break;
case 'application/javascript':
{
if (!ctx.workspace.isTrusted) {
return;
}
renderJavascript(outputInfo, element);
}
break;
case 'image/gif':
case 'image/png':
case 'image/jpeg':
case 'image/git':
{
const disposable = renderImage(outputInfo, element);
disposables.set(outputInfo.id, disposable);
}
break;
case 'application/vnd.code.notebook.error':
{
renderError(outputInfo, element, latestContext);
}
break;
case 'application/vnd.code.notebook.stdout':
case 'application/x.notebook.stdout':
case 'application/x.notebook.stream':
{
renderStream(outputInfo, element, false, latestContext);
}
break;
case 'application/vnd.code.notebook.stderr':
case 'application/x.notebook.stderr':
{
renderStream(outputInfo, element, true, latestContext);
}
break;
case 'text/plain':
{
renderText(outputInfo, element, latestContext);
}
break;
default:
break;
}
},
disposeOutputItem: (id: string | undefined) => {
if (id) {
disposables.get(id)?.dispose();
} else {
disposables.forEach(d => d.dispose());
}
}
};
};

Просмотреть файл

@ -0,0 +1,181 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
const CONTROL_CODES = '\\u0000-\\u0020\\u007f-\\u009f';
const WEB_LINK_REGEX = new RegExp('(?:[a-zA-Z][a-zA-Z0-9+.-]{2,}:\\/\\/|data:|www\\.)[^\\s' + CONTROL_CODES + '"]{2,}[^\\s' + CONTROL_CODES + '"\')}\\],:;.!?]', 'ug');
const WIN_ABSOLUTE_PATH = /(?:[a-zA-Z]:(?:(?:\\|\/)[\w\.-]*)+)/;
const WIN_RELATIVE_PATH = /(?:(?:\~|\.)(?:(?:\\|\/)[\w\.-]*)+)/;
const WIN_PATH = new RegExp(`(${WIN_ABSOLUTE_PATH.source}|${WIN_RELATIVE_PATH.source})`);
const POSIX_PATH = /((?:\~|\.)?(?:\/[\w\.-]*)+)/;
const LINE_COLUMN = /(?:\:([\d]+))?(?:\:([\d]+))?/;
const isWindows = navigator.userAgent.indexOf('Windows') >= 0;
const PATH_LINK_REGEX = new RegExp(`${isWindows ? WIN_PATH.source : POSIX_PATH.source}${LINE_COLUMN.source}`, 'g');
const MAX_LENGTH = 2000;
type LinkKind = 'web' | 'path' | 'text';
type LinkPart = {
kind: LinkKind;
value: string;
captures: string[];
};
export class LinkDetector {
constructor(
) {
// noop
}
/**
* Matches and handles web urls, absolute and relative file links in the string provided.
* Returns <span/> element that wraps the processed string, where matched links are replaced by <a/>.
* 'onclick' event is attached to all anchored links that opens them in the editor.
* When splitLines is true, each line of the text, even if it contains no links, is wrapped in a <span>
* and added as a child of the returned <span>.
*/
linkify(text: string, splitLines?: boolean, workspaceFolder?: string): HTMLElement {
if (splitLines) {
const lines = text.split('\n');
for (let i = 0; i < lines.length - 1; i++) {
lines[i] = lines[i] + '\n';
}
if (!lines[lines.length - 1]) {
// Remove the last element ('') that split added.
lines.pop();
}
const elements = lines.map(line => this.linkify(line, false, workspaceFolder));
if (elements.length === 1) {
// Do not wrap single line with extra span.
return elements[0];
}
const container = document.createElement('span');
elements.forEach(e => container.appendChild(e));
return container;
}
const container = document.createElement('span');
for (const part of this.detectLinks(text)) {
try {
switch (part.kind) {
case 'text':
container.appendChild(document.createTextNode(part.value));
break;
case 'web':
container.appendChild(this.createWebLink(part.value));
break;
case 'path': {
container.appendChild(document.createTextNode(part.value));
// const path = part.captures[0];
// const lineNumber = part.captures[1] ? Number(part.captures[1]) : 0;
// const columnNumber = part.captures[2] ? Number(part.captures[2]) : 0;
// container.appendChild(this.createPathLink(part.value, path, lineNumber, columnNumber, workspaceFolder));
break;
}
}
} catch (e) {
container.appendChild(document.createTextNode(part.value));
}
}
return container;
}
private createWebLink(url: string): Node {
const link = this.createLink(url);
return link;
}
// private createPathLink(text: string, path: string, lineNumber: number, columnNumber: number, workspaceFolder: string | undefined): Node {
// if (path[0] === '/' && path[1] === '/') {
// // Most likely a url part which did not match, for example ftp://path.
// return document.createTextNode(text);
// }
// const options = { selection: { startLineNumber: lineNumber, startColumn: columnNumber } };
// if (path[0] === '.') {
// if (!workspaceFolder) {
// return document.createTextNode(text);
// }
// const uri = workspaceFolder.toResource(path);
// const link = this.createLink(text);
// this.decorateLink(link, uri, (preserveFocus: boolean) => this.editorService.openEditor({ resource: uri, options: { ...options, preserveFocus } }));
// return link;
// }
// if (path[0] === '~') {
// const userHome = this.pathService.resolvedUserHome;
// if (userHome) {
// path = osPath.join(userHome.fsPath, path.substring(1));
// }
// }
// const link = this.createLink(text);
// link.tabIndex = 0;
// const uri = URI.file(osPath.normalize(path));
// this.fileService.resolve(uri).then(stat => {
// if (stat.isDirectory) {
// return;
// }
// this.decorateLink(link, uri, (preserveFocus: boolean) => this.editorService.openEditor({ resource: uri, options: { ...options, preserveFocus } }));
// }).catch(() => {
// // If the uri can not be resolved we should not spam the console with error, remain quite #86587
// });
// return link;
// }
private createLink(text: string): HTMLElement {
const link = document.createElement('a');
link.textContent = text;
return link;
}
private detectLinks(text: string): LinkPart[] {
if (text.length > MAX_LENGTH) {
return [{ kind: 'text', value: text, captures: [] }];
}
const regexes: RegExp[] = [WEB_LINK_REGEX, PATH_LINK_REGEX];
const kinds: LinkKind[] = ['web', 'path'];
const result: LinkPart[] = [];
const splitOne = (text: string, regexIndex: number) => {
if (regexIndex >= regexes.length) {
result.push({ value: text, kind: 'text', captures: [] });
return;
}
const regex = regexes[regexIndex];
let currentIndex = 0;
let match;
regex.lastIndex = 0;
while ((match = regex.exec(text)) !== null) {
const stringBeforeMatch = text.substring(currentIndex, match.index);
if (stringBeforeMatch) {
splitOne(stringBeforeMatch, regexIndex + 1);
}
const value = match[0];
result.push({
value: value,
kind: kinds[regexIndex],
captures: match.slice(1)
});
currentIndex = match.index + value.length;
}
const stringAfterMatches = text.substring(currentIndex);
if (stringAfterMatches) {
splitOne(stringAfterMatches, regexIndex + 1);
}
};
splitOne(text, 0);
return result;
}
}
const linkDetector = new LinkDetector();
export function linkify(text: string, splitLines?: boolean, workspaceFolder?: string) {
return linkDetector.linkify(text, splitLines, workspaceFolder);
}

Просмотреть файл

@ -0,0 +1,51 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { handleANSIOutput } from './ansi';
function generateViewMoreElement(outputId: string) {
const container = document.createElement('span');
const first = document.createElement('span');
first.textContent = 'Output exceeds the ';
const second = document.createElement('a');
second.textContent = 'size limit';
second.href = `command:workbench.action.openSettings?["notebook.output.textLineLimit"]`;
const third = document.createElement('span');
third.textContent = '. Open the full output data';
const forth = document.createElement('a');
forth.textContent = ' in a text editor';
forth.href = `command:workbench.action.openLargeOutput?${outputId}`;
container.appendChild(first);
container.appendChild(second);
container.appendChild(third);
container.appendChild(forth);
return container;
}
export function truncatedArrayOfString(id: string, outputs: string[], linesLimit: number, container: HTMLElement) {
let buffer = outputs.join('\n').split(/\r|\n|\r\n/g);
let lineCount = buffer.length;
if (lineCount < linesLimit) {
const spanElement = handleANSIOutput(buffer.slice(0, linesLimit).join('\n'));
container.appendChild(spanElement);
return;
}
container.appendChild(generateViewMoreElement(id));
const div = document.createElement('div');
container.appendChild(div);
div.appendChild(handleANSIOutput(buffer.slice(0, linesLimit - 5).join('\n')));
// view more ...
const viewMoreSpan = document.createElement('span');
viewMoreSpan.innerText = '...';
container.appendChild(viewMoreSpan);
const div2 = document.createElement('div');
container.appendChild(div2);
div2.appendChild(handleANSIOutput(buffer.slice(lineCount - 5).join('\n')));
}

Просмотреть файл

@ -0,0 +1,15 @@
{
"extends": "../tsconfig.base.json",
"compilerOptions": {
"outDir": "./out",
"lib": [
"dom"
]
},
"include": [
"src/**/*",
"../../src/vscode-dts/vscode.d.ts",
"../../src/vscode-dts/vscode.proposed.notebookEditor.d.ts",
"../../src/vscode-dts/vscode.proposed.notebookEditorEdit.d.ts",
]
}

Просмотреть файл

@ -0,0 +1,8 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@types/vscode-notebook-renderer@^1.60.0":
version "1.60.0"
resolved "https://registry.yarnpkg.com/@types/vscode-notebook-renderer/-/vscode-notebook-renderer-1.60.0.tgz#8a67d561f48ddf46a95dfa9f712a79c72c7b8f7a"
integrity sha512-u7TD2uuEZTVuitx0iijOJdKI0JLiQP6PsSBSRy2XmHXUOXcp5p1S56NrjOEDoF+PIHd3NL3eO6KTRSf5nukDqQ==

Просмотреть файл

@ -23,13 +23,12 @@ if "%INTEGRATION_TEST_ELECTRON_PATH%"=="" (
:: and the build bundles extensions into .build webpacked
:: {{SQL CARBON EDIT}} Don't compile unused extensions
call yarn gulp compile-extension:azurecore^
compile-extension:git
compile-extension:git^
:: compile-extension:vscode-api-tests^
:: compile-extension:vscode-colorize-tests^
:: compile-extension:markdown-language-features^
:: compile-extension:typescript-language-features^
:: compile-extension:vscode-custom-editor-tests^
:: compile-extension:vscode-notebook-tests^
:: compile-extension:emmet^
:: compile-extension:css-language-features-server^
:: compile-extension:html-language-features-server^
@ -77,9 +76,6 @@ set ALL_PLATFORMS_API_TESTS_EXTRA_ARGS=--disable-telemetry --skip-welcome --skip
:: call "%INTEGRATION_TEST_ELECTRON_PATH%" $%~dp0\..\extensions\emmet\out\test\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\emmet --extensionTestsPath=%~dp0\..\extensions\emmet\out\test %ALL_PLATFORMS_API_TESTS_EXTRA_ARGS% .
:: if %errorlevel% neq 0 exit /b %errorlevel%
:: call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-notebook-tests\test --enable-proposed-api=vscode.vscode-notebook-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-notebook-tests --extensionTestsPath=%~dp0\..\extensions\vscode-notebook-tests\out %ALL_PLATFORMS_API_TESTS_EXTRA_ARGS%
:: if %errorlevel% neq 0 exit /b %errorlevel%
:: call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\emmet\test-workspace --extensionDevelopmentPath=%~dp0\..\extensions\emmet --extensionTestsPath=%~dp0\..\extensions\emmet\out\test %ALL_PLATFORMS_API_TESTS_EXTRA_ARGS%
:: if %errorlevel% neq 0 exit /b %errorlevel%

Просмотреть файл

@ -34,11 +34,10 @@ else
# and the build bundles extensions into .build webpacked
# {{SQL CARBON EDIT}} Don't compile unused extensions
yarn gulp compile-extension:azurecore \
compile-extension:git
compile-extension:git \
# compile-extension:vscode-api-tests \
# compile-extension:vscode-colorize-tests \
# compile-extension:vscode-custom-editor-tests \
# compile-extension:vscode-notebook-tests \
# compile-extension:markdown-language-features \
# compile-extension:typescript-language-features \
# compile-extension:emmet \

Просмотреть файл

@ -67,3 +67,28 @@ export const terminalIntegratedEnvOsxDescription = localize('terminal.integrated
export const terminalIntegratedEnvLinuxDescription = localize('terminal.integrated.env.linux', "Object with environment variables that will be added to the Azure Data Studio process to be used by the terminal on Linux. Set to `null` to delete the environment variable.")
export const terminalIntegratedEnvWindowsDescription = localize('terminal.integrated.env.windows', "Object with environment variables that will be added to the Azure Data Studio process to be used by the terminal on Windows. Set to `null` to delete the environment variable.")
export const terminalIntegratedInheritEnvDescription = localize('terminal.integrated.inheritEnv', "Whether new shells should inherit their environment from Azure Data Studio, which may source a login shell to ensure $PATH and other development variables are initialized. This has no effect on Windows.")
//#region VS Code Notebook settings
export const displayOrderDescription = localize('notebook.displayOrder.description', "Priority list for output mime types. (for VS Code Notebooks only)");
export const cellToolbarLocationDescription = localize('notebook.cellToolbarLocation.description', "Where the cell toolbar should be shown, or whether it should be hidden. (for VS Code Notebooks only)");
export const showCellStatusbarDescription = localize('notebook.showCellStatusbar.description', "Whether the cell status bar should be shown. (for VS Code Notebooks only)");
export const diffEnablePreviewDescription = localize('notebook.diff.enablePreview.description', "Whether to use the enhanced text diff editor for notebook. (for VS Code Notebooks only)");
export const cellToolbarVisibilityDescription = localize('notebook.cellToolbarVisibility.description', "Whether the cell toolbar should appear on hover or click. (for VS Code Notebooks only)");
export const undoRedoPerCellDescription = localize('notebook.undoRedoPerCell.description', "Whether to use separate undo/redo stack for each cell. (for VS Code Notebooks only)");
export const compactViewDescription = localize('notebook.compactView.description', "Control whether the notebook editor should be rendered in a compact form. For example, when turned on, it will decrease the left margin width. (for VS Code Notebooks only)");
export const focusIndicatorDescription = localize('notebook.focusIndicator.description', "Controls where the focus indicator is rendered, either along the cell borders or on the left gutter. (for VS Code Notebooks only)");
export const insertToolbarPositionDescription = localize('notebook.insertToolbarPosition.description', "Control where the insert cell actions should appear. (for VS Code Notebooks only)");
export const globalToolbarDescription = localize('notebook.globalToolbar.description', "Control whether to render a global toolbar inside the notebook editor. (for VS Code Notebooks only)");
export const consolidatedOutputButtonDescription = localize('notebook.consolidatedOutputButton.description', "Control whether outputs action should be rendered in the output toolbar. (for VS Code Notebooks only)");
export const showFoldingControlsDescription = localize('notebook.showFoldingControls.description', "Controls when the Markdown header folding arrow is shown. (for VS Code Notebooks only)");
export const dragAndDropDescription = localize('notebook.dragAndDrop.description', "Control whether the notebook editor should allow moving cells through drag and drop. (for VS Code Notebooks only)");
export const consolidatedRunButtonDescription = localize('notebook.consolidatedRunButton.description', "Control whether extra actions are shown in a dropdown next to the run button. (for VS Code Notebooks only)");
export const globalToolbarShowLabelDescription = localize('notebook.globalToolbarShowLabel', "Control whether the actions on the notebook toolbar should render label or not. (for VS Code Notebooks only)");
export const textOutputLineLimitDescription = localize('notebook.textOutputLineLimit', "Control how many lines of text in a text output is rendered. (for VS Code Notebooks only)");
export const markupFontSizeDescription = localize('notebook.markup.fontSize', "Controls the font size in pixels of rendered markup in notebooks. When set to `0`, 120% of `#editor.fontSize#` is used. (for VS Code Notebooks only)");
export const interactiveWindowCollapseCodeCellsDescription = localize('notebook.interactiveWindow.collapseCodeCells', "Controls whether code cells in the interactive window are collapsed by default. (for VS Code Notebooks only)");
export const outputLineHeightDescription = localize('notebook.outputLineHeight', "Line height of the output text for notebook cells.\n - Values between 0 and 8 will be used as a multiplier with the font size.\n - Values greater than or equal to 8 will be used as effective values. (for VS Code Notebooks only)");
export const outputFontSizeDescription = localize('notebook.outputFontSize', "Font size for the output text for notebook cells. When set to 0 `#editor.fontSize#` is used. (for VS Code Notebooks only)");
export const outputFontFamilyDescription = localize('notebook.outputFontFamily', "The font family for the output text for notebook cells. When set to empty, the `#editor.fontFamily#` is used. (for VS Code Notebooks only)");
export const experimentalCustomizationDescription = localize('notebook.editorOptions.experimentalCustomization', 'Settings for code editors used in notebooks. This can be used to customize most editor.* settings. (for VS Code Notebooks only)');
//#endregion

Просмотреть файл

@ -727,7 +727,7 @@ export interface MainThreadConnectionManagementShape extends IDisposable {
$listDatabases(connectionId: string): Thenable<string[]>;
$getConnectionString(connectionId: string, includePassword: boolean): Thenable<string>;
$getUriForConnection(connectionId: string): Thenable<string>;
$connect(connectionProfile: azdata.IConnectionProfile, saveConnection: boolean, showDashboard: boolean): Thenable<azdata.ConnectionResult>;
$connect(connectionProfile: azdata.IConnectionProfile, saveConnection: boolean, showDashboard: boolean, ownerUri?: string): Thenable<azdata.ConnectionResult>;
}
export interface MainThreadCredentialManagementShape extends IDisposable {

Просмотреть файл

@ -51,3 +51,16 @@ Registry.as<IConfigurationRegistry>(ConfigExtensions.Configuration).registerConf
}
}
});
Registry.as<IConfigurationRegistry>(ConfigExtensions.Configuration).registerConfiguration({
'id': 'useVSCodeNotebooks',
'title': nls.localize('useVSCodeNotebooksTitle', "Use VS Code notebooks"),
'type': 'object',
'properties': {
'workbench.useVSCodeNotebooks': {
'type': 'boolean',
'default': false,
'description': nls.localize('useVSCodeNotebooks', "(Preview) Use VS Code notebooks as the default notebook experience. Note: Azure Data Studio will need to be restarted to enable this setting.")
}
}
});

Просмотреть файл

@ -27,6 +27,7 @@ export const ToggleRegexCommandId = 'toggleSearchRegex';
export const AddCursorsAtSearchResults = 'addCursorsAtSearchResults';
export const CONFIG_WORKBENCH_ENABLEPREVIEWFEATURES = 'workbench.enablePreviewFeatures';
export const CONFIG_WORKBENCH_USEVSCODENOTEBOOKS = 'workbench.useVSCodeNotebooks';
export const SearchViewFocusedKey = new RawContextKey<boolean>('notebookSearchViewletFocus', false);
export const InputBoxFocusedKey = new RawContextKey<boolean>('inputBoxFocus', false);

Просмотреть файл

@ -37,6 +37,8 @@ import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { KernelsLanguage } from 'sql/workbench/services/notebook/common/notebookConstants';
import { INotebookViews } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViews';
import { Schemas } from 'vs/base/common/network';
import { CONFIG_WORKBENCH_ENABLEPREVIEWFEATURES, CONFIG_WORKBENCH_USEVSCODENOTEBOOKS } from 'sql/workbench/common/constants';
import { ICommandService } from 'vs/platform/commands/common/commands';
const msgLoading = localize('loading', "Loading kernels...");
export const msgChanging = localize('changing', "Changing kernel...");
@ -844,6 +846,8 @@ export class NewNotebookAction extends Action {
@IObjectExplorerService private objectExplorerService: IObjectExplorerService,
@IAdsTelemetryService private _telemetryService: IAdsTelemetryService,
@INotebookService private _notebookService: INotebookService,
@IConfigurationService private _configurationService: IConfigurationService,
@ICommandService private _commandService: ICommandService,
) {
super(id, label);
this.class = 'notebook-action new-notebook';
@ -853,14 +857,21 @@ export class NewNotebookAction extends Action {
this._telemetryService.createActionEvent(TelemetryKeys.TelemetryView.Notebook, TelemetryKeys.NbTelemetryAction.NewNotebookFromConnections)
.withConnectionInfo(context?.connectionProfile)
.send();
let connProfile: azdata.IConnectionProfile;
if (context && context.nodeInfo) {
let node = await this.objectExplorerService.getTreeNode(context.connectionProfile.id, context.nodeInfo.nodePath);
connProfile = TreeUpdateUtils.getConnectionProfile(node).toIConnectionProfile();
} else if (context && context.connectionProfile) {
connProfile = context.connectionProfile;
const usePreviewFeatures = this._configurationService.getValue(CONFIG_WORKBENCH_ENABLEPREVIEWFEATURES);
const useVSCodeNotebooks = this._configurationService.getValue(CONFIG_WORKBENCH_USEVSCODENOTEBOOKS);
if (usePreviewFeatures && useVSCodeNotebooks) {
await this._commandService.executeCommand('ipynb.newUntitledIpynb');
} else {
let connProfile: azdata.IConnectionProfile;
if (context && context.nodeInfo) {
let node = await this.objectExplorerService.getTreeNode(context.connectionProfile.id, context.nodeInfo.nodePath);
connProfile = TreeUpdateUtils.getConnectionProfile(node).toIConnectionProfile();
} else if (context && context.connectionProfile) {
connProfile = context.connectionProfile;
}
await this._notebookService.openNotebook(URI.from({ scheme: 'untitled' }), { connectionProfile: connProfile });
}
await this._notebookService.openNotebook(URI.from({ scheme: 'untitled' }), { connectionProfile: connProfile });
}
}

Просмотреть файл

@ -9,18 +9,18 @@ import { Emitter } from 'vs/base/common/event';
import { DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { NotebookDto } from 'vs/workbench/api/browser/mainThreadNotebookDto';
import { IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; // {{SQL CARBON EDIT}} Remove unused
import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers';
import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService';
import { INotebookCellStatusBarItemProvider, INotebookContributionData, NotebookData as NotebookData, NotebookExtensionDescription, TransientCellMetadata, TransientDocumentMetadata, TransientOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { INotebookContentProvider, INotebookService, SimpleNotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookService';
import { SerializableObjectWithBuffers } from 'vs/workbench/services/extensions/common/proxyIdentifier';
import { ExtHostContext, ExtHostNotebookShape, MainThreadNotebookShape } from '../common/extHost.protocol'; // {{SQL CARBON EDIT}} Remove unused
import { ExtHostContext, ExtHostNotebookShape, MainContext, MainThreadNotebookShape } from '../common/extHost.protocol';
import { ILogService } from 'vs/platform/log/common/log';
import { StopWatch } from 'vs/base/common/stopwatch';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { assertType } from 'vs/base/common/types';
// @extHostNamedCustomer(MainContext.MainThreadNotebook) {{SQL CARBON EDIT}} Disable VS Code notebooks
@extHostNamedCustomer(MainContext.MainThreadNotebook)
export class MainThreadNotebooks implements MainThreadNotebookShape {
private readonly _disposables = new DisposableStore();
@ -196,7 +196,7 @@ CommandsRegistry.registerCommand('_executeDataToNotebook', async (accessor, ...a
const notebookService = accessor.get(INotebookService);
const info = await notebookService.withNotebookDataProvider(notebookType);
if (!(info instanceof SimpleNotebookProviderInfo)) {
return undefined;
return undefined; // {{SQL CARBON EDIT}} strict nulls
}
const dto = await info.serializer.dataToNotebook(bytes);
@ -212,7 +212,7 @@ CommandsRegistry.registerCommand('_executeNotebookToData', async (accessor, ...a
const notebookService = accessor.get(INotebookService);
const info = await notebookService.withNotebookDataProvider(notebookType);
if (!(info instanceof SimpleNotebookProviderInfo)) {
return undefined;
return undefined; // {{SQL CARBON EDIT}} strict nulls
}
const data = NotebookDto.fromNotebookDataDto(dto.value);

Просмотреть файл

@ -10,7 +10,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { MainThreadNotebookDocuments } from 'vs/workbench/api/browser/mainThreadNotebookDocuments';
import { NotebookDto } from 'vs/workbench/api/browser/mainThreadNotebookDto';
import { MainThreadNotebookEditors } from 'vs/workbench/api/browser/mainThreadNotebookEditors';
import { IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers';
import { extHostCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers';
import { editorGroupToColumn } from 'vs/workbench/services/editor/common/editorGroupColumn';
import { getNotebookEditorFromEditorPane, IActiveNotebookEditor, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/notebookEditorService';
@ -69,7 +69,7 @@ class NotebookAndEditorState {
}
}
// @extHostCustomer {{SQL CARBON EDIT}} Disable VS Code notebooks
@extHostCustomer
export class MainThreadNotebooksAndEditors {
// private readonly _onDidAddNotebooks = new Emitter<NotebookTextModel[]>();

Просмотреть файл

@ -11,13 +11,13 @@ import { URI, UriComponents } from 'vs/base/common/uri';
import { ILanguageService } from 'vs/editor/common/languages/language';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { NotebookDto } from 'vs/workbench/api/browser/mainThreadNotebookDto';
import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers';
import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/notebookEditorService';
import { INotebookCellExecution, INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService';
import { IResolvedNotebookKernel, INotebookKernelChangeEvent, INotebookKernelService, NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService';
import { IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers';
import { SerializableObjectWithBuffers } from 'vs/workbench/services/extensions/common/proxyIdentifier';
import { ExtHostContext, ExtHostNotebookKernelsShape, ICellExecuteUpdateDto, ICellExecutionCompleteDto, INotebookKernelDto2, MainThreadNotebookKernelsShape } from '../common/extHost.protocol';
import { ExtHostContext, ExtHostNotebookKernelsShape, ICellExecuteUpdateDto, ICellExecutionCompleteDto, INotebookKernelDto2, MainContext, MainThreadNotebookKernelsShape } from '../common/extHost.protocol';
abstract class MainThreadKernel implements IResolvedNotebookKernel {
readonly type: NotebookKernelType.Resolved = NotebookKernelType.Resolved;
@ -98,7 +98,7 @@ abstract class MainThreadKernel implements IResolvedNotebookKernel {
abstract cancelNotebookCellExecution(uri: URI, cellHandles: number[]): Promise<void>;
}
// @extHostNamedCustomer(MainContext.MainThreadNotebookKernels) {{SQL CARBON EDIT}} Disable VS Code notebooks
@extHostNamedCustomer(MainContext.MainThreadNotebookKernels)
export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape {
private readonly _editors = new Map<INotebookEditor, IDisposable>();

Просмотреть файл

@ -6,9 +6,9 @@
import { Emitter, Event } from 'vs/base/common/event';
import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; // {{SQL CARBON EDIT}} Remove unused
import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers';
import { INotebookKernelService, INotebookProxyKernel, INotebookProxyKernelChangeEvent, ProxyKernelState, NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService';
import { ExtHostContext, ExtHostNotebookProxyKernelsShape, INotebookProxyKernelDto, MainThreadNotebookProxyKernelsShape } from '../common/extHost.protocol'; // {{SQL CARBON EDIT}} Remove unused
import { ExtHostContext, ExtHostNotebookProxyKernelsShape, INotebookProxyKernelDto, MainContext, MainThreadNotebookProxyKernelsShape } from '../common/extHost.protocol';
import { onUnexpectedError } from 'vs/base/common/errors';
abstract class MainThreadProxyKernel implements INotebookProxyKernel {
@ -64,7 +64,7 @@ abstract class MainThreadProxyKernel implements INotebookProxyKernel {
abstract resolveKernel(): Promise<string | null>;
}
// @extHostNamedCustomer(MainContext.MainThreadNotebookProxyKernels) {{SQL CARBON EDIT}} Disable VS Code notebooks
@extHostNamedCustomer(MainContext.MainThreadNotebookProxyKernels)
export class MainThreadNotebookProxyKernels implements MainThreadNotebookProxyKernelsShape {
private readonly _disposables = new DisposableStore();

Просмотреть файл

@ -4,11 +4,11 @@
*--------------------------------------------------------------------------------------------*/
import { Disposable } from 'vs/base/common/lifecycle';
import { ExtHostContext, ExtHostNotebookRenderersShape, MainThreadNotebookRenderersShape } from 'vs/workbench/api/common/extHost.protocol';
import { IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers';
import { ExtHostContext, ExtHostNotebookRenderersShape, MainContext, MainThreadNotebookRenderersShape } from 'vs/workbench/api/common/extHost.protocol';
import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers';
import { INotebookRendererMessagingService } from 'vs/workbench/contrib/notebook/common/notebookRendererMessagingService';
// @extHostNamedCustomer(MainContext.MainThreadNotebookRenderers) {{SQL CARBON EDIT}} Disable VS Code notebooks
@extHostNamedCustomer(MainContext.MainThreadNotebookRenderers)
export class MainThreadNotebookRenderers extends Disposable implements MainThreadNotebookRenderersShape {
private readonly proxy: ExtHostNotebookRenderersShape;

Просмотреть файл

@ -64,7 +64,7 @@ import { ILoggerService, 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'; {{SQL CARBON EDIT}} Disable VS Code notebooks
import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook';
import { ExtHostTheming } from 'vs/workbench/api/common/extHostTheming';
import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService';
import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService';
@ -82,19 +82,19 @@ import { ExtHostUriOpeners } from 'vs/workbench/api/common/extHostUriOpener';
import { IExtHostSecretState } from 'vs/workbench/api/common/exHostSecretState';
import { IExtHostEditorTabs } from 'vs/workbench/api/common/extHostEditorTabs';
import { IExtHostTelemetry } from 'vs/workbench/api/common/extHostTelemetry';
// import { ExtHostNotebookKernels } from 'vs/workbench/api/common/extHostNotebookKernels'; {{SQL CARBON EDIT}} Disable VS Code notebooks
import { ExtHostNotebookKernels } from 'vs/workbench/api/common/extHostNotebookKernels';
import { TextSearchCompleteMessageType } from 'vs/workbench/services/search/common/searchExtTypes';
// import { ExtHostNotebookRenderers } from 'vs/workbench/api/common/extHostNotebookRenderers'; {{SQL CARBON EDIT}} Disable VS Code notebooks
import { ExtHostNotebookRenderers } from 'vs/workbench/api/common/extHostNotebookRenderers';
import { Schemas } from 'vs/base/common/network';
import { matchesScheme } from 'vs/platform/opener/common/opener';
// import { ExtHostNotebookEditors } from 'vs/workbench/api/common/extHostNotebookEditors'; {{SQL CARBON EDIT}} Disable VS Code notebooks
// import { ExtHostNotebookDocuments } from 'vs/workbench/api/common/extHostNotebookDocuments'; {{SQL CARBON EDIT}} Disable VS Code notebooks
import { ExtHostNotebookEditors } from 'vs/workbench/api/common/extHostNotebookEditors';
import { ExtHostNotebookDocuments } from 'vs/workbench/api/common/extHostNotebookDocuments';
// import { ExtHostInteractive } from 'vs/workbench/api/common/extHostInteractive'; {{SQL CARBON EDIT}} Remove until we need it
import { combinedDisposable } from 'vs/base/common/lifecycle';
import { checkProposedApiEnabled, ExtensionIdentifierSet, isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
import { DebugConfigurationProviderTriggerKind } from 'vs/workbench/contrib/debug/common/debug';
import { notebooksNotSupportedError } from 'sql/base/common/locConstants';
import { ExtHostNotebookProxyKernels } from 'vs/workbench/api/common/extHostNotebookProxyKernels';
import { CONFIG_WORKBENCH_ENABLEPREVIEWFEATURES, CONFIG_WORKBENCH_USEVSCODENOTEBOOKS } from 'sql/workbench/common/constants';
export interface IExtensionRegistries {
mine: ExtensionDescriptionRegistry;
@ -158,15 +158,12 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
const extHostDocuments = rpcProtocol.set(ExtHostContext.ExtHostDocuments, new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors));
const extHostDocumentContentProviders = rpcProtocol.set(ExtHostContext.ExtHostDocumentContentProviders, new ExtHostDocumentContentProvider(rpcProtocol, extHostDocumentsAndEditors, extHostLogService));
const extHostDocumentSaveParticipant = rpcProtocol.set(ExtHostContext.ExtHostDocumentSaveParticipant, new ExtHostDocumentSaveParticipant(extHostLogService, extHostDocuments, rpcProtocol.getProxy(MainContext.MainThreadBulkEdits)));
/* {{SQL CARBON EDIT }} Disable VS Code notebooks
const extHostNotebook = rpcProtocol.set(ExtHostContext.ExtHostNotebook, new ExtHostNotebookController(rpcProtocol, extHostCommands, extHostDocumentsAndEditors, extHostDocuments, extensionStoragePaths));
const extHostNotebookDocuments = rpcProtocol.set(ExtHostContext.ExtHostNotebookDocuments, new ExtHostNotebookDocuments(extHostNotebook));
const extHostNotebookEditors = rpcProtocol.set(ExtHostContext.ExtHostNotebookEditors, new ExtHostNotebookEditors(extHostLogService, rpcProtocol, extHostNotebook));
const extHostNotebookRenderers = rpcProtocol.set(ExtHostContext.ExtHostNotebookRenderers, new ExtHostNotebookRenderers(rpcProtocol, extHostNotebook));
const extHostNotebookKernels = rpcProtocol.set(ExtHostContext.ExtHostNotebookKernels, new ExtHostNotebookKernels(rpcProtocol, initData, <any>extHostNotebook, extHostCommands, extHostLogService));
const extHostNotebookKernels = rpcProtocol.set(ExtHostContext.ExtHostNotebookKernels, new ExtHostNotebookKernels(rpcProtocol, initData, extHostNotebook, extHostCommands, extHostLogService));
const extHostNotebookProxyKernels = rpcProtocol.set(ExtHostContext.ExtHostNotebookProxyKernels, new ExtHostNotebookProxyKernels(rpcProtocol, extHostNotebookKernels, extHostLogService));
*/
const extHostNotebookRenderers = rpcProtocol.set(ExtHostContext.ExtHostNotebookRenderers, new ExtHostNotebookRenderers(rpcProtocol, extHostNotebook));
const extHostEditors = rpcProtocol.set(ExtHostContext.ExtHostEditors, new ExtHostEditors(rpcProtocol, extHostDocumentsAndEditors));
const extHostTreeViews = rpcProtocol.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(rpcProtocol.getProxy(MainContext.MainThreadTreeViews), extHostCommands, extHostLogService));
const extHostEditorInsets = rpcProtocol.set(ExtHostContext.ExtHostEditorInsets, new ExtHostEditorInsets(rpcProtocol.getProxy(MainContext.MainThreadEditorInsets), extHostEditors, initData.remote));
@ -195,13 +192,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
// {{SQL CARBON EDIT}} filter out the services we don't expose
const filteredProxies: Set<ProxyIdentifier<any>> = new Set([
ExtHostContext.ExtHostDebugService,
ExtHostContext.ExtHostNotebook,
ExtHostContext.ExtHostNotebookDocuments,
ExtHostContext.ExtHostNotebookEditors,
ExtHostContext.ExtHostNotebookKernels,
ExtHostContext.ExtHostNotebookProxyKernels,
ExtHostContext.ExtHostNotebookRenderers,
ExtHostContext.ExtHostNotebookProxyKernels,
ExtHostContext.ExtHostInteractive
]);
const expected: ProxyIdentifier<any>[] = values(ExtHostContext).filter(v => !filteredProxies.has(v));
@ -219,6 +209,15 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
ExtHostApiCommands.register(extHostCommands);
return function (extension: IExtensionDescription, extensionInfo: IExtensionRegistries, configProvider: ExtHostConfigProvider): typeof vscode {
// {{SQL CARBON EDIT}}
const checkVSCodeNotebooksEnabled = (extension: IExtensionDescription) => {
const usePreviewFeatures = configProvider.getConfiguration(CONFIG_WORKBENCH_ENABLEPREVIEWFEATURES);
const useVSCodeNotebooks = configProvider.getConfiguration(CONFIG_WORKBENCH_USEVSCODENOTEBOOKS);
const notebooksEnabled = usePreviewFeatures && useVSCodeNotebooks;
if (!notebooksEnabled) {
throw new Error(`Notebook extension '${extension.identifier.value}' is not supported. VS Code notebook functionality is currently disabled.`);
}
}
// Check document selectors for being overly generic. Technically this isn't a problem but
// in practice many extensions say they support `fooLang` but need fs-access to do so. Those
@ -775,32 +774,33 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
return extHostWebviewViews.registerWebviewViewProvider(extension, viewId, provider, options?.webviewOptions);
},
get activeNotebookEditor(): vscode.NotebookEditor | undefined {
// {{SQL CARBON EDIT}} Disable VS Code notebooks
throw new Error(notebooksNotSupportedError);
checkProposedApiEnabled(extension, 'notebookEditor');
return extHostNotebook.activeNotebookEditor;
},
onDidChangeActiveNotebookEditor(listener, thisArgs?, disposables?) {
// {{SQL CARBON EDIT}} Disable VS Code notebooks
throw new Error(notebooksNotSupportedError);
checkProposedApiEnabled(extension, 'notebookEditor');
return extHostNotebook.onDidChangeActiveNotebookEditor(listener, thisArgs, disposables);
},
get visibleNotebookEditors() {
// {{SQL CARBON EDIT}} Disable VS Code notebooks
return undefined;
checkProposedApiEnabled(extension, 'notebookEditor');
return extHostNotebook.visibleNotebookEditors;
},
get onDidChangeVisibleNotebookEditors() {
// {{SQL CARBON EDIT}} Disable VS Code notebooks
return undefined;
checkProposedApiEnabled(extension, 'notebookEditor');
return extHostNotebook.onDidChangeVisibleNotebookEditors;
},
onDidChangeNotebookEditorSelection(listener, thisArgs?, disposables?) {
// {{SQL CARBON EDIT}} Disable VS Code notebooks
throw new Error(notebooksNotSupportedError);
checkProposedApiEnabled(extension, 'notebookEditor');
return extHostNotebookEditors.onDidChangeNotebookEditorSelection(listener, thisArgs, disposables);
},
onDidChangeNotebookEditorVisibleRanges(listener, thisArgs?, disposables?) {
// {{SQL CARBON EDIT}} Disable VS Code notebooks
throw new Error(notebooksNotSupportedError);
checkProposedApiEnabled(extension, 'notebookEditor');
return extHostNotebookEditors.onDidChangeNotebookEditorVisibleRanges(listener, thisArgs, disposables);
},
showNotebookDocument(uriOrDocument: URI | vscode.NotebookDocument, options?: vscode.NotebookDocumentShowOptions): Thenable<vscode.NotebookEditor> {
// {{SQL CARBON EDIT}} Disable VS Code notebooks
throw new Error(notebooksNotSupportedError);
showNotebookDocument(uriOrDocument, options?) {
checkProposedApiEnabled(extension, 'notebookEditor');
checkVSCodeNotebooksEnabled(extension); // {{SQL CARBON EDIT}}
return extHostNotebook.showNotebookDocument(uriOrDocument, options);
},
registerExternalUriOpener(id: string, opener: vscode.ExternalUriOpener, metadata: vscode.ExternalUriOpenerMetadata) {
checkProposedApiEnabled(extension, 'externalUriOpener');
@ -925,36 +925,39 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
return extHostDocumentSaveParticipant.getOnWillSaveTextDocumentEvent(extension)(listener, thisArgs, disposables);
},
get notebookDocuments(): vscode.NotebookDocument[] {
// {{SQL CARBON EDIT}} Disable VS Code notebooks
throw new Error(notebooksNotSupportedError);
return extHostNotebook.notebookDocuments.map(d => d.apiNotebook);
},
async openNotebookDocument(uriOrType?: URI | string, content?: vscode.NotebookData): Promise<vscode.NotebookDocument> {
// {{SQL CARBON EDIT}} Disable VS Code notebooks
throw new Error(notebooksNotSupportedError);
async openNotebookDocument(uriOrType?: URI | string, content?: vscode.NotebookData) {
checkVSCodeNotebooksEnabled(extension); // {{SQL CARBON EDIT}}
let uri: URI;
if (URI.isUri(uriOrType)) {
uri = uriOrType;
await extHostNotebook.openNotebookDocument(uriOrType);
} else if (typeof uriOrType === 'string') {
uri = URI.revive(await extHostNotebook.createNotebookDocument({ viewType: uriOrType, content }));
} else {
throw new Error('Invalid arguments');
}
return extHostNotebook.getNotebookDocument(uri).apiNotebook;
},
onDidSaveNotebookDocument(listener, thisArg, disposables) {
// {{SQL CARBON EDIT}} Disable VS Code notebooks
throw new Error(notebooksNotSupportedError);
return extHostNotebookDocuments.onDidSaveNotebookDocument(listener, thisArg, disposables);
},
onDidChangeNotebookDocument(listener, thisArg, disposables) {
// {{SQL CARBON EDIT}} Disable VS Code notebooks
throw new Error(notebooksNotSupportedError);
return extHostNotebookDocuments.onDidChangeNotebookDocument(listener, thisArg, disposables);
},
get onDidOpenNotebookDocument(): Event<vscode.NotebookDocument> {
// {{SQL CARBON EDIT}} Disable VS Code notebooks
throw new Error(notebooksNotSupportedError);
return extHostNotebook.onDidOpenNotebookDocument;
},
get onDidCloseNotebookDocument(): Event<vscode.NotebookDocument> {
// {{SQL CARBON EDIT}} Disable VS Code notebooks
throw new Error(notebooksNotSupportedError);
return extHostNotebook.onDidCloseNotebookDocument;
},
registerNotebookSerializer(viewType: string, serializer: vscode.NotebookSerializer, options?: vscode.NotebookDocumentContentOptions, registration?: vscode.NotebookRegistrationData) {
// {{SQL CARBON EDIT}} Disable VS Code notebooks
throw new Error(notebooksNotSupportedError);
return extHostNotebook.registerNotebookSerializer(extension, viewType, serializer, options, isProposedApiEnabled(extension, 'notebookLiveShare') ? registration : undefined);
},
registerNotebookContentProvider: (viewType: string, provider: vscode.NotebookContentProvider, options?: vscode.NotebookDocumentContentOptions, registration?: vscode.NotebookRegistrationData) => {
// {{SQL CARBON EDIT}} Disable VS Code notebooks
throw new Error(notebooksNotSupportedError);
checkProposedApiEnabled(extension, 'notebookContentProvider');
return extHostNotebook.registerNotebookContentProvider(extension, viewType, provider, options, isProposedApiEnabled(extension, 'notebookLiveShare') ? registration : undefined);
},
onDidChangeConfiguration: (listener: (_: any) => any, thisArgs?: any, disposables?: extHostTypes.Disposable[]) => {
return configProvider.onDidChangeConfiguration(listener, thisArgs, disposables);
@ -1191,28 +1194,25 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
// namespace: notebook
const notebooks: typeof vscode.notebooks = {
createNotebookController(id: string, notebookType: string, label: string, handler?, rendererScripts?: vscode.NotebookRendererScript[]) {
// {{SQL CARBON EDIT}} Disable VS Code notebooks
throw new Error(notebooksNotSupportedError);
return extHostNotebookKernels.createNotebookController(extension, id, notebookType, label, handler, isProposedApiEnabled(extension, 'notebookMessaging') ? rendererScripts : undefined);
},
registerNotebookCellStatusBarItemProvider: (notebookType: string, provider: vscode.NotebookCellStatusBarItemProvider) => {
// {{SQL CARBON EDIT}} Disable VS Code notebooks
throw new Error(notebooksNotSupportedError);
return extHostNotebook.registerNotebookCellStatusBarItemProvider(extension, notebookType, provider);
},
createNotebookEditorDecorationType(options: vscode.NotebookDecorationRenderOptions): vscode.NotebookEditorDecorationType {
// {{SQL CARBON EDIT}} Disable VS Code notebooks
throw new Error(notebooksNotSupportedError);
checkProposedApiEnabled(extension, 'notebookEditorDecorationType');
return extHostNotebookEditors.createNotebookEditorDecorationType(options);
},
createRendererMessaging(rendererId) {
// {{SQL CARBON EDIT}} Disable VS Code notebooks
throw new Error(notebooksNotSupportedError);
return extHostNotebookRenderers.createRendererMessaging(extension, rendererId);
},
onDidChangeNotebookCellExecutionState(listener, thisArgs?, disposables?) {
// {{SQL CARBON EDIT}} Disable VS Code notebooks
throw new Error(notebooksNotSupportedError);
checkProposedApiEnabled(extension, 'notebookCellExecutionState');
return extHostNotebookKernels.onDidChangeNotebookCellExecutionState(listener, thisArgs, disposables);
},
createNotebookProxyController(id: string, notebookType: string, label: string, handler: () => vscode.NotebookController | string | Thenable<vscode.NotebookController | string>) {
// {{SQL CARBON EDIT}} Disable VS Code notebooks
throw new Error(notebooksNotSupportedError);
checkProposedApiEnabled(extension, 'notebookProxyController');
return extHostNotebookProxyKernels.createNotebookProxyController(extension, id, notebookType, label, handler);
}
};

Просмотреть файл

@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/* {{SQL CARBON EDIT}} Disable notebook breakpoints because the debug service is not enabled
import { RunOnceScheduler } from 'vs/base/common/async';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { ResourceMap } from 'vs/base/common/map';
@ -197,3 +197,4 @@ class NotebookCellPausing extends Disposable implements IWorkbenchContribution {
}
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(NotebookCellPausing, LifecyclePhase.Restored);
*/

Просмотреть файл

@ -15,7 +15,7 @@ import { IModelService } from 'vs/editor/common/services/model';
import { ILanguageSelection, ILanguageService } from 'vs/editor/common/languages/language';
import { ITextModelContentProvider, ITextModelService } from 'vs/editor/common/services/resolverService';
import * as nls from 'vs/nls';
import { Extensions, IConfigurationPropertySchema, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; // {{SQL CARBON EDIT}} Remove unused
import { Extensions, IConfigurationPropertySchema, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@ -105,7 +105,7 @@ import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeat
import { NotebookInfo } from 'vs/editor/common/languageFeatureRegistry';
import { COMMENTEDITOR_DECORATION_KEY } from 'vs/workbench/contrib/comments/browser/commentReply';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import * as locConstants from 'sql/base/common/locConstants';
/*--------------------------------------------------------------------------------------------- */
@ -728,9 +728,9 @@ for (const editorOption of editorOptionsRegistry) {
}
}
/* {{SQL CARBON EDIT}} Remove VS Notebook configurations
// {{SQL CARBON EDIT}} Add updated description
const editorOptionsCustomizationSchema: IConfigurationPropertySchema = {
description: nls.localize('notebook.editorOptions.experimentalCustomization', 'Settings for code editors used in notebooks. This can be used to customize most editor.* settings.'),
description: locConstants.experimentalCustomizationDescription,
default: {},
allOf: [
{
@ -748,8 +748,8 @@ const editorOptionsCustomizationSchema: IConfigurationPropertySchema = {
],
tags: ['notebookLayout']
};
*/
// {{SQL CARBON EDIT}} Add updated descriptions
const configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
configurationRegistry.registerConfiguration({
id: 'notebook',
@ -757,161 +757,160 @@ configurationRegistry.registerConfiguration({
title: nls.localize('notebookConfigurationTitle', "Notebook"),
type: 'object',
properties: {
// {{SQL CARBON EDIT}} Remove unused VS Code Notebook configurations
// [DisplayOrderKey]: {
// description: nls.localize('notebook.displayOrder.description', "Priority list for output mime types"),
// type: ['array'],
// items: {
// type: 'string'
// },
// default: []
// },
// [NotebookSetting.cellToolbarLocation]: {
// description: nls.localize('notebook.cellToolbarLocation.description', "Where the cell toolbar should be shown, or whether it should be hidden."),
// type: 'object',
// additionalProperties: {
// markdownDescription: nls.localize('notebook.cellToolbarLocation.viewType', "Configure the cell toolbar position for for specific file types"),
// type: 'string',
// enum: ['left', 'right', 'hidden']
// },
// default: {
// 'default': 'right'
// },
// tags: ['notebookLayout']
// },
// [ShowCellStatusBar]: {
// description: nls.localize('notebook.showCellStatusbar.description', "Whether the cell status bar should be shown."),
// type: 'string',
// enum: ['hidden', 'visible', 'visibleAfterExecute'],
// enumDescriptions: [
// nls.localize('notebook.showCellStatusbar.hidden.description', "The cell Status bar is always hidden."),
// nls.localize('notebook.showCellStatusbar.visible.description', "The cell Status bar is always visible."),
// nls.localize('notebook.showCellStatusbar.visibleAfterExecute.description', "The cell Status bar is hidden until the cell has executed. Then it becomes visible to show the execution status.")],
// default: 'visible',
// tags: ['notebookLayout']
// },
// [NotebookTextDiffEditorPreview]: {
// description: nls.localize('notebook.diff.enablePreview.description', "Whether to use the enhanced text diff editor for notebook."),
// type: 'boolean',
// default: true,
// tags: ['notebookLayout']
// },
// [CellToolbarVisibility]: {
// markdownDescription: nls.localize('notebook.cellToolbarVisibility.description', "Whether the cell toolbar should appear on hover or click."),
// type: 'string',
// enum: ['hover', 'click'],
// default: 'click',
// tags: ['notebookLayout']
// },
// [UndoRedoPerCell]: {
// description: nls.localize('notebook.undoRedoPerCell.description', "Whether to use separate undo/redo stack for each cell."),
// type: 'boolean',
// default: true,
// tags: ['notebookLayout']
// },
// [CompactView]: {
// description: nls.localize('notebook.compactView.description', "Control whether the notebook editor should be rendered in a compact form. "),
// type: 'boolean',
// default: true,
// tags: ['notebookLayout']
// },
// [FocusIndicator]: {
// description: nls.localize('notebook.focusIndicator.description', "Controls where the focus indicator is rendered, either along the cell borders or on the left gutter"),
// type: 'string',
// enum: ['border', 'gutter'],
// default: 'gutter',
// tags: ['notebookLayout']
// },
// [InsertToolbarLocation]: {
// description: nls.localize('notebook.insertToolbarPosition.description', "Control where the insert cell actions should appear."),
// type: 'string',
// enum: ['betweenCells', 'notebookToolbar', 'both', 'hidden'],
// enumDescriptions: [
// nls.localize('insertToolbarLocation.betweenCells', "A toolbar that appears on hover between cells."),
// nls.localize('insertToolbarLocation.notebookToolbar', "The toolbar at the top of the notebook editor."),
// nls.localize('insertToolbarLocation.both', "Both toolbars."),
// nls.localize('insertToolbarLocation.hidden', "The insert actions don't appear anywhere."),
// ],
// default: 'both',
// tags: ['notebookLayout']
// },
// [GlobalToolbar]: {
// description: nls.localize('notebook.globalToolbar.description', "Control whether to render a global toolbar inside the notebook editor."),
// type: 'boolean',
// default: true,
// tags: ['notebookLayout']
// },
// [ConsolidatedOutputButton]: {
// description: nls.localize('notebook.consolidatedOutputButton.description', "Control whether outputs action should be rendered in the output toolbar."),
// type: 'boolean',
// default: true,
// tags: ['notebookLayout']
// },
// [ShowFoldingControls]: {
// description: nls.localize('notebook.showFoldingControls.description', "Controls when the Markdown header folding arrow is shown."),
// type: 'string',
// enum: ['always', 'mouseover'],
// enumDescriptions: [
// nls.localize('showFoldingControls.always', "The folding controls are always visible."),
// nls.localize('showFoldingControls.mouseover', "The folding controls are visible only on mouseover."),
// ],
// default: 'mouseover',
// tags: ['notebookLayout']
// },
// [DragAndDropEnabled]: {
// description: nls.localize('notebook.dragAndDrop.description', "Control whether the notebook editor should allow moving cells through drag and drop."),
// type: 'boolean',
// default: true,
// tags: ['notebookLayout']
// },
// [ConsolidatedRunButton]: {
// description: nls.localize('notebook.consolidatedRunButton.description', "Control whether extra actions are shown in a dropdown next to the run button."),
// type: 'boolean',
// default: false,
// tags: ['notebookLayout']
// },
// [GlobalToolbarShowLabel]: {
// description: nls.localize('notebook.globalToolbarShowLabel', "Control whether the actions on the notebook toolbar should render label or not."),
// type: 'boolean',
// enum: ['always', 'never', 'dynamic'],
// default: true,
// tags: ['notebookLayout']
// },
// [TextOutputLineLimit]: {
// description: nls.localize('notebook.textOutputLineLimit', "Control how many lines of text in a text output is rendered."),
// type: 'number',
// default: 30,
// tags: ['notebookLayout']
// },
// [NotebookSetting.markupFontSize]: {
// markdownDescription: nls.localize('notebook.markup.fontSize', "Controls the font size of rendered markup in notebooks. When set to `0`, 120% of `#editor.fontSize#` is used."),
// type: 'number',
// default: 0,
// tags: ['notebookLayout']
// },
// [NotebookSetting.cellEditorOptionsCustomizations]: editorOptionsCustomizationSchema
// [NotebookSetting.interactiveWindowCollapseCodeCells]: {
// markdownDescription: nls.localize('notebook.interactiveWindow.collapseCodeCells', "Controls whether code cells in the interactive window are collapsed by default."),
// type: 'string',
// enum: ['always', 'never', 'fromEditor'],
// default: 'fromEditor'
// },
// [NotebookSetting.outputLineHeight]: {
// markdownDescription: nls.localize('notebook.outputLineHeight', "Line height of the output text for notebook cells.\n - Values between 0 and 8 will be used as a multiplier with the font size.\n - Values greater than or equal to 8 will be used as effective values."),
// type: 'number',
// default: 22,
// tags: ['notebookLayout']
// },
// [NotebookSetting.outputFontSize]: {
// markdownDescription: nls.localize('notebook.outputFontSize', "Font size for the output text for notebook cells. When set to 0 `#editor.fontSize#` is used."),
// type: 'number',
// default: 0,
// tags: ['notebookLayout']
// },
// [NotebookSetting.outputFontFamily]: {
// markdownDescription: nls.localize('notebook.outputFontFamily', "The font family for the output text for notebook cells. When set to empty, the `#editor.fontFamily#` is used."),
// type: 'string',
// tags: ['notebookLayout']
// },
[NotebookSetting.displayOrder]: {
description: locConstants.displayOrderDescription,
type: 'array',
items: {
type: 'string'
},
default: []
},
[NotebookSetting.cellToolbarLocation]: {
description: locConstants.cellToolbarLocationDescription,
type: 'object',
additionalProperties: {
markdownDescription: nls.localize('notebook.cellToolbarLocation.viewType', "Configure the cell toolbar position for for specific file types"),
type: 'string',
enum: ['left', 'right', 'hidden']
},
default: {
'default': 'right'
},
tags: ['notebookLayout']
},
[NotebookSetting.showCellStatusBar]: {
description: locConstants.showCellStatusbarDescription,
type: 'string',
enum: ['hidden', 'visible', 'visibleAfterExecute'],
enumDescriptions: [
nls.localize('notebook.showCellStatusbar.hidden.description', "The cell Status bar is always hidden."),
nls.localize('notebook.showCellStatusbar.visible.description', "The cell Status bar is always visible."),
nls.localize('notebook.showCellStatusbar.visibleAfterExecute.description', "The cell Status bar is hidden until the cell has executed. Then it becomes visible to show the execution status.")],
default: 'visible',
tags: ['notebookLayout']
},
[NotebookSetting.textDiffEditorPreview]: {
description: locConstants.diffEnablePreviewDescription,
type: 'boolean',
default: true,
tags: ['notebookLayout']
},
[NotebookSetting.cellToolbarVisibility]: {
markdownDescription: locConstants.cellToolbarVisibilityDescription,
type: 'string',
enum: ['hover', 'click'],
default: 'click',
tags: ['notebookLayout']
},
[NotebookSetting.undoRedoPerCell]: {
description: locConstants.undoRedoPerCellDescription,
type: 'boolean',
default: true,
tags: ['notebookLayout']
},
[NotebookSetting.compactView]: {
description: locConstants.compactViewDescription,
type: 'boolean',
default: true,
tags: ['notebookLayout']
},
[NotebookSetting.focusIndicator]: {
description: locConstants.focusIndicatorDescription,
type: 'string',
enum: ['border', 'gutter'],
default: 'gutter',
tags: ['notebookLayout']
},
[NotebookSetting.insertToolbarLocation]: {
description: locConstants.insertToolbarPositionDescription,
type: 'string',
enum: ['betweenCells', 'notebookToolbar', 'both', 'hidden'],
enumDescriptions: [
nls.localize('insertToolbarLocation.betweenCells', "A toolbar that appears on hover between cells."),
nls.localize('insertToolbarLocation.notebookToolbar', "The toolbar at the top of the notebook editor."),
nls.localize('insertToolbarLocation.both', "Both toolbars."),
nls.localize('insertToolbarLocation.hidden', "The insert actions don't appear anywhere."),
],
default: 'both',
tags: ['notebookLayout']
},
[NotebookSetting.globalToolbar]: {
description: locConstants.globalToolbarDescription,
type: 'boolean',
default: true,
tags: ['notebookLayout']
},
[NotebookSetting.consolidatedOutputButton]: {
description: locConstants.consolidatedOutputButtonDescription,
type: 'boolean',
default: true,
tags: ['notebookLayout']
},
[NotebookSetting.showFoldingControls]: {
description: locConstants.showFoldingControlsDescription,
type: 'string',
enum: ['always', 'mouseover'],
enumDescriptions: [
nls.localize('showFoldingControls.always', "The folding controls are always visible."),
nls.localize('showFoldingControls.mouseover', "The folding controls are visible only on mouseover."),
],
default: 'mouseover',
tags: ['notebookLayout']
},
[NotebookSetting.dragAndDropEnabled]: {
description: locConstants.dragAndDropDescription,
type: 'boolean',
default: true,
tags: ['notebookLayout']
},
[NotebookSetting.consolidatedRunButton]: {
description: locConstants.consolidatedRunButtonDescription,
type: 'boolean',
default: false,
tags: ['notebookLayout']
},
[NotebookSetting.globalToolbarShowLabel]: {
description: locConstants.globalToolbarShowLabelDescription,
type: 'string',
enum: ['always', 'never', 'dynamic'],
default: 'always',
tags: ['notebookLayout']
},
[NotebookSetting.textOutputLineLimit]: {
description: locConstants.textOutputLineLimitDescription,
type: 'number',
default: 30,
tags: ['notebookLayout']
},
[NotebookSetting.markupFontSize]: {
markdownDescription: locConstants.markupFontSizeDescription,
type: 'number',
default: 0,
tags: ['notebookLayout']
},
[NotebookSetting.cellEditorOptionsCustomizations]: editorOptionsCustomizationSchema,
[NotebookSetting.interactiveWindowCollapseCodeCells]: {
markdownDescription: locConstants.interactiveWindowCollapseCodeCellsDescription,
type: 'string',
enum: ['always', 'never', 'fromEditor'],
default: 'fromEditor'
},
[NotebookSetting.outputLineHeight]: {
markdownDescription: locConstants.outputLineHeightDescription,
type: 'number',
default: 22,
tags: ['notebookLayout']
},
[NotebookSetting.outputFontSize]: {
markdownDescription: locConstants.outputFontSizeDescription,
type: 'number',
default: 0,
tags: ['notebookLayout']
},
[NotebookSetting.outputFontFamily]: {
markdownDescription: locConstants.outputFontFamilyDescription,
type: 'string',
tags: ['notebookLayout']
},
}
});

Просмотреть файл

@ -3,6 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CONFIG_WORKBENCH_ENABLEPREVIEWFEATURES, CONFIG_WORKBENCH_USEVSCODENOTEBOOKS } from 'sql/workbench/common/constants';
import { PixelRatio } from 'vs/base/browser/browser';
import { Emitter, Event } from 'vs/base/common/event';
import * as glob from 'vs/base/common/glob';
@ -16,7 +17,6 @@ import { URI } from 'vs/base/common/uri';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { BareFontInfo } from 'vs/editor/common/config/fontInfo';
// import { localize } from 'vs/nls'; {{SQL CARBON EDIT}} Disable VS Code notebooks
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IResourceEditorInput } from 'vs/platform/editor/common/editor';
@ -24,12 +24,12 @@ import { IFileService } from 'vs/platform/files/common/files';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { Memento } from 'vs/workbench/common/memento';
import { notebookRendererExtensionPoint } from 'vs/workbench/contrib/notebook/browser/extensionPoint'; // {{SQL CARBON EDIT}} Remove INotebookEditorContribution, notebooksExtensionPoint
import { INotebookEditorContribution, notebookRendererExtensionPoint, notebooksExtensionPoint } from 'vs/workbench/contrib/notebook/browser/extensionPoint';
import { INotebookEditorOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { NotebookDiffEditorInput } from 'vs/workbench/contrib/notebook/browser/notebookDiffEditorInput';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellUri, NotebookSetting, INotebookContributionData, INotebookExclusiveDocumentFilter, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, IOutputDto, MimeTypeDisplayOrder, NotebookData, NotebookRendererMatch, NOTEBOOK_DISPLAY_ORDER, RENDERER_EQUIVALENT_EXTENSIONS, RENDERER_NOT_AVAILABLE, TransientOptions, NotebookExtensionDescription } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellUri, NotebookSetting, INotebookContributionData, INotebookExclusiveDocumentFilter, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, IOutputDto, MimeTypeDisplayOrder, NotebookData, NotebookEditorPriority, NotebookRendererMatch, NOTEBOOK_DISPLAY_ORDER, RENDERER_EQUIVALENT_EXTENSIONS, RENDERER_NOT_AVAILABLE, TransientOptions, NotebookExtensionDescription } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/common/notebookEditorInput';
import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService';
import { updateEditorTopPadding } from 'vs/workbench/contrib/notebook/common/notebookOptions';
@ -38,7 +38,7 @@ import { NotebookEditorDescriptor, NotebookProviderInfo } from 'vs/workbench/con
import { ComplexNotebookProviderInfo, INotebookContentProvider, INotebookSerializer, INotebookService, SimpleNotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookService';
import { DiffEditorInputFactoryFunction, EditorInputFactoryFunction, IEditorResolverService, IEditorType, RegisteredEditorInfo, RegisteredEditorPriority, UntitledEditorInputFactoryFunction } from 'vs/workbench/services/editor/common/editorResolverService';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
// import { IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; {{SQL CARBON EDIT}} Disable VS Code notebooks
import { IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry';
export class NotebookProviderInfoStore extends Disposable {
@ -79,7 +79,12 @@ export class NotebookProviderInfoStore extends Disposable {
}
}));
// notebooksExtensionPoint.setHandler(extensions => this._setupHandler(extensions)); {{SQL CARBON EDIT}} Disable VS Code notebooks
// {{SQL CARBON EDIT}} Disable file associations here if we're not using VS Code notebooks by default
const usePreviewFeatures = this._configurationService.getValue(CONFIG_WORKBENCH_ENABLEPREVIEWFEATURES);
const useVSCodeNotebooks = this._configurationService.getValue(CONFIG_WORKBENCH_USEVSCODENOTEBOOKS);
if (usePreviewFeatures && useVSCodeNotebooks) {
notebooksExtensionPoint.setHandler(extensions => this._setupHandler(extensions));
}
}
override dispose(): void {
@ -87,7 +92,6 @@ export class NotebookProviderInfoStore extends Disposable {
super.dispose();
}
/* // {{SQL CARBON EDIT}} Disable VS Code notebooks
private _setupHandler(extensions: readonly IExtensionPointUser<INotebookEditorContribution[]>[]) {
this._handled = true;
const builtins: NotebookProviderInfo[] = [...this._contributedEditors.values()].filter(info => !info.extension);
@ -153,7 +157,6 @@ export class NotebookProviderInfoStore extends Disposable {
return RegisteredEditorPriority.option;
}
*/
private _registerContributionPoint(notebookProviderInfo: NotebookProviderInfo): IDisposable {
@ -221,6 +224,7 @@ export class NotebookProviderInfoStore extends Disposable {
return disposables;
}
private _clear(): void {
this._contributedEditors.clear();
this._contributedEditorDisposables.clear();
@ -421,35 +425,40 @@ export class NotebookService extends Disposable implements INotebookService {
) {
super();
notebookRendererExtensionPoint.setHandler((renderers) => {
this._notebookRenderersInfoStore.clear();
// {{SQL CARBON EDIT}} Disable renderer associations here if we're not using VS Code notebooks by default
const usePreviewFeatures = this._configurationService.getValue(CONFIG_WORKBENCH_ENABLEPREVIEWFEATURES);
const useVSCodeNotebooks = this._configurationService.getValue(CONFIG_WORKBENCH_USEVSCODENOTEBOOKS);
if (usePreviewFeatures && useVSCodeNotebooks) {
notebookRendererExtensionPoint.setHandler((renderers) => {
this._notebookRenderersInfoStore.clear();
for (const extension of renderers) {
for (const notebookContribution of extension.value) {
if (!notebookContribution.entrypoint) { // avoid crashing
extension.collector.error(`Notebook renderer does not specify entry point`);
continue;
for (const extension of renderers) {
for (const notebookContribution of extension.value) {
if (!notebookContribution.entrypoint) { // avoid crashing
extension.collector.error(`Notebook renderer does not specify entry point`);
continue;
}
const id = notebookContribution.id;
if (!id) {
extension.collector.error(`Notebook renderer does not specify id-property`);
continue;
}
this._notebookRenderersInfoStore.add(new NotebookOutputRendererInfo({
id,
extension: extension.description,
entrypoint: notebookContribution.entrypoint,
displayName: notebookContribution.displayName,
mimeTypes: notebookContribution.mimeTypes || [],
dependencies: notebookContribution.dependencies,
optionalDependencies: notebookContribution.optionalDependencies,
requiresMessaging: notebookContribution.requiresMessaging,
}));
}
const id = notebookContribution.id;
if (!id) {
extension.collector.error(`Notebook renderer does not specify id-property`);
continue;
}
this._notebookRenderersInfoStore.add(new NotebookOutputRendererInfo({
id,
extension: extension.description,
entrypoint: notebookContribution.entrypoint,
displayName: notebookContribution.displayName,
mimeTypes: notebookContribution.mimeTypes || [],
dependencies: notebookContribution.dependencies,
optionalDependencies: notebookContribution.optionalDependencies,
requiresMessaging: notebookContribution.requiresMessaging,
}));
}
}
});
});
}
const updateOrder = () => {
this._displayOrder = new MimeTypeDisplayOrder(
@ -518,7 +527,7 @@ export class NotebookService extends Disposable implements INotebookService {
}
clearEditorCache(): void {
// this.notebookProviderInfoStore.clearEditorCache(); // {{SQL CARBON EDIT}} - method disabled
this.notebookProviderInfoStore.clearEditorCache();
}
private _postDocumentOpenActivation(viewType: string) {

Просмотреть файл

@ -313,13 +313,6 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager {
// {{SQL CARBON EDIT}} filter out services we don't expose
const filtered: ProxyIdentifier<any>[] = [
MainContext.MainThreadDebugService,
MainContext.MainThreadNotebook,
MainContext.MainThreadNotebookDocuments,
MainContext.MainThreadNotebookEditors,
MainContext.MainThreadNotebookKernels,
MainContext.MainThreadNotebookProxyKernels,
MainContext.MainThreadNotebookRenderers,
MainContext.MainThreadNotebookProxyKernels,
MainContext.MainThreadInteractive
];
const expected: ProxyIdentifier<any>[] = Object.keys(MainContext).map((key) => (<any>MainContext)[key]).filter(v => !filtered.some(x => x === v));

Просмотреть файл

@ -3,20 +3,16 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { join } from 'path';
import * as os from 'os';
import * as cp from 'child_process';
import { IElement, ILocalizedStrings, ILocaleInfo } from './driver';
import { launch as launchPlaywrightBrowser } from './playwrightBrowser';
import { launch as launchPlaywrightElectron } from './playwrightElectron';
import { Logger, measureAndLog } from './logger';
import { copyExtension } from './extensions';
import * as treekill from 'tree-kill';
import { teardown } from './processes';
import { PlaywrightDriver } from './playwrightDriver';
const rootPath = join(__dirname, '../../..');
export interface LaunchOptions {
codePath?: string;
readonly workspacePath: string;
@ -75,8 +71,6 @@ export async function launch(options: LaunchOptions): Promise<Code> {
throw new Error('Smoke test process has terminated, refusing to spawn Code');
}
await measureAndLog(copyExtension(rootPath, options.extensionsPath, 'vscode-notebook-tests'), 'copyExtension(vscode-notebook-tests)', options.logger);
// Browser smoke tests
if (options.web) {
const { serverProcess, driver } = await measureAndLog(launchPlaywrightBrowser(options), 'launch playwright (browser)', options.logger);

Просмотреть файл

@ -57,13 +57,6 @@ export async function resolveElectronConfiguration(options: LaunchOptions): Prom
const remoteDataDir = `${userDataDir}-server`;
mkdirp.sync(remoteDataDir);
if (codePath) {
// running against a build: copy the test resolver extension into remote extensions dir
const remoteExtensionsDir = join(remoteDataDir, 'extensions');
mkdirp.sync(remoteExtensionsDir);
await measureAndLog(copyExtension(root, remoteExtensionsDir, 'vscode-notebook-tests'), 'copyExtension(vscode-notebook-tests)', logger);
}
env['TESTRESOLVER_DATA_FOLDER'] = remoteDataDir;
env['TESTRESOLVER_LOGS_FOLDER'] = join(logsPath, 'server');
if (options.verbose) {
@ -71,8 +64,6 @@ export async function resolveElectronConfiguration(options: LaunchOptions): Prom
}
}
args.push('--enable-proposed-api=vscode.vscode-notebook-tests');
if (!codePath) {
args.unshift(root);
}

Просмотреть файл

@ -5,6 +5,7 @@
import { Code } from './code';
import { QuickAccess } from './quickaccess';
import { QuickInput } from './quickinput';
const activeRowSelector = `.notebook-editor .monaco-list-row.focused`;
@ -12,11 +13,13 @@ export class Notebook {
constructor(
private readonly quickAccess: QuickAccess,
private readonly quickInput: QuickInput,
private readonly code: Code) {
}
async openNotebook() {
await this.quickAccess.runCommand('vscode-notebook-tests.createNewNotebook');
await this.quickAccess.openFileQuickAccessAndWait('notebook.ipynb', 1);
await this.quickInput.selectQuickInputElement(0);
await this.code.waitForElement(activeRowSelector);
await this.focusFirstCell();
await this.waitForActiveCellEditorContents('code()');

Просмотреть файл

@ -103,7 +103,7 @@ export class Workbench {
this.addRemoteBookDialog = new AddRemoteBookDialog(code);
this.taskPanel = new TaskPanel(code, this.quickaccess);
// {{END}}
this.notebook = new Notebook(this.quickaccess, code);
this.notebook = new Notebook(this.quickaccess, this.quickinput, code);
this.localization = new Localization(code);
}
}