mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-14 01:25:37 -05:00
* Start rerouting VSCode cell execution APIs. * Add more conversion code. * Convert VSCode notebook registrations into ADS equivalents. * Update vscode notebook provider kernels when notebook controller's supportedLanguages are set. * Update an error message. * Add another session argument. * Add base classes for converting notebook serializers. * Disable some vscode notebook methods. * Disable more vscode APIs. * Disable more stuff. * Start implementing serializer notebook data conversions. * Use direct references to extension host notebook methods, rather than azdata ones. * Add a comment. * Remove a space. * Use import type to fix module loading errors. * Use internal cancellation token class. * Start adding cell output conversion. * Convert data from byte array to a string. * More output work. * Use a Set for proxy filtering. * Start adding tests. * Include metadata in cell conversion. Fix other test failures. * Fix serialize tests. * Add more tests. * Remove wildcard characters from vscode filenames. * Start implementing session details. * Add more kernel info. * Add kernel spec. * Add Future callback wrapper class. * Start implementing execute conversion. * Pass notebook URI to requestExecute. * Start working on CellExecution methods. * Move some code around to fix layering issues. * Use proxy to access browser code, rather than direct imports. * Move files around to fix layering issues. * Remove unused imports. * Start implementing some notebook cell execution behaviors. * Revert some unnecessary extHost API changes. * Check for nbformat. * Also handle nbformat in serialize case. * Active notebook extensions when resolving NotebookInput. * Fix nbformat handling. * Disable VSCode notebooks code. * Filter out notebook services from registration assertion. * Wait for providers to load before calling canResolve. * Use controller's viewType for notebook provider ID, instead of controller ID. * Start adding extHostNotebook tests for new APIs. * Re-order proxy calls. * Remove commented code. * Move vscode provider files to browser folder. Fix RPC serialization issues by using readonly field instead of getter for providerId. * Add a comment. * Remove unnecessary dispose call. * Handle disposable from registerExecuteProvider. * Remove a comment. * Remove unnecessary provider fields. * Remove reference to notebook service to fix circular reference issue in stringify. * Add object types for methods in ADSNotebookController. * Wait for controller languages to be ready before marking session manager as ready. * Add correct promise. * Add undefined return type for optional supportedLanguages property. * Refine promise logic. * Move vscode functionality back to ExtHostNotebook, since the NotebookService can't be passed back over RPC (some kind of circular reference error). * Fix remaining issues from last commit. * Replace "not implemented" methods with placeholder return types in order to enable testing. * Also wait for execution handler to be set before marking session manager as ready. * Fix usage of NotebookRegistry when updating provider description languages. * Refine file extension conversion. * Fix file extension conversion to match ADS extension behavior. * Emit new provider registration event when adding supported languages. * Remove checks for duplicate file providers and kernels. * Fix a test failure. * Fix file extension parsing. * Use default executeManager if one isn't defined for provider in notebookModel. * Add descriptors for waiting on standardKernels registration. * Increase timeout * Add an error message. * Start working on retrieving default kernel from registered providers, rather than always falling back to SQL. * Revert "Start working on retrieving default kernel from registered providers, rather than always falling back to SQL." This reverts commit 1916ea1ce3a0072f51bec683116dc7bb6c7aefdc. * Emit activation events after provider registration. * Wait on standard kernels availability when getting an execute provider. * Throw an error if session manager isn't ready yet. * Actually resolve language promise correctly. * Add some checks for undefined notebook data objects. * Create kernel spec data correctly. * Add extension changes for local testing only. * Clean up test class. * Add a reminder comment. * Undo commented out notebook stuff * Temporarily hard code default kernel. * Retrieve default kernel in notebookModel if it's not already provided. * Revert an import change. * Remove unnecessary method from extHostNotebook. * Move an interface around. * wip * Check for proposed API for some VSCode extHost methods. * Remove a comment. * Fix notebookUtils tests. * Fix notebookModel tests. * Fix notebookFindModel tests. * Fix notebookViewsExtension tests. * Fix remaining notebookView tests. * Refactor output conversion functionality into separate methods. * Update some unit tests for output conversion. * Move a method. * Rename conversion methods to fit acronym styling. * Add another conversion test case. * Revert local testing changes. * Remove old method. * cleanup * Remove some comments. * Move localized string to locConstants. * Add a space to loc string. * Add comments to new SQL Carbon Edit tags. * Create constants for nbformat and nbformat_minor. * Move some vscode-only fields to proposed APIs. * Check for valid state * Properly null check * Adding logging for provider wait timeouts. * wip update * Fix compile * Switch to cell edits * Update docs * Remove custom output type * cleanup * fix * cleanup * more cleanup * Fixes * Fix tests and lint errors Co-authored-by: Cory Rivera <corivera@microsoft.com>
249 lines
7.7 KiB
TypeScript
249 lines
7.7 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
import * as azdata from 'azdata';
|
|
import * as vscode from 'vscode';
|
|
|
|
import { ok } from 'vs/base/common/assert';
|
|
import { IDisposable } from 'vs/base/common/lifecycle';
|
|
import { readonly } from 'vs/base/common/errors';
|
|
|
|
import { MainThreadNotebookDocumentsAndEditorsShape } from 'sql/workbench/api/common/sqlExtHost.protocol';
|
|
import { ExtHostNotebookDocumentData } from 'sql/workbench/api/common/extHostNotebookDocumentData';
|
|
import { CellRange, INotebookEditOperation, ICellRange, NotebookEditOperationType } from 'sql/workbench/api/common/sqlExtHostTypes';
|
|
import { HideInputTag } from 'sql/platform/notebooks/common/outputRegistry';
|
|
|
|
export interface INotebookEditData {
|
|
documentVersionId: number;
|
|
edits: INotebookEditOperation[];
|
|
undoStopBefore: boolean;
|
|
undoStopAfter: boolean;
|
|
}
|
|
|
|
function toICellRange(range: azdata.nb.CellRange): ICellRange {
|
|
return {
|
|
start: range.start,
|
|
end: range.end
|
|
};
|
|
}
|
|
|
|
export class NotebookEditorEdit {
|
|
|
|
private readonly _document: azdata.nb.NotebookDocument;
|
|
private readonly _documentVersionId: number;
|
|
private _collectedEdits: INotebookEditOperation[];
|
|
private readonly _undoStopBefore: boolean;
|
|
private readonly _undoStopAfter: boolean;
|
|
|
|
constructor(document: azdata.nb.NotebookDocument, options: { undoStopBefore: boolean; undoStopAfter: boolean; }) {
|
|
this._document = document;
|
|
// TODO add version handling
|
|
this._documentVersionId = 0;
|
|
// this._documentVersionId = document.version;
|
|
this._collectedEdits = [];
|
|
this._undoStopBefore = options ? options.undoStopBefore : true;
|
|
this._undoStopAfter = options ? options.undoStopAfter : false;
|
|
}
|
|
|
|
finalize(): INotebookEditData {
|
|
return {
|
|
documentVersionId: this._documentVersionId,
|
|
edits: this._collectedEdits,
|
|
undoStopBefore: this._undoStopBefore,
|
|
undoStopAfter: this._undoStopAfter
|
|
};
|
|
}
|
|
|
|
replace(location: number | CellRange, value: Partial<azdata.nb.ICellContents>): void {
|
|
let range: CellRange = this.getAsRange(location);
|
|
this._pushEdit(NotebookEditOperationType.ReplaceCells, range, value);
|
|
}
|
|
|
|
private getAsRange(location: number | CellRange): CellRange {
|
|
let range: CellRange = null;
|
|
if (typeof (location) === 'number') {
|
|
range = new CellRange(location, location + 1);
|
|
}
|
|
else if (location instanceof CellRange) {
|
|
range = location;
|
|
}
|
|
else {
|
|
throw new Error('Unrecognized location');
|
|
}
|
|
return range;
|
|
}
|
|
|
|
setTrusted(isTrusted: boolean) {
|
|
this._document.setTrusted(isTrusted);
|
|
}
|
|
|
|
insertCell(value: Partial<azdata.nb.ICellContents>, index?: number, collapsed?: boolean): void {
|
|
if (index === null || index === undefined) {
|
|
// If not specified, assume adding to end of list
|
|
index = this._document.cells.length;
|
|
}
|
|
if (!!collapsed) {
|
|
if (!value.metadata) {
|
|
value.metadata = { tags: [HideInputTag] };
|
|
} else if (!value.metadata.tags) {
|
|
value.metadata.tags = [HideInputTag];
|
|
} else if (!value.metadata.tags.find(x => x === HideInputTag)) {
|
|
value.metadata.tags.push(HideInputTag);
|
|
}
|
|
}
|
|
this._pushEdit(NotebookEditOperationType.InsertCell, new CellRange(index, index), value);
|
|
}
|
|
|
|
deleteCell(index: number): void {
|
|
let range: CellRange = null;
|
|
|
|
if (typeof (index) === 'number') {
|
|
// Currently only allowing single-cell deletion.
|
|
// Do this by saying the range extends over 1 cell so on the main thread
|
|
// we can delete that cell, then handle insertions
|
|
range = new CellRange(index, index + 1);
|
|
} else {
|
|
throw new Error('Unrecognized index');
|
|
}
|
|
|
|
this._pushEdit(NotebookEditOperationType.DeleteCell, range, null);
|
|
}
|
|
|
|
updateCell(index: number, updatedContent: Partial<azdata.nb.ICellContents>, append: boolean): void {
|
|
this._pushEdit(NotebookEditOperationType.UpdateCell, new CellRange(index, index + 1), updatedContent, append);
|
|
}
|
|
|
|
updateCellOutput(cellIndex: number, updatedContent: Partial<azdata.nb.ICellContents>, append: boolean): void {
|
|
this._pushEdit(NotebookEditOperationType.UpdateCellOutput, new CellRange(cellIndex, cellIndex + 1), updatedContent, append);
|
|
}
|
|
|
|
private _pushEdit(type: NotebookEditOperationType, range: azdata.nb.CellRange, cell: Partial<azdata.nb.ICellContents>, append?: boolean): void {
|
|
let validRange = this._document.validateCellRange(range);
|
|
this._collectedEdits.push({
|
|
type: type,
|
|
range: validRange,
|
|
cell: cell,
|
|
append: append
|
|
});
|
|
}
|
|
}
|
|
|
|
export class ExtHostNotebookEditor implements azdata.nb.NotebookEditor, IDisposable {
|
|
private _disposed: boolean = false;
|
|
|
|
constructor(
|
|
private _proxy: MainThreadNotebookDocumentsAndEditorsShape,
|
|
private _id: string,
|
|
private readonly _documentData: ExtHostNotebookDocumentData,
|
|
private _viewColumn: vscode.ViewColumn
|
|
) {
|
|
|
|
}
|
|
|
|
dispose() {
|
|
ok(!this._disposed);
|
|
this._disposed = true;
|
|
}
|
|
|
|
get document(): azdata.nb.NotebookDocument {
|
|
return this._documentData.document;
|
|
}
|
|
|
|
set document(value) {
|
|
throw readonly('document');
|
|
}
|
|
|
|
get viewColumn(): vscode.ViewColumn {
|
|
return this._viewColumn;
|
|
}
|
|
|
|
set viewColumn(value) {
|
|
throw readonly('viewColumn');
|
|
}
|
|
|
|
get id(): string {
|
|
return this._id;
|
|
}
|
|
|
|
public runCell(cell: azdata.nb.NotebookCell): Thenable<boolean> {
|
|
let uri = cell ? cell.uri : undefined;
|
|
return this._proxy.$runCell(this._id, uri);
|
|
}
|
|
|
|
public runAllCells(startCell?: azdata.nb.NotebookCell, endCell?: azdata.nb.NotebookCell): Thenable<boolean> {
|
|
let startCellUri = startCell ? startCell.uri : undefined;
|
|
let endCellUri = endCell ? endCell.uri : undefined;
|
|
return this._proxy.$runAllCells(this._id, startCellUri, endCellUri);
|
|
}
|
|
|
|
public clearOutput(cell: azdata.nb.NotebookCell): Thenable<boolean> {
|
|
let uri = cell ? cell.uri : undefined;
|
|
return this._proxy.$clearOutput(this._id, uri);
|
|
}
|
|
|
|
public clearAllOutputs(): Thenable<boolean> {
|
|
return this._proxy.$clearAllOutputs(this._id);
|
|
}
|
|
|
|
public changeKernel(kernel: azdata.nb.IKernelSpec): Thenable<boolean> {
|
|
return this._proxy.$changeKernel(this._id, kernel);
|
|
}
|
|
|
|
public edit(callback: (editBuilder: NotebookEditorEdit) => void, options?: { undoStopBefore: boolean; undoStopAfter: boolean; }): Thenable<boolean> {
|
|
if (this._disposed) {
|
|
return Promise.reject(new Error('NotebookEditor#edit not possible on closed editors'));
|
|
}
|
|
let edit = new NotebookEditorEdit(this._documentData.document, options);
|
|
callback(edit);
|
|
return this._applyEdit(edit);
|
|
}
|
|
|
|
private _applyEdit(editBuilder: NotebookEditorEdit): Promise<boolean> {
|
|
let editData = editBuilder.finalize();
|
|
|
|
// return when there is nothing to do
|
|
if (editData.edits.length === 0) {
|
|
return Promise.resolve(true);
|
|
}
|
|
|
|
// check that the edits are not overlapping (i.e. illegal)
|
|
let editRanges = editData.edits.map(edit => edit.range);
|
|
|
|
// sort ascending (by end and then by start)
|
|
editRanges.sort((a, b) => {
|
|
if (a.end === b.end) {
|
|
return a.start - b.start;
|
|
}
|
|
return a.end - b.end;
|
|
});
|
|
|
|
// check that no edits are overlapping
|
|
for (let i = 0, count = editRanges.length - 1; i < count; i++) {
|
|
const rangeEnd = editRanges[i].end;
|
|
const nextRangeStart = editRanges[i + 1].start;
|
|
|
|
if (nextRangeStart < rangeEnd) {
|
|
// overlapping ranges
|
|
return Promise.reject(new Error('Overlapping ranges are not allowed!'));
|
|
}
|
|
}
|
|
|
|
// prepare data for serialization
|
|
let edits: INotebookEditOperation[] = editData.edits.map((edit) => {
|
|
return {
|
|
type: edit.type,
|
|
range: toICellRange(edit.range),
|
|
cell: edit.cell,
|
|
append: edit.append
|
|
};
|
|
});
|
|
|
|
return this._proxy.$tryApplyEdits(this._id, editData.documentVersionId, edits, {
|
|
undoStopBefore: editData.undoStopBefore,
|
|
undoStopAfter: editData.undoStopAfter
|
|
});
|
|
}
|
|
}
|