diff --git a/ivette/src/frama-c/kernel/ASTview.tsx b/ivette/src/frama-c/kernel/ASTview.tsx index 8b9d6c424731d74161137968376c0676e43073dc..19f9632f676dd14aebcb85aaad3b5c3477a24551 100644 --- a/ivette/src/frama-c/kernel/ASTview.tsx +++ b/ivette/src/frama-c/kernel/ASTview.tsx @@ -49,7 +49,9 @@ import * as Preferences from 'ivette/prefs'; // An alias type for functions and locations. type Fct = string | undefined; -type Marker = string | undefined; +type Marker = Ast.marker | undefined; + +const noMarker = Ast.jMarker(''); // A range is just a pair of position in the code. type Range = Editor.Range; @@ -74,12 +76,12 @@ function mapFilter<A, B>(xs: readonly A[], fn: (x: A) => B | undefined): B[] { // without information on the ranges of each element. It will be converted in a // good old tree that carry those information. interface Leaf extends Range { text: string } -interface Node extends Range { id: string, children: Tree[] } +interface Node extends Range { marker: Ast.marker, children: Tree[] } type Tree = Leaf | Node; // Utility functions on trees. -function isLeaf (t: Tree): t is Leaf { return 'text' in t; } -function isNode (t: Tree): t is Node { return 'id' in t && 'children' in t; } +function isLeaf(t: Tree): t is Leaf { return 'text' in t; } +function isNode(t: Tree): t is Node { return 'marker' in t && 'children' in t; } const empty: Tree = { text: '', from: 0, to: 0 }; // Convert an Ivette text (i.e a function's code) into a Tree, adding range @@ -98,7 +100,7 @@ function textToTree(t: text): Tree | undefined { if (node) children.push(node); acc = to; } - return [{ id: t[0], from, to: acc, children }, acc]; + return [{ marker: Ast.jMarker(t[0]), from, to: acc, children }, acc]; } const [res] = aux(t, 0); return res; @@ -120,9 +122,9 @@ function markersRanges(tree: Tree): Map<string, Range[]>{ const ranges: Map<string, Range[]> = new Map(); const toRanges = (tree: Tree): void => { if (!isNode(tree)) return; - const trees = ranges.get(tree.id) ?? []; + const trees = ranges.get(tree.marker) ?? []; trees.push(tree); - ranges.set(tree.id, trees); + ranges.set(tree.marker, trees); for (const child of tree.children) toRanges(child); }; toRanges(tree); @@ -198,8 +200,8 @@ function createMarkerUpdater(): Editor.Extension { return Editor.createEventHandler(deps, { mouseup: ({ fct, tree, update }, view, event) => { const main = view.state.selection.main; - const id = coveringNode(tree, main.from)?.id; - const location = { fct, marker: Ast.jMarker(id) }; + const marker = coveringNode(tree, main.from)?.marker; + const location = { fct, marker }; update({ location }); if (event.altKey) States.MetaSelection.emit(location); } @@ -263,7 +265,7 @@ function createHoveredUpdater(): Editor.Extension { const horizontallyOk = left <= coords.x && coords.x <= right; const verticallyOk = top <= coords.y && coords.y <= bottom; if (!horizontallyOk || !verticallyOk) { reset(); return; } - const marker = Ast.jMarker(hov.id); + const marker = hov.marker; updateHovered(marker ? { fct, marker } : undefined); } }); @@ -407,7 +409,7 @@ function getPropertiesNodes(tree: Tree): Node[] { if (isLeaf(tree)) return []; /* Must be consistent with the id chosen by the Frama-C server for property markers. Ideally, this test should not depend on markers id syntax. */ - if (tree.id.startsWith('#p')) return [tree]; + if (tree.marker.startsWith('#p')) return [tree]; return tree.children.map(getPropertiesNodes).flat(); } @@ -419,7 +421,7 @@ function createPropertiesNodes() : Editor.Aspect<Property[]> { return Editor.createAspect(deps, ({ tree, tags, statuses }) => { const nodes = getPropertiesNodes(tree); return mapFilter(nodes, (n) => { - const s = statuses.find((s) => s.key === n.id); + const s = statuses.find((s) => s.key === n.marker); if (!s) return undefined; const tag = tags.get(s.status); if (!tag) return undefined; @@ -531,7 +533,7 @@ async function studia(props: StudiaProps): Promise<StudiaInfos> { const Callers = Editor.createField<Eva.CallSite[]>([]); // This field contains information on markers. -type GetMarkerData = (key: string) => Ast.markerAttributesData | undefined; +type GetMarkerData = (key: Ast.marker) => Ast.markerAttributesData | undefined; const GetMarkerData = Editor.createField<GetMarkerData>(() => undefined); const ContextMenuHandler = createContextMenuHandler(); @@ -544,9 +546,9 @@ function createContextMenuHandler(): Editor.Extension { const coords = { x: event.clientX, y: event.clientY }; const position = view.posAtCoords(coords); if (!position) return; const node = coveringNode(tree, position); - if (!node || !node.id) return; + if (!node || !node.marker) return; const items: Dome.PopupMenuItem[] = []; - const attrs = getData(node.id); + const attrs = getData(node.marker); if (attrs?.isFunDecl) { const groupedCallers = Lodash.groupBy(callers, (cs) => cs.kf); const locations = @@ -571,8 +573,8 @@ function createContextMenuHandler(): Editor.Extension { } const enabled = attrs?.isLval; const onClick = (kind: access): void => { - if (attrs && node.id) - studia({ marker: node.id, attrs, kind }).then(update); + if (attrs && node.marker) + studia({ marker: node.marker, attrs, kind }).then(update); }; const reads = 'Studia: select reads'; const writes = 'Studia: select writes'; @@ -721,8 +723,9 @@ export default function ASTview(): JSX.Element { // Updating CodeMirror when the <updateHovered> callback is changed. const [hov, setHov] = States.useHovered(); + const hovered = hov?.marker ?? noMarker; React.useEffect(() => UpdateHovered.set(view, setHov), [view, setHov]); - React.useEffect(() => Hovered.set(view, hov?.marker ?? ''), [view, hov]); + React.useEffect(() => Hovered.set(view, hovered), [view, hovered]); // Updating CodeMirror when the <properties> synchronized array is changed. const props = States.useSyncArrayData(Properties.status); @@ -733,8 +736,7 @@ export default function ASTview(): JSX.Element { React.useEffect(() => Tags.set(view, tags), [view, tags]); // Updating CodeMirror when the <markersInfo> synchronized array is changed. - const info = States.useSyncArrayModel(Ast.markerAttributes); - const getData = React.useCallback((key) => info.getData(key), [info]); + const getData = States.useSyncArrayGetter(Ast.markerAttributes); React.useEffect(() => GetMarkerData.set(view, getData), [view, getData]); // Retrieving data on currently selected function and updating CodeMirror when