Skip to content

Commit

Permalink
Merge pull request #30 from JBBianchi/feat-29-automatic-opening
Browse files Browse the repository at this point in the history
Automatic opening
  • Loading branch information
ricardozanini authored Mar 26, 2024
2 parents be3b9ea + 45ed045 commit 025f3a9
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 43 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 11 additions & 14 deletions resources/diagram-panel.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,21 @@
<div id="diagram-container"></div>
<script type="module">
const vscode = acquireVsCodeApi();
/** Waits for the page to be fully loaded before trying to render the graph */
window.addEventListener('load', async() => {
await vscode.postMessage({
command: 'panel-content-loaded'
})
});
});
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid/+esm';
(async () => {
try {
/** The diagram container */
const diagramContainerEl = document.getElementById('diagram-container');

function updateState(partialState) {
vscode.setState({
...vscode.getState(),
...partialState
});
}

/**
* Renders the provided graph definition
*/
async function updateDiagram({ graphDefinition }) {
try {
const { svg, bindFunctions } = await mermaid.render('sw-diagram', graphDefinition);
Expand All @@ -38,7 +36,6 @@
command: 'diagram-rendered',
svg: sanitizedSvg
});
updateState({ graphDefinition });
}
catch (ex) {
await vscode.postMessage({
Expand All @@ -49,6 +46,9 @@
}
}

/**
* Generates a PNG using a canvas
*/
function generatePng() {
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
Expand Down Expand Up @@ -76,6 +76,7 @@
img.src = `data:image/svg+xml;charset=utf-8;base64,${base64}`;
}

/** Handles commands from the DiagramPanel */
window.addEventListener('message', async (event) => {
const { command, ...args } = event.data;
switch (command) {
Expand All @@ -88,14 +89,10 @@
}
});

/** Init Mermaid JS */
mermaid.initialize({
startOnLoad: false
});

const state = vscode.getState();
if (state?.graphDefinition) {
await updateDiagram({ graphDefinition: state.graphDefinition });
}
}
catch (ex) {
await vscode.postMessage({
Expand Down
69 changes: 67 additions & 2 deletions src/diagram-panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,46 @@ import { MermaidDiagram, Specification } from '@severlessworkflow/sdk-typescript

export const diagramViewPanelType = 'serverlessWorkflowDiagramPanel';
export const diagramViewPanelTitle = 'Diagram Preview';
/**
* Type representing the mode of the diagram panel.
* - preview: opens a panel and displays the diagram
* - svg: generates a SVG from the diagram
* - png: generates a PNG from the diagram
*/
export type DiagramPanelMode = 'preview' | 'svg' | 'png';
/**
* Options for the diagram panel.
*/
export type DiagramPanelOptions = {
mode?: DiagramPanelMode
};

let panelContent: string | undefined;

/**
* Class representing a diagram panel.
*/
export class DiagramPanel {
#context: vscode.ExtensionContext;
#panel: vscode.WebviewPanel | undefined;
#subscriptions: vscode.Disposable[] = [];
#target: vscode.TextDocument | undefined;
#disposeEventEmitter: vscode.EventEmitter<void> = new vscode.EventEmitter<void>();
#mode: DiagramPanelMode;
/** Gets the current @see vscode.TextDocument target */
get target(): vscode.TextDocument | undefined {
return this.#target;
}
/** Gets the current @see DiagramPanelMode */
get mode(): DiagramPanelMode {
return this.#mode;
}

/**
* Instanciates a new @see DiagramPanel
* @param context The @see vscode.ExtensionContext
* @param options The @see DiagramPanelOptions to instanciate the panel with
*/
constructor(context: vscode.ExtensionContext, options?: DiagramPanelOptions) {
this.#context = context;
this.#mode = options?.mode || 'preview';
Expand Down Expand Up @@ -59,10 +84,17 @@ export class DiagramPanel {
);
}

/**
* Event that fires when the panel is disposed.
*/
public get onDidDispose(): vscode.Event<void> {
return this.#disposeEventEmitter.event;
}


/**
* Renders the panel with the specified target text document.
* @param target The target text document.
*/
async render(target: vscode.TextDocument): Promise<void> {
if (!this.#panel) {
console.warn('No active diagram panel.');
Expand All @@ -80,10 +112,28 @@ export class DiagramPanel {
this.#panel.webview.html = panelContent;
}

/**
* Shows the diagram
* @returns
*/
async focus(): Promise<void> {
if (!this.#panel) {
console.warn('No active diagram panel.');
return;
}
this.#panel.reveal(undefined, true);
}

/**
* Disposes the panel.
*/
dispose() {
this.#panel?.dispose();
}

/**
* Initializes the panel.
*/
async #initPanel(): Promise<void> {
if (panelContent) {
return;
Expand All @@ -94,6 +144,10 @@ export class DiagramPanel {
panelContent = decoder.decode(panelSourceContent);
}

/**
* Gets the destination file URI when generating an image.
* @returns The destination file URI.
*/
#getFileDestination(): vscode.Uri | undefined {
if (!this.#target) {
return;
Expand All @@ -114,6 +168,10 @@ export class DiagramPanel {
return destination;
}

/**
* Saves the diagram to a file.
* @param buffer The buffer to save.
*/
async #saveToFile(buffer: Buffer): Promise<void> {
try {
let destination = this.#getFileDestination();
Expand All @@ -133,6 +191,10 @@ export class DiagramPanel {
this.dispose();
}

/**
* Handles receiving message from the panel.
* @param message The message received.
*/
async #onPanelReceiveMessage(message: any): Promise<void> {
const { command, ...args } = message;
switch (command) {
Expand Down Expand Up @@ -167,7 +229,10 @@ export class DiagramPanel {
}
}
}


/**
* Updates the diagram.
*/
async #updateDiagram(): Promise<void> {
if (!this.#panel) {
console.warn('No active diagram panel.');
Expand Down
93 changes: 68 additions & 25 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,35 @@ import * as path from 'path';
import { DiagramPanel, DiagramPanelMode } from './diagram-panel';

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

/**
* Creates and registers a diagram panel
* @param context The current @see vscode.ExtensionContext
* @param mode The @see DiagramPanelMode
* @param target The target @see vscode.TextDocument
*/
async function registerDiagramPanel(context: vscode.ExtensionContext, mode: DiagramPanelMode, target: vscode.TextDocument): Promise<void> {
let diagramPanel: DiagramPanel | undefined = diagramPanels.find(panel => panel.mode === mode && panel.target === target);
if (diagramPanel) {
await diagramPanel.focus();
return;
}
diagramPanel = new DiagramPanel(context, { mode });
diagramPanels.push(diagramPanel);
diagramPanel.onDidDispose((_) => {
diagramPanels = diagramPanels.filter(panel => panel !== diagramPanel);
}, null, context.subscriptions);
await diagramPanel.render(target!);
}

/**
* Handles a command used to open/export a diagram
* @param context The current @see vscode.ExtensionContext
* @param mode The @see DiagramPanelMode
* @param selectionSource The explorer menu item the action has been made on
* @param selectedItems The selected items in the explorer menu
*/
async function handleDiagramCommand(context: vscode.ExtensionContext, mode: DiagramPanelMode, selectionSource?: vscode.Uri, selectedItems?: vscode.Uri[]): Promise<void> {
let title: string = 'Loading';
switch (mode) {
Expand All @@ -19,35 +47,51 @@ async function handleDiagramCommand(context: vscode.ExtensionContext, mode: Diag
title = 'Generating PNG...';
break;
}
vscode.window.withProgress({
location: vscode.ProgressLocation.Window,
cancellable: false,
title
}, async() => {
if (selectionSource && selectedItems) {
for (let item of selectedItems) {
const ext = path.extname(item.fsPath);
if (supportedExtensions.includes(ext)) {
const target = await vscode.workspace.openTextDocument(item);
const diagramPanel = new DiagramPanel(context, { mode });
await diagramPanel.render(target!);
vscode.window.withProgress(
{
location: vscode.ProgressLocation.Window,
cancellable: false,
title
},
async() => {
if (selectionSource && selectedItems) {
for (let item of selectedItems) {
const ext = path.extname(item.fsPath);
if (supportedExtensions.includes(ext)) {
const target = await vscode.workspace.openTextDocument(item);
if (target) {
registerDiagramPanel(context, mode, target);
}
}
}
return;
}
const target = vscode.window.activeTextEditor?.document;
if (target) {
registerDiagramPanel(context, mode, target);
}
}
return;
}
else {
const target = vscode.window.activeTextEditor?.document;
if (target) {
const diagramPanel = new DiagramPanel(context, { mode });
await diagramPanel.render(target);
}
return;
}
});
);
}

/**
* Activate the extension.
* @param context The context of the extension.
*/
export function activate(context: vscode.ExtensionContext) {

// Detects when a file is focused
vscode.window.onDidChangeActiveTextEditor(editor => {
if ((editor?.document?.languageId === 'json' || editor?.document?.languageId === 'yaml')
&& editor?.document?.fileName.includes('.sw.')
) {
registerDiagramPanel(context, 'preview', editor?.document /* === vscode.window.activeTextEditor?.document*/);
}
}, null, context.subscriptions);
//Detect when a file is closed
vscode.workspace.onDidCloseTextDocument(document => {
diagramPanels.filter(panel => panel.target === document).forEach(panel => panel.dispose());
}, null, context.subscriptions);
// Handles commands (palette/right click/keybinding, see package.json for more info)
context.subscriptions.push(
vscode.commands.registerCommand('serverlessWorkflow.diagram.preview', handleDiagramCommand.bind(null, context, 'preview'))
);
Expand All @@ -57,5 +101,4 @@ export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.commands.registerCommand('serverlessWorkflow.diagram.png', handleDiagramCommand.bind(null, context, 'png'))
);

}

0 comments on commit 025f3a9

Please sign in to comment.