Skip to content

Commit e55c385

Browse files
committed
Improves the manager and adds a registry to register blocks, generators and toolboxes
1 parent 4680034 commit e55c385

File tree

14 files changed

+759
-588
lines changed

14 files changed

+759
-588
lines changed

README.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,25 @@ Repo: https://github.com/google/blockly
1313

1414
## Requirements
1515

16-
* JupyterLab >= 4.0.0a0
16+
* JupyterLab == 3.3
1717

1818
## Install
1919

2020
To install the extension, execute:
2121

2222
```bash
23-
micromamba create -n blockly -c conda-forge python
23+
micromamba create -n blockly -c conda-forge python ipykernel xeus-python xeus-lua
2424
micromamba activate blockly
2525
pip install jupyterlab_blockly
2626
```
2727

28+
#### Kernels
29+
* ipykernel
30+
* xeus-python
31+
* xeus-lua
32+
* [JavaScript](https://github.com/n-riesco/ijavascript#installation)
33+
* [JavaScript](https://github.com/yunabe/tslab)
34+
2835
## Uninstall
2936

3037
To remove the extension, execute:

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,12 @@
4848
"dependencies": {
4949
"@jupyterlab/application": "^3.3",
5050
"@jupyterlab/apputils": "^3.3",
51+
"@jupyterlab/coreutils": "^5.3",
5152
"@jupyterlab/docregistry": "^3.3",
5253
"@jupyterlab/outputarea": "^3.3",
5354
"@jupyterlab/rendermime": "^3.3",
55+
"@jupyterlab/services": "^6.3",
56+
"@jupyterlab/ui-components": "^3.3",
5457
"@lumino/algorithm": "^1.9.1",
5558
"@lumino/coreutils": "^1.12.0",
5659
"@lumino/messaging": "^1.10.1",

src/factory.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
77

88
import { BlocklyEditor, BlocklyPanel } from './widget';
9+
import { BlocklyRegistry } from './registry';
910
import { BlocklyManager } from './manager';
1011

1112
/**
@@ -15,7 +16,7 @@ export class BlocklyEditorFactory extends ABCWidgetFactory<
1516
BlocklyEditor,
1617
DocumentModel
1718
> {
18-
private _manager: BlocklyManager;
19+
private _registry: BlocklyRegistry;
1920
private _rendermime: IRenderMimeRegistry;
2021

2122
/**
@@ -25,12 +26,12 @@ export class BlocklyEditorFactory extends ABCWidgetFactory<
2526
*/
2627
constructor(options: BlocklyEditorFactory.IOptions) {
2728
super(options);
28-
this._manager = new BlocklyManager();
29+
this._registry = new BlocklyRegistry();
2930
this._rendermime = options.rendermime;
3031
}
3132

32-
get manager(): BlocklyManager {
33-
return this._manager;
33+
get registry(): BlocklyRegistry {
34+
return this._registry;
3435
}
3536

3637
/**
@@ -42,10 +43,9 @@ export class BlocklyEditorFactory extends ABCWidgetFactory<
4243
protected createNewWidget(
4344
context: DocumentRegistry.IContext<DocumentModel>
4445
): BlocklyEditor {
45-
return new BlocklyEditor({
46-
context,
47-
content: new BlocklyPanel(context, this._manager, this._rendermime)
48-
});
46+
const manager = new BlocklyManager(this._registry, context.sessionContext);
47+
const content = new BlocklyPanel(context, manager, this._rendermime);
48+
return new BlocklyEditor({ context, content, manager });
4949
}
5050
}
5151

src/index.ts

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ import {
33
JupyterFrontEndPlugin,
44
ILayoutRestorer
55
} from '@jupyterlab/application';
6+
import { jsonIcon } from '@jupyterlab/ui-components';
67
import { WidgetTracker } from '@jupyterlab/apputils';
78
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
89

910
import { BlocklyEditorFactory } from './factory';
10-
import { IBlocklyManager } from './token';
11+
import { IBlocklyRegisty } from './token';
1112
import { BlocklyEditor } from './widget';
1213

1314
/**
@@ -18,16 +19,16 @@ const FACTORY = 'Blockly editor';
1819
/**
1920
* Initialization data for the jupyterlab-blocky extension.
2021
*/
21-
const plugin: JupyterFrontEndPlugin<IBlocklyManager> = {
22+
const plugin: JupyterFrontEndPlugin<IBlocklyRegisty> = {
2223
id: 'jupyterlab-blocky:plugin',
2324
autoStart: true,
2425
requires: [ILayoutRestorer, IRenderMimeRegistry],
25-
provides: IBlocklyManager,
26+
provides: IBlocklyRegisty,
2627
activate: (
2728
app: JupyterFrontEnd,
2829
restorer: ILayoutRestorer,
2930
rendermime: IRenderMimeRegistry
30-
): IBlocklyManager => {
31+
): IBlocklyRegisty => {
3132
console.log('JupyterLab extension jupyterlab-blocky is activated!');
3233

3334
// Namespace for the tracker
@@ -51,8 +52,8 @@ const plugin: JupyterFrontEndPlugin<IBlocklyManager> = {
5152
const widgetFactory = new BlocklyEditorFactory({
5253
name: FACTORY,
5354
modelName: 'text',
54-
fileTypes: ['json'],
55-
defaultFor: ['json'],
55+
fileTypes: ['blockly'],
56+
defaultFor: ['blockly'],
5657

5758
// Kernel options, in this case we need to execute the code generated
5859
// in the blockly editor. The best way would be to use kernels, for
@@ -75,10 +76,21 @@ const plugin: JupyterFrontEndPlugin<IBlocklyManager> = {
7576
});
7677
tracker.add(widget);
7778
});
79+
// Registering the file type
80+
app.docRegistry.addFileType({
81+
name: 'blockly',
82+
displayName: 'Blockly',
83+
contentType: 'file',
84+
fileFormat: 'json',
85+
extensions: ['.jpblockly'],
86+
mimeTypes: ['application/json'],
87+
icon: jsonIcon,
88+
iconLabel: 'JupyterLab-Blockly'
89+
});
7890
// Registering the widget factory
7991
app.docRegistry.addWidgetFactory(widgetFactory);
8092

81-
return widgetFactory.manager;
93+
return widgetFactory.registry;
8294
}
8395
};
8496

src/layout.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { SimplifiedOutputArea, OutputAreaModel } from '@jupyterlab/outputarea';
22
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
3-
import { ISessionContext } from '@jupyterlab/apputils';
3+
import { ISessionContext, showErrorMessage } from '@jupyterlab/apputils';
44

55
import { Message } from '@lumino/messaging';
66
import { PartialJSONValue } from '@lumino/coreutils';
@@ -98,12 +98,21 @@ export class BlocklyLayout extends PanelLayout {
9898

9999
// Execute the code using the kernel, by using a static method from the
100100
// same class to make an execution request.
101-
SimplifiedOutputArea.execute(code, this._outputArea, this._sessionContext)
102-
.then(resp => {
103-
this.addWidget(this._outputArea);
104-
this._resizeWorkspace();
105-
})
106-
.catch(e => console.error(e));
101+
if (this._sessionContext.hasNoKernel) {
102+
showErrorMessage(
103+
'Select a valid kernel',
104+
`There is not a valid kernel selected, select one from the dropdown menu in the toolbar.
105+
If there isn't a valid kernel please install 'xeus-python' from Pypi.org or using mamba.
106+
`
107+
);
108+
} else {
109+
SimplifiedOutputArea.execute(code, this._outputArea, this._sessionContext)
110+
.then(resp => {
111+
this.addWidget(this._outputArea);
112+
this._resizeWorkspace();
113+
})
114+
.catch(e => console.error(e));
115+
}
107116
}
108117

109118
/**

src/manager.ts

Lines changed: 67 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,93 @@
1+
import { ISessionContext } from '@jupyterlab/apputils';
2+
import { KernelSpec, KernelConnection } from '@jupyterlab/services';
3+
import { IChangedArgs } from '@jupyterlab/coreutils';
4+
5+
import { ISignal, Signal } from '@lumino/signaling';
16
import { JSONObject } from '@lumino/coreutils';
27

38
import * as Blockly from 'blockly';
49

5-
import BlocklyPy from 'blockly/python';
6-
7-
import { IBlocklyManager } from './token';
8-
import { TOOLBOX } from './utils';
10+
import { BlocklyRegistry } from './registry';
911

10-
export class BlocklyManager implements IBlocklyManager {
12+
/**
13+
* BlocklyManager
14+
*/
15+
export class BlocklyManager {
1116
private _toolbox: JSONObject;
12-
private _activeGenerator: Blockly.Generator;
13-
private _generators: Map<string, Blockly.Generator>;
17+
private _generator: Blockly.Generator;
18+
private _registry: BlocklyRegistry;
19+
private _selectedKernel: KernelSpec.ISpecModel;
20+
private _sessionContext: ISessionContext;
21+
private _changed: Signal<this, BlocklyManager.Change>;
1422

1523
/**
16-
* Constructor of BlocklyEditorFactory.
17-
*
18-
* @param options Constructor options
24+
* Constructor of BlocklyManager.
1925
*/
20-
constructor() {
21-
this._toolbox = TOOLBOX;
22-
this._activeGenerator = BlocklyPy;
23-
this._generators = new Map<string, Blockly.Generator>();
26+
constructor(registry: BlocklyRegistry, sessionContext: ISessionContext) {
27+
this._registry = registry;
28+
this._sessionContext = sessionContext;
29+
30+
this._toolbox = this._registry.toolboxes.get('default');
31+
this._generator = this._registry.generators.get('python');
32+
33+
this._changed = new Signal<this, BlocklyManager.Change>(this);
34+
this._sessionContext.kernelChanged.connect(this._onKernelChanged, this);
2435
}
2536

2637
get toolbox(): JSONObject {
2738
return this._toolbox;
2839
}
2940

30-
set activeGenerator(name: string) {
31-
this._activeGenerator = this._generators.get(name);
41+
get kernel(): string | undefined {
42+
return this._selectedKernel?.name || 'No kernel';
3243
}
3344

3445
get generator(): Blockly.Generator {
35-
return this._activeGenerator;
46+
return this._generator;
47+
}
48+
49+
get changed(): ISignal<this, BlocklyManager.Change> {
50+
return this._changed;
3651
}
3752

38-
registerToolbox(value: JSONObject): void {
39-
this._toolbox = value;
53+
dispose(): void {
54+
this._sessionContext.kernelChanged.disconnect(this._onKernelChanged, this);
4055
}
4156

42-
registerBlocks(blocks: JSONObject[]): void {
43-
Blockly.defineBlocksWithJsonArray(blocks);
57+
setToolbox(name: string) {
58+
this._toolbox = this._registry.toolboxes.get(name);
4459
}
4560

46-
registerGenerator(kernel: string, generator: Blockly.Generator): void {
47-
this._generators.set(kernel, generator);
61+
selectKernel(name: string) {
62+
this._sessionContext.changeKernel({ name });
4863
}
64+
65+
listKernels(): { label: string; value: string }[] {
66+
const specs = this._sessionContext.specsManager.specs.kernelspecs;
67+
const list: { label: string; value: string }[] = [];
68+
Object.keys(specs).forEach(key => {
69+
const language = specs[key].language;
70+
if (this._registry.generators.has(language)) {
71+
list.push({ label: specs[key].display_name, value: specs[key].name });
72+
}
73+
});
74+
return list;
75+
}
76+
77+
private _onKernelChanged(
78+
sender: ISessionContext,
79+
args: IChangedArgs<KernelConnection, KernelConnection, 'kernel'>
80+
): void {
81+
const specs = this._sessionContext.specsManager.specs.kernelspecs;
82+
if (args.newValue && specs[args.newValue.name] !== undefined) {
83+
this._selectedKernel = specs[args.newValue.name];
84+
const language = specs[args.newValue.name].language;
85+
this._generator = this._registry.generators.get(language);
86+
this._changed.emit('kernel');
87+
}
88+
}
89+
}
90+
91+
export namespace BlocklyManager {
92+
export type Change = 'toolbox' | 'kernel';
4993
}

src/registry.ts

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { JSONObject } from '@lumino/coreutils';
2+
3+
import * as Blockly from 'blockly';
4+
5+
import BlocklyPy from 'blockly/python';
6+
import BlocklyJS from 'blockly/javascript';
7+
import BlocklyLua from 'blockly/lua';
8+
9+
import { IBlocklyRegisty } from './token';
10+
import { TOOLBOX } from './utils';
11+
12+
/**
13+
* BlocklyRegistry is the class that JupyterLab-Blockly exposes
14+
* to other plugins. This registry allows other plugins to register
15+
* new Toolboxes, Blocks and Generators that users can use in the
16+
* Blockly editor.
17+
*/
18+
export class BlocklyRegistry implements IBlocklyRegisty {
19+
private _toolboxes: Map<string, JSONObject>;
20+
private _generators: Map<string, Blockly.Generator>;
21+
22+
/**
23+
* Constructor of BlocklyRegistry.
24+
*/
25+
constructor() {
26+
this._toolboxes = new Map<string, JSONObject>();
27+
this._toolboxes.set('default', TOOLBOX);
28+
29+
this._generators = new Map<string, Blockly.Generator>();
30+
this._generators.set('python', BlocklyPy);
31+
this._generators.set('javascript', BlocklyJS);
32+
this._generators.set('lua', BlocklyLua);
33+
}
34+
35+
/**
36+
* Returns a map with all the toolboxes.
37+
*/
38+
get toolboxes(): Map<string, JSONObject> {
39+
return this._toolboxes;
40+
}
41+
42+
/**
43+
* Returns a map with all the generators.
44+
*/
45+
get generators(): Map<string, Blockly.Generator> {
46+
return this._generators;
47+
}
48+
49+
/**
50+
* Register a toolbox for the editor.
51+
*
52+
* @argument name Name of the toolbox.
53+
*
54+
* @argument value Toolbox to register.
55+
*/
56+
registerToolbox(name: string, value: JSONObject): void {
57+
this._toolboxes.set(name, value);
58+
}
59+
60+
/**
61+
* Register new blocks.
62+
*
63+
* @argument name Name of the toolbox.
64+
*
65+
* @argument value Toolbox to register.
66+
*/
67+
registerBlocks(blocks: JSONObject[]): void {
68+
Blockly.defineBlocksWithJsonArray(blocks);
69+
}
70+
71+
/**
72+
* Register new generators.
73+
*
74+
* @argument name Name of the toolbox.
75+
*
76+
* @argument value Toolbox to register.
77+
*
78+
* #### Notes
79+
* When registering a generator, the name should correspond to the language
80+
* used by a kernel.
81+
*
82+
* If you register a generator for an existing language this will be overwritten.
83+
*/
84+
registerGenerator(name: string, generator: Blockly.Generator): void {
85+
this._generators.set(name, generator);
86+
}
87+
}

0 commit comments

Comments
 (0)