From 0fda3de642109badfb77fef217e48003b36e09c8 Mon Sep 17 00:00:00 2001 From: Maxime Jacquemin <maxime2.jacquemin@gmail.com> Date: Mon, 9 Jan 2023 15:39:48 +0100 Subject: [PATCH] [Ivette] Handling multiple occurrences of same tag --- ivette/src/dome/renderer/text/editor.tsx | 23 +++++------ ivette/src/dome/renderer/text/style.css | 2 +- ivette/src/frama-c/kernel/ASTview.tsx | 49 ++++++++++++++---------- 3 files changed, 39 insertions(+), 35 deletions(-) diff --git a/ivette/src/dome/renderer/text/editor.tsx b/ivette/src/dome/renderer/text/editor.tsx index 37bc190091f..a7cf8b2a73e 100644 --- a/ivette/src/dome/renderer/text/editor.tsx +++ b/ivette/src/dome/renderer/text/editor.tsx @@ -32,21 +32,18 @@ import { showTooltip, Tooltip } from '@codemirror/view'; import { DecorationSet } from '@codemirror/view'; import { lineNumbers } from '@codemirror/view'; -export type { Extension } from '@codemirror/state'; -export { GutterMarker } from '@codemirror/view'; -export { Decoration } from '@codemirror/view'; -export { RangeSet } from '@codemirror/state'; - import { parser } from '@lezer/cpp'; import { tags } from '@lezer/highlight'; import { SyntaxNode } from '@lezer/common'; import * as Language from '@codemirror/language'; -import { foldGutter, foldNodeProp } from '@codemirror/language'; -import { LRLanguage, LanguageSupport } from "@codemirror/language"; -import { syntaxHighlighting, HighlightStyle } from '@codemirror/language'; import './style.css'; +export type { Extension } from '@codemirror/state'; +export { GutterMarker } from '@codemirror/view'; +export { Decoration } from '@codemirror/view'; +export { RangeSet } from '@codemirror/state'; + // ----------------------------------------------------------------------------- @@ -265,7 +262,7 @@ export function createEventHandler<I extends Dict>( // ----------------------------------------------------------------------------- // Plugin specifying how to highlight the code. The theme is handled by the CSS. -const Highlight = syntaxHighlighting(HighlightStyle.define([ +const Highlight = Language.syntaxHighlighting(Language.HighlightStyle.define([ { tag: tags.comment, class: 'cm-comment' }, { tag: tags.typeName, class: 'cm-type' }, { tag: tags.number, class: 'cm-number' }, @@ -277,9 +274,9 @@ const Highlight = syntaxHighlighting(HighlightStyle.define([ // highlighting and folding information. Only comments can be folded. // (Source: https://github.com/lezer-parser/cpp) const comment = (t: SyntaxNode): Range => ({ from: t.from + 2, to: t.to - 2}); -const folder = foldNodeProp.add({ BlockComment: comment }); +const folder = Language.foldNodeProp.add({ BlockComment: comment }); const stringPrefixes = [ "L", "u", "U", "u8", "LR", "UR", "uR", "u8R", "R" ]; -const cppLanguage = LRLanguage.define({ +const cppLanguage = Language.LRLanguage.define({ parser: parser.configure({ props: [ folder ] }), languageData: { commentTokens: { line: "//", block: { open: "/*", close: "*/" } }, @@ -290,7 +287,7 @@ const cppLanguage = LRLanguage.define({ // This extension enables all the language highlighting features. export const LanguageHighlighter: Extension = - [Highlight, new LanguageSupport(cppLanguage)]; + [Highlight, new Language.LanguageSupport(cppLanguage)]; // ----------------------------------------------------------------------------- @@ -327,7 +324,7 @@ function createLineNumbers(): Extension { export const FoldGutter = createFoldGutter(); function createFoldGutter(): Extension { - return foldGutter(); + return Language.foldGutter(); } export function foldAll(view: EditorView | null): void { diff --git a/ivette/src/dome/renderer/text/style.css b/ivette/src/dome/renderer/text/style.css index 51e441417e5..53ff7035136 100644 --- a/ivette/src/dome/renderer/text/style.css +++ b/ivette/src/dome/renderer/text/style.css @@ -138,7 +138,7 @@ background: var(--code-bullet); } -.cm-hovered-code:hover { +.cm-hovered-code { background-color: var(--code-hover); } diff --git a/ivette/src/frama-c/kernel/ASTview.tsx b/ivette/src/frama-c/kernel/ASTview.tsx index a2c4934ab44..91cb9a99de9 100644 --- a/ivette/src/frama-c/kernel/ASTview.tsx +++ b/ivette/src/frama-c/kernel/ASTview.tsx @@ -115,17 +115,24 @@ function textToString(text: text): string { // Computes, for each markers of a tree, its range. Returns the map containing // all those bindings. -function markersRanges(tree: Tree): Map<string, Range>{ - const ranges: Map<string, Range> = new Map(); +function markersRanges(tree: Tree): Map<string, Range[]>{ + const ranges: Map<string, Range[]> = new Map(); const toRanges = (tree: Tree): void => { if (!isNode(tree)) return; - ranges.set(tree.id, tree); + const trees = ranges.get(tree.id) ?? []; + trees.push(tree); + ranges.set(tree.id, trees); for (const child of tree.children) toRanges(child); }; toRanges(tree); return ranges; } +function uniqueRange(m: string, rs: Map<string, Range[]>): Range | undefined { + const ranges = rs.get(m); + return (ranges && ranges.length > 0) ? ranges[0] : undefined; +} + // Find the closest covering tagged node of a given position. Returns // undefined if there is not relevant covering node. function coveringNode(tree: Tree, pos: number): Node | undefined { @@ -226,7 +233,7 @@ function createHoveredUpdater(): Editor.Extension { const horizontallyOk = left <= coords.x && coords.x <= right; const verticallyOk = top <= coords.y && coords.y <= bottom; if (!horizontallyOk || !verticallyOk) return; - const marker = Ast.jMarker(hov?.id); + const marker = Ast.jMarker(hov.id); updateHovered(marker ? { fct, marker } : undefined); } }); @@ -244,12 +251,11 @@ function createCodeDecorator(): Editor.Extension { const selectedClass = Editor.Decoration.mark({ class: 'cm-selected-code' }); const deps = { ranges: Ranges, marker: Marker, hovered: Hovered }; return Editor.createDecorator(deps, ({ ranges, marker: m, hovered: h }) => { - const selected = m && ranges.get(m); - const hovered = h && ranges.get(h); - const range = selected && selectedClass.range(selected.from, selected.to); - const add = hovered && [ hoveredClass.range(hovered.from, hovered.to) ]; - const set = range ? Editor.RangeSet.of(range) : Editor.RangeSet.empty; - return set.update({ add, sort: true }); + const hoveredRanges = (h && ranges.get(h)) ?? []; + const selectedRanges = (m && ranges.get(m)) ?? []; + const hovered = hoveredRanges.map(r => hoveredClass.range(r.from, r.to)); + const selected = selectedRanges.map(r => selectedClass.range(r.from, r.to)); + return Editor.RangeSet.of(selected).update({ add: hovered, sort: true }); }); } @@ -269,10 +275,10 @@ function createDeadCodeDecorator(): Editor.Extension { const tClass = Editor.Decoration.mark({ class: 'cm-non-term-code' }); const deps = { dead: Dead, ranges: Ranges }; return Editor.createDecorator(deps, ({ dead, ranges }) => { - const range = (marker: string): Range | undefined => ranges.get(marker); - const unreachableRanges = mapFilter(dead.unreachable, range); + const range = (m: string): Range[] | undefined => ranges.get(m); + const unreachableRanges = mapFilter(dead.unreachable, range).flat(); const unreachable = unreachableRanges.map(r => uClass.range(r.from, r.to)); - const nonTermRanges = mapFilter(dead.nonTerminating, range); + const nonTermRanges = mapFilter(dead.nonTerminating, range).flat(); const nonTerm = nonTermRanges.map(r => tClass.range(r.from, r.to)); return Editor.RangeSet.of(unreachable.concat(nonTerm), true); }); @@ -296,11 +302,11 @@ const PropertiesStatuses = Editor.createField<Properties.statusData[]>([]); const PropertiesRanges = createPropertiesRange(); interface PropertyRange extends Properties.statusData { range: Range } function createPropertiesRange(): Editor.Aspect<PropertyRange[]> { - const deps = { ps: PropertiesStatuses, ranges: Ranges }; - return Editor.createAspect(deps, ({ ps, ranges }) => mapFilter(ps, (p) => { - const range = ranges.get(p.key); - return range && { ...p, range }; - })); + const deps = { properties: PropertiesStatuses, ranges: Ranges }; + return Editor.createAspect(deps, ({ properties, ranges }) => { + const get = (p: Properties.statusData): Range[] => ranges.get(p.key) ?? []; + return properties.map(p => get(p).map(r => ({ ...p, range: r }))).flat(); + }); } // This aspect computes the tag associated to each property. @@ -502,8 +508,9 @@ function createTaintedLvaluesDecorator(): Editor.Extension { const mark = Editor.Decoration.mark({ class: 'cm-tainted' }); const deps = { ranges: Ranges, tainted: TaintedLvalues }; return Editor.createDecorator(deps, ({ ranges, tainted = [] }) => { - const find = (t: Taints): Range | undefined => ranges.get(t.lval); - const marks = mapFilter(tainted, find).map(r => mark.range(r.from, r.to)); + const find = (t: Taints): Range[] | undefined => ranges.get(t.lval); + const taintedRanges = mapFilter(tainted, find).flat(); + const marks = taintedRanges.map(r => mark.range(r.from, r.to)); return Editor.RangeSet.of(marks, true); }); } @@ -513,7 +520,7 @@ function createTaintTooltip(): Editor.Extension { const deps = { hovered: Hovered, ranges: Ranges, tainted: TaintedLvalues }; return Editor.createTooltip(deps, ({ hovered, ranges, tainted }) => { const hoveredTaint = tainted?.find(t => t.lval === hovered); - const hoveredNode = hovered && ranges.get(hovered); + const hoveredNode = hovered && uniqueRange(hovered, ranges); if (!hoveredTaint || !hoveredNode) return undefined; return { pos: hoveredNode.from, -- GitLab