diff --git a/extension/src-language-server/diagram-server.ts b/extension/src-language-server/diagram-server.ts index 9695de36..0c830288 100644 --- a/extension/src-language-server/diagram-server.ts +++ b/extension/src-language-server/diagram-server.ts @@ -26,7 +26,9 @@ import { import { Connection } from "vscode-languageserver"; import { SetSynthesisOptionsAction, UpdateOptionsAction } from "./options/actions"; import { DropDownOption } from "./options/option-models"; -import { StpaDiagramSnippets } from './snippets/stpa-templates'; +import { SnippetDiagramServer } from './snippets/snippet-diagram-server'; +import { LanguageSnippet } from './snippets/snippet-model'; +import { StpaDiagramSnippets } from './snippets/stpa-snippets'; import { GenerateSVGsAction, RequestSvgAction, SvgAction } from "./stpa/actions"; import { StpaSynthesisOptions, filteringUCAsID } from "./stpa/diagram/stpa-synthesis-options"; import { @@ -54,13 +56,10 @@ import { setSystemConstraintGraphOptions, } from "./stpa/result-report/svg-generator"; import { SynthesisOptions } from "./synthesis-options"; -import { SnippetDiagramServer } from './snippets/snippet-diagram-server'; -import { LanguageSnippet } from './snippets/snippet-model'; export class PastaDiagramServer extends SnippetDiagramServer { protected synthesisOptions: SynthesisOptions | undefined; - protected stpaTemps: StpaDiagramSnippets | undefined; - // clientId: string; + protected stpaSnippets: StpaDiagramSnippets | undefined; protected connection: Connection | undefined; constructor( @@ -70,10 +69,10 @@ export class PastaDiagramServer extends SnippetDiagramServer { options: JsonMap | undefined, connection: Connection | undefined, synthesisOptions?: SynthesisOptions, - stpaTemps?: StpaDiagramSnippets + stpaSnippets?: StpaDiagramSnippets ) { - super(dispatch, services, clientId, stpaTemps?.getSnippets() ?? [], options, connection); - this.stpaTemps = stpaTemps; + super(dispatch, services, clientId, stpaSnippets?.getSnippets() ?? [], options, connection); + this.stpaSnippets = stpaSnippets; this.synthesisOptions = synthesisOptions; this.clientId = clientId; this.connection = connection; @@ -99,8 +98,13 @@ export class PastaDiagramServer extends SnippetDiagramServer { return super.handleAction(action); } - protected createTempFromString(text: string): LanguageSnippet { - return this.stpaTemps?.createTemp(text) ?? {} as LanguageSnippet; + /** + * Creates a snippet from a string. + * @param text The text that should be inserted when clicking on the snippet. + * @returns a snippet for the given {@code text} + */ + protected createSnippetFromString(text: string): LanguageSnippet { + return this.stpaSnippets?.createSnippet(text) ?? {} as LanguageSnippet; } /** diff --git a/extension/src-language-server/fta/fta-module.ts b/extension/src-language-server/fta/fta-module.ts index 01ca2449..cbe74c7c 100644 --- a/extension/src-language-server/fta/fta-module.ts +++ b/extension/src-language-server/fta/fta-module.ts @@ -18,19 +18,14 @@ import ElkConstructor from "elkjs/lib/elk.bundled"; import { Module, PartialLangiumServices } from "langium"; import { LangiumSprottyServices, SprottyDiagramServices } from "langium-sprotty"; -import { - DefaultElementFilter, - ElkFactory, - IElementFilter, - ILayoutConfigurator -} from "sprotty-elk/lib/elk-layout"; +import { DefaultElementFilter, ElkFactory, IElementFilter, ILayoutConfigurator } from "sprotty-elk/lib/elk-layout"; import { LayoutEngine } from "../layout-engine"; +import { StpaDiagramSnippets } from "../snippets/stpa-snippets"; import { FtaDiagramGenerator } from "./diagram/fta-diagram-generator"; import { FtaLayoutConfigurator } from "./diagram/fta-layout-config"; import { FtaSynthesisOptions } from "./diagram/fta-synthesis-options"; import { FtaScopeProvider } from "./fta-scopeProvider"; import { FtaValidationRegistry, FtaValidator } from "./fta-validator"; -import { StpaTemplates } from '../stpa-templates'; /** * Declaration of custom services. @@ -50,8 +45,8 @@ export type FtaAddedServices = { options: { SynthesisOptions: FtaSynthesisOptions; }; - templates: { - StpaTemplates: StpaTemplates; + snippets: { + StpaDiagramSnippets: StpaDiagramSnippets; }; }; @@ -68,8 +63,8 @@ export type FtaServices = LangiumSprottyServices & FtaAddedServices; */ export const FtaModule: Module = { diagram: { - DiagramGenerator: (services) => new FtaDiagramGenerator(services), - ModelLayoutEngine: (services) => + DiagramGenerator: services => new FtaDiagramGenerator(services), + ModelLayoutEngine: services => new LayoutEngine( services.layout.ElkFactory, services.layout.ElementFilter, @@ -77,11 +72,11 @@ export const FtaModule: Module new FtaScopeProvider(services), - FtaScopeProvider: (services) => new FtaScopeProvider(services), + ScopeProvider: services => new FtaScopeProvider(services), + FtaScopeProvider: services => new FtaScopeProvider(services), }, validation: { - ValidationRegistry: (services) => new FtaValidationRegistry(services), + ValidationRegistry: services => new FtaValidationRegistry(services), FtaValidator: () => new FtaValidator(), }, layout: { @@ -92,7 +87,7 @@ export const FtaModule: Module new FtaSynthesisOptions(), }, - templates: { - StpaTemplates: services => new StpaTemplates(services) - } + snippets: { + StpaDiagramSnippets: services => new StpaDiagramSnippets(services), + }, }; diff --git a/extension/src-language-server/handler.ts b/extension/src-language-server/handler.ts index 0012cc9e..ed0f7339 100644 --- a/extension/src-language-server/handler.ts +++ b/extension/src-language-server/handler.ts @@ -17,10 +17,10 @@ import { LangiumSprottySharedServices } from "langium-sprotty"; import { Connection, Range } from "vscode-languageserver"; +import { getRangeOfNodeFTA } from "./fta/utils"; import { Model, isModel, isModelFTA } from "./generated/ast"; import { getRangeOfNodeSTPA } from "./stpa/utils"; import { getModel } from "./utils"; -import { getRangeOfNodeFTA } from "./fta/utils"; /** * Adds handler for notifications. diff --git a/extension/src-language-server/main.ts b/extension/src-language-server/main.ts index 754cae59..567e59cc 100644 --- a/extension/src-language-server/main.ts +++ b/extension/src-language-server/main.ts @@ -39,7 +39,7 @@ addFTANotificationHandler(connection, fta, shared); addNotificationHandler(connection, shared); // handle configuration changes for the validation checks -connection.onNotification("configuration", (options) => { +connection.onNotification("configuration", options => { for (const option of options) { switch (option.id) { case "checkResponsibilitiesForConstraints": diff --git a/extension/src-language-server/module.ts b/extension/src-language-server/module.ts index e8114258..ca055524 100644 --- a/extension/src-language-server/module.ts +++ b/extension/src-language-server/module.ts @@ -20,7 +20,7 @@ import { DefaultDiagramServerManager, DiagramActionNotification, LangiumSprottySharedServices, - SprottySharedServices + SprottySharedServices, } from "langium-sprotty"; import { DiagramOptions } from "sprotty-protocol"; import { URI } from "vscode-uri"; @@ -49,11 +49,7 @@ export function createServices(context: DefaultSharedModuleContext): { stpa: StpaServices; fta: FtaServices; } { - const shared = inject( - createDefaultSharedModule(context), - PastaGeneratedSharedModule, - PastaSprottySharedModule - ); + const shared = inject(createDefaultSharedModule(context), PastaGeneratedSharedModule, PastaSprottySharedModule); const stpa = inject(createDefaultModule({ shared }), StpaGeneratedModule, STPAModule); const fta = inject(createDefaultModule({ shared }), FtaGeneratedModule, FtaModule); shared.ServiceRegistry.register(stpa); @@ -71,20 +67,20 @@ const pastaDiagramServerFactory = ( if (!sourceUri) { throw new Error("Missing 'sourceUri' option in request."); } - const language = serviceRegistry.getServices(URI.parse(sourceUri as string)) as (StpaServices | FtaServices); + const language = serviceRegistry.getServices(URI.parse(sourceUri as string)) as StpaServices | FtaServices; if (!language.diagram) { throw new Error(`The '${language.LanguageMetaData.languageId}' language does not support diagrams.`); } return new PastaDiagramServer( - async (action) => { + async action => { connection?.sendNotification(DiagramActionNotification.type, { clientId, action }); }, language.diagram, clientId, options, connection, - language.options.SynthesisOptions, - language.templates.StpaTemplates + language.options.SynthesisOptions, + language.snippets.StpaDiagramSnippets ); }; }; @@ -95,6 +91,6 @@ const pastaDiagramServerFactory = ( const PastaSprottySharedModule: Module = { diagram: { diagramServerFactory: pastaDiagramServerFactory, - DiagramServerManager: (services) => new DefaultDiagramServerManager(services), + DiagramServerManager: services => new DefaultDiagramServerManager(services), }, -}; \ No newline at end of file +}; diff --git a/extension/src-language-server/options/actions.ts b/extension/src-language-server/options/actions.ts index f5117f6a..7cbc2b3f 100644 --- a/extension/src-language-server/options/actions.ts +++ b/extension/src-language-server/options/actions.ts @@ -47,8 +47,8 @@ export namespace UpdateOptionsAction { /** Change the value of one or multiple synthesis options. */ export interface SetSynthesisOptionsAction extends Action { - kind: typeof SetSynthesisOptionsAction.KIND; - options: SynthesisOption[]; + kind: typeof SetSynthesisOptionsAction.KIND + options: SynthesisOption[] } export namespace SetSynthesisOptionsAction { diff --git a/extension/src-language-server/snippets/actions.ts b/extension/src-language-server/snippets/actions.ts index 6fe584e1..4796b95d 100644 --- a/extension/src-language-server/snippets/actions.ts +++ b/extension/src-language-server/snippets/actions.ts @@ -19,7 +19,7 @@ import { VNode } from "snabbdom"; import { Action, RequestAction, ResponseAction, generateRequestId } from "sprotty-protocol"; import { WebviewSnippet } from "./snippet-model"; -/** Request message from the server to get the svgs of the snippets. */ +/** Request message from the server to the client to get the svgs of the snippets. */ export interface RequestWebviewSnippetsAction extends RequestAction { kind: typeof RequestWebviewSnippetsAction.KIND; snippets: WebviewSnippet[]; @@ -88,10 +88,10 @@ export namespace ExecuteSnippetAction { } } -/** Message containing snippets as string. */ +/** Message from extension to langauge server containing snippets as string. (Used to add default snippets) */ export interface SendSnippetsAction extends Action { kind: typeof SendSnippetsAction.KIND; - temps: string[]; + snippets: string[]; } export namespace SendSnippetsAction { @@ -100,7 +100,7 @@ export namespace SendSnippetsAction { export function create(temps: string[]): SendSnippetsAction { return { kind: KIND, - temps, + snippets: temps, }; } diff --git a/extension/src-language-server/snippets/snippet-diagram-server.ts b/extension/src-language-server/snippets/snippet-diagram-server.ts index 782220f0..dd36d54b 100644 --- a/extension/src-language-server/snippets/snippet-diagram-server.ts +++ b/extension/src-language-server/snippets/snippet-diagram-server.ts @@ -15,13 +15,27 @@ * SPDX-License-Identifier: EPL-2.0 */ -import { Action, applyBounds, ComputedBoundsAction, DiagramServer, DiagramServices, JsonMap, RequestBoundsAction, RequestModelAction } from 'sprotty-protocol'; -import { Connection } from 'vscode-languageserver'; -import { AddSnippetAction, ExecuteSnippetAction, RequestWebviewSnippetsAction, SendSnippetsAction, SendWebviewSnippetsAction } from './actions'; -import { LanguageSnippet, SnippetGraphGenerator, WebviewSnippet } from './snippet-model'; +import { + Action, + applyBounds, + ComputedBoundsAction, + DiagramServer, + DiagramServices, + JsonMap, + RequestBoundsAction, + RequestModelAction, +} from "sprotty-protocol"; +import { Connection } from "vscode-languageserver"; +import { + AddSnippetAction, + ExecuteSnippetAction, + RequestWebviewSnippetsAction, + SendSnippetsAction, + SendWebviewSnippetsAction, +} from "./actions"; +import { LanguageSnippet, SnippetGraphGenerator, WebviewSnippet } from "./snippet-model"; export abstract class SnippetDiagramServer extends DiagramServer { - protected clientId: string; protected snippets: LanguageSnippet[] = []; protected defaultTemps: LanguageSnippet[]; @@ -30,8 +44,14 @@ export abstract class SnippetDiagramServer extends DiagramServer { protected snippetGraphGenerator: SnippetGraphGenerator; protected tempRdy: boolean = false; - constructor(dispatch: (action: A) => Promise, - services: DiagramServices, clientId: string, snippets: LanguageSnippet[], options: JsonMap | undefined, connection: Connection | undefined) { + constructor( + dispatch: (action: A) => Promise, + services: DiagramServices, + clientId: string, + snippets: LanguageSnippet[], + options: JsonMap | undefined, + connection: Connection | undefined + ) { super(dispatch, services); this.clientId = clientId; this.options = options; @@ -57,7 +77,7 @@ export abstract class SnippetDiagramServer extends DiagramServer { } const webTemp = { graph: graph, - id: snippet.id + id: snippet.id, }; webviewSnippets.push(webTemp); } else { @@ -65,7 +85,6 @@ export abstract class SnippetDiagramServer extends DiagramServer { } } return webviewSnippets; - } protected handleAction(action: Action): Promise { @@ -83,20 +102,25 @@ export abstract class SnippetDiagramServer extends DiagramServer { /** * Creates a snippet based on {@code action} and adds it to the snippet list & config. * @param action Action containing the text to create a snippet. - * @returns + * @returns */ protected async handleAddSnippet(action: AddSnippetAction): Promise { - const temp = this.createTempFromString(action.text); + const temp = this.createSnippetFromString(action.text); if (await this.parseable(temp)) { this.snippetGraphGenerator.deleteDanglingEdges(temp); this.addSnippets([temp]); - this.connection?.sendNotification('config/add', [temp.baseCode]); + this.connection?.sendNotification("config/add", [temp.baseCode]); } else { - this.connection?.sendNotification('snippets/creationFailed'); + this.connection?.sendNotification("snippets/creationFailed"); } return Promise.resolve(); } + /** + * Determines if a snippet is parseable i.e. an AST can be created. + * @param snippet The snippet to check. + * @returns whether the snippet is parseable. + */ protected async parseable(snippet: LanguageSnippet): Promise { const graph = await this.snippetGraphGenerator.generateSnippetRoot(snippet); if (graph) { @@ -107,45 +131,71 @@ export abstract class SnippetDiagramServer extends DiagramServer { } /** - * Creates snippets based on the texts in {@code action}. + * Creates snippets based on the texts in {@code action} and updates the webview. * @param action Action containing the snippets texts. - * @returns + * @returns */ protected handleSendSnippets(action: SendSnippetsAction): Promise { // if no snippets are in the config file, add the default ones - if (action.temps.length === 0) { + if (action.snippets.length === 0) { this.snippets = this.defaultTemps; - this.connection?.sendNotification('config/add', this.defaultTemps.map(temp => temp.baseCode)); + this.connection?.sendNotification( + "config/add", + this.defaultTemps.map(temp => temp.baseCode) + ); } else { - this.snippets = action.temps.map(temp => this.createTempFromString(temp)); + this.snippets = action.snippets.map(temp => this.createSnippetFromString(temp)); } this.update(); return Promise.resolve(); } - protected abstract createTempFromString(text: string): LanguageSnippet; + /** + * Creates snippets based on the given {@code text}. + * @param text The text to create the snippets from. + * @returns a snippet created from the text. + */ + protected abstract createSnippetFromString(text: string): LanguageSnippet; + /** + * Adds the given snippets to the snippet list and updates the webview. + * @param temps The snippets to add. + */ addSnippets(temps: LanguageSnippet[]): void { this.snippets.push(...temps); this.update(); } + /** + * Updates the webview with the current snippets. + */ async update(): Promise { const temps = await this.getSnippets(); - // send the avaiable snippets to the client - const response = await this.request({ kind: RequestWebviewSnippetsAction.KIND, snippets: temps, clientId: this.clientId } as RequestWebviewSnippetsAction); + // send the avaiable snippets to the client to get them rendered + const response = await this.request({ + kind: RequestWebviewSnippetsAction.KIND, + snippets: temps, + clientId: this.clientId, + } as RequestWebviewSnippetsAction); // send graph svgs to extension - this.connection?.sendNotification('snippets/add', { snippets: response.snippets }); + this.connection?.sendNotification("snippets/add", { snippets: response.snippets }); } + /** + * Adds the text of the snippet in {@code action} to the editor. + * @param action + */ protected async handleExecuteSnippet(action: ExecuteSnippetAction): Promise { + // uri of the file in which the snippet should be inserted const uri = this.options?.sourceUri; const temp = this.snippets.find(temp => temp.id === action.id); if (temp) { + // determine position where to add const pos = temp.getPosition(uri as string); - this.connection?.sendNotification('editor/add', { uri: uri, text: temp.insertText, position: pos }); + // add the text of the snippet to the editor + this.connection?.sendNotification("editor/add", { uri: uri, text: temp.insertText, position: pos }); } else { - console.error('There is no Diagram Snippet with id ' + action.id); + console.error("There is no Diagram Snippet with id " + action.id); } } @@ -154,5 +204,4 @@ export abstract class SnippetDiagramServer extends DiagramServer { this.options = action.options; super.handleRequestModel(action); } - -} \ No newline at end of file +} diff --git a/extension/src-language-server/snippets/snippet-model.ts b/extension/src-language-server/snippets/snippet-model.ts index 6ebf9ca1..c2c56838 100644 --- a/extension/src-language-server/snippets/snippet-model.ts +++ b/extension/src-language-server/snippets/snippet-model.ts @@ -15,24 +15,51 @@ * SPDX-License-Identifier: EPL-2.0 */ -import { LangiumDiagramGenerator } from 'langium-sprotty'; -import { SModelElement, SModelRoot } from 'sprotty-protocol'; -import { Position } from 'vscode-languageserver'; +import { LangiumDiagramGenerator } from "langium-sprotty"; +import { SModelElement, SModelRoot } from "sprotty-protocol"; +import { Position } from "vscode-languageserver"; +/** + * Represents a snippet that can be inserted into a document. + */ export interface LanguageSnippet { + /** the code that should be added when executing the snippet */ baseCode: string; + /** the original text representing the snippet */ insertText: string; + /** unique id of the snippet */ id: string; + /** + * Calculates the position where the snippet should be inserted. + * @param uri The uri of the document in which the snippet should be inserted. + * @returns the position where the snippet should be inserted. + */ getPosition(uri: string): Position; } +/** + * Represents a snippet that can be displayed in a webview. + */ export interface WebviewSnippet { + /** the graph to display */ graph: Readonly; + /** unique id of the snippet */ id: string; } +/** + * A generator for snippet diagrams. + */ export abstract class SnippetGraphGenerator extends LangiumDiagramGenerator { + /** + * Deletes all edges that are not connected to a node. + * @param snippet The snippet to clean up. + */ abstract deleteDanglingEdges(snippet: LanguageSnippet): void; + /** + * Generates the root element of the snippet diagram. + * @param snippet The snippet to generate the diagram for. + */ abstract generateSnippetRoot(snippet: LanguageSnippet): Promise; -} \ No newline at end of file +} diff --git a/extension/src-language-server/snippets/stpa-templates.ts b/extension/src-language-server/snippets/stpa-snippets.ts similarity index 79% rename from extension/src-language-server/snippets/stpa-templates.ts rename to extension/src-language-server/snippets/stpa-snippets.ts index 978d8815..0bff8aaa 100644 --- a/extension/src-language-server/snippets/stpa-templates.ts +++ b/extension/src-language-server/snippets/stpa-snippets.ts @@ -15,28 +15,30 @@ * SPDX-License-Identifier: EPL-2.0 */ -import { Position } from 'vscode-languageserver'; -import { LanguageSnippet } from './snippet-model'; -import { LangiumDocuments, LangiumServices } from 'langium'; -import { URI } from 'vscode-uri'; -import { TextDocument } from 'vscode-languageserver-textdocument'; +import { LangiumDocuments, LangiumServices } from "langium"; +import { Position } from "vscode-languageserver"; +import { TextDocument } from "vscode-languageserver-textdocument"; +import { URI } from "vscode-uri"; +import { LanguageSnippet } from "./snippet-model"; +/** + * Class that handles snippets for the STPA diagram. + */ export class StpaDiagramSnippets { - protected readonly langiumDocuments: LangiumDocuments; - protected defaultSnippets: LanguageSnippet[]; + /** Counts how much custom snippets exist */ + protected customSnippetsNumber: number = 0; + /** The currently existing snippets */ protected snippets: LanguageSnippet[]; - protected customTempsNumber: number = 0; constructor(services: LangiumServices) { this.langiumDocuments = services.shared.workspace.LangiumDocuments; - this.defaultSnippets = this.generateDefaultSnippets(); - this.snippets = this.defaultSnippets; + this.snippets = this.generateDefaultSnippets(); } /** * Creates the default snippets. - * @returns A list with the default snippets. + * @returns a list with the default snippets. */ protected generateDefaultSnippets(): LanguageSnippet[] { return [ @@ -44,35 +46,43 @@ export class StpaDiagramSnippets { new SimpleCSWithAcsSnippet(this.langiumDocuments, "T1"), new ConsControllersSnippet(this.langiumDocuments, "T3"), new ParallelContsSnippet(this.langiumDocuments, "T4"), - new ConsContsWithLoopSnippet(this.langiumDocuments, "T5") + new ConsContsWithLoopSnippet(this.langiumDocuments, "T5"), ]; } - createTemp(text: string): LanguageSnippet { + /** + * Creates a snippet for the given {@code text}. + * @param text The text that should be inserted when clicking on the snippet. + * @returns a snippet for the given {@code text}. + */ + createSnippet(text: string): LanguageSnippet { // TODO: currently only control structure - this.customTempsNumber++; - return new CustomCSSnippet(this.langiumDocuments, text, 'CS' + this.customTempsNumber, text); + this.customSnippetsNumber++; + return new CustomCSSnippet(this.langiumDocuments, text, "CS" + this.customSnippetsNumber, text); } + /** + * Get the snippets that are available. + * @returns all available snippets. + */ getSnippets(): LanguageSnippet[] { return this.snippets; } - } /** - * Calculates the actual text of snippets for the control structure and theri position in the document. + * Calculates the actual text of snippets for the control structure and their position in the document. * @param document The document in which the snippet should be inserted. * @param snippet The snippet that should be inserted. - * @returns The position where the tempalte should be added to the {@code document}. + * @returns the position where the snippet should be added to the {@code document}. */ function getPositionForCSSnippet(document: TextDocument, snippet: LanguageSnippet): Position { const docText = document.getText(); // determine range of already existing definition of control structure - const titleIndex = docText.indexOf('ControlStructure'); + const titleIndex = docText.indexOf("ControlStructure"); const startIndex = titleIndex !== -1 ? titleIndex : 0; - const nextTitleIndex = docText.indexOf('Responsibilities'); + const nextTitleIndex = docText.indexOf("Responsibilities"); const endIndex = nextTitleIndex !== -1 ? nextTitleIndex - 1 : docText.length - 1; if (titleIndex === -1) { return document.positionAt(endIndex); @@ -80,20 +90,22 @@ function getPositionForCSSnippet(document: TextDocument, snippet: LanguageSnippe snippet.insertText = snippet.insertText.substring(18, snippet.insertText.length); // check whether a graph ID already exist const csText = docText.substring(startIndex, endIndex); - const graphIndex = csText.indexOf('{'); + const graphIndex = csText.indexOf("{"); if (graphIndex === -1) { return document.positionAt(endIndex); } else { - snippet.insertText = snippet.insertText.substring(snippet.insertText.indexOf('{') + 1, snippet.insertText.lastIndexOf('}')); - const bracketIndex = csText.lastIndexOf('}'); + snippet.insertText = snippet.insertText.substring( + snippet.insertText.indexOf("{") + 1, + snippet.insertText.lastIndexOf("}") + ); + const bracketIndex = csText.lastIndexOf("}"); return document.positionAt(titleIndex + bracketIndex - 1); } } - } - +// TODO: maybe need adjustments since CS can be nested now /** - * Adds {@code id} to the node names in {@code text}. + * Adds {@code id} to the control structure node names in {@code text}. * @param text The control structure text. * @param id The id to append. * @returns The modified text. @@ -103,7 +115,7 @@ function addNodeIDs(text: string, id: string): string { // collect node names const names: string[] = []; for (let i = 3; i < splits.length; i++) { - if (splits[i] === '{' && !isKeyWord(splits[i - 1])) { + if (splits[i] === "{" && !isKeyWord(splits[i - 1])) { names.push(splits[i - 1]); } } @@ -116,15 +128,30 @@ function addNodeIDs(text: string, id: string): string { return text; } +/** + * Checks whether the given {@code text} is a keyword. + * @param text The text to check. + * @returns whether the given {@code text} is a keyword. + */ function isKeyWord(text: string): boolean { - return text === 'hierarchyLevel' || text === 'label' || text === 'processModel' || text === 'controlActions' || text === 'feedback'; + return ( + text === "hierarchyLevel" || + text === "label" || + text === "processModel" || + text === "controlActions" || + text === "feedback" + ); } +/** + * Custom snippet for the control structure. + */ export class CustomCSSnippet implements LanguageSnippet { insertText: string; documents: LangiumDocuments; id: string; baseCode: string; + /** Counts how many times the snippet was added already */ protected counter: number = 0; constructor(documents: LangiumDocuments, insertText: string, id: string, baseCode: string) { @@ -136,16 +163,16 @@ export class CustomCSSnippet implements LanguageSnippet { } /** - * Check whether the CS caption and graph name exists. If not, adds it. + * Check whether the CS caption and graph name exists. If not, adds it to the baseCode. */ protected checkCaption(): void { const splits = this.baseCode.split(/[^a-zA-Z0-9\{\}]/); const words = splits.filter(child => child !== ""); - if (words[0] !== 'ControlStructure') { - if (isKeyWord(words[2]) || words[2] === '}' && words.length > 3) { - this.baseCode = 'ControlStructure\r\nCS {\r\n' + this.baseCode + '\r\n}'; + if (words[0] !== "ControlStructure") { + if (isKeyWord(words[2]) || (words[2] === "}" && words.length > 3)) { + this.baseCode = "ControlStructure\r\nCS {\r\n" + this.baseCode + "\r\n}"; } else { - this.baseCode = 'ControlStructure\r\n' + this.baseCode; + this.baseCode = "ControlStructure\r\n" + this.baseCode; } } } @@ -156,7 +183,6 @@ export class CustomCSSnippet implements LanguageSnippet { const document = this.documents.getOrCreateDocument(URI.parse(uri)).textDocument; return getPositionForCSSnippet(document, this); } - } /** @@ -165,7 +191,7 @@ export class CustomCSSnippet implements LanguageSnippet { export class SimpleCSSnippet implements LanguageSnippet { insertText: string; documents: LangiumDocuments; - id: string = 'simpleCSSnippet'; + id: string = "simpleCSSnippet"; protected counter: number = 0; protected shortId: string; baseCode: string = ` @@ -197,7 +223,7 @@ CS { const document = this.documents.getOrCreateDocument(URI.parse(uri)).textDocument; return getPositionForCSSnippet(document, this); } -}; +} /** * Snippet for a control structure with one controller, one controlled process, one actuator, and one sensor. @@ -205,7 +231,7 @@ CS { export class SimpleCSWithAcsSnippet implements LanguageSnippet { insertText: string; documents: LangiumDocuments; - id: string = 'simpleCSWithAcsSnippet'; + id: string = "simpleCSWithAcsSnippet"; protected counter: number = 0; protected shortId: string; baseCode: string = ` @@ -249,7 +275,7 @@ CS { const document = this.documents.getOrCreateDocument(URI.parse(uri)).textDocument; return getPositionForCSSnippet(document, this); } -}; +} /** * Snippet for a control structure with two consecutive controllers and one controlled process. @@ -257,7 +283,7 @@ CS { export class ConsControllersSnippet implements LanguageSnippet { insertText: string; documents: LangiumDocuments; - id: string = 'consControllerSnippet'; + id: string = "consControllerSnippet"; protected counter: number = 0; protected shortId: string; baseCode: string = ` @@ -298,7 +324,7 @@ CS { const document = this.documents.getOrCreateDocument(URI.parse(uri)).textDocument; return getPositionForCSSnippet(document, this); } -}; +} /** * Snippet for a control structure with two parallel controllers and one controlled process. @@ -306,7 +332,7 @@ CS { export class ParallelContsSnippet implements LanguageSnippet { insertText: string; documents: LangiumDocuments; - id: string = 'parallelContsSnippet'; + id: string = "parallelContsSnippet"; protected counter: number = 0; protected shortId: string; baseCode: string = ` @@ -345,7 +371,7 @@ CS { const document = this.documents.getOrCreateDocument(URI.parse(uri)).textDocument; return getPositionForCSSnippet(document, this); } -}; +} /** * Snippet for a control structure with two consecutive controllers, one controlled process. @@ -353,7 +379,7 @@ CS { export class ConsContsWithLoopSnippet implements LanguageSnippet { insertText: string; documents: LangiumDocuments; - id: string = 'consContsWithLoopSnippet'; + id: string = "consContsWithLoopSnippet"; protected counter: number = 0; protected shortId: string; baseCode: string = ` @@ -396,4 +422,4 @@ CS { const document = this.documents.getOrCreateDocument(URI.parse(uri)).textDocument; return getPositionForCSSnippet(document, this); } -}; \ No newline at end of file +} diff --git a/extension/src-language-server/stpa-document-builder.ts b/extension/src-language-server/stpa-document-builder.ts index 3dab28b4..0652a4f4 100644 --- a/extension/src-language-server/stpa-document-builder.ts +++ b/extension/src-language-server/stpa-document-builder.ts @@ -15,13 +15,18 @@ * SPDX-License-Identifier: EPL-2.0 */ -import { AstNode, BuildOptions, DefaultDocumentBuilder, LangiumDocument } from 'langium'; -import { CancellationToken } from 'vscode-languageserver'; +import { AstNode, BuildOptions, DefaultDocumentBuilder, LangiumDocument } from "langium"; +import { CancellationToken } from "vscode-languageserver"; +/** + * Custom document builder to make the buildDocuments method public. + */ export class StpaDocumentBuilder extends DefaultDocumentBuilder { - - async buildDocuments(documents: LangiumDocument[], options: BuildOptions, cancelToken: CancellationToken): Promise { + async buildDocuments( + documents: LangiumDocument[], + options: BuildOptions, + cancelToken: CancellationToken + ): Promise { return super.buildDocuments(documents, options, cancelToken); } - -} \ No newline at end of file +} diff --git a/extension/src-language-server/stpa/diagram/diagram-controlStructure.ts b/extension/src-language-server/stpa/diagram/diagram-controlStructure.ts index c0c0b0de..4a8f44b3 100644 --- a/extension/src-language-server/stpa/diagram/diagram-controlStructure.ts +++ b/extension/src-language-server/stpa/diagram/diagram-controlStructure.ts @@ -40,7 +40,7 @@ import { getCommonAncestor, setLevelOfCSNodes, sortPorts } from "./utils"; * @param controlStructure The control structure. * @param idToSNode The map of IDs to SNodes. * @param options The synthesis options of the STPA model. - * @param args The GeneratorContext of the STPA model. + * @param idCache The ID cache of the STPA model. * @returns the generated control structure diagram. */ export function createControlStructure( @@ -178,7 +178,7 @@ export function createProcessModelNodes(variables: Variable[], idCache: IdCache< /** * Creates the edges for the control structure. * @param nodes The nodes of the control structure. - * @param args GeneratorContext of the STPA model + * @param idCache The ID cache of the STPA model. * @returns A list of edges for the control structure. */ export function generateVerticalCSEdges( @@ -207,7 +207,7 @@ export function generateVerticalCSEdges( * Translates the commands (control action or feedback) of a node to (intermediate) edges and adds them to the correct nodes. * @param commands The control actions or feedback of a node. * @param edgeType The type of the edge (control action or feedback). - * @param args GeneratorContext of the STPA model. + * @param idCache The ID cache of the STPA model. * @returns A list of edges representing the commands that should be added at the top level. */ export function translateCommandsToEdges( @@ -276,7 +276,7 @@ export function translateCommandsToEdges( * @param io The inputs or outputs of a node. * @param node The node of the inputs or outputs. * @param edgetype The type of the edge (input or output). - * @param args GeneratorContext of the STPA model. + * @param idCache The ID cache of the STPA model. * @returns a list of edges representing the inputs or outputs that should be added at the top level. */ export function translateIOToEdgeAndNode( @@ -357,7 +357,7 @@ export function translateIOToEdgeAndNode( * @param target The target of the edge. * @param edgeId The ID of the original edge. * @param edgeType The type of the edge. - * @param args The GeneratorContext of the STPA model. + * @param idCache The ID cache of the STPA model. * @param ancestor The common ancestor of the source and target. * @returns the IDs of the source and target port at the hierarchy level of the {@code ancestor}. */ diff --git a/extension/src-language-server/stpa/diagram/diagram-elements.ts b/extension/src-language-server/stpa/diagram/diagram-elements.ts index 2a7c8987..1472cc80 100644 --- a/extension/src-language-server/stpa/diagram/diagram-elements.ts +++ b/extension/src-language-server/stpa/diagram/diagram-elements.ts @@ -108,7 +108,7 @@ export function createSTPAEdge( * @param targetId The ID of the target of the edge. * @param label The labels of the edge. * @param edgeType The type of the edge (control action or feedback edge). - * @param param5 GeneratorContext of the STPA model. + * @param idCache The ID cache of the STPA model. * @returns A control structure edge. */ export function createControlStructureEdge( diff --git a/extension/src-language-server/stpa/diagram/diagram-generator.ts b/extension/src-language-server/stpa/diagram/diagram-generator.ts index 6a71ec01..75bd3fbe 100644 --- a/extension/src-language-server/stpa/diagram/diagram-generator.ts +++ b/extension/src-language-server/stpa/diagram/diagram-generator.ts @@ -15,26 +15,23 @@ * SPDX-License-Identifier: EPL-2.0 */ -import { AstNode, LangiumDocumentFactory, LangiumParser, ParseResult } from 'langium'; +import { AstNode, LangiumDocumentFactory, LangiumParser, ParseResult } from "langium"; import { GeneratorContext, IdCache, IdCacheImpl } from "langium-sprotty"; import { SModelElement, SModelRoot, SNode } from "sprotty-protocol"; -import { CancellationToken } from 'vscode-languageserver'; -import { TextDocument } from 'vscode-languageserver-textdocument'; +import { CancellationToken } from "vscode-languageserver"; +import { TextDocument } from "vscode-languageserver-textdocument"; import { Model } from "../../generated/ast"; -import { StpaDocumentBuilder } from '../../stpa-document-builder'; -import { LanguageTemplate, TemplateGraphGenerator } from '../../templates/template-model'; +import { LanguageSnippet, SnippetGraphGenerator } from "../../snippets/snippet-model"; +import { StpaDocumentBuilder } from "../../stpa-document-builder"; import { StpaServices } from "../stpa-module"; import { createControlStructure } from "./diagram-controlStructure"; import { createRelationshipGraph } from "./diagram-relationshipGraph"; import { filterModel } from "./filtering"; import { StpaSynthesisOptions } from "./stpa-synthesis-options"; -export class StpaDiagramGenerator extends TemplateGraphGenerator { +export class StpaDiagramGenerator extends SnippetGraphGenerator { protected readonly options: StpaSynthesisOptions; - protected readonly parser: LangiumParser; - protected readonly docBuilder: StpaDocumentBuilder; - protected readonly docFactory: LangiumDocumentFactory; - protected readonly languageId: string; + protected readonly services: StpaServices; /** Saves the Ids of the generated SNodes */ protected idToSNode: Map = new Map(); @@ -44,60 +41,73 @@ export class StpaDiagramGenerator extends TemplateGraphGenerator { constructor(services: StpaServices) { super(services); this.options = services.options.SynthesisOptions; - this.parser = services.parser.LangiumParser; - this.docBuilder = services.shared.workspace.DocumentBuilder as StpaDocumentBuilder; - this.docFactory = services.shared.workspace.LangiumDocumentFactory as LangiumDocumentFactory; - this.languageId = services.LanguageMetaData.languageId; + this.services = services; } /** - * Generates an SGraph for the given {@code template}. - * @param template The template for which a graph should be generated. - * @returns the SGraph. + * Generates an SGraph for the given {@code snippet}. + * @param snippet The snippet for which a graph should be generated. + * @returns the SGraph for {@code snippet}. */ - async generateTemplateRoot(template: LanguageTemplate): Promise { + async generateSnippetRoot(snippet: LanguageSnippet): Promise { + // id cache is needed if (!this.idCache) { this.idCache = new IdCacheImpl(); } - const parseRes = await this.getTempalteAST(template); + // parse the snippet to an AST + const parseRes = await this.getSnippetAST(snippet); if (!parseRes) { return undefined; } else { + // create the SGraph const graph = this.generateGraph(parseRes); - graph.id = template.id; + graph.id = snippet.id; return graph; } } - /** - * Calculates the parse result for {@code template}. - * @param template The template that should be parsed. - * @returns The AST for {@code template}. + /** + * Calculates the parse result for {@code snippet}. + * @param snippet The snippet that should be parsed. + * @returns the AST for {@code snippet}. */ - protected async getTempalteAST(template: LanguageTemplate): Promise { + protected async getSnippetAST(snippet: LanguageSnippet): Promise { // in order for the cross-references to be correctly evaluated, a document must be build - const uri = 'file:///template.stpa'; - const textDocument = TextDocument.create(uri, this.languageId, 0, template.baseCode ?? ''); - const parseResult: ParseResult = this.parser.parse(template.baseCode); + const uri = "file:///snippet.stpa"; + const textDocument = TextDocument.create( + uri, + this.services.LanguageMetaData.languageId, + 0, + snippet.baseCode ?? "" + ); + const parseResult: ParseResult = this.services.parser.LangiumParser.parse(snippet.baseCode); if (parseResult.parserErrors.length > 0) { return undefined; } // const doc = documentFromText(textDocument, parseResult); - const doc = this.docFactory.fromTextDocument(textDocument); - await this.docBuilder.buildDocuments([doc], {validationChecks: 'none'}, CancellationToken.None); + const doc = ( + this.services.shared.workspace.LangiumDocumentFactory as LangiumDocumentFactory + ).fromTextDocument(textDocument); + await (this.services.shared.workspace.DocumentBuilder as StpaDocumentBuilder).buildDocuments( + [doc], + { validationChecks: "none" }, + CancellationToken.None + ); return doc.parseResult.value; } /** - * Deletes the dangling edges in {@code template}. - * @param template The template which edges should be inspected. + * Deletes the dangling edges in {@code snippet}. + * @param snippet The snippet, which edges should be inspected. */ - async deleteDanglingEdges(template: LanguageTemplate): Promise { - const model = await this.getTempalteAST(template); + async deleteDanglingEdges(snippet: LanguageSnippet): Promise { + const model = await this.getSnippetAST(snippet); if (model) { + // collect nodes and their ids const nodes = model.controlStructure?.nodes; const nodeIDs = new Set(); nodes?.forEach(node => nodeIDs.add(node.name)); + // collect dangling node ids from control actions and feedbacks const danglingNodes = new Set(); for (const node of nodes ?? []) { node.actions.filter(action => { @@ -115,17 +125,19 @@ export class StpaDiagramGenerator extends TemplateGraphGenerator { return true; }); } - danglingNodes.forEach((node) => { - const newString = template.baseCode.replace(/->( )*/, "-> "); + // remove dangling edges + danglingNodes.forEach(node => { + const newString = snippet.baseCode.replace(/->( )*/, "-> "); const endIndex = newString.indexOf("-> " + node) + 3 + node.length; - const startIndex = newString.substring(0, endIndex).lastIndexOf('['); - template.baseCode = newString.substring(0, startIndex).trimEnd() + newString.substring(endIndex + 1, newString.length); + const startIndex = newString.substring(0, endIndex).lastIndexOf("["); + snippet.baseCode = + newString.substring(0, startIndex).trimEnd() + newString.substring(endIndex + 1, newString.length); }); } } /** - * Generates a SGraph for the STPA model contained in {@code args}. + * Generates an SGraph for the STPA model contained in {@code args}. * @param args GeneratorContext for the STPA model. * @returns the root of the generated SGraph. */ @@ -138,14 +150,12 @@ export class StpaDiagramGenerator extends TemplateGraphGenerator { return this.generateGraph(model); } - /** * Generates an SGraph for the given {@code model}. * @param model The Model for whcih a graph should be generated. * @returns an SGraph. */ private generateGraph(model: Model): SModelRoot { - // filter model based on the options set by the user const filteredModel = filterModel(model, this.options); diff --git a/extension/src-language-server/stpa/diagram/diagram-relationshipGraph.ts b/extension/src-language-server/stpa/diagram/diagram-relationshipGraph.ts index bfaa4596..63517924 100644 --- a/extension/src-language-server/stpa/diagram/diagram-relationshipGraph.ts +++ b/extension/src-language-server/stpa/diagram/diagram-relationshipGraph.ts @@ -46,7 +46,7 @@ import { * @param model The STPA model. * @param idToSNode The map of the generated IDs to their generated SNodes. * @param options The synthesis options of the STPA model. - * @param args The generator context of the STPA model. + * @param idCache The ID cache of the STPA model. * @returns the relationship graph for the STPA model. */ export function createRelationshipGraph( @@ -82,7 +82,7 @@ export function createRelationshipGraph( * @param model The STPA model. * @param idToSNode The map of the generated IDs to their generated SNodes. * @param options The synthesis options of the STPA model. - * @param args The generator context of the STPA model. + * @param idCache The ID cache of the STPA model. * @returns the children of the relationship graph. */ export function createRelationshipGraphChildren( @@ -284,7 +284,7 @@ export function createRelationshipGraphChildren( /** * Generates a node and the edges for the given {@code node}. * @param node STPA component for which a node and edges should be generated. - * @param args GeneratorContext of the STPA model. + * @param idCache The ID cache of the STPA model. * @returns A node representing {@code node} and edges representing the references {@code node} contains. */ export function generateAspectWithEdges( @@ -308,7 +308,7 @@ export function generateAspectWithEdges( /** * Generates a single STPANode for the given {@code node}. * @param node The STPA component the node should be created for. - * @param args GeneratorContext of the STPA model. + * @param idCache The ID cache of the STPA model. * @returns A STPANode representing {@code node}. */ export function generateSTPANode( @@ -366,7 +366,7 @@ export function generateSTPANode( /** * Generates the edges for {@code node}. * @param node STPA component for which the edges should be created. - * @param args GeneratorContext of the STPA model. + * @param idCache The ID cache of the STPA model. * @returns Edges representing the references {@code node} contains. */ export function generateEdgesForSTPANode( diff --git a/extension/src-language-server/stpa/stpa-module.ts b/extension/src-language-server/stpa/stpa-module.ts index cde0db7b..fe524fe0 100644 --- a/extension/src-language-server/stpa/stpa-module.ts +++ b/extension/src-language-server/stpa/stpa-module.ts @@ -16,21 +16,11 @@ */ import ElkConstructor from "elkjs/lib/elk.bundled"; -import { - Module, - PartialLangiumServices -} from "langium"; -import { - LangiumSprottyServices, - SprottyDiagramServices -} from "langium-sprotty"; -import { - DefaultElementFilter, - ElkFactory, - IElementFilter, - ILayoutConfigurator -} from "sprotty-elk/lib/elk-layout"; +import { Module, PartialLangiumServices } from "langium"; +import { LangiumSprottyServices, SprottyDiagramServices } from "langium-sprotty"; +import { DefaultElementFilter, ElkFactory, IElementFilter, ILayoutConfigurator } from "sprotty-elk/lib/elk-layout"; import { LayoutEngine } from "../layout-engine"; +import { StpaDiagramSnippets } from "../snippets/stpa-snippets"; import { IDEnforcer } from "./ID-enforcer"; import { ContextTableProvider } from "./contextTable/context-dataProvider"; import { StpaDiagramGenerator } from "./diagram/diagram-generator"; @@ -38,7 +28,6 @@ import { StpaLayoutConfigurator } from "./diagram/layout-config"; import { StpaSynthesisOptions } from "./diagram/stpa-synthesis-options"; import { StpaScopeProvider } from "./stpa-scopeProvider"; import { StpaValidationRegistry, StpaValidator } from "./stpa-validator"; -import { StpaTemplates } from '../stpa-templates'; /** * Declaration of custom services - add your own service classes here. @@ -64,8 +53,8 @@ export type StpaAddedServices = { utility: { IDEnforcer: IDEnforcer; }; - templates: { - StpaTemplates: StpaTemplates; + snippets: { + StpaDiagramSnippets: StpaDiagramSnippets; }; }; @@ -82,8 +71,8 @@ export type StpaServices = LangiumSprottyServices & StpaAddedServices; */ export const STPAModule: Module = { diagram: { - DiagramGenerator: (services) => new StpaDiagramGenerator(services), - ModelLayoutEngine: (services) => + DiagramGenerator: services => new StpaDiagramGenerator(services), + ModelLayoutEngine: services => new LayoutEngine( services.layout.ElkFactory, services.layout.ElementFilter, @@ -91,11 +80,11 @@ export const STPAModule: Module new StpaScopeProvider(services), - StpaScopeProvider: (services) => new StpaScopeProvider(services), + ScopeProvider: services => new StpaScopeProvider(services), + StpaScopeProvider: services => new StpaScopeProvider(services), }, validation: { - ValidationRegistry: (services) => new StpaValidationRegistry(services), + ValidationRegistry: services => new StpaValidationRegistry(services), StpaValidator: () => new StpaValidator(), }, layout: { @@ -107,12 +96,12 @@ export const STPAModule: Module new StpaSynthesisOptions(), }, contextTable: { - ContextTableProvider: (services) => new ContextTableProvider(services), + ContextTableProvider: services => new ContextTableProvider(services), }, utility: { - IDEnforcer: (services) => new IDEnforcer(services), + IDEnforcer: services => new IDEnforcer(services), + }, + snippets: { + StpaDiagramSnippets: services => new StpaDiagramSnippets(services), }, - templates: { - StpaTemplates: services => new StpaTemplates(services) - } }; diff --git a/extension/src-webview/template/actions.ts b/extension/src-webview/template/actions.ts index e36e5998..e489cfe3 100644 --- a/extension/src-webview/template/actions.ts +++ b/extension/src-webview/template/actions.ts @@ -15,10 +15,10 @@ * SPDX-License-Identifier: EPL-2.0 */ -import { VNode } from 'snabbdom'; -import { Action, Bounds, RequestAction, ResponseAction, generateRequestId } from "sprotty-protocol"; +import { VNode } from "snabbdom"; import { ModelRenderer } from "sprotty"; -import { WebviewTemplate } from "./template-models"; +import { Action, Bounds, RequestAction, ResponseAction, generateRequestId } from "sprotty-protocol"; +import { WebviewSnippet } from "./template-models"; /** Sent from the view. */ export interface SendModelRendererAction extends Action { @@ -28,13 +28,13 @@ export interface SendModelRendererAction extends Action { } export namespace SendModelRendererAction { - export const KIND = 'sendModelRendererAction'; + export const KIND = "sendModelRendererAction"; export function create(renderer: ModelRenderer, bounds: Bounds): SendModelRendererAction { return { kind: KIND, renderer, - bounds + bounds, }; } @@ -43,55 +43,50 @@ export namespace SendModelRendererAction { } } -/** Request message from the server to get the svgs for the templates. */ -export interface RequestWebviewTemplatesAction extends RequestAction { - kind: typeof RequestWebviewTemplatesAction.KIND; - templates: WebviewTemplate[]; +/** Request message from the server to the client to get the svgs of the snippets. */ +export interface RequestWebviewSnippetsAction extends RequestAction { + kind: typeof RequestWebviewSnippetsAction.KIND; + snippets: WebviewSnippet[]; clientId: string; requestId: string; } -export namespace RequestWebviewTemplatesAction { - export const KIND = "updateTemplates"; +export namespace RequestWebviewSnippetsAction { + export const KIND = "updateSnippets"; - export function create( - templates: WebviewTemplate[], - clientId: string - ): RequestWebviewTemplatesAction { + export function create(snippets: WebviewSnippet[], clientId: string): RequestWebviewSnippetsAction { return { kind: KIND, - templates, + snippets: snippets, clientId, - requestId: generateRequestId() + requestId: generateRequestId(), }; } - export function isThisAction(action: Action): action is RequestWebviewTemplatesAction { - return action.kind === RequestWebviewTemplatesAction.KIND; + export function isThisAction(action: Action): action is RequestWebviewSnippetsAction { + return action.kind === RequestWebviewSnippetsAction.KIND; } } -export interface SendWebviewTemplatesAction extends ResponseAction { - kind: typeof SendWebviewTemplatesAction.KIND; - templates: VNode[]; +/** Message to the language server containing the svgs of the snippets. */ +export interface SendWebviewSnippetsAction extends ResponseAction { + kind: typeof SendWebviewSnippetsAction.KIND; + snippets: VNode[]; responseId: string; } -export namespace SendWebviewTemplatesAction { - export const KIND = "updateTemplates"; +export namespace SendWebviewSnippetsAction { + export const KIND = "updateSnippets"; - export function create( - templates: VNode[], - requestId: string = '' - ): SendWebviewTemplatesAction { + export function create(snippets: VNode[], requestId: string = ""): SendWebviewSnippetsAction { return { kind: KIND, - templates, - responseId: requestId + snippets: snippets, + responseId: requestId, }; } - export function isThisAction(action: Action): action is SendWebviewTemplatesAction { - return action.kind === SendWebviewTemplatesAction.KIND; + export function isThisAction(action: Action): action is SendWebviewSnippetsAction { + return action.kind === SendWebviewSnippetsAction.KIND; } -} +} \ No newline at end of file diff --git a/extension/src-webview/template/template-models.ts b/extension/src-webview/template/template-models.ts index 8555d7f2..10aacb95 100644 --- a/extension/src-webview/template/template-models.ts +++ b/extension/src-webview/template/template-models.ts @@ -17,7 +17,12 @@ import { SModelElement } from "sprotty"; -export interface WebviewTemplate { +/** + * Represents a snippet that can be displayed in a webview. + */ +export interface WebviewSnippet { + /** the graph to display */ graph: Readonly; + /** unique id of the snippet */ id: string; } diff --git a/extension/src-webview/template/template-registry.ts b/extension/src-webview/template/template-registry.ts index b56a3afc..5f14cc1f 100644 --- a/extension/src-webview/template/template-registry.ts +++ b/extension/src-webview/template/template-registry.ts @@ -24,7 +24,7 @@ import { HOST_EXTENSION } from "vscode-messenger-common"; import { Messenger } from "vscode-messenger-webview"; import { Registry } from "../base/registry"; import { DISymbol } from "../di.symbols"; -import { RequestWebviewTemplatesAction, SendModelRendererAction, SendWebviewTemplatesAction } from "./actions"; +import { RequestWebviewSnippetsAction, SendModelRendererAction, SendWebviewSnippetsAction } from "./actions"; import { TemplateRenderer } from "./template-renderer"; /** @@ -39,23 +39,23 @@ export class TemplateRegistry extends Registry implements IActionHandlerInitiali @inject(DISymbol.TemplateRenderer) private templateRenderer: TemplateRenderer; initialize(registry: ActionHandlerRegistry): void { - registry.register(RequestWebviewTemplatesAction.KIND, this); + registry.register(RequestWebviewSnippetsAction.KIND, this); registry.register(SendModelRendererAction.KIND, this); } handle(action: Action): void | Action | ICommand { - if (RequestWebviewTemplatesAction.isThisAction(action)) { + if (RequestWebviewSnippetsAction.isThisAction(action)) { this.handleRequestWebviewTemps(action); } else if (SendModelRendererAction.isThisAction(action)) { this.handleSendModelRenderer(action); } } - private handleRequestWebviewTemps(action: RequestWebviewTemplatesAction): void { - const temps = this.templateRenderer.renderTemplates(action.templates); - const response: SendWebviewTemplatesAction = { - kind: SendWebviewTemplatesAction.KIND, - templates: temps, + private handleRequestWebviewTemps(action: RequestWebviewSnippetsAction): void { + const temps = this.templateRenderer.renderTemplates(action.snippets); + const response: SendWebviewSnippetsAction = { + kind: SendWebviewSnippetsAction.KIND, + snippets: temps, responseId: action.requestId, }; this.messenger.sendNotification(ActionNotification, HOST_EXTENSION, { diff --git a/extension/src-webview/template/template-renderer.tsx b/extension/src-webview/template/template-renderer.tsx index 8fc277f3..0d47357d 100644 --- a/extension/src-webview/template/template-renderer.tsx +++ b/extension/src-webview/template/template-renderer.tsx @@ -20,7 +20,7 @@ import { inject, injectable } from "inversify"; import { VNode } from "snabbdom"; import { html, IModelFactory, ModelRenderer, SGraph, SNode, TYPES } from "sprotty"; // eslint-disable-line @typescript-eslint/no-unused-vars import { Bounds } from 'sprotty-protocol'; -import { WebviewTemplate } from "./template-models"; +import { WebviewSnippet } from "./template-models"; /** Renderer that is capable of rendering templates to jsx. */ @@ -41,7 +41,7 @@ export class TemplateRenderer { /** * Renders all templates provided by the server. */ - renderTemplates(templates: WebviewTemplate[]): VNode[] { + renderTemplates(templates: WebviewSnippet[]): VNode[] { if (templates.length === 0) return
; // labels and edges are only visible if they are within the canvas bounds diff --git a/extension/src/actions.ts b/extension/src/actions.ts index 640c17f7..c1639b2a 100644 --- a/extension/src/actions.ts +++ b/extension/src/actions.ts @@ -99,44 +99,44 @@ export namespace MinimalCutSetAnalysisAction { } } -/** Message to the language server to add a template. */ -export interface AddTemplateAction extends Action { - kind: typeof AddTemplateAction.KIND; +/** Message to the language server to add a snippet to the librabry. */ +export interface AddSnippetAction extends Action { + kind: typeof AddSnippetAction.KIND; text: string; } -export namespace AddTemplateAction { - export const KIND = "addTemplate"; +export namespace AddSnippetAction { + export const KIND = "addSnippet"; - export function create(text: string): AddTemplateAction { + export function create(text: string): AddSnippetAction { return { kind: KIND, - text + text, }; } - export function isThisAction(action: Action): action is AddTemplateAction { - return action.kind === AddTemplateAction.KIND; + export function isThisAction(action: Action): action is AddSnippetAction { + return action.kind === AddSnippetAction.KIND; } } -/** Message containing templates as string. */ -export interface SendTemplatesAction extends Action { - kind: typeof SendTemplatesAction.KIND; - temps: string[]; +/** Message from extension to langauge server containing snippets as string. (Used to add default snippets) */ +export interface SendSnippetsAction extends Action { + kind: typeof SendSnippetsAction.KIND; + snippets: string[]; } -export namespace SendTemplatesAction { - export const KIND = "sendTemplates"; +export namespace SendSnippetsAction { + export const KIND = "sendSnippets"; - export function create(temps: string[]): SendTemplatesAction { + export function create(temps: string[]): SendSnippetsAction { return { kind: KIND, - temps + snippets: temps, }; } - export function isThisAction(action: Action): action is SendTemplatesAction { - return action.kind === SendTemplatesAction.KIND; + export function isThisAction(action: Action): action is SendSnippetsAction { + return action.kind === SendSnippetsAction.KIND; } } \ No newline at end of file diff --git a/extension/src/extension.ts b/extension/src/extension.ts index 04753596..1df5e9c9 100644 --- a/extension/src/extension.ts +++ b/extension/src/extension.ts @@ -353,7 +353,7 @@ function registerTemplateWebview(manager: StpaLspVscodeExtension, context: vscod webviewView.title = title; tWebview.initializeWebview(webviewView.webview, title); tWebview.connect(); - languageClient.onNotification('templates/add', (msg: any) => tWebview.sendToWebview(msg)); + languageClient.onNotification('snippets/add', (msg: any) => tWebview.sendToWebview(msg)); } }; vscode.window.registerWebviewViewProvider("stpa-snippets", provider); diff --git a/extension/src/language-extension.ts b/extension/src/language-extension.ts index 5a312651..32156224 100644 --- a/extension/src/language-extension.ts +++ b/extension/src/language-extension.ts @@ -20,7 +20,7 @@ import { createFileUri } from "sprotty-vscode"; import { SprottyDiagramIdentifier } from "sprotty-vscode-protocol"; import { LspWebviewEndpoint, LspWebviewPanelManager, LspWebviewPanelManagerOptions, acceptMessageType } from "sprotty-vscode/lib/lsp"; import * as vscode from "vscode"; -import { AddTemplateAction, GenerateSVGsAction } from "./actions"; +import { AddSnippetAction, GenerateSVGsAction } from "./actions"; import { ContextTablePanel } from "./context-table-panel"; import { StpaFormattingEditProvider } from "./stpa-formatter"; import { applyTextEdits, collectOptions, createFile } from "./utils"; @@ -53,9 +53,9 @@ export class StpaLspVscodeExtension extends LspWebviewPanelManager { const mes: ActionMessage = { clientId: this.clientId!, action: { - kind: AddTemplateAction.KIND, + kind: AddSnippetAction.KIND, text: text - } as AddTemplateAction + } as AddSnippetAction }; this.languageClient.sendNotification(acceptMessageType, mes); } @@ -66,9 +66,9 @@ export class StpaLspVscodeExtension extends LspWebviewPanelManager { * @param temps Text of templates. */ handleAddToConfig(temps: string[]): void { - const configTemps = vscode.workspace.getConfiguration('stpa').get('templates'); + const configTemps = vscode.workspace.getConfiguration('pasta.stpa').get('snippets'); const newTemps = (configTemps as string[]).concat(temps); - vscode.workspace.getConfiguration('stpa').update('templates', newTemps); + vscode.workspace.getConfiguration('pasta.stpa').update('snippets', newTemps); } /** diff --git a/extension/src/template-webview.ts b/extension/src/template-webview.ts index 79aff9c0..32665f91 100644 --- a/extension/src/template-webview.ts +++ b/extension/src/template-webview.ts @@ -19,7 +19,7 @@ import * as vscode from 'vscode'; import { ActionMessage } from 'sprotty-protocol'; import { StpaLspVscodeExtension } from './language-extension'; import { acceptMessageType } from 'sprotty-vscode/lib/lsp'; -import { SendTemplatesAction } from './actions'; +import { SendSnippetsAction } from './actions'; export class TemplateWebview { @@ -104,8 +104,8 @@ export class TemplateWebview { // TODO: guarantee that sprotty webview exist if (this.extension.clientId) { // send the templates saved in the config file to the language server - const temps = vscode.workspace.getConfiguration('stpa').get('templates'); - const action = { kind: SendTemplatesAction.KIND, temps: temps } as SendTemplatesAction; + const temps = vscode.workspace.getConfiguration('pasta.stpa').get('snippets'); + const action = { kind: SendSnippetsAction.KIND, snippets: temps } as SendSnippetsAction; const mes2: ActionMessage = { clientId: this.extension.clientId, action: action