From da3b48becb0446d7c36a53bad4b79da68fd66078 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20B=C3=BChler?= <david.buhler@cea.fr>
Date: Thu, 6 Jul 2023 15:08:22 +0200
Subject: [PATCH] [ivette] Studia can be applied on arbitrary lvalues via a
 dedicated search mode.

---
 ivette/src/dome/renderer/dark.css           |  2 +
 ivette/src/dome/renderer/light.css          |  2 +
 ivette/src/frama-c/plugins/studia/studia.ts | 53 +++++++++++++++++++++
 ivette/src/frama-c/plugins/studia/style.css |  5 ++
 ivette/src/renderer/Application.tsx         |  3 ++
 5 files changed, 65 insertions(+)
 create mode 100644 ivette/src/frama-c/plugins/studia/style.css

diff --git a/ivette/src/dome/renderer/dark.css b/ivette/src/dome/renderer/dark.css
index cbe9a2b73b5..b58523a06c7 100644
--- a/ivette/src/dome/renderer/dark.css
+++ b/ivette/src/dome/renderer/dark.css
@@ -88,6 +88,8 @@
     --meter-subopti:  linear-gradient(to bottom, #770 0%, #cc0 20%, #770 100%);
     --meter-evenless: linear-gradient(to bottom, #710 0%, #c50 20%, #710 100%);
 
+    --studia-search-mode: #c2c71e;
+
     --eva-evaluation-mode: #714074;
     --eva-state-before: #082032;
     --eva-state-after: #321428;
diff --git a/ivette/src/dome/renderer/light.css b/ivette/src/dome/renderer/light.css
index 135bd1ef348..919c24be478 100644
--- a/ivette/src/dome/renderer/light.css
+++ b/ivette/src/dome/renderer/light.css
@@ -88,6 +88,8 @@
     --meter-subopti:  linear-gradient(to bottom, #aa0 0%, #ff0 20%, #aa0 100%);
     --meter-evenless: linear-gradient(to bottom, #a40 0%, #f80 20%, #a40 100%);
 
+    --studia-search-mode: #c5dc19;
+
     --eva-evaluation-mode: #dc8fe1;
     --eva-state-before: #95f5ff;
     --eva-state-after: #fff819;
diff --git a/ivette/src/frama-c/plugins/studia/studia.ts b/ivette/src/frama-c/plugins/studia/studia.ts
index d84e1fb7e2a..a45a2b06e6e 100644
--- a/ivette/src/frama-c/plugins/studia/studia.ts
+++ b/ivette/src/frama-c/plugins/studia/studia.ts
@@ -20,11 +20,21 @@
 /*                                                                          */
 /* ************************************************************************ */
 
+import React from 'react';
 import * as Dome from 'dome';
 import * as States from 'frama-c/states';
 import * as Server from 'frama-c/server';
+import * as Toolbars from 'dome/frame/toolbars';
+import * as Status from 'frama-c/kernel/Status';
 import * as Ast from 'frama-c/kernel/api/ast';
 import { getWritesLval, getReadsLval } from 'frama-c/plugins/studia/api/studia';
+import { ipcRenderer } from 'electron';
+import './style.css';
+
+const studiaWritesEvent = new Dome.Event('dome.studia.writes');
+const studiaReadsEvent = new Dome.Event('dome.studia.reads');
+ipcRenderer.on('dome.ipc.studia.writes', () => studiaWritesEvent.emit());
+ipcRenderer.on('dome.ipc.studia.reads', () => studiaReadsEvent.emit());
 
 type access = 'Reads' | 'Writes';
 
@@ -68,3 +78,46 @@ export function buildMenu(props: MenuProps) : void {
   menu.push({ label: reads, enabled, onClick: () => onClick('Reads') });
   menu.push({ label: writes, enabled, onClick: () => onClick('Writes') });
 }
+
+export function useStudiaMode(): void {
+  const [selection, setSelection] = States.useSelection();
+  async function handleError(err: string): Promise<void> {
+    const msg = `Studia failure: ${err}.`;
+    Status.setMessage({ text: msg, kind: 'error' });
+  }
+  async function onEnter(label: string, kind: access): Promise<void> {
+    const stmt = selection?.current?.marker;
+    const data = { stmt, term: label };
+    const marker = await Server.send(Ast.parseLval, data).catch(handleError);
+    if (!marker) return;
+    compute(marker, label, kind).then(setSelection).catch(handleError);
+  }
+  const shared = {
+    placeholder: "lvalue",
+    icon: 'EDIT',
+    className: 'studia-search-mode',
+    hints: () => { return Promise.resolve([]); },
+  };
+  const writesMode = {
+    ...shared,
+    label: 'Studia: select writes',
+    title: `Select all statements writing the given lvalue`,
+    onEnter: (pattern:string) => onEnter(pattern, "Writes"),
+    event: studiaWritesEvent,
+  };
+  const readsMode = {
+    ...shared,
+    label: 'Studia: select reads',
+    title: `Selects all statements reading the given lvalue`,
+    onEnter: (pattern:string) => onEnter(pattern, "Reads"),
+    event: studiaReadsEvent,
+  };
+  React.useEffect(() => {
+    Toolbars.RegisterMode.emit(writesMode);
+    Toolbars.RegisterMode.emit(readsMode);
+    return () => {
+      Toolbars.UnregisterMode.emit(writesMode);
+      Toolbars.UnregisterMode.emit(readsMode);
+    };
+  });
+}
diff --git a/ivette/src/frama-c/plugins/studia/style.css b/ivette/src/frama-c/plugins/studia/style.css
new file mode 100644
index 00000000000..37090901780
--- /dev/null
+++ b/ivette/src/frama-c/plugins/studia/style.css
@@ -0,0 +1,5 @@
+
+.studia-search-mode {
+  background: var(--studia-search-mode);
+  fill: var(--text-discrete);
+}
diff --git a/ivette/src/renderer/Application.tsx b/ivette/src/renderer/Application.tsx
index c590f57fb6c..c6a1f7276e0 100644
--- a/ivette/src/renderer/Application.tsx
+++ b/ivette/src/renderer/Application.tsx
@@ -36,6 +36,7 @@ import * as Controller from './Controller';
 import * as Extensions from './Extensions';
 import * as Laboratory from './Laboratory';
 import * as IvettePrefs from 'ivette/prefs';
+import * as Studia from 'frama-c/plugins/studia/studia';
 import './loader';
 import './sandbox';
 
@@ -49,6 +50,8 @@ export default function Application(): JSX.Element {
   const [viewbar, flipViewbar] =
     Dome.useFlipSettings('frama-c.viewbar.unfold', true);
 
+  Studia.useStudiaMode();
+
   return (
     <Vfill>
       <Toolbar.ToolBar>
-- 
GitLab