Skip to content

Commit

Permalink
Merge pull request #43 from kieler/jep/completionItems
Browse files Browse the repository at this point in the history
Jep/completion items
  • Loading branch information
Drakae authored Sep 26, 2024
2 parents e52b6f4 + 492defe commit dc7fc92
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 27 deletions.
2 changes: 1 addition & 1 deletion extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "A DSL for STPA. Includes an automatic visualization of the defined relationships and control structure.",
"version": "0.5.0",
"publisher": "kieler",
"author": "Kiel University <rt-kieler-devel@informatik.uni-kiel.de>",
"author": "KIELER <kieler@rtsys.informatik.uni-kiel.de>",
"icon": "icon.png",
"license": "EPL-2.0",
"repository": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,14 @@ export class ContextTableProvider {
}
//determine hazards
const hazards: string[] = [];
const hazardList = context.list.refs;
hazardList.forEach((hazard) => {
const hazardList = context.list?.refs;
hazardList?.forEach((hazard) => {
if (hazard.ref?.name) {
hazards.push(hazard.ref.name);
}
});
// create rule/uca
if (rule.action.ref?.name && rule.system.ref?.name) {
if (rule.action?.ref?.name && rule.system?.ref?.name) {
rules.push({
id: context.name,
controlAction: { controller: rule.system.ref!.name, action: rule.action.ref!.name },
Expand Down
123 changes: 101 additions & 22 deletions extension/src-language-server/stpa/stpa-completion-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,18 @@ import {
NextFeature,
} from "langium";
import { CompletionItemKind } from "vscode-languageserver";
import { Context, isModel, isVerticalEdge, LossScenario, Model, Node, Rule, UCA, VerticalEdge } from "../generated/ast";
import {
Context,
ControllerConstraint,
isModel,
isVerticalEdge,
LossScenario,
Model,
Node,
Rule,
UCA,
VerticalEdge,
} from "../generated/ast";

/**
* Generates UCA text for loss scenarios by providing an additional completion item.
Expand All @@ -49,6 +60,70 @@ export class STPACompletionProvider extends DefaultCompletionProvider {
this.completionForScenario(context, next, acceptor);
this.completionForUCA(context, next, acceptor);
this.completionForUCARule(context, next, acceptor);
this.completionForControllerConstraints(context, next, acceptor);
}
}

/**
* Adds a completion item for generating controller constraints if the current context is a controller constraint.
* @param context The completion context.
* @param next The next feature of the current rule to be called.
* @param acceptor The completion acceptor to add the completion items.
*/
protected completionForControllerConstraints(
context: CompletionContext,
next: NextFeature,
acceptor: CompletionAcceptor
): void {
if (next.type === ControllerConstraint && next.property === "name") {
// get the model for the current controller constraint
let model = context.node;
while (model && !isModel(model)) {
model = model.$container;
}
if (isModel(model)) {
let generatedText = ``;
model.rules.forEach(rule => {
const system = rule.system.ref?.label ?? rule.system.$refText;
const controlAction = `the control action '${rule.action.ref?.label}'`;
rule.contexts.forEach(context => {
// create constraint text for each context
generatedText += `C "${system}`;
const contextText = this.createContextText(context, true);
switch (rule.type) {
case "not-provided":
generatedText += ` must provide ${controlAction}, while ${contextText}.`;
break;
case "provided":
generatedText += ` must not provided ${controlAction}, while ${contextText}.`;
break;
case "too-late":
generatedText += ` must provide ${controlAction} in time, while ${contextText}.`;
break;
case "too-early":
generatedText += ` must not provide ${controlAction} before ${contextText}.`;
break;
case "stopped-too-soon":
generatedText += ` must not stop ${controlAction} too soon, while ${contextText}.`;
break;
case "applied-too-long":
generatedText += ` must not apply ${controlAction} too long, while ${contextText}.`;
break;
}
// add reference to the UCA
generatedText += `" [${context.name}]\n`;
});
});

// add the generated text as completion item
acceptor({
label: "Generate Constraints for the UCAs",
kind: CompletionItemKind.Snippet,
insertText: generatedText,
detail: "Inserts a controller constraint for each UCA.",
sortText: "0",
});
}
}
}

Expand Down Expand Up @@ -308,21 +383,23 @@ export class STPACompletionProvider extends DefaultCompletionProvider {
* @param acceptor The completion acceptor to add the completion items.
*/
protected completionForScenario(context: CompletionContext, next: NextFeature, acceptor: CompletionAcceptor): void {
if (context.node?.$type === LossScenario) {
if (next.property === "description") {
const generatedText = this.generateScenarioForUCA(context.node as LossScenario);
if (generatedText !== "") {
acceptor({
label: "Generate UCA Text",
kind: CompletionItemKind.Text,
insertText: generatedText,
detail: "Inserts the UCA text for this scenario.",
sortText: "0",
});
}
if (context.node?.$type === LossScenario && next.property === "description") {
const generatedText = this.generateScenarioForUCA(context.node as LossScenario);
if (generatedText !== "") {
acceptor({
label: "Generate UCA Text",
kind: CompletionItemKind.Text,
insertText: generatedText,
detail: "Inserts the UCA text for this scenario.",
sortText: "0",
});
}
if (next.type === LossScenario && next.property === "name") {
const generatedBasicScenariosText = this.generateBasicScenarios(context.node.$container as Model);
}
if (next.type === LossScenario && next.property === "name") {
const model =
context.node?.$type === LossScenario ? context.node.$container : context.node?.$container?.$container;
if (isModel(model)) {
const generatedBasicScenariosText = this.generateBasicScenarios(model);
if (generatedBasicScenariosText !== "") {
acceptor({
label: "Generate Basic Scenarios",
Expand All @@ -349,7 +426,7 @@ export class STPACompletionProvider extends DefaultCompletionProvider {
rule.contexts.forEach(context => {
// add scenario for actuator/controlled process failure
let scenario = `${system}`;
const contextText = this.createContextText(context);
const contextText = this.createContextText(context, false);
switch (rule.type) {
case "not-provided":
scenario += ` provided ${controlAction}, while ${contextText}, but it is not executed.`;
Expand Down Expand Up @@ -453,7 +530,7 @@ export class STPACompletionProvider extends DefaultCompletionProvider {
}

text += `, while`;
text += this.createContextText(context);
text += this.createContextText(context, false);
text += ".";

return text;
Expand All @@ -462,18 +539,20 @@ export class STPACompletionProvider extends DefaultCompletionProvider {
/**
* Creates a text for the given context {@code context}.
* @param context The context for which the text should be generated.
* @param present If true the text is generated in present tense, otherwise in past tense.
* @returns the generated text.
*/
protected createContextText(context: Context): string {
protected createContextText(context: Context, present: boolean): string {
let text = ``;
const tense = present ? "is" : "was";
context.assignedValues.forEach((assignedValue, index) => {
if (index > 0) {
text += ", ";
text += ",";
}
if ((index += context.assignedValues.length - 1)) {
text += ", and";
if (context.assignedValues.length > 1 && index === context.assignedValues.length - 1) {
text += " and";
}
text += ` ${assignedValue.variable.$refText} was ${assignedValue.value.$refText}`;
text += ` ${assignedValue.variable.$refText} ${tense} ${assignedValue.value.$refText}`;
});
return text;
}
Expand Down
4 changes: 3 additions & 1 deletion extension/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -537,7 +537,9 @@ async function updateViews(manager: StpaLspVscodeExtension, document?: vscode.Te

// update the context table
if (manager.contextTable) {
languageClient.sendNotification("contextTable/getData", document.uri.toString());
if (document.uri.toString().endsWith(".stpa")) {
languageClient.sendNotification("contextTable/getData", document.uri.toString());
}
}
}
}
Expand Down

0 comments on commit dc7fc92

Please sign in to comment.