Skip to content

Commit 025f3a9

Browse files
Merge pull request #30 from JBBianchi/feat-29-automatic-opening
Automatic opening
2 parents be3b9ea + 45ed045 commit 025f3a9

File tree

4 files changed

+148
-43
lines changed

4 files changed

+148
-43
lines changed

package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

resources/diagram-panel.html

+11-14
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,21 @@
1212
<div id="diagram-container"></div>
1313
<script type="module">
1414
const vscode = acquireVsCodeApi();
15+
/** Waits for the page to be fully loaded before trying to render the graph */
1516
window.addEventListener('load', async() => {
1617
await vscode.postMessage({
1718
command: 'panel-content-loaded'
18-
})
19+
});
1920
});
2021
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid/+esm';
2122
(async () => {
2223
try {
24+
/** The diagram container */
2325
const diagramContainerEl = document.getElementById('diagram-container');
2426

25-
function updateState(partialState) {
26-
vscode.setState({
27-
...vscode.getState(),
28-
...partialState
29-
});
30-
}
31-
27+
/**
28+
* Renders the provided graph definition
29+
*/
3230
async function updateDiagram({ graphDefinition }) {
3331
try {
3432
const { svg, bindFunctions } = await mermaid.render('sw-diagram', graphDefinition);
@@ -38,7 +36,6 @@
3836
command: 'diagram-rendered',
3937
svg: sanitizedSvg
4038
});
41-
updateState({ graphDefinition });
4239
}
4340
catch (ex) {
4441
await vscode.postMessage({
@@ -49,6 +46,9 @@
4946
}
5047
}
5148

49+
/**
50+
* Generates a PNG using a canvas
51+
*/
5252
function generatePng() {
5353
const canvas = document.createElement('canvas');
5454
const context = canvas.getContext('2d');
@@ -76,6 +76,7 @@
7676
img.src = `data:image/svg+xml;charset=utf-8;base64,${base64}`;
7777
}
7878

79+
/** Handles commands from the DiagramPanel */
7980
window.addEventListener('message', async (event) => {
8081
const { command, ...args } = event.data;
8182
switch (command) {
@@ -88,14 +89,10 @@
8889
}
8990
});
9091

92+
/** Init Mermaid JS */
9193
mermaid.initialize({
9294
startOnLoad: false
9395
});
94-
95-
const state = vscode.getState();
96-
if (state?.graphDefinition) {
97-
await updateDiagram({ graphDefinition: state.graphDefinition });
98-
}
9996
}
10097
catch (ex) {
10198
await vscode.postMessage({

src/diagram-panel.ts

+67-2
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,46 @@ import { MermaidDiagram, Specification } from '@severlessworkflow/sdk-typescript
55

66
export const diagramViewPanelType = 'serverlessWorkflowDiagramPanel';
77
export const diagramViewPanelTitle = 'Diagram Preview';
8+
/**
9+
* Type representing the mode of the diagram panel.
10+
* - preview: opens a panel and displays the diagram
11+
* - svg: generates a SVG from the diagram
12+
* - png: generates a PNG from the diagram
13+
*/
814
export type DiagramPanelMode = 'preview' | 'svg' | 'png';
15+
/**
16+
* Options for the diagram panel.
17+
*/
918
export type DiagramPanelOptions = {
1019
mode?: DiagramPanelMode
1120
};
1221

1322
let panelContent: string | undefined;
1423

24+
/**
25+
* Class representing a diagram panel.
26+
*/
1527
export class DiagramPanel {
1628
#context: vscode.ExtensionContext;
1729
#panel: vscode.WebviewPanel | undefined;
1830
#subscriptions: vscode.Disposable[] = [];
1931
#target: vscode.TextDocument | undefined;
2032
#disposeEventEmitter: vscode.EventEmitter<void> = new vscode.EventEmitter<void>();
2133
#mode: DiagramPanelMode;
34+
/** Gets the current @see vscode.TextDocument target */
35+
get target(): vscode.TextDocument | undefined {
36+
return this.#target;
37+
}
38+
/** Gets the current @see DiagramPanelMode */
39+
get mode(): DiagramPanelMode {
40+
return this.#mode;
41+
}
2242

43+
/**
44+
* Instanciates a new @see DiagramPanel
45+
* @param context The @see vscode.ExtensionContext
46+
* @param options The @see DiagramPanelOptions to instanciate the panel with
47+
*/
2348
constructor(context: vscode.ExtensionContext, options?: DiagramPanelOptions) {
2449
this.#context = context;
2550
this.#mode = options?.mode || 'preview';
@@ -59,10 +84,17 @@ export class DiagramPanel {
5984
);
6085
}
6186

87+
/**
88+
* Event that fires when the panel is disposed.
89+
*/
6290
public get onDidDispose(): vscode.Event<void> {
6391
return this.#disposeEventEmitter.event;
6492
}
65-
93+
94+
/**
95+
* Renders the panel with the specified target text document.
96+
* @param target The target text document.
97+
*/
6698
async render(target: vscode.TextDocument): Promise<void> {
6799
if (!this.#panel) {
68100
console.warn('No active diagram panel.');
@@ -80,10 +112,28 @@ export class DiagramPanel {
80112
this.#panel.webview.html = panelContent;
81113
}
82114

115+
/**
116+
* Shows the diagram
117+
* @returns
118+
*/
119+
async focus(): Promise<void> {
120+
if (!this.#panel) {
121+
console.warn('No active diagram panel.');
122+
return;
123+
}
124+
this.#panel.reveal(undefined, true);
125+
}
126+
127+
/**
128+
* Disposes the panel.
129+
*/
83130
dispose() {
84131
this.#panel?.dispose();
85132
}
86133

134+
/**
135+
* Initializes the panel.
136+
*/
87137
async #initPanel(): Promise<void> {
88138
if (panelContent) {
89139
return;
@@ -94,6 +144,10 @@ export class DiagramPanel {
94144
panelContent = decoder.decode(panelSourceContent);
95145
}
96146

147+
/**
148+
* Gets the destination file URI when generating an image.
149+
* @returns The destination file URI.
150+
*/
97151
#getFileDestination(): vscode.Uri | undefined {
98152
if (!this.#target) {
99153
return;
@@ -114,6 +168,10 @@ export class DiagramPanel {
114168
return destination;
115169
}
116170

171+
/**
172+
* Saves the diagram to a file.
173+
* @param buffer The buffer to save.
174+
*/
117175
async #saveToFile(buffer: Buffer): Promise<void> {
118176
try {
119177
let destination = this.#getFileDestination();
@@ -133,6 +191,10 @@ export class DiagramPanel {
133191
this.dispose();
134192
}
135193

194+
/**
195+
* Handles receiving message from the panel.
196+
* @param message The message received.
197+
*/
136198
async #onPanelReceiveMessage(message: any): Promise<void> {
137199
const { command, ...args } = message;
138200
switch (command) {
@@ -167,7 +229,10 @@ export class DiagramPanel {
167229
}
168230
}
169231
}
170-
232+
233+
/**
234+
* Updates the diagram.
235+
*/
171236
async #updateDiagram(): Promise<void> {
172237
if (!this.#panel) {
173238
console.warn('No active diagram panel.');

src/extension.ts

+68-25
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,35 @@ import * as path from 'path';
55
import { DiagramPanel, DiagramPanelMode } from './diagram-panel';
66

77
const supportedExtensions = [ '.json', '.yml', '.yaml' ];
8+
let diagramPanels: Array<DiagramPanel> = [];
89

10+
/**
11+
* Creates and registers a diagram panel
12+
* @param context The current @see vscode.ExtensionContext
13+
* @param mode The @see DiagramPanelMode
14+
* @param target The target @see vscode.TextDocument
15+
*/
16+
async function registerDiagramPanel(context: vscode.ExtensionContext, mode: DiagramPanelMode, target: vscode.TextDocument): Promise<void> {
17+
let diagramPanel: DiagramPanel | undefined = diagramPanels.find(panel => panel.mode === mode && panel.target === target);
18+
if (diagramPanel) {
19+
await diagramPanel.focus();
20+
return;
21+
}
22+
diagramPanel = new DiagramPanel(context, { mode });
23+
diagramPanels.push(diagramPanel);
24+
diagramPanel.onDidDispose((_) => {
25+
diagramPanels = diagramPanels.filter(panel => panel !== diagramPanel);
26+
}, null, context.subscriptions);
27+
await diagramPanel.render(target!);
28+
}
29+
30+
/**
31+
* Handles a command used to open/export a diagram
32+
* @param context The current @see vscode.ExtensionContext
33+
* @param mode The @see DiagramPanelMode
34+
* @param selectionSource The explorer menu item the action has been made on
35+
* @param selectedItems The selected items in the explorer menu
36+
*/
937
async function handleDiagramCommand(context: vscode.ExtensionContext, mode: DiagramPanelMode, selectionSource?: vscode.Uri, selectedItems?: vscode.Uri[]): Promise<void> {
1038
let title: string = 'Loading';
1139
switch (mode) {
@@ -19,35 +47,51 @@ async function handleDiagramCommand(context: vscode.ExtensionContext, mode: Diag
1947
title = 'Generating PNG...';
2048
break;
2149
}
22-
vscode.window.withProgress({
23-
location: vscode.ProgressLocation.Window,
24-
cancellable: false,
25-
title
26-
}, async() => {
27-
if (selectionSource && selectedItems) {
28-
for (let item of selectedItems) {
29-
const ext = path.extname(item.fsPath);
30-
if (supportedExtensions.includes(ext)) {
31-
const target = await vscode.workspace.openTextDocument(item);
32-
const diagramPanel = new DiagramPanel(context, { mode });
33-
await diagramPanel.render(target!);
50+
vscode.window.withProgress(
51+
{
52+
location: vscode.ProgressLocation.Window,
53+
cancellable: false,
54+
title
55+
},
56+
async() => {
57+
if (selectionSource && selectedItems) {
58+
for (let item of selectedItems) {
59+
const ext = path.extname(item.fsPath);
60+
if (supportedExtensions.includes(ext)) {
61+
const target = await vscode.workspace.openTextDocument(item);
62+
if (target) {
63+
registerDiagramPanel(context, mode, target);
64+
}
65+
}
66+
}
67+
return;
68+
}
69+
const target = vscode.window.activeTextEditor?.document;
70+
if (target) {
71+
registerDiagramPanel(context, mode, target);
3472
}
3573
}
36-
return;
37-
}
38-
else {
39-
const target = vscode.window.activeTextEditor?.document;
40-
if (target) {
41-
const diagramPanel = new DiagramPanel(context, { mode });
42-
await diagramPanel.render(target);
43-
}
44-
return;
45-
}
46-
});
74+
);
4775
}
4876

77+
/**
78+
* Activate the extension.
79+
* @param context The context of the extension.
80+
*/
4981
export function activate(context: vscode.ExtensionContext) {
50-
82+
// Detects when a file is focused
83+
vscode.window.onDidChangeActiveTextEditor(editor => {
84+
if ((editor?.document?.languageId === 'json' || editor?.document?.languageId === 'yaml')
85+
&& editor?.document?.fileName.includes('.sw.')
86+
) {
87+
registerDiagramPanel(context, 'preview', editor?.document /* === vscode.window.activeTextEditor?.document*/);
88+
}
89+
}, null, context.subscriptions);
90+
//Detect when a file is closed
91+
vscode.workspace.onDidCloseTextDocument(document => {
92+
diagramPanels.filter(panel => panel.target === document).forEach(panel => panel.dispose());
93+
}, null, context.subscriptions);
94+
// Handles commands (palette/right click/keybinding, see package.json for more info)
5195
context.subscriptions.push(
5296
vscode.commands.registerCommand('serverlessWorkflow.diagram.preview', handleDiagramCommand.bind(null, context, 'preview'))
5397
);
@@ -57,5 +101,4 @@ export function activate(context: vscode.ExtensionContext) {
57101
context.subscriptions.push(
58102
vscode.commands.registerCommand('serverlessWorkflow.diagram.png', handleDiagramCommand.bind(null, context, 'png'))
59103
);
60-
61104
}

0 commit comments

Comments
 (0)