Skip to content

Commit

Permalink
Notebook API Support (eclipse-theia#12442)
Browse files Browse the repository at this point in the history
Full feature support including:
* Notebook widget rendering
* Kernel selection
* Cell execution
* Custom output rendering
* Plugin host support

Signed-off-by: Jonah Iden <jonah.iden@typefox.io>
Co-authored-by: Mark Sujew <mark.sujew@typefox.io>
  • Loading branch information
jonah-iden and msujew authored Aug 31, 2023
1 parent 109adc4 commit 97fe17e
Show file tree
Hide file tree
Showing 98 changed files with 11,549 additions and 830 deletions.
1 change: 1 addition & 0 deletions examples/browser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"@theia/mini-browser": "1.40.0",
"@theia/monaco": "1.40.0",
"@theia/navigator": "1.40.0",
"@theia/notebook": "1.40.0",
"@theia/outline-view": "1.40.0",
"@theia/output": "1.40.0",
"@theia/plugin-dev": "1.40.0",
Expand Down
3 changes: 3 additions & 0 deletions examples/browser/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@
{
"path": "../../packages/navigator"
},
{
"path": "../../packages/notebook"
},
{
"path": "../../packages/outline-view"
},
Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@
"vscode.git-base",
"vscode.github",
"vscode.github-authentication",
"vscode.ipynb",
"vscode.microsoft-authentication",
"ms-vscode.references-view"
]
Expand Down
43 changes: 41 additions & 2 deletions packages/core/src/browser/saveable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@

import { Widget } from '@phosphor/widgets';
import { Message } from '@phosphor/messaging';
import { Event } from '../common/event';
import { Emitter, Event } from '../common/event';
import { MaybePromise } from '../common/types';
import { Key } from './keyboard/keys';
import { AbstractDialog } from './dialogs';
import { waitForClosed } from './widgets';
import { nls } from '../common/nls';
import { isObject } from '../common';
import { Disposable, isObject } from '../common';

export interface Saveable {
readonly dirty: boolean;
Expand Down Expand Up @@ -50,6 +50,45 @@ export interface SaveableSource {
readonly saveable: Saveable;
}

export class SaveableDelegate implements Saveable {
dirty = false;
protected readonly onDirtyChangedEmitter = new Emitter<void>();

get onDirtyChanged(): Event<void> {
return this.onDirtyChangedEmitter.event;
}
autoSave: 'off' | 'afterDelay' | 'onFocusChange' | 'onWindowChange' = 'off';

async save(options?: SaveOptions): Promise<void> {
await this.delegate?.save(options);
}

revert?(options?: Saveable.RevertOptions): Promise<void>;
createSnapshot?(): Saveable.Snapshot;
applySnapshot?(snapshot: object): void;

protected delegate?: Saveable;
protected toDispose?: Disposable;

set(delegate: Saveable): void {
this.toDispose?.dispose();
this.delegate = delegate;
this.toDispose = this.delegate.onDirtyChanged(() => {
this.dirty = delegate.dirty;
this.onDirtyChangedEmitter.fire();
});
this.autoSave = delegate.autoSave;
if (this.dirty !== delegate.dirty) {
this.dirty = delegate.dirty;
this.onDirtyChangedEmitter.fire();
}
this.revert = delegate.revert?.bind(delegate);
this.createSnapshot = delegate.createSnapshot?.bind(delegate);
this.applySnapshot = delegate.applySnapshot?.bind(delegate);
}

}

export namespace Saveable {
export interface RevertOptions {
/**
Expand Down
20 changes: 20 additions & 0 deletions packages/core/src/common/array-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,24 @@ export namespace ArrayUtils {
export function coalesce<T>(array: ReadonlyArray<T | undefined | null>): T[] {
return <T[]>array.filter(e => !!e);
}

/**
* groups array elements through a comparator function
* @param data array of elements to group
* @param compare comparator function: return of 0 means should group, anything above means not group
* @returns array of arrays with grouped elements
*/
export function groupBy<T>(data: ReadonlyArray<T>, compare: (a: T, b: T) => number): T[][] {
const result: T[][] = [];
let currentGroup: T[] | undefined = undefined;
for (const element of data.slice(0).sort(compare)) {
if (!currentGroup || compare(currentGroup[0], element) !== 0) {
currentGroup = [element];
result.push(currentGroup);
} else {
currentGroup.push(element);
}
}
return result;
}
}
14 changes: 12 additions & 2 deletions packages/core/src/common/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

/* eslint-disable @typescript-eslint/no-explicit-any */

import { Disposable, DisposableGroup } from './disposable';
import { Disposable, DisposableGroup, DisposableCollection } from './disposable';
import { MaybePromise } from './types';

/**
Expand Down Expand Up @@ -67,6 +67,16 @@ export namespace Event {
set maxListeners(maxListeners: number) { }
});
}

/**
* Given a collection of events, returns a single event which emits whenever any of the provided events emit.
*/
export function any<T>(...events: Event<T>[]): Event<T>;
export function any(...events: Event<any>[]): Event<void>;
export function any<T>(...events: Event<T>[]): Event<T> {
return (listener, thisArgs = undefined, disposables?: Disposable[]) =>
new DisposableCollection(...events.map(event => event(e => listener.call(thisArgs, e), undefined, disposables)));
}
}

type Callback = (...args: any[]) => any;
Expand Down Expand Up @@ -276,7 +286,7 @@ export class Emitter<T = any> {
*/
fire(event: T): any {
if (this._callbacks) {
this._callbacks.invoke(event);
return this._callbacks.invoke(event);
}
}

Expand Down
17 changes: 17 additions & 0 deletions packages/core/src/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,23 @@ export function isFunction<T extends (...args: unknown[]) => unknown>(value: unk
return typeof value === 'function';
}

/**
* @returns whether the provided parameter is an empty JavaScript Object or not.
*/
export function isEmptyObject(obj: unknown): obj is object {
if (!isObject(obj)) {
return false;
}

for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
return false;
}
}

return true;
}

export function isObject<T extends object>(value: unknown): value is UnknownObject<T> {
// eslint-disable-next-line no-null/no-null
return typeof value === 'object' && value !== null;
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/common/uri.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ export class URI {
return new URI(Uri.file(path));
}

public static isUri(uri: unknown): boolean {
return Uri.isUri(uri);
}

private readonly codeUri: Uri;
private _path: Path | undefined;

Expand Down
3 changes: 3 additions & 0 deletions packages/editor/src/browser/editor-frontend-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { QuickAccessContribution } from '@theia/core/lib/browser/quick-input/qui
import { QuickEditorService } from './quick-editor-service';
import { EditorLanguageStatusService } from './language-status/editor-language-status-service';
import { EditorLineNumberContribution } from './editor-linenumber-contribution';
import { UndoRedoService } from './undo-redo-service';

export default new ContainerModule(bind => {
bindEditorPreferences(bind);
Expand Down Expand Up @@ -86,4 +87,6 @@ export default new ContainerModule(bind => {
bind(ActiveEditorAccess).toSelf().inSingletonScope();
bind(EditorAccess).to(CurrentEditorAccess).inSingletonScope().whenTargetNamed(EditorAccess.CURRENT);
bind(EditorAccess).to(ActiveEditorAccess).inSingletonScope().whenTargetNamed(EditorAccess.ACTIVE);

bind(UndoRedoService).toSelf().inSingletonScope();
});
Original file line number Diff line number Diff line change
Expand Up @@ -94,16 +94,16 @@ export class ResourceEditStack {
this.past.push(element);
}

getClosestPastElement(): StackElement | null {
getClosestPastElement(): StackElement | undefined {
if (this.past.length === 0) {
return null;
return undefined;
}
return this.past[this.past.length - 1];
}

getClosestFutureElement(): StackElement | null {
getClosestFutureElement(): StackElement | undefined {
if (this.future.length === 0) {
return null;
return undefined;
}
return this.future[this.future.length - 1];
}
Expand Down
Loading

0 comments on commit 97fe17e

Please sign in to comment.