diff --git a/ivette/.eslintrc.js b/ivette/.eslintrc.js
index b2580f8d832220299877a3d52e9e4a436e1f22f0..932e81f20ab965d17d4c95ec35da3ecf251dc3c1 100644
--- a/ivette/.eslintrc.js
+++ b/ivette/.eslintrc.js
@@ -39,8 +39,6 @@ module.exports = {
     "lines-between-class-members": [
       "error", "always", { "exceptAfterSingleLine": true }
     ],
-    // Force curly brackets on newline if some item is
-    "object-curly-newline": ["error", { "multiline": true }],
     // Allow infinite loops but still disallow constant if-then-else
     "no-constant-condition": ["error", { "checkLoops": false }],
     // Prefer const including when destructuring when _all_ destructured values
diff --git a/ivette/CONTRIBUTING.md b/ivette/CONTRIBUTING.md
index d17eec67bd6ea0411c6e5ad9aeb18549787ca98b..ebdfaf6fc1be8890abe50e806fcbfb7a17ddb3a9 100644
--- a/ivette/CONTRIBUTING.md
+++ b/ivette/CONTRIBUTING.md
@@ -129,8 +129,6 @@ console.log('test');
 - [lines-between-class-members](https://eslint.org/docs/rules/lines-between-class-members).
   Forces empty lines between class members, except for single line declarations
   (e.g. properties).
-- [object-curly-newline](https://eslint.org/docs/rules/object-curly-newline).
-  Unify curly braces positions.
 - [no-constant-condition](https://eslint.org/docs/rules/no-constant-condition).
   Forbids constant conditions which are often used during the debug process but
   should not remain in release versions. While recommended in Eslint, this
diff --git a/ivette/src/frama-c/kernel/ASTinfo.tsx b/ivette/src/frama-c/kernel/ASTinfo.tsx
index 2069f6803d7b9388a82d6fcedc3a8cc064e74134..bb18e11256fea20bf8f980a27aff1d93f8a78984 100644
--- a/ivette/src/frama-c/kernel/ASTinfo.tsx
+++ b/ivette/src/frama-c/kernel/ASTinfo.tsx
@@ -124,12 +124,12 @@ function MarkButton(props: MarkButtonProps): JSX.Element {
 // --------------------------------------------------------------------------
 
 interface InfoSectionProps {
-  fct: string | undefined;
   scroll: React.RefObject<HTMLDivElement> | undefined;
-  marker: AST.marker;
-  scrolled: AST.marker | undefined;
-  selected: AST.marker | undefined;
-  hovered: AST.marker | undefined;
+  current: AST.decl | undefined; // current selection
+  marker: AST.marker; // of the info-section
+  scrolled: AST.marker | undefined; // among info-sections
+  selected: AST.marker | undefined; // in current selection
+  hovered: AST.marker | undefined;  // in current selection
   marked: boolean;
   excluded: string[];
   setPinned: (m: AST.marker) => void;
@@ -137,10 +137,10 @@ interface InfoSectionProps {
 }
 
 function MarkInfos(props: InfoSectionProps): JSX.Element {
-  const { marker, fct, scrolled, selected, hovered, excluded } = props;
-  const attrs = States.useMarker(marker);
-  const scope = attrs.scope ?? fct;
-  const foreign = !!scope && fct !== scope;
+  const { current, marker, scrolled, selected, hovered, excluded } = props;
+  const { decl, labelKind, titleKind, name, descr } = States.useMarker(marker);
+  const { label } = States.useDeclaration(decl);
+  const foreign = !!current && !!decl && current !== decl;
   const [unfold, setUnfold] = React.useState(true);
   const [expand, setExpand] = React.useState(false);
   const req = React.useMemo(() =>
@@ -153,7 +153,6 @@ function MarkInfos(props: InfoSectionProps): JSX.Element {
     isSelected && 'selected',
     isHovered && 'hovered',
   );
-  const { labelKind, titleKind, name, descr } = attrs;
   const filtered = markerFields.filter((fd) => !excluded.includes(fd.id));
   const hasMore = filtered.length < markerFields.length;
   const displayed = expand ? markerFields : filtered;
@@ -161,34 +160,30 @@ function MarkInfos(props: InfoSectionProps): JSX.Element {
     evt.stopPropagation();
     setUnfold(!unfold);
   };
-  const onChildSelected = (m: AST.marker, meta: Modifier): void => {
+  const onChildSelected = (m: AST.marker, modifier: Modifier): void => {
     props.setPinned(marker);
-    switch (meta) {
+    switch (modifier) {
       case 'NORMAL':
-        States.setSelection({ fct, marker: m });
+        States.gotoLocalMarker(m);
         break;
       case 'META':
-        States.setSelection({ fct, marker: m }, m !== marker);
+        States.gotoLocalMarker(m, m !== marker);
         break;
       case 'DOUBLE':
-        States.setSelection({ fct: scope, marker: m });
+        States.gotoGlobalMarker(m);
         break;
     }
   };
   const onChildHovered = (m: AST.marker | undefined): void => {
-    if (m) {
-      States.setHovered({ fct, marker: m });
-    } else {
-      States.setHovered({ fct, marker });
-    }
+    States.setHovered(m || marker);
   };
   return (
     <div
       ref={isScrolled ? props.scroll : undefined}
       className={`astinfo-section ${highlight}`}
-      onMouseEnter={() => States.setHovered({ fct, marker })}
+      onMouseEnter={() => States.setHovered(marker)}
       onMouseLeave={() => States.setHovered(undefined)}
-      onClick={() => onChildSelected(marker, foreign ? 'DOUBLE' : 'NORMAL')}
+      onClick={() => onChildSelected(marker, 'NORMAL')}
       onDoubleClick={() => onChildSelected(marker, 'DOUBLE')}
     >
       <div
@@ -210,9 +205,7 @@ function MarkInfos(props: InfoSectionProps): JSX.Element {
             {labelKind}
           </span> {name}
         </Code>
-        <Code key="SCOPE" className="" display={foreign}>
-          [in: {scope}]
-        </Code>
+        <Code key="SCOPE" className="" display={foreign}>[in {label}]</Code>
         <MarkButton
           key="MORE"
           icon="CIRC.PLUS"
@@ -280,8 +273,8 @@ export default function ASTinfo(): JSX.Element {
   // Selection Hooks
   const [markers, setMarkers] = React.useState<AST.marker[]>([]);
   const [setting, setSetting] = Dome.useStringSettings(filterSettings, '');
-  const [selection] = States.useSelection();
-  const [hovering] = States.useHovered();
+  const { decl: current, marker: selected } = States.useCurrent();
+  const hovered = States.useHovered();
   const allFields = States.useRequest(AST.getInformation, null) ?? [];
   const excluded = React.useMemo(() => makeFilter(setting), [setting]);
   Dome.useEvent(States.MetaSelection, (loc: States.Location) => {
@@ -295,9 +288,6 @@ export default function ASTinfo(): JSX.Element {
     scrollDiv?.scrollIntoView({ block: 'nearest' });
   }, [scrollDiv]);
   // Derived
-  const fct = selection?.current?.fct;
-  const selected = selection?.current?.marker;
-  const hovered = hovering?.marker;
   const allMarkers = addMarker(addMarker(markers, selected), hovered);
   const scrolled = inside ? selected : (hovered || selected);
   // Callbacks
@@ -312,7 +302,7 @@ export default function ASTinfo(): JSX.Element {
     return (
       <MarkInfos
         key={marker}
-        fct={fct}
+        current={current}
         scroll={scroll}
         marker={marker}
         scrolled={scrolled}
diff --git a/ivette/src/frama-c/kernel/ASTview.tsx b/ivette/src/frama-c/kernel/ASTview.tsx
index 35a124c9ea372b193fc00bb233abbbce9d303283..f29202829458fbab9e5982784d89c7fc1e41b266 100644
--- a/ivette/src/frama-c/kernel/ASTview.tsx
+++ b/ivette/src/frama-c/kernel/ASTview.tsx
@@ -28,7 +28,6 @@ import * as Editor from 'dome/text/editor';
 import * as Utils from 'dome/data/arrays';
 import * as Server from 'frama-c/server';
 import * as States from 'frama-c/states';
-import { key } from 'dome/data/json';
 import * as Settings from 'dome/data/settings';
 import { IconButton } from 'dome/controls/buttons';
 import { Filler, Inset } from 'dome/frame/toolbars';
@@ -36,6 +35,7 @@ import * as Ast from 'frama-c/kernel/api/ast';
 import { text } from 'frama-c/kernel/api/data';
 import * as Eva from 'frama-c/plugins/eva/api/general';
 import * as Properties from 'frama-c/kernel/api/properties';
+import * as Locations from './Locations';
 import { getWritesLval, getReadsLval } from 'frama-c/plugins/studia/api/studia';
 
 import { TitleBar } from 'ivette';
@@ -49,10 +49,9 @@ import * as Preferences from 'ivette/prefs';
 
 // An alias type for functions and locations.
 type Fct = string | undefined;
+type Decl = Ast.decl | 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;
 
@@ -176,7 +175,7 @@ const Ranges = Editor.createAspect({ t: Tree }, ({ t }) => markersRanges(t));
 // -----------------------------------------------------------------------------
 
 // This field contains the currently selected function.
-const Fct = Editor.createField<Fct>(undefined);
+const Decl = Editor.createField<Decl>(undefined);
 
 // This field contains the currently selected marker.
 const Marker = Editor.createField<Marker>(undefined);
@@ -184,11 +183,6 @@ const Marker = Editor.createField<Marker>(undefined);
 // This field contains the current multiple selection.
 const Multiple = Editor.createField<Marker[]>([]);
 
-// The Ivette selection must be updated by CodeMirror plugins. This input
-// add the callback in the CodeMirror internal state.
-type UpdateSelection = (a: States.SelectionActions) => void;
-const UpdateSelection = Editor.createField<UpdateSelection>(() => { return; });
-
 // The marker field is considered as the ground truth on what is selected in the
 // CodeMirror document. To do so, we catch the mouseup event (so when the user
 // select a new part of the document) and update the Ivette selection
@@ -196,14 +190,12 @@ const UpdateSelection = Editor.createField<UpdateSelection>(() => { return; });
 // component's render and thus update everything else.
 const MarkerUpdater = createMarkerUpdater();
 function createMarkerUpdater(): Editor.Extension {
-  const deps = { fct: Fct, tree: Tree, update: UpdateSelection };
+  const deps = { decl: Decl, tree: Tree };
   return Editor.createEventHandler(deps, {
-    mouseup: ({ fct, tree, update }, view, event) => {
+    mouseup: ({ decl, tree }, view, event) => {
       const main = view.state.selection.main;
       const marker = coveringNode(tree, main.from)?.marker;
-      const location = { fct, marker };
-      update({ location });
-      if (event.altKey) States.MetaSelection.emit(location);
+      States.setCurrent({ decl, marker }, event.altKey);
     }
   });
 }
@@ -235,39 +227,32 @@ function createMarkerScroller(): Editor.Extension {
 // This field contains the currently hovered marker.
 const Hovered = Editor.createField<Marker>(undefined);
 
-// The Ivette hovered element must be updated by CodeMirror plugins. This
-// field add the callback in the CodeMirror internal state.
-type UpdateHovered = (h: States.Hovered) => void;
-const UpdateHovered = Editor.createField<UpdateHovered>(() => { return; });
-
 // The Hovered field is updated each time the mouse moves through the CodeMirror
 // document. The handlers updates the Ivette hovered information, which is then
 // reflected on the Hovered field by the Editor component itself.
 const HoveredUpdater = createHoveredUpdater();
 function createHoveredUpdater(): Editor.Extension {
-  const deps = { fct: Fct, tree: Tree, update: UpdateHovered };
+  const deps = { tree: Tree };
   return Editor.createEventHandler(deps, {
     mousemove: (inputs, view, event) => {
-      const { fct, tree, update: updateHovered } = inputs;
+      const { tree } = inputs;
       const coords = { x: event.clientX, y: event.clientY };
-      const reset = (): void => updateHovered(undefined);
       const pos = view.posAtCoords(coords);
-      if (!pos) { reset(); return; }
-      const hov = coveringNode(tree, pos);
-      if (!hov) { reset(); return; }
-      const from = view.coordsAtPos(hov.from);
-      if (!from) { reset(); return; }
-      const to = view.coordsAtPos(hov.to);
-      if (!to) { reset(); return; }
+      if (!pos) { States.setHovered(); return; }
+      const node = coveringNode(tree, pos);
+      if (!node) { States.setHovered(); return; }
+      const from = view.coordsAtPos(node.from);
+      if (!from) { States.setHovered(); return; }
+      const to = view.coordsAtPos(node.to);
+      if (!to) { States.setHovered(); return; }
       const left = Math.min(from.left, to.left);
       const right = Math.max(from.left, to.left);
       const top = Math.min(from.top, to.top);
       const bottom = Math.max(from.bottom, to.bottom);
       const horizontallyOk = left <= coords.x && coords.x <= right;
       const verticallyOk = top <= coords.y && coords.y <= bottom;
-      if (!horizontallyOk || !verticallyOk) { reset(); return; }
-      const marker = hov.marker;
-      updateHovered(marker ? { fct, marker } : undefined);
+      if (!horizontallyOk || !verticallyOk) { States.setHovered(); return; }
+      States.setHovered(node.marker);
     }
   });
 }
@@ -498,28 +483,21 @@ interface StudiaProps {
   kind: access,
 }
 
-interface StudiaInfos {
-  name: string,
-  title: string,
-  locations: { fct: key<'#fct'>, marker: Ast.marker }[],
-  index: number,
-}
-
-async function studia(props: StudiaProps): Promise<StudiaInfos> {
+async function invokeStudia(props: StudiaProps): Promise<void> {
   const { marker, attrs, kind } = props;
   const request = kind === 'Reads' ? getReadsLval : getWritesLval;
   const data = await Server.send(request, marker);
-  const locations = data.direct.map(([f, m]) => ({ fct: f, marker: m }));
+  const markers = data.direct.map(([_, marker]) => marker);
   const lval = attrs.name;
-  if (locations.length > 0) {
-    const name = `${kind} of ${lval}`;
-    const acc = (kind === 'Reads') ? 'accessing' : 'modifying';
-    const title =
-      `List of statements ${acc} the memory location pointed by ${lval}.`;
-    return { name, title, locations, index: 0 };
-  }
-  const name = `No ${kind.toLowerCase()} of ${lval}`;
-  return { name, title: '', locations: [], index: 0 };
+  let label;
+  if (markers.length > 0)
+    label = `${kind} of ${lval}`;
+  else
+    label = `No ${kind.toLowerCase()} of ${lval}`;
+  const acc = (kind === 'Reads') ? 'accessing' : 'modifying';
+  const title =
+    `List of statements ${acc} the memory location pointed by ${lval}.`;
+  Locations.setSelection({ label, title, markers });
 }
 
 // -----------------------------------------------------------------------------
@@ -543,11 +521,15 @@ const GetMarkerData = Editor.createField<GetMarkerData>(() => undefined);
 
 const ContextMenuHandler = createContextMenuHandler();
 function createContextMenuHandler(): Editor.Extension {
-  const data = { tree: Tree, callers: Callers, callees: Callees };
-  const deps = { ...data, update: UpdateSelection, getData: GetMarkerData };
+  const deps = {
+    tree: Tree,
+    callers: Callers,
+    callees: Callees,
+    getData: GetMarkerData,
+  };
   return Editor.createEventHandler(deps, {
     contextmenu: (inputs, view, event) => {
-      const { tree, callers, callees, update, getData } = inputs;
+      const { tree, callers, callees, getData } = inputs;
       const coords = { x: event.clientX, y: event.clientY };
       const position = view.posAtCoords(coords); if (!position) return;
       const node = coveringNode(tree, position);
@@ -556,29 +538,29 @@ function createContextMenuHandler(): Editor.Extension {
       const attrs = getData(node.marker);
       if (attrs?.isFunDecl) {
         const groupedCallers = Lodash.groupBy(callers, (cs) => cs.kf);
-        const locations =
-          callers.map(({ kf, stmt }) => ({ fct: kf, marker: stmt }));
-        Lodash.forEach(groupedCallers, (e) => {
-          const callerName = e[0].kf;
-          const callSites = e.length > 1 ? `(${e.length} call sites)` : '';
-          items.push({
-            label: `Go to caller ${callerName} ` + callSites,
-            onClick: () => update({
-              name: `Call sites of function ${attrs.name}`,
-              locations: locations,
-              index: locations.findIndex(l => l.fct === callerName)
-            })
+        Lodash.forEach(groupedCallers, (group) => {
+          const n = group.length;
+          const site = group[0];
+          const call = `Go to caller ${site.kf}`;
+          const label = n > 1 ? `${call} (${n} call sites)` : call;
+          const markers = callers.map(cs => cs.stmt);
+          const index = callers.findIndex(cs => cs.kf === site.kf);
+          const title = `Call sites of function ${attrs.name}`;
+          const onClick = (): void => Locations.setSelection({
+            label, title, markers, index
           });
+          items.push({ label, onClick });
         });
       } else if (attrs?.isFunction) {
-        const location = { fct: attrs.name };
-        const onClick = (): void => update({ location });
         const label = `Go to definition of ${attrs.name}`;
+        // TODO: use declaration of pointed function (not available yet)
+        const onClick = (): void => States.gotoDeclaration(attrs.decl);
         items.push({ label, onClick });
       }
       else if (attrs?.isFunctionPointer) {
         Lodash.forEach(callees, (fct) => {
-          const onClick = (): void => update({ location: { fct } });
+          // TODO: use declaration of pointed function (not available yet)
+          const onClick = (): void => States.gotoDeclaration(attrs.decl);
           const label = `Go to definition of ${fct} (indirect)`;
           items.push({ label, onClick });
         });
@@ -586,7 +568,7 @@ function createContextMenuHandler(): Editor.Extension {
       const enabled = attrs?.isLval;
       const onClick = (kind: access): void => {
         if (attrs && node.marker)
-          studia({ marker: node.marker, attrs, kind }).then(update);
+          invokeStudia({ marker: node.marker, attrs, kind });
       };
       const reads = 'Studia: select reads';
       const writes = 'Studia: select writes';
@@ -671,8 +653,8 @@ function createTaintTooltip(): Editor.Extension {
 // -----------------------------------------------------------------------------
 
 // Server request handler returning the given function's text.
-function useFctText(fct: Fct): text {
-  return States.useRequest(Ast.printFunction, fct) ?? null;
+function useAST(decl: Ast.decl | undefined): text {
+  return States.useRequest(Ast.printDeclaration, decl) ?? [];
 }
 
 // Server request handler returning the given function's dead code information.
@@ -728,48 +710,44 @@ export default function ASTview(): JSX.Element {
   const [fontSize] = Settings.useGlobalSettings(Preferences.EditorFontSize);
   const { view, Component } = Editor.Editor(extensions);
 
-  // Updating CodeMirror when the selection or its callback are changed.
-  const [selection, setSel] = States.useSelection();
-  React.useEffect(() => UpdateSelection.set(view, setSel), [view, setSel]);
-  const fct = selection?.current?.fct;
-  React.useEffect(() => Fct.set(view, fct), [view, fct]);
-  const marker = selection?.current?.marker;
+  // Current selection
+  const { decl, marker } = States.useCurrent();
+  React.useEffect(() => Decl.set(view, decl), [view, decl]);
   React.useEffect(() => Marker.set(view, marker), [view, marker]);
-  const multiple = selection?.multiple.allSelections.map(l => l.marker);
-  React.useEffect(() => Multiple.set(view, multiple), [view, multiple]);
-
-  // 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]);
+  const hovered = States.useHovered();
   React.useEffect(() => Hovered.set(view, hovered), [view, hovered]);
 
-  // Updating CodeMirror when the <properties> synchronized array is changed.
+  // Multiple selection
+  const { markers } = Locations.useSelection();
+  React.useEffect(() => Multiple.set(view, markers), [view, markers]);
+
+  // Property status
   const props = States.useSyncArrayData(Properties.status);
   React.useEffect(() => PropertiesStatuses.set(view, props), [view, props]);
 
-  // Updating CodeMirror when the <propStatusTags> map is changed.
+  // Property tags
   const tags = States.useTags(Properties.propStatusTags);
   React.useEffect(() => Tags.set(view, tags), [view, tags]);
 
-  // Updating CodeMirror when the <markersInfo> synchronized array is changed.
+  // Marker attributes
   const getData = States.useSyncArrayGetter(Ast.markerAttributes);
   React.useEffect(() => GetMarkerData.set(view, getData), [view, getData]);
 
-  // Retrieving data on currently selected function and updating CodeMirror when
-  // they have changed.
-  const text = useFctText(fct);
+  // Printed AST
+  const text = useAST(decl);
   React.useEffect(() => Text.set(view, text), [view, text]);
+
+  // EVA Callbacks
+  const { kind, name } = States.useDeclaration(decl);
+  const fct = kind==='FUNCTION' ? name : undefined;
   const dead = useFctDead(fct);
   React.useEffect(() => Dead.set(view, dead), [view, dead]);
   const callers = useFctCallers(fct);
   React.useEffect(() => Callers.set(view, callers), [view, callers]);
-  const taints = useFctTaints(fct);
-  React.useEffect(() => TaintedLvalues.set(view, taints), [view, taints]);
-
-  // Retrieving data on currently hovered marker.
   const callees = useCallees(hovered);
   React.useEffect(() => Callees.set(view, callees), [view, callees]);
+  const taints = useFctTaints(fct);
+  React.useEffect(() => TaintedLvalues.set(view, taints), [view, taints]);
 
   return (
     <>
diff --git a/ivette/src/frama-c/kernel/Globals.tsx b/ivette/src/frama-c/kernel/Globals.tsx
index 827957171c9f59670571422193e636da8544c890..bbfdb1fbc9a11cd0912ad80bc3b3f0843c6d9b7d 100644
--- a/ivette/src/frama-c/kernel/Globals.tsx
+++ b/ivette/src/frama-c/kernel/Globals.tsx
@@ -34,7 +34,7 @@ import { Button } from 'dome/controls/buttons';
 import * as Toolbars from 'dome/frame/toolbars';
 
 import * as States from 'frama-c/states';
-import * as Kernel from 'frama-c/kernel/api/ast';
+import * as Ast from 'frama-c/kernel/api/ast';
 import { computationState } from 'frama-c/plugins/eva/api/general';
 import * as Eva from 'frama-c/plugins/eva/api/general';
 
@@ -42,21 +42,17 @@ import * as Eva from 'frama-c/plugins/eva/api/general';
 // --- Global Search Hints
 // --------------------------------------------------------------------------
 
-function makeFunctionHint(fct: functionsData): Toolbars.Hint {
-  return {
-    id: fct.key,
-    label: fct.name,
-    title: fct.signature,
-    value: () => States.setSelection({ fct: fct.name }),
-  };
-}
-
 async function lookupGlobals(pattern: string): Promise<Toolbars.Hint[]> {
   const lookup = pattern.toLowerCase();
-  const fcts = States.getSyncArray(Kernel.functions).getArray();
-  return fcts.filter((fn) => (
-    0 <= fn.name.toLowerCase().indexOf(lookup)
-  )).map(makeFunctionHint);
+  const globals = States.getSyncArray(Ast.declAttributes).getArray();
+  return globals.filter((g) => (
+    0 <= g.name.toLowerCase().indexOf(lookup)
+  )).map((g : Ast.declAttributesData) => ({
+    id: g.decl,
+    label: g.name,
+    title: g.label,
+    value: () => States.gotoDeclaration(g.decl)
+  }));
 }
 
 Toolbars.registerSearchHints('frama-c.globals', lookupGlobals);
@@ -68,11 +64,10 @@ Toolbars.registerSearchHints('frama-c.globals', lookupGlobals);
 interface FctItemProps {
   fct: functionsData;
   current: string | undefined;
-  onSelection: (name: string) => void;
 }
 
 function FctItem(props: FctItemProps): JSX.Element {
-  const { name, signature, main, stdlib, builtin, defined } = props.fct;
+  const { name, signature, main, stdlib, builtin, defined, decl } = props.fct;
   const className = classes(
     main && 'globals-main',
     (stdlib || builtin) && 'globals-stdlib',
@@ -87,7 +82,7 @@ function FctItem(props: FctItemProps): JSX.Element {
       label={name}
       title={signature}
       selected={name === props.current}
-      onSelection={() => props.onSelection(name)}
+      onSelection={() => States.gotoDeclaration(decl)}
     >
       {attributes && <span className="globals-attr">{attributes}</span>}
     </Item>
@@ -99,12 +94,12 @@ function FctItem(props: FctItemProps): JSX.Element {
 // --------------------------------------------------------------------------
 
 type functionsData =
-  Kernel.functionsData | (Kernel.functionsData & Eva.functionsData);
+  Ast.functionsData | (Ast.functionsData & Eva.functionsData);
 
 type FctKey = Json.key<'#functions'>;
 
 function computeFcts(
-  ker: States.ArrayProxy<FctKey, Kernel.functionsData>,
+  ker: States.ArrayProxy<FctKey, Ast.functionsData>,
   eva: States.ArrayProxy<FctKey, Eva.functionsData>,
 ): functionsData[] {
   const arr: functionsData[] = [];
@@ -118,8 +113,9 @@ function computeFcts(
 export default function Globals(): JSX.Element {
 
   // Hooks
-  const [selection, updateSelection] = States.useSelection();
-  const ker = States.useSyncArrayProxy(Kernel.functions);
+  const { decl } = States.useCurrent();
+  const { kind, name } = States.useDeclaration(decl);
+  const ker = States.useSyncArrayProxy(Ast.functions);
   const eva = States.useSyncArrayProxy(Eva.functions);
   const fcts = React.useMemo(() => computeFcts(ker, eva), [ker, eva]);
   const { useFlipSettings } = Dome;
@@ -139,20 +135,10 @@ export default function Globals(): JSX.Element {
     useFlipSettings('ivette.globals.eva-analyzed', true);
   const [evaUnreached, flipEvaUnreached] =
     useFlipSettings('ivette.globals.eva-unreached', true);
-    const [selected, flipSelected] =
-      useFlipSettings('ivette.globals.selected', false);
-  const multipleSelection = selection?.multiple;
-  const multipleSelectionActive = multipleSelection?.allSelections.length > 0;
   const evaComputed = States.useSyncValue(computationState) === 'computed';
 
-  function isSelected(fct: functionsData): boolean {
-    return multipleSelection?.allSelections.some(
-      (l) => fct.name === l?.fct,
-    );
-  }
-
   // Currently selected function.
-  const current: undefined | string = selection?.current?.fct;
+  const current = (decl && kind === 'FUNCTION') ? name : undefined;
 
   function showFunction(fct: functionsData): boolean {
     const visible =
@@ -165,15 +151,10 @@ export default function Globals(): JSX.Element {
       && (evaAnalyzed || !evaComputed ||
         !('eva_analyzed' in fct && fct.eva_analyzed === true))
       && (evaUnreached || !evaComputed ||
-        ('eva_analyzed' in fct && fct.eva_analyzed === true))
-      && (!selected || !multipleSelectionActive || isSelected(fct));
+        ('eva_analyzed' in fct && fct.eva_analyzed === true));
     return !!visible;
   }
 
-  function onSelection(name: string): void {
-    updateSelection({ location: { fct: name } });
-  }
-
   async function onContextMenu(): Promise<void> {
     const items: Dome.PopupMenuItem[] = [
       {
@@ -221,13 +202,6 @@ export default function Globals(): JSX.Element {
         checked: evaUnreached,
         onClick: flipEvaUnreached,
       },
-      'separator',
-      {
-        label: 'Selected only',
-        enabled: multipleSelectionActive,
-        checked: selected,
-        onClick: flipSelected,
-      },
     ];
     Dome.popupMenu(items);
   }
@@ -248,10 +222,9 @@ export default function Globals(): JSX.Element {
   const filteredFunctions =
     filtered.map((fct) => (
       <FctItem
-        key={fct.name}
+        key={fct.key}
         fct={fct}
         current={current}
-        onSelection={onSelection}
       />
     ));
 
diff --git a/ivette/src/frama-c/kernel/History.tsx b/ivette/src/frama-c/kernel/History.tsx
index 90f3fe717957615cc0876d0411cb4872614011f4..a6a350d8e63cc87a3aca24943b4d28bd4eeca610 100644
--- a/ivette/src/frama-c/kernel/History.tsx
+++ b/ivette/src/frama-c/kernel/History.tsx
@@ -29,23 +29,19 @@ import * as Toolbar from 'dome/frame/toolbars';
 import * as States from 'frama-c/states';
 
 export default function History(): JSX.Element {
-  const [selection, updateSelection] = States.useSelection();
-
-  const doPrevSelect = () => void updateSelection('HISTORY_PREV');
-  const doNextSelect = () => void updateSelection('HISTORY_NEXT');
-
+  const selection = States.useSelection();
   return (
     <Toolbar.ButtonGroup>
       <Toolbar.Button
         icon="ANGLE.LEFT"
-        onClick={doPrevSelect}
-        disabled={!selection || selection.history.prevSelections.length === 0}
+        onClick={States.gotoNext}
+        enabled={selection.next.length > 0}
         title="Previous location"
       />
       <Toolbar.Button
         icon="ANGLE.RIGHT"
-        onClick={doNextSelect}
-        disabled={!selection || selection.history.nextSelections.length === 0}
+        onClick={States.gotoPrev}
+        enabled={selection.prev.length > 0}
         title="Next location"
       />
     </Toolbar.ButtonGroup>
diff --git a/ivette/src/frama-c/kernel/Locations.tsx b/ivette/src/frama-c/kernel/Locations.tsx
index 63f6d068369d70fbf8e8966e9a2c1c32f33f1fa5..60e9802e662c7942aef2f846fb78addcac8c83e1 100644
--- a/ivette/src/frama-c/kernel/Locations.tsx
+++ b/ivette/src/frama-c/kernel/Locations.tsx
@@ -25,132 +25,161 @@
 // --------------------------------------------------------------------------
 
 import React from 'react';
+import { GlobalState, useGlobalState } from 'dome/data/states';
 import * as States from 'frama-c/states';
 
 import { CompactModel } from 'dome/table/arrays';
-import { Table, Column, Renderer } from 'dome/table/views';
+import { Table, Column } from 'dome/table/views';
 import { Label } from 'dome/controls/labels';
 import { IconButton } from 'dome/controls/buttons';
 import { Space } from 'dome/frame/toolbars';
 import { TitleBar } from 'ivette';
-import { marker, jMarker } from 'frama-c/kernel/api/ast';
+import * as Ast from 'frama-c/kernel/api/ast';
+
+// --------------------------------------------------------------------------
+// --- Global Multi-Selection
+// --------------------------------------------------------------------------
+
+export interface MultiSelection {
+  label: string;
+  title: string;
+  markers: Ast.marker[];
+  index?: number;
+}
+
+export interface MultiSelectionState {
+  label: string;
+  title: string;
+  markers: Ast.marker[];
+  index: number;
+}
+
+const emptySelection = { label: '', title: '', markers: [], index: 0 };
+const MultiSelection = new GlobalState<MultiSelectionState>(emptySelection);
+
+export function useSelection(): MultiSelectionState {
+  const [s] = useGlobalState(MultiSelection);
+  return s;
+}
+
+export function setSelection(s: MultiSelection): void
+{
+  const { index=0, ...data } = s;
+  MultiSelection.setValue({ ...data, index });
+  States.gotoGlobalMarker(data.markers[index]);
+}
+
+export function setIndex(index: number): void {
+  const s = MultiSelection.getValue();
+  setSelection({ ...s, index });
+}
+
+/**
+   Update the list of markers and select its first element,
+   or cycle to the next element wrt current selection.
+ */
+export function setNextSelection(locs: Ast.marker[]): void {
+  const selection = MultiSelection.getValue();
+  const { markers, index } = selection;
+  if (markers === locs) {
+    const target = index+1;
+    const select = target < locs.length ? target : 0;
+    setSelection({ ...selection, index: select });
+  } else {
+    setSelection({ ...selection, markers: locs, index: 0 });
+  }
+}
+
+export function clear(): void {
+  MultiSelection.setValue(emptySelection);
+}
+
+function gotoIndex(index: number): void {
+  const selection = MultiSelection.getValue();
+  if (0 <= index && index <= selection.markers.length)
+    MultiSelection.setValue({ ...selection, index });
+}
 
 // --------------------------------------------------------------------------
 // --- Locations Panel
 // --------------------------------------------------------------------------
 
-type LocationId = States.Location & { id: number };
+interface Data {
+  index: number,
+  marker: Ast.marker,
+}
 
-function SourceLoc(props: { marker: marker }): JSX.Element {
-  const { sloc, descr } = States.useMarker(props.marker);
-  const position = `${sloc?.base}:${sloc?.line}`;
-  return <Label label={position} title={descr} />;
+class Model extends CompactModel<Ast.marker, Data> {
+  constructor() { super(({ marker }) => marker); }
 }
 
+const getIndex = (d : Data): number => d.index + 1;
+
 export default function LocationsTable(): JSX.Element {
 
   // Hooks
-  const [selection, updateSelection] = States.useSelection();
-  const model = React.useMemo(() => (
-    new CompactModel<number, LocationId>(({ id }: LocationId) => id)
-  ), []);
-  const multipleSelections = selection?.multiple;
-  const numberOfSelections = multipleSelections?.allSelections?.length;
-
-  // Renderer for statement markers.
-  const renderMarker: Renderer<string> = (loc: string) => (
-    <SourceLoc marker={jMarker(loc)} />
-  );
-
-  // Updates [[model]] with the current multiple selections.
+  const model = React.useMemo(() => new Model(), []);
+  const getDecl = States.useSyncArrayGetter(Ast.declAttributes);
+  const getAttr = States.useSyncArrayGetter(Ast.markerAttributes);
+  const { label, title, markers, index } = useSelection();
   React.useEffect(() => {
-    if (numberOfSelections > 0) {
-      const data: LocationId[] =
-        multipleSelections.allSelections.map((d, i) => ({ ...d, id: i }));
-      model.replaceAllDataWith(data);
-    } else
-      model.clear();
-  }, [numberOfSelections, multipleSelections, model]);
-
-  // Callbacks
-  const onTableSelection = React.useCallback(
-    ({ id }) => updateSelection({ index: id }),
-    [updateSelection],
-  );
+    model.replaceAllDataWith(
+      markers.map((marker, index): Data => ({ index, marker }))
+    );
+  }, [model, markers]);
+  const selected = markers[index];
+  const size = markers.length;
 
-  const reload = (): void => {
-    const location = multipleSelections.allSelections[multipleSelections.index];
-    updateSelection({ location });
-  };
+  const indexLabel = `${index+1} / ${size}`;
+
+  const getLocation = React.useCallback((d: Data): string => {
+    const attr = getAttr(d.marker);
+    if (!attr) return '';
+    const decl = getDecl(attr.decl);
+    if (!decl) return '';
+    return `${decl.label} ${attr.descr}`;
+  }, [getDecl, getAttr]);
 
   // Component
   return (
     <>
       <TitleBar>
         <IconButton
-          icon="RELOAD"
-          onClick={reload}
-          enabled={numberOfSelections > 0}
-          title="Reload the current location"
-        />
-        <IconButton
-          icon="ANGLE.LEFT"
-          onClick={() => updateSelection('MULTIPLE_PREV')}
-          enabled={numberOfSelections > 1 && multipleSelections?.index > 0}
-          title="Previous location"
+          icon='ANGLE.LEFT'
+          title='Previous location'
+          enabled={0 < index}
+          onClick={() => gotoIndex(index-1)}
         />
         <IconButton
-          icon="ANGLE.RIGHT"
-          onClick={() => updateSelection('MULTIPLE_NEXT')}
-          enabled={
-            numberOfSelections > 1 &&
-            multipleSelections?.index < numberOfSelections - 1
-          }
-          title="Next location"
+          icon='ANGLE.RIGHT'
+          title='Next location'
+          enabled={index + 1 < size}
+          onClick={() => gotoIndex(index+1)}
         />
         <Space />
         <Label
-          className="component-info"
-          title={
-            `${numberOfSelections} selected ` +
-            `location${numberOfSelections > 1 ? 's' : ''}`
-          }
-        >
-          {multipleSelections?.allSelections.length === 0 ?
-            '0 / 0' :
-            `${multipleSelections?.index + 1} / ${numberOfSelections}`}
-        </Label>
+          className='component-info'
+          display={0 <= index && index < size}
+          label={indexLabel}
+          title='Current location index / Number of locations' />
         <Space />
         <IconButton
-          icon="TRASH"
-          onClick={() => updateSelection('MULTIPLE_CLEAR')}
-          enabled={numberOfSelections > 0}
-          title={`Clear location${numberOfSelections > 1 ? 's' : ''}`}
+          icon='TRASH'
         />
       </TitleBar>
-      <Label
-        label={multipleSelections?.name}
-        title={multipleSelections?.title}
-        style={{ textAlign: 'center' }}
-      />
-      <Table
+      <Label label={label} title={title} style={{ textAlign: 'center' }} />
+      <Table<Ast.marker, Data>
         model={model}
-        selection={multipleSelections?.index}
-        onSelection={onTableSelection}
+        selection={selected}
+        onSelection={(_marker, _data, index) => gotoIndex(index)}
       >
         <Column
-          id="id"
-          label="#"
-          align="center"
-          width={25}
-          getter={(r: { id: number }) => r.id + 1}
+          id='index' label='#' align='center' width={25}
+          getter={getIndex}
         />
-        <Column id="fct" label="Function" width={120} />
         <Column
-          id="marker"
-          label="Statement"
-          fill
-          render={renderMarker}
+          id='marker' label='Location' fill
+          getter={getLocation}
         />
       </Table>
     </>
diff --git a/ivette/src/frama-c/kernel/Messages.tsx b/ivette/src/frama-c/kernel/Messages.tsx
index d635b8fa23449d2a3661fc008177daf3b796ff1c..2d5e58380f9ede1697d86b3ec8909e3dfa2d3bb5 100644
--- a/ivette/src/frama-c/kernel/Messages.tsx
+++ b/ivette/src/frama-c/kernel/Messages.tsx
@@ -61,7 +61,7 @@ type EmitterFilter = {
 };
 
 interface Filter {
-  currentFct: boolean;
+  currentDecl: boolean;
   search: Search;
   kind: KindFilter;
   emitter: EmitterFilter;
@@ -107,7 +107,7 @@ const emitterFilter = {
 };
 
 const defaultFilter: Filter = {
-  currentFct: false,
+  currentDecl: false,
   search: {},
   kind: kindFilter,
   emitter: emitterFilter,
@@ -175,25 +175,27 @@ function filterSearched(search: Search, msg: Message): boolean {
     searchCategory(search.category, msg.category));
 }
 
-function filterFunction(
+function filterDecl(
   filter: Filter,
-  kf: string | undefined,
+  decl: Ast.decl | undefined,
   msg: Message
 ): boolean {
-  if (filter.currentFct)
-    return (kf === msg.fct);
+  if (filter.currentDecl)
+    return (decl === msg.decl);
   return true;
 }
 
 function filterMessage(
   filter: Filter,
-  kf: string | undefined,
+  decl: Ast.decl | undefined,
   msg: Message
 ): boolean {
-  return (filterFunction(filter, kf, msg) &&
+  return (
+    filterDecl(filter, decl, msg) &&
     filterSearched(filter.search, msg) &&
     filterKind(filter.kind, msg) &&
-    filterEmitter(filter.emitter, msg));
+    filterEmitter(filter.emitter, msg)
+  );
 }
 
 // --------------------------------------------------------------------------
@@ -255,7 +257,7 @@ function MessageFilter(props: { filter: State<Filter> }): JSX.Element {
       <Forms.CheckboxField
         label="Current function"
         title="Only show messages emitted at the current function"
-        state={Forms.useProperty(state, 'currentFct')}
+        state={Forms.useProperty(state, 'currentDecl')}
       />
       <Section label="Search">
         <Forms.TextField
@@ -370,7 +372,7 @@ const MessageColumns = (): JSX.Element => (
       render={renderMessage}
     />
     <Column
-      id="fct"
+      id="decl"
       label="Function"
       width={150}
       render={renderCell}
@@ -412,7 +414,7 @@ const byMessage: Compare.ByFields<Message> = {
   kind: Compare.structural,
   plugin: Compare.string,
   category: Compare.defined(Compare.string),
-  fct: Compare.defined(Compare.alpha),
+  decl: Compare.defined(Compare.alpha),
   source: Compare.defined(bySource),
 };
 
@@ -437,29 +439,27 @@ export default function RenderMessages(): JSX.Element {
 
   const filterState = useGlobalState(globalFilterState);
   const [filter] = filterState;
-  const [selection, updateSelection] = States.useSelection();
-  const selectedFct = selection?.current?.fct;
+  const { decl: selectedDecl } = States.useCurrent();
   const [selectedMsg, selectMsg] = React.useState<Message|undefined>(undefined);
   const [text, setText] = React.useState('');
 
   React.useEffect(() => {
-    if (selectedFct !== selectedMsg?.fct)
+    if (selectedDecl !== selectedMsg?.decl)
       selectMsg(undefined);
-  }, [selectedFct, selectedMsg?.fct]);
+  }, [selectedDecl, selectedMsg?.decl]);
 
   React.useEffect(() => {
-    model.setFilter((msg: Message) => filterMessage(filter, selectedFct, msg));
-  }, [model, filter, selectedFct]);
+    model.setFilter((msg: Message) => filterMessage(filter, selectedDecl, msg));
+  }, [model, filter, selectedDecl]);
 
   const onMessageSelection = React.useCallback(
     (msg: Message) => {
       selectMsg(msg);
       setText(msg.message);
-      if (msg.fct && msg.marker) {
-        const location = { fct: msg.fct, marker: msg.marker };
-        updateSelection({ location });
+      if (msg.decl || msg.marker) {
+        States.setCurrent({ decl: msg.decl, marker: msg.marker });
       }
-    }, [updateSelection],
+    }, []
   );
 
   const [showFilter, flipFilter] =
diff --git a/ivette/src/frama-c/kernel/Properties.tsx b/ivette/src/frama-c/kernel/Properties.tsx
index 5be49e7b932d9f44dea7bd4ae0bd4fdbf2ed644b..e394979c4909bae20b78ccdcd57262fc4aa3356c 100644
--- a/ivette/src/frama-c/kernel/Properties.tsx
+++ b/ivette/src/frama-c/kernel/Properties.tsx
@@ -680,14 +680,16 @@ export default function RenderProperties(): JSX.Element {
     populateModel(model, props, evaps);
   }, [model, props, evaps]);
 
-  const [selection, updateSelection] = States.useSelection();
+  const { marker } = States.useCurrent();
+  const { scope } = States.useMarker(marker);
+  const propertySelection = marker;
+  const selectedFunction = marker && scope;
 
   const [showFilter, flipFilter] =
     Dome.useFlipSettings('ivette.properties.showFilter', true);
 
   // Updating the filter
   Dome.useEvent(Reload, model.reload);
-  const selectedFunction = selection?.current?.fct;
   React.useEffect(() => {
     model.setFilterFunction(selectedFunction);
   }, [model, selectedFunction]);
@@ -695,14 +697,9 @@ export default function RenderProperties(): JSX.Element {
   // Callbacks
 
   const onPropertySelection = React.useCallback(
-    ({ key: marker, fct }: Property) => {
-      const location = { fct, marker };
-      updateSelection({ location });
-    }, [updateSelection],
+    (p: Property) => States.gotoGlobalMarker(p.key), []
   );
 
-  const propertySelection = selection?.current?.marker;
-
   return (
     <>
       <TitleBar>
diff --git a/ivette/src/frama-c/kernel/SourceCode.tsx b/ivette/src/frama-c/kernel/SourceCode.tsx
index 69f9cc5cdc0e1b7b07397049571d33e00b54473c..63c131c4436f28cfc3650a52108104008b5101b2 100644
--- a/ivette/src/frama-c/kernel/SourceCode.tsx
+++ b/ivette/src/frama-c/kernel/SourceCode.tsx
@@ -84,11 +84,6 @@ async function edit(file: string, pos: Position, cmd: string): Promise<void> {
 //  Fields declarations
 // -----------------------------------------------------------------------------
 
-// The Ivette selection must be updated by the CodeMirror plugin. This field
-// adds the callback in the CodeMirror internal state.
-type UpdateSelection = (a: States.SelectionActions) => void;
-const UpdateSelection = Editor.createField<UpdateSelection>(() => { return; });
-
 // Those fields contain the source code and the file name.
 const Source = Editor.createTextField<string>('', (s) => s);
 const File = Editor.createField<string>('');
@@ -104,19 +99,19 @@ const GetSource = Editor.createField<GetSource>(() => undefined);
 // current CodeMirror cursor) and the ivette location (i.e the locations as
 // seen by the outside world). Keeping track of both of them together force
 // their update in one dispatch, which prevents strange bugs.
-interface Locations { cursor?: States.Location, ivette: States.Location }
+interface Locations { cursor?: States.Location, current: States.Location }
 
 // The Locations field. When given Locations, it will update the cursor field if
 // and only if the new cursor location is not undefined. It simplifies the
 // update in the SourceCode component itself.
 const Locations = createLocationsField();
 function createLocationsField(): Editor.Field<Locations> {
-  const noField = { cursor: undefined, ivette: {} };
+  const noField = { cursor: undefined, current: {} };
   const field = Editor.createField<Locations>(noField);
   const set: Editor.Set<Locations> = (view, toBe) => {
     const hasBeen = field.get(view?.state);
     const cursor = toBe.cursor ?? hasBeen.cursor;
-    field.set(view, { cursor, ivette: toBe.ivette });
+    field.set(view, { cursor, current: toBe.current });
   };
   return { ...field, set };
 }
@@ -132,21 +127,21 @@ function createLocationsField(): Editor.Field<Locations> {
 // Update the outside world when the user click somewhere in the source code.
 const SyncOnUserSelection = createSyncOnUserSelection();
 function createSyncOnUserSelection(): Editor.Extension {
-  const actions = { cmd: Command, update: UpdateSelection };
+  const actions = { cmd: Command };
   const deps = { file: File, selection: Selection, doc: Document, ...actions };
   return Editor.createEventHandler(deps, {
-    mouseup: async ({ file, cmd, update }, view, event) => {
+    mouseup: async ({ file, cmd }, view, event) => {
       if (!view || file === '') return;
       const pos = getCursorPosition(view);
-      const cursor = [file, pos.line, pos.column];
+      const cursor = { file, line: pos.line, column: pos.column };
       try {
-        const [fct, marker] = await Server.send(Ast.getMarkerAt, cursor);
-        if (fct || marker) {
+        const marker = await Server.send(Ast.getMarkerAt, cursor);
+        if (marker) {
           // The forced reload should NOT be necessary but... It is...
           await Server.send(Ast.reloadMarkerAttributes, null);
-          const location = { fct, marker };
-          update({ location });
-          Locations.set(view, { cursor: location, ivette: location });
+          States.gotoGlobalMarker(marker);
+          const location = States.getCurrent();
+          Locations.set(view, { cursor: location, current: location });
         }
       } catch {
         setError('Failure');
@@ -162,12 +157,13 @@ const SyncOnOutsideSelection = createSyncOnOutsideSelection();
 function createSyncOnOutsideSelection(): Editor.Extension {
   const deps = { locations: Locations, get: GetSource };
   return Editor.createViewUpdater(deps, ({ locations, get }, view) => {
-    const { cursor, ivette } = locations;
-    if (ivette === undefined || ivette === cursor) return;
-    const source = get(ivette); if (!source) return;
-    const newFct = ivette.fct !== cursor?.fct && ivette.marker === undefined;
-    const onTop = cursor === undefined || newFct;
-    Locations.set(view, { cursor: ivette, ivette });
+    const { cursor, current } = locations;
+    if (current === undefined || current === cursor) return;
+    const source = get(current); if (!source) return;
+    const newDecl =
+      current.decl !== cursor?.decl && current.marker === undefined;
+    const onTop = cursor === undefined || newDecl;
+    Locations.set(view, { cursor: current, current });
     Editor.selectLine(view, source.line, onTop);
   });
 }
@@ -183,7 +179,7 @@ function createSyncOnOutsideSelection(): Editor.Extension {
 // This events handler takes care of the context menu.
 const ContextMenu = createContextMenu();
 function createContextMenu(): Editor.Extension {
-  const deps = { file: File, command: Command, update: UpdateSelection };
+  const deps = { file: File, command: Command };
   return Editor.createEventHandler(deps, {
     contextmenu: ({ file, command }, view) => {
       if (file === '') return;
@@ -212,13 +208,13 @@ function useFctSource(file: string): string {
 // Build a callback that retrieves a location's source information.
 function useSourceGetter(): GetSource {
   const getAttr = States.useSyncArrayGetter(Ast.markerAttributes);
-  const functionsData = States.useSyncArrayData(Ast.functions);
-  return React.useCallback(({ fct, marker }) => {
-    const markerSloc = getAttr(marker)?.sloc;
-    const fctSloc = (fct !== undefined && fct !== '') ?
-      functionsData.find((e) => e.name === fct)?.sloc : undefined;
-    return markerSloc ?? fctSloc;
-  }, [getAttr, functionsData]);
+  const getDecl = States.useSyncArrayGetter(Ast.declAttributes);
+  return React.useCallback(({ decl, marker }) => {
+    const { sloc, decl: markerDecl } = getAttr(marker) ?? {};
+    if (sloc) return sloc;
+    const { source } = getDecl(decl ?? markerDecl) ?? {};
+    return source;
+  }, [getAttr, getDecl]);
 }
 
 // -----------------------------------------------------------------------------
@@ -249,20 +245,18 @@ export default function SourceCode(): JSX.Element {
   const [fontSize] = Settings.useGlobalSettings(Preferences.EditorFontSize);
   const [command] = Settings.useGlobalSettings(Preferences.EditorCommand);
   const { view, Component } = Editor.Editor(extensions);
-  const [selection, update] = States.useSelection();
-  const loc = React.useMemo(() => selection?.current ?? {}, [selection]);
+  const current = States.useCurrent();
   const getSource = useSourceGetter();
-  const file = getSource(loc)?.file ?? '';
+  const file = getSource(current)?.file ?? '';
   const filename = Path.parse(file).base;
   const pos = getCursorPosition(view);
   const source = useFctSource(file);
 
-  React.useEffect(() => UpdateSelection.set(view, update), [view, update]);
   React.useEffect(() => GetSource.set(view, getSource), [view, getSource]);
   React.useEffect(() => File.set(view, file), [view, file]);
   React.useEffect(() => Source.set(view, source), [view, source]);
   React.useEffect(() => Command.set(view, command), [view, command]);
-  React.useEffect(() => Locations.set(view, { ivette: loc }), [view, loc]);
+  React.useEffect(() => Locations.set(view, { current }), [view, current]);
 
   const externalEditorTitle =
     'Open the source file in an external editor.';
diff --git a/ivette/src/frama-c/kernel/api/ast/index.ts b/ivette/src/frama-c/kernel/api/ast/index.ts
index 2c78d9fba901a5b8f7afe7041fa7fec69a90f8d8..2f5fdbff1bad4692b43ce481c8988d9d10b91dab 100644
--- a/ivette/src/frama-c/kernel/api/ast/index.ts
+++ b/ivette/src/frama-c/kernel/api/ast/index.ts
@@ -423,6 +423,8 @@ export const printFunction: Server.GetRequest<fct,text>= printFunction_internal;
 export interface functionsData {
   /** Entry identifier. */
   key: Json.key<'#functions'>;
+  /** Declaration Tag */
+  decl: decl;
   /** Name */
   name: string;
   /** Signature */
@@ -445,6 +447,7 @@ export interface functionsData {
 export const jFunctionsData: Json.Decoder<functionsData> =
   Json.jObject({
     key: Json.jKey<'#functions'>('#functions'),
+    decl: jDecl,
     name: Json.jString,
     signature: Json.jString,
     main: Json.jOption(Json.jBoolean),
@@ -458,10 +461,11 @@ export const jFunctionsData: Json.Decoder<functionsData> =
 /** Natural order for `functionsData` */
 export const byFunctionsData: Compare.Order<functionsData> =
   Compare.byFields
-    <{ key: Json.key<'#functions'>, name: string, signature: string,
-       main?: boolean, defined?: boolean, stdlib?: boolean,
-       builtin?: boolean, extern?: boolean, sloc: source }>({
+    <{ key: Json.key<'#functions'>, decl: decl, name: string,
+       signature: string, main?: boolean, defined?: boolean,
+       stdlib?: boolean, builtin?: boolean, extern?: boolean, sloc: source }>({
     key: Compare.string,
+    decl: byDecl,
     name: Compare.alpha,
     signature: Compare.string,
     main: Compare.defined(Compare.boolean),
@@ -523,9 +527,10 @@ export const functions: State.Array<Json.key<'#functions'>,functionsData> = func
 
 /** Default value for `functionsData` */
 export const functionsDataDefault: functionsData =
-  { key: Json.jKey<'#functions'>('#functions')(''), name: '', signature: '',
-    main: undefined, defined: undefined, stdlib: undefined,
-    builtin: undefined, extern: undefined, sloc: sourceDefault };
+  { key: Json.jKey<'#functions'>('#functions')(''), decl: declDefault,
+    name: '', signature: '', main: undefined, defined: undefined,
+    stdlib: undefined, builtin: undefined, extern: undefined,
+    sloc: sourceDefault };
 
 /** Updated AST information */
 export const getInformationUpdate: Server.Signal = {
@@ -558,19 +563,25 @@ export const getInformation: Server.GetRequest<
   >= getInformation_internal;
 
 const getMarkerAt_internal: Server.GetRequest<
-  [ string, number, number ],
-  [ fct | undefined, marker | undefined ]
+  { file: string, line: number, column: number },
+  marker |
+  undefined
   > = {
   kind: Server.RqKind.GET,
   name:   'kernel.ast.getMarkerAt',
-  input:  Json.jTriple( Json.jString, Json.jNumber, Json.jNumber,),
-  output: Json.jPair( Json.jOption(jFct), Json.jOption(jMarker),),
-  signals: [],
+  input:  Json.jObject({
+            file: Json.jString,
+            line: Json.jNumber,
+            column: Json.jNumber,
+          }),
+  output: Json.jOption(jMarker),
+  signals: [ { name: 'kernel.ast.changed' } ],
 };
 /** Returns the marker and function at a source file position, if any. Input: file path, line and column. */
 export const getMarkerAt: Server.GetRequest<
-  [ string, number, number ],
-  [ fct | undefined, marker | undefined ]
+  { file: string, line: number, column: number },
+  marker |
+  undefined
   >= getMarkerAt_internal;
 
 const getFiles_internal: Server.GetRequest<null,string[]> = {
diff --git a/ivette/src/frama-c/kernel/api/services/index.ts b/ivette/src/frama-c/kernel/api/services/index.ts
index 145b1f0827579f831915bf44ce7291690ff60159..957ed06058fc4d4308d20d80bd8b5c4c3b3bf316 100644
--- a/ivette/src/frama-c/kernel/api/services/index.ts
+++ b/ivette/src/frama-c/kernel/api/services/index.ts
@@ -38,17 +38,17 @@ import * as Server from 'frama-c/server';
 import * as State from 'frama-c/states';
 
 //@ts-ignore
-import { byFct } from 'frama-c/kernel/api/ast';
+import { byDecl } from 'frama-c/kernel/api/ast';
 //@ts-ignore
 import { byMarker } from 'frama-c/kernel/api/ast';
 //@ts-ignore
 import { bySource } from 'frama-c/kernel/api/ast';
 //@ts-ignore
-import { fct } from 'frama-c/kernel/api/ast';
+import { decl } from 'frama-c/kernel/api/ast';
 //@ts-ignore
-import { fctDefault } from 'frama-c/kernel/api/ast';
+import { declDefault } from 'frama-c/kernel/api/ast';
 //@ts-ignore
-import { jFct } from 'frama-c/kernel/api/ast';
+import { jDecl } from 'frama-c/kernel/api/ast';
 //@ts-ignore
 import { jMarker } from 'frama-c/kernel/api/ast';
 //@ts-ignore
@@ -161,8 +161,8 @@ export interface messageData {
   source?: source;
   /** Marker at the message position (if any) */
   marker?: marker;
-  /** Function containing the message position (if any) */
-  fct?: fct;
+  /** Declaration containing the message position (if any) */
+  decl?: decl;
 }
 
 /** Decoder for `messageData` */
@@ -175,7 +175,7 @@ export const jMessageData: Json.Decoder<messageData> =
     category: Json.jOption(Json.jString),
     source: Json.jOption(jSource),
     marker: Json.jOption(jMarker),
-    fct: Json.jOption(jFct),
+    decl: Json.jOption(jDecl),
   });
 
 /** Natural order for `messageData` */
@@ -183,7 +183,7 @@ export const byMessageData: Compare.Order<messageData> =
   Compare.byFields
     <{ key: Json.key<'#message'>, kind: logkind, plugin: string,
        message: string, category?: string, source?: source, marker?: marker,
-       fct?: fct }>({
+       decl?: decl }>({
     key: Compare.string,
     kind: byLogkind,
     plugin: Compare.alpha,
@@ -191,7 +191,7 @@ export const byMessageData: Compare.Order<messageData> =
     category: Compare.defined(Compare.string),
     source: Compare.defined(bySource),
     marker: Compare.defined(byMarker),
-    fct: Compare.defined(byFct),
+    decl: Compare.defined(byDecl),
   });
 
 /** Signal for array [`message`](#message)  */
@@ -247,7 +247,7 @@ export const message: State.Array<Json.key<'#message'>,messageData> = message_in
 export const messageDataDefault: messageData =
   { key: Json.jKey<'#message'>('#message')(''), kind: logkindDefault,
     plugin: '', message: '', category: undefined, source: undefined,
-    marker: undefined, fct: undefined };
+    marker: undefined, decl: undefined };
 
 /** Message event record. */
 export interface log {
diff --git a/ivette/src/frama-c/menu.ts b/ivette/src/frama-c/menu.ts
index accc957ef5b41d000a9ecedcff13e7655e001199..6879ef815357d8a22bae15a915a4b38b06a6f1fe 100644
--- a/ivette/src/frama-c/menu.ts
+++ b/ivette/src/frama-c/menu.ts
@@ -57,7 +57,7 @@ async function setFiles(): Promise<void> {
   });
   if (files) {
     await parseFiles(files);
-    States.resetSelection();
+    States.clearSelection();
   }
   return;
 }
@@ -90,7 +90,7 @@ async function loadSession(): Promise<void> {
   const file = await Dialogs.showOpenFile({ title: 'Load a saved session' });
   Status.setMessage({ text: 'Loading session…', kind: 'progress' });
   const error = await Server.send(Services.load, file);
-  States.resetSelection();
+  States.clearSelection();
   if (error) {
     Status.setMessage({
       text: 'Error when loading the session',
diff --git a/ivette/src/frama-c/plugins/callgraph/api/index.ts b/ivette/src/frama-c/plugins/callgraph/api/index.ts
index e231a66fbbae3bb315cd9a85a06f47096f0311db..0ccf01428e0a1ef9bf0a61e5c85c635f31355e22 100644
--- a/ivette/src/frama-c/plugins/callgraph/api/index.ts
+++ b/ivette/src/frama-c/plugins/callgraph/api/index.ts
@@ -37,13 +37,21 @@ import * as Server from 'frama-c/server';
 //@ts-ignore
 import * as State from 'frama-c/states';
 
+//@ts-ignore
+import { byDecl } from 'frama-c/kernel/api/ast';
 //@ts-ignore
 import { byFct } from 'frama-c/kernel/api/ast';
 //@ts-ignore
+import { decl } from 'frama-c/kernel/api/ast';
+//@ts-ignore
+import { declDefault } from 'frama-c/kernel/api/ast';
+//@ts-ignore
 import { fct } from 'frama-c/kernel/api/ast';
 //@ts-ignore
 import { fctDefault } from 'frama-c/kernel/api/ast';
 //@ts-ignore
+import { jDecl } from 'frama-c/kernel/api/ast';
+//@ts-ignore
 import { jFct } from 'frama-c/kernel/api/ast';
 //@ts-ignore
 import { byTag } from 'frama-c/kernel/api/data';
@@ -57,6 +65,8 @@ import { tagDefault } from 'frama-c/kernel/api/data';
 export interface vertex {
   /** The function represented by the node */
   kf: fct;
+  /** The declaration tag of the function */
+  decl: decl;
   /** whether this node is the root of a service */
   is_root: boolean;
   /** the root of this node's service */
@@ -65,20 +75,21 @@ export interface vertex {
 
 /** Decoder for `vertex` */
 export const jVertex: Json.Decoder<vertex> =
-  Json.jObject({ kf: jFct, is_root: Json.jBoolean, root: jFct,});
+  Json.jObject({ kf: jFct, decl: jDecl, is_root: Json.jBoolean, root: jFct,});
 
 /** Natural order for `vertex` */
 export const byVertex: Compare.Order<vertex> =
   Compare.byFields
-    <{ kf: fct, is_root: boolean, root: fct }>({
+    <{ kf: fct, decl: decl, is_root: boolean, root: fct }>({
     kf: byFct,
+    decl: byDecl,
     is_root: Compare.boolean,
     root: byFct,
   });
 
 /** Default value for `vertex` */
 export const vertexDefault: vertex =
-  { kf: fctDefault, is_root: false, root: fctDefault };
+  { kf: fctDefault, decl: declDefault, is_root: false, root: fctDefault };
 
 /** Whether the call goes through services or not */
 export enum edgeKind {
diff --git a/ivette/src/frama-c/plugins/callgraph/index.tsx b/ivette/src/frama-c/plugins/callgraph/index.tsx
index e0b1861ef05335fefc5bed6d61b246789f732162..d5561731df95c37fd7e168116d5a8d1571c1a835 100644
--- a/ivette/src/frama-c/plugins/callgraph/index.tsx
+++ b/ivette/src/frama-c/plugins/callgraph/index.tsx
@@ -20,7 +20,7 @@
 /*                                                                          */
 /* ************************************************************************ */
 
-import React, { useEffect, useState } from 'react';
+import React from 'react';
 import _ from 'lodash';
 import * as Ivette from 'ivette';
 import * as Server from 'frama-c/server';
@@ -36,7 +36,7 @@ import 'cytoscape-panzoom/cytoscape.js-panzoom.css';
 import style from './graph-style.json';
 
 import { useGlobalState } from 'dome/data/states';
-import { useRequest, useSelection, useSyncValue } from 'frama-c/states';
+import * as States from 'frama-c/states';
 
 import gearsIcon from 'frama-c/plugins/eva/images/gears.svg';
 import { CallstackState } from 'frama-c/plugins/eva/valuetable';
@@ -123,13 +123,14 @@ function selectCallstack(cy: Cy.Core, callstack: callstack | undefined): void {
 }
 
 function Callgraph() : JSX.Element {
-  const isComputed = useSyncValue(CgAPI.isComputed);
-  const graph = useSyncValue(CgAPI.callgraph);
-  const [cy, setCy] = useState<Cy.Core>();
+  const isComputed = States.useSyncValue(CgAPI.isComputed);
+  const graph = States.useSyncValue(CgAPI.callgraph);
+  const [cy, setCy] = React.useState<Cy.Core>();
   const [cs] = useGlobalState(CallstackState);
-  const [selection, setSelection] = useSelection();
-  const callstack = useRequest(ValuesAPI.getCallstackInfo, cs);
-
+  const callstack = States.useRequest(ValuesAPI.getCallstackInfo, cs);
+  const { decl } = States.useCurrent();
+  const { kind, name } = States.useDeclaration(decl);
+  const fct = kind === 'FUNCTION' ? name : undefined;
   const layout = { name: 'cola', nodeSpacing: 32 };
   const computedStyle = getComputedStyle(document.documentElement);
   const styleVariables =
@@ -144,26 +145,23 @@ function Callgraph() : JSX.Element {
   ];
 
   // Marker selection
-  useEffect(() => {
-    cy && selectFct(cy, selection.current?.fct);
-  }, [cy, selection]);
+  React.useEffect(() => { cy && selectFct(cy, fct); }, [cy, fct]);
 
   // Callstack selection
-  useEffect(() => {
+  React.useEffect(() => {
     cy && selectCallstack(cy, callstack);
   }, [cy, callstack]);
 
   // Click on graph
-  useEffect(() => {
+  React.useEffect(() => {
     if (cy) {
       cy.off('click');
       cy.on('click', 'node', (event) => {
-        const fct = event.target.id() as string;
-        setSelection({ location: { fct } });
+        const { decl } = event.target.data;
+        States.setCurrent({ decl });
       });
     }
-  }, [cy, setSelection]);
-
+  }, [cy]);
 
   if (isComputed === false) {
     Server.send(CgAPI.compute, null);
diff --git a/ivette/src/frama-c/plugins/dive/index.tsx b/ivette/src/frama-c/plugins/dive/index.tsx
index 77dd2a9f9aa1316c467648d7493450d287645c96..ddb9de3edb42f6c4d292acdb39008b6b99651e0b 100644
--- a/ivette/src/frama-c/plugins/dive/index.tsx
+++ b/ivette/src/frama-c/plugins/dive/index.tsx
@@ -581,7 +581,7 @@ const GraphView = React.forwardRef<GraphViewRef | undefined, GraphViewProps>(
   const { lock, layout, selectionMode } = props;
 
   const [dive, setDive] = useState(() => new Dive());
-  const [selection, updateSelection] = States.useSelection();
+  const selection = States.useCurrent();
   const graph = States.useSyncArrayData(API.graph);
 
   function setCy(cy: Cytoscape.Core): void {
@@ -609,19 +609,9 @@ const GraphView = React.forwardRef<GraphViewRef | undefined, GraphViewProps>(
   }, [dive, selectionMode]);
 
   useEffect(() => {
-    /* When clicking on a node, select its writes locations as a multiple
-       selection. If these locations were already selected, select the next
-       location in the multiple selection. */
-    dive.onSelect = (locations) => {
-      if (_.isEqual(locations, selection?.multiple?.allSelections))
-        updateSelection('MULTIPLE_CYCLE');
-      else
-        updateSelection({ locations, index: 0 });
-    };
-
     // Updates the graph according to the selected marker.
-    dive.selectLocation(selection?.current, !lock);
-  }, [dive, lock, selection, updateSelection]);
+    dive.selectLocation(selection, !lock);
+  }, [dive, lock, selection]);
 
   return (
     <CytoscapeComponent
diff --git a/ivette/src/frama-c/plugins/eva/Coverage.tsx b/ivette/src/frama-c/plugins/eva/Coverage.tsx
index 0a048970850663ee84404e3c369409b220e97509..53dbcf591d3ea066f7953f6789be258f51e315f9 100644
--- a/ivette/src/frama-c/plugins/eva/Coverage.tsx
+++ b/ivette/src/frama-c/plugins/eva/Coverage.tsx
@@ -76,16 +76,18 @@ export function CoverageTable(): JSX.Element {
     model.setSorting({ sortBy: 'coverage', sortDirection: 'ASC' });
   }, [model]);
 
-  const [selection, updateSelection] = States.useSelection();
-  const onSelection = ({ key }: stats): void => {
-    updateSelection({ location: { fct: key } });
+  const { decl } = States.useCurrent();
+  const { kind, name } = States.useDeclaration(decl);
+  const selection = kind==='FUNCTION' ? name : undefined;
+  const onSelection = ({ decl }: stats): void => {
+    States.gotoDeclaration(decl);
   };
 
   return (
     <Table
       model={model}
       sorting={model}
-      selection={selection?.current?.fct}
+      selection={selection}
       onSelection={onSelection}
       settings="ivette.coverage.table"
     >
diff --git a/ivette/src/frama-c/plugins/eva/DomainStates.tsx b/ivette/src/frama-c/plugins/eva/DomainStates.tsx
index 503d95e8dc93e3a33579eb94cbe091d3fbe8c947..9f7da4ed9893e9bb957850f5bcbd3886a88e5ded 100644
--- a/ivette/src/frama-c/plugins/eva/DomainStates.tsx
+++ b/ivette/src/frama-c/plugins/eva/DomainStates.tsx
@@ -36,9 +36,7 @@ const globalSelectedDomain = new GlobalState("");
 const globalFilter = new GlobalState(true);
 
 export function EvaStates(): JSX.Element {
-  const [selection] = States.useSelection();
-  const selectedLoc = selection?.current;
-  const marker = selectedLoc?.marker;
+  const { marker } = States.useCurrent();
   const [domains, setDomains] = React.useState<string[]>([]);
   const [selected, setSelected] = useGlobalState(globalSelectedDomain);
   const [stateBefore, setStateBefore] = React.useState("");
diff --git a/ivette/src/frama-c/plugins/eva/api/general/index.ts b/ivette/src/frama-c/plugins/eva/api/general/index.ts
index 681fa6de976e04dacd381a9b1626ae76cf8a8ee5..9bb203d2527f9d4302bcb5be29f8aa3fb7078a9d 100644
--- a/ivette/src/frama-c/plugins/eva/api/general/index.ts
+++ b/ivette/src/frama-c/plugins/eva/api/general/index.ts
@@ -37,15 +37,23 @@ import * as Server from 'frama-c/server';
 //@ts-ignore
 import * as State from 'frama-c/states';
 
+//@ts-ignore
+import { byDecl } from 'frama-c/kernel/api/ast';
 //@ts-ignore
 import { byFct } from 'frama-c/kernel/api/ast';
 //@ts-ignore
 import { byMarker } from 'frama-c/kernel/api/ast';
 //@ts-ignore
+import { decl } from 'frama-c/kernel/api/ast';
+//@ts-ignore
+import { declDefault } from 'frama-c/kernel/api/ast';
+//@ts-ignore
 import { fct } from 'frama-c/kernel/api/ast';
 //@ts-ignore
 import { fctDefault } from 'frama-c/kernel/api/ast';
 //@ts-ignore
+import { jDecl } from 'frama-c/kernel/api/ast';
+//@ts-ignore
 import { jFct } from 'frama-c/kernel/api/ast';
 //@ts-ignore
 import { jMarker } from 'frama-c/kernel/api/ast';
@@ -607,6 +615,8 @@ export const programStats: State.Value<programStatsType> = programStats_internal
 export interface functionStatsData {
   /** Entry identifier. */
   key: fct;
+  /** Function declaration tag */
+  decl: decl;
   /** Coverage of the Eva analysis */
   coverage: { reachable: number, dead: number };
   /** Alarms raised by the Eva analysis by category */
@@ -619,6 +629,7 @@ export interface functionStatsData {
 export const jFunctionStatsData: Json.Decoder<functionStatsData> =
   Json.jObject({
     key: jFct,
+    decl: jDecl,
     coverage: Json.jObject({ reachable: Json.jNumber, dead: Json.jNumber,}),
     alarmCount: Json.jArray(jAlarmEntry),
     alarmStatuses: jStatusesEntry,
@@ -627,9 +638,10 @@ export const jFunctionStatsData: Json.Decoder<functionStatsData> =
 /** Natural order for `functionStatsData` */
 export const byFunctionStatsData: Compare.Order<functionStatsData> =
   Compare.byFields
-    <{ key: fct, coverage: { reachable: number, dead: number },
+    <{ key: fct, decl: decl, coverage: { reachable: number, dead: number },
        alarmCount: alarmEntry[], alarmStatuses: statusesEntry }>({
     key: byFct,
+    decl: byDecl,
     coverage: Compare.byFields
                 <{ reachable: number, dead: number }>({
                 reachable: Compare.number,
@@ -690,8 +702,8 @@ export const functionStats: State.Array<fct,functionStatsData> = functionStats_i
 
 /** Default value for `functionStatsData` */
 export const functionStatsDataDefault: functionStatsData =
-  { key: fctDefault, coverage: { reachable: 0, dead: 0 }, alarmCount: [],
-    alarmStatuses: statusesEntryDefault };
+  { key: fctDefault, decl: declDefault, coverage: { reachable: 0, dead: 0 },
+    alarmCount: [], alarmStatuses: statusesEntryDefault };
 
 const getStates_internal: Server.GetRequest<
   [ marker, boolean ],
diff --git a/ivette/src/frama-c/plugins/eva/valuetable.tsx b/ivette/src/frama-c/plugins/eva/valuetable.tsx
index 95a83acbc7044e37a0de10da29d3254b55f1f7ab..0ce7db49e7e585496449ad21433583d035eaf460 100644
--- a/ivette/src/frama-c/plugins/eva/valuetable.tsx
+++ b/ivette/src/frama-c/plugins/eva/valuetable.tsx
@@ -132,11 +132,12 @@ interface Callsite {
 
 /* Builds a cached version of the `getCallstackInfo` request */
 function useCallsitesCache(): Request<callstack, Callsite[]> {
-  const get = React.useCallback((c) => {
-    if (c !== 'Summary') return Server.send(Values.getCallstackInfo, c);
-    else return Promise.resolve([]);
-  }, []);
-  return Dome.useCache(get);
+  const getter = React.useCallback(
+    (c: callstack): Promise<Callsite[]> => {
+      if (c !== 'Summary') return Server.send(Values.getCallstackInfo, c);
+      else return Promise.resolve([]);
+    }, []);
+  return Dome.useCache(getter);
 }
 
 /* -------------------------------------------------------------------------- */
@@ -149,8 +150,9 @@ function useCallsitesCache(): Request<callstack, Callsite[]> {
 
 /* A Location is a marker in a function */
 interface Location {
-  target: Ast.marker;
   fct: string;
+  decl: Ast.decl;
+  marker: Ast.marker;
 }
 
 /* An Evaluation keeps track of the values at relevant control point around a
@@ -181,11 +183,12 @@ interface Probe extends Location {
 function useEvaluationCache(): Request<[ Location, callstack ], Evaluation> {
   type LocStack = [ Location, callstack ];
   const toString = React.useCallback(([ l, c ] : LocStack): string => {
-    return `${l.fct}:${l.target}:${c}`;
+    return `${l.fct}:${l.marker}:${c}`;
   }, []);
   const get: Request<LocStack, Evaluation> = React.useCallback(([ l, c ]) => {
     const callstack = c === 'Summary' ? undefined : c;
-    return Server.send(Values.getValues, { ...l, callstack });
+    const target = l.marker;
+    return Server.send(Values.getValues, { target, callstack });
   }, []);
   return Dome.useCache(get, toString);
 }
@@ -195,7 +198,7 @@ function useProbeCache(): Request<Location, Probe> {
   const toString = React.useCallback((l) => `${l.fct}:${l.target}`, []);
   const cache = useEvaluationCache();
   const get = React.useCallback(async (loc: Location): Promise<Probe> => {
-    const infos = await Server.send(Values.getProbeInfo, loc.target);
+    const infos = await Server.send(Values.getProbeInfo, loc.marker);
     const evaluate: Request<callstack, Evaluation> = (c) => cache([ loc, c ]);
     return { ...loc, ...infos, evaluate };
   }, [ cache ]);
@@ -262,22 +265,19 @@ function AlarmsInfos(probe?: Probe): Request<callstack, JSX.Element> {
 interface StackInfosProps {
   callsites: Callsite[];
   isSelected: boolean;
-  setSelection: (a: States.SelectionActions) => void;
   close: () => void;
 }
 
 async function StackInfos(props: StackInfosProps): Promise<JSX.Element> {
-  const { callsites, setSelection, isSelected, close } = props;
+  const { callsites, isSelected, close } = props;
   const selectedClass = isSelected ? 'eva-focused' : '';
   const className = classes('eva-callsite', selectedClass);
   if (callsites.length <= 1) return <></>;
   const makeCallsite = ({ caller, stmt }: Callsite): JSX.Element => {
     if (!caller || !stmt) return <></>;
     const key = `${caller}@${stmt}`;
-    const location = { fct: caller, marker: stmt };
     const select = (meta: boolean): void => {
-      setSelection({ location });
-      if (meta) States.MetaSelection.emit(location);
+      States.gotoGlobalMarker(stmt, meta);
     };
     const onClick = (evt: React.MouseEvent): void => { select(evt.altKey); };
     const onDoubleClick = (evt: React.MouseEvent): void => {
@@ -328,13 +328,12 @@ interface ProbeHeaderProps {
   pinProbe: (pin: boolean) => void;
   selectProbe: () => void;
   removeProbe: () => void;
-  setSelection: (a: States.SelectionActions) => void;
   locEvt: Dome.Event<Location>;
 }
 
 function ProbeHeader(props: ProbeHeaderProps): JSX.Element {
-  const { probe, status, setSelection, locEvt } = props;
-  const { code = '(error)', stmt, target, fct } = probe;
+  const { probe, status, locEvt } = props;
+  const { code = '(error)', stmt, marker, decl, fct } = probe;
   const color = classes(MarkerStatusClass(status), 'eva-table-header-sticky');
   const { selectProbe, removeProbe, pinProbe } = props;
   const span = 1 + (probe.effects ? 1 : 0) + (probe.condition ? 2 : 0);
@@ -347,8 +346,10 @@ function ProbeHeader(props: ProbeHeaderProps): JSX.Element {
 
   const isPinned = isPinnedMarker(status);
   const pinText = isPinned ? 'Unpin' : 'Pin';
-  const loc: States.SelectionActions = { location: { fct, marker: target } };
-  const onClick = (): void => { setSelection(loc); selectProbe(); };
+  const onClick = (): void => {
+    States.setCurrent({ marker, decl });
+    selectProbe();
+  };
   const onDoubleClick = (): void => pinProbe(!isPinned);
   const onContextMenu = (): void => {
     const items: Dome.PopupMenuItem[] = [];
@@ -389,7 +390,7 @@ function ProbeHeader(props: ProbeHeaderProps): JSX.Element {
         <div className='eva-header-text-overflow'>
           <span className='dome-text-cell' title={code}>{code}</span>
         </div>
-        <Stmt fct={fct} stmt={stmt} marker={target} short={true}/>
+        <Stmt fct={fct} stmt={stmt} marker={marker} short={true}/>
       </TableCell>
     </th>
   );
@@ -469,6 +470,7 @@ interface ProbeValuesProps {
 
 function ProbeValues(props: ProbeValuesProps): Request<callstack, JSX.Element> {
   const { probe, addLoc, isSelectedCallstack } = props;
+  const { fct, decl } = probe;
 
   // Building common parts
   const onContextMenu = (evaluation?: Values.evaluation) => (): void => {
@@ -480,7 +482,7 @@ function ProbeValues(props: ProbeValuesProps): Request<callstack, JSX.Element> {
     pointedVars.forEach((lval) => {
       const [text, lvalMarker] = lval;
       const label = `Display values for ${text}`;
-      const location = { fct: probe.fct, target: lvalMarker };
+      const location = { decl, fct, marker: lvalMarker };
       const onItemClick = (): void => addLoc(location);
       items.push({ label, onClick: onItemClick });
     });
@@ -581,6 +583,7 @@ async function CallsiteCell(props: CallsiteCellProps): Promise<JSX.Element> {
 
 interface FunctionProps {
   fct: string;
+  decl: Ast.decl;
   markers: Map<Ast.marker, MarkerStatus>;
   close: () => void;
   getProbe: Request<Location, Probe>;
@@ -596,7 +599,6 @@ interface FunctionProps {
   setByCallstacks: (byCallstack: boolean) => void;
   selectCallstack: (callstack: callstack) => void;
   isSelectedCallstack: (c: callstack) => boolean;
-  setSelection: (a: States.SelectionActions) => void;
   locEvt: Dome.Event<Location>;
   startingCallstack: number;
   changeStartingCallstack: (n: number) => void;
@@ -605,8 +607,8 @@ interface FunctionProps {
 const PageSize = 99;
 
 async function FunctionSection(props: FunctionProps): Promise<JSX.Element> {
-  const { fct, folded, isSelectedCallstack, locEvt } = props;
-  const { byCallstacks, setSelection, getCallsites } = props;
+  const { fct, decl, folded, isSelectedCallstack, locEvt } = props;
+  const { byCallstacks, getCallsites } = props;
   const { addLoc, getCallstacks: getCS } = props;
   const { setFolded, setByCallstacks, close } = props;
   const { startingCallstack, changeStartingCallstack } = props;
@@ -628,8 +630,8 @@ async function FunctionSection(props: FunctionProps): Promise<JSX.Element> {
   /* Computes the relevant data for each marker */
   interface Data { probe: Probe; summary: Evaluation; status: MarkerStatus }
   const entries = Array.from(props.markers.entries());
-  const data = await Promise.all(entries.map(async ([ target, status ]) => {
-    const probe = await props.getProbe({ target, fct });
+  const data = await Promise.all(entries.map(async ([ marker, status ]) => {
+    const probe = await props.getProbe({ marker, decl, fct });
     const summary = await probe.evaluate('Summary');
     return { probe, summary, status } as Data;
   }));
@@ -641,7 +643,7 @@ async function FunctionSection(props: FunctionProps): Promise<JSX.Element> {
     const pinProbe = (pin: boolean): void => props.pinProbe(d.probe, pin);
     const selectProbe = (): void => props.selectProbe(d.probe);
     const removeProbe = (): void => props.removeProbe(d.probe);
-    const fcts = { selectProbe, pinProbe, removeProbe, setSelection };
+    const fcts = { selectProbe, pinProbe, removeProbe };
     return ProbeHeader({ ...d, ...fcts, locEvt });
   }));
 
@@ -763,14 +765,16 @@ async function FunctionSection(props: FunctionProps): Promise<JSX.Element> {
 class FunctionInfos {
 
   readonly fct: string;                     // Function's name
+  readonly decl: Ast.decl;                  // Function's declaration
   readonly pinned = new Set<Ast.marker>();  // Pinned markers
   readonly tracked = new Set<Ast.marker>(); // Tracked markers
   startingCallstack = 1;                    // First displayed callstack
   byCallstacks = false;                     // True if displayed by callstacks
   folded = false;                           // True if folded
 
-  constructor(fct: string) {
+  constructor(fct: string, decl: Ast.decl) {
     this.fct = fct;
+    this.decl = decl;
   }
 
   has(marker: Ast.marker): boolean {
@@ -799,7 +803,7 @@ class FunctionInfos {
   }
 
   markers(focusedLoc?: Location): Map<Ast.marker, MarkerStatus> {
-    const { target: tgt, fct } = focusedLoc ?? {};
+    const { marker: tgt, fct } = focusedLoc ?? {};
     const inFct = fct !== undefined && fct === this.fct;
     const ms = new Map<Ast.marker, MarkerStatus>();
     this.pinned.forEach((p) => ms.set(p, [ 'Pinned', inFct && tgt === p ]));
@@ -828,14 +832,14 @@ class FunctionsManager {
     this.map = this.map.bind(this);
   }
 
-  newFunction(fct: string): void {
-    if (!this.cache.has(fct)) this.cache.set(fct, new FunctionInfos(fct));
+  newFunction(fct: string, decl: Ast.decl): void {
+    if (!this.cache.has(fct)) this.cache.set(fct, new FunctionInfos(fct, decl));
   }
 
-  private getInfos(fct: string): FunctionInfos {
+  private getInfos(fct: string, decl: Ast.decl): FunctionInfos {
     const { cache } = this;
     if (cache.has(fct)) return cache.get(fct) as FunctionInfos;
-    const infos = new FunctionInfos(fct);
+    const infos = new FunctionInfos(fct, decl);
     this.cache.set(fct, infos);
     return infos;
   }
@@ -861,24 +865,24 @@ class FunctionsManager {
   }
 
   pin(loc: Location): void {
-    const { target, fct } = loc;
-    this.getInfos(fct).pin(target);
+    const { marker, fct, decl } = loc;
+    this.getInfos(fct, decl).pin(marker);
   }
 
   unpin(loc: Location): void {
-    const { target, fct } = loc;
-    this.cache.get(fct)?.pinned.delete(target);
+    const { marker, fct } = loc;
+    this.cache.get(fct)?.pinned.delete(marker);
   }
 
   track(loc: Location): void {
-    const { target, fct } = loc;
-    this.getInfos(fct).track(target);
+    const { marker, fct, decl } = loc;
+    this.getInfos(fct, decl).track(marker);
   }
 
   removeLocation(loc: Location): void {
-    const { target, fct } = loc;
+    const { marker, fct } = loc;
     const infos = this.cache.get(fct);
-    if (infos) infos.delete(target);
+    if (infos) infos.delete(marker);
   }
 
   delete(fct: string): void {
@@ -917,7 +921,7 @@ ipcRenderer.on('dome.ipc.evaluate', () => evaluateEvent.emit());
 
 interface EvaluationModeProps {
   computationState : Eva.computationStateType | undefined;
-  selection: States.Selection;
+  current: Location | undefined;
   setLocPin: (loc: Location, pin: boolean) => void;
 }
 
@@ -931,19 +935,19 @@ Dome.addMenuItem({
 });
 
 function useEvaluationMode(props: EvaluationModeProps): void {
-  const { computationState, selection, setLocPin } = props;
+  const { computationState, current, setLocPin } = props;
   const handleError = (): void => { return; };
-  const addProbe = (target: Ast.marker | undefined): void => {
-    const fct = selection?.current?.fct;
-    if (fct && target) setLocPin({ fct, target }, true);
+  const addProbe = (marker: Ast.marker | undefined): void => {
+    if (current && marker) setLocPin({ ...current, marker }, true);
   };
   React.useEffect(() => {
     if (computationState !== 'computed') return () => { return; };
     const shortcut = System.platform === 'macos' ? 'Cmd+E' : 'Ctrl+E';
     const onEnter = (pattern: string): void => {
-      const marker = selection?.current?.marker;
-      const data = { stmt: marker, term: pattern };
-      Server.send(Ast.parseExpr, data).then(addProbe).catch(handleError);
+      if (current) {
+        const data = { stmt: current.marker, term: pattern };
+        Server.send(Ast.parseExpr, data).then(addProbe).catch(handleError);
+      }
     };
     const evalMode = {
       label: 'Evaluation',
@@ -958,7 +962,7 @@ function useEvaluationMode(props: EvaluationModeProps): void {
     return () => Toolbars.UnregisterMode.emit(evalMode);
   });
   React.useEffect(() => {
-    Dome.setMenuItem({ id: 'EvaluateMenu', enabled: true });
+    Dome.setMenuItem({ id: 'EvaluateMenu', enabled: current !== undefined });
     return () => Dome.setMenuItem({ id: 'EvaluateMenu', enabled: false });
   });
 }
@@ -980,8 +984,14 @@ const FocusState = new GlobalState<Probe | undefined>(undefined);
 function EvaTable(): JSX.Element {
 
   /* Component state */
-  const [ selection, select ] = States.useSelection();
-  const [ cs, setCS ] = useGlobalState(CallstackState);
+  const { marker, decl } = States.useCurrent();
+  const { kind, name } = States.useDeclaration(decl);
+  const fct = kind === 'FUNCTION' ? name : undefined;
+  const current: Location | undefined = React.useMemo(
+    (): Location | undefined =>
+      ((marker && decl && fct) ? { marker, decl, fct } : undefined)
+    , [marker, decl, fct]);
+    const [ cs, setCS ] = useGlobalState(CallstackState);
   const [ fcts ] = useGlobalState(FunctionsManagerState);
   const [ focus, setFocus ] = useGlobalState(FocusState);
 
@@ -1018,18 +1028,15 @@ function EvaTable(): JSX.Element {
   /* Updated the focused Probe when the selection changes. Also emit on the
    * `locEvent` event. */
   React.useEffect(() => {
-    const target = selection?.current?.marker;
-    const fct = selection?.current?.fct;
-    const loc = (target && fct) ? { target, fct } : undefined;
-    fcts.clean(loc);
+    fcts.clean(current);
     const doUpdate = (p: Probe): void => {
       if (!p.evaluable) { setFocus(undefined); return; }
-      if (fct && p.code) fcts.newFunction(fct);
+      if (fct && decl && p.code) fcts.newFunction(fct, decl);
       setFocus(p); locEvt.emit(p);
     };
-    if (loc) getProbe(loc).then(doUpdate);
+    if (current) getProbe(current).then(doUpdate);
     else setFocus(undefined);
-  }, [ fcts, selection, getProbe, setFocus, locEvt ]);
+  }, [ fcts, current, fct, decl, getProbe, setFocus, locEvt ]);
 
   /* Callback used to pin or unpin a location */
   const setLocPin = React.useCallback((loc: Location, pin: boolean): void => {
@@ -1041,8 +1048,8 @@ function EvaTable(): JSX.Element {
   /* On meta-selection, pin the selected location. */
   React.useEffect(() => {
     const pin = (loc: States.Location): void => {
-      const { marker, fct } = loc;
-      if (marker && fct) setLocPin({ target: marker, fct }, true);
+      const { marker, decl } = loc;
+      if (marker && decl && fct) setLocPin({ marker, decl, fct }, true);
     };
     States.MetaSelection.on(pin);
     return () => States.MetaSelection.off(pin);
@@ -1051,7 +1058,7 @@ function EvaTable(): JSX.Element {
   /* Callback used to remove a probe */
   const remove = React.useCallback((probe: Probe): void => {
     fcts.removeLocation(probe);
-    if (probe.target === focus?.target) {
+    if (probe.marker === focus?.marker) {
       setFocus(undefined);
       fcts.clean(undefined);
     }
@@ -1066,7 +1073,7 @@ function EvaTable(): JSX.Element {
    * memoize the promises building. */
   const functionsPromise = React.useMemo(() => {
     const ps = fcts.map((infos, fct) => {
-      const { byCallstacks, folded } = infos;
+      const { byCallstacks, folded, decl } = infos;
       const isSelectedCallstack = (c: callstack): boolean => c === cs;
       const setFolded = (folded: boolean): void => {
         fcts.setFolded(fct, folded);
@@ -1087,6 +1094,7 @@ function EvaTable(): JSX.Element {
       };
       return {
         fct,
+        decl,
         markers: infos.markers(focus),
         close,
         pinProbe: setLocPin,
@@ -1102,7 +1110,6 @@ function EvaTable(): JSX.Element {
         setByCallstacks: setByCS,
         selectCallstack: (c: callstack) => { setCS(c); setTic(tac + 1); },
         isSelectedCallstack,
-        setSelection: select,
         locEvt,
         startingCallstack: infos.startingCallstack,
         changeStartingCallstack,
@@ -1111,7 +1118,7 @@ function EvaTable(): JSX.Element {
     return Promise.all(ps.map(FunctionSection));
   },
   [ cs, setCS, fcts, focus, setFocus, tac, getCallsites, setLocPin, csFct,
-    getCallstacks, getProbe, remove, select, locEvt ]);
+    getCallstacks, getProbe, remove, locEvt ]);
   const { result: functions } = Dome.usePromise(functionsPromise);
 
   /* Builds the alarms component. As for the function sections, it is an
@@ -1123,17 +1130,17 @@ function EvaTable(): JSX.Element {
    * asynchronous process. */
   const stackInfosPromise = React.useMemo(async () => {
     const callsites = await getCallsites(cs);
-    const tgt = selection.current?.marker;
-    const p = (c: Callsite): boolean => c.stmt !== undefined && c.stmt === tgt;
+    const p = (c: Callsite): boolean =>
+      c.stmt !== undefined && c.stmt === marker;
     const isSelected = callsites.find(p) !== undefined;
     const close = (): void => setCS('Summary');
-    return StackInfos({ callsites, isSelected, setSelection: select, close });
-  }, [ cs, setCS, select, getCallsites, selection ]);
+    return StackInfos({ callsites, isSelected, close });
+  }, [ cs, setCS, getCallsites, marker ]);
   const { result: stackInfos } = Dome.usePromise(stackInfosPromise);
 
   /* Handle Evaluation mode */
   const computationState = States.useSyncValue(Eva.computationState);
-  useEvaluationMode({ computationState, selection, setLocPin });
+  useEvaluationMode({ computationState, current, setLocPin });
 
   /* Builds the component */
   return (
diff --git a/ivette/src/frama-c/plugins/wp/api/index.ts b/ivette/src/frama-c/plugins/wp/api/index.ts
index 583cfa274865831fa597f5221d2781b6a46412af..74357758fe451362fcb8e02e4815d4fe9e211955 100644
--- a/ivette/src/frama-c/plugins/wp/api/index.ts
+++ b/ivette/src/frama-c/plugins/wp/api/index.ts
@@ -37,15 +37,23 @@ import * as Server from 'frama-c/server';
 //@ts-ignore
 import * as State from 'frama-c/states';
 
+//@ts-ignore
+import { byDecl } from 'frama-c/kernel/api/ast';
 //@ts-ignore
 import { byFct } from 'frama-c/kernel/api/ast';
 //@ts-ignore
 import { byMarker } from 'frama-c/kernel/api/ast';
 //@ts-ignore
+import { decl } from 'frama-c/kernel/api/ast';
+//@ts-ignore
+import { declDefault } from 'frama-c/kernel/api/ast';
+//@ts-ignore
 import { fct } from 'frama-c/kernel/api/ast';
 //@ts-ignore
 import { fctDefault } from 'frama-c/kernel/api/ast';
 //@ts-ignore
+import { jDecl } from 'frama-c/kernel/api/ast';
+//@ts-ignore
 import { jFct } from 'frama-c/kernel/api/ast';
 //@ts-ignore
 import { jMarker } from 'frama-c/kernel/api/ast';
@@ -183,6 +191,8 @@ export interface goalsData {
   wpo: goal;
   /** Property Marker */
   property: marker;
+  /** Associated declaration, if any */
+  scope?: decl;
   /** Associated function, if any */
   fct?: fct;
   /** Associated behavior, if any */
@@ -210,6 +220,7 @@ export const jGoalsData: Json.Decoder<goalsData> =
   Json.jObject({
     wpo: jGoal,
     property: jMarker,
+    scope: Json.jOption(jDecl),
     fct: Json.jOption(jFct),
     bhv: Json.jOption(Json.jString),
     thy: Json.jOption(Json.jString),
@@ -225,11 +236,12 @@ export const jGoalsData: Json.Decoder<goalsData> =
 /** Natural order for `goalsData` */
 export const byGoalsData: Compare.Order<goalsData> =
   Compare.byFields
-    <{ wpo: goal, property: marker, fct?: fct, bhv?: string, thy?: string,
-       name: string, smoke: boolean, passed: boolean, status: status,
-       stats: stats, script?: string, saved: boolean }>({
+    <{ wpo: goal, property: marker, scope?: decl, fct?: fct, bhv?: string,
+       thy?: string, name: string, smoke: boolean, passed: boolean,
+       status: status, stats: stats, script?: string, saved: boolean }>({
     wpo: byGoal,
     property: byMarker,
+    scope: Compare.defined(byDecl),
     fct: Compare.defined(byFct),
     bhv: Compare.defined(Compare.string),
     thy: Compare.defined(Compare.string),
@@ -291,10 +303,10 @@ export const goals: State.Array<goal,goalsData> = goals_internal;
 
 /** Default value for `goalsData` */
 export const goalsDataDefault: goalsData =
-  { wpo: goalDefault, property: markerDefault, fct: undefined,
-    bhv: undefined, thy: undefined, name: '', smoke: false, passed: false,
-    status: statusDefault, stats: statsDefault, script: undefined,
-    saved: false };
+  { wpo: goalDefault, property: markerDefault, scope: undefined,
+    fct: undefined, bhv: undefined, thy: undefined, name: '', smoke: false,
+    passed: false, status: statusDefault, stats: statsDefault,
+    script: undefined, saved: false };
 
 /** Proof Server Activity */
 export const serverActivity: Server.Signal = {
diff --git a/ivette/src/frama-c/plugins/wp/goals.tsx b/ivette/src/frama-c/plugins/wp/goals.tsx
index 71d7af84859452f94af698093aaf6f98208af8dc..fe784bc40ceca98f6c3871cb5268a77cd67d36c0 100644
--- a/ivette/src/frama-c/plugins/wp/goals.tsx
+++ b/ivette/src/frama-c/plugins/wp/goals.tsx
@@ -25,6 +25,7 @@ import { IconKind, Cell } from 'dome/controls/labels';
 import { Filter } from 'dome/table/models';
 import { Table, Column } from 'dome/table/views';
 import * as States from 'frama-c/states';
+import * as Ast from 'frama-c/kernel/api/ast';
 import * as WP from 'frama-c/plugins/wp/api';
 
 /* -------------------------------------------------------------------------- */
@@ -79,11 +80,11 @@ function renderStatus(s : Status): JSX.Element {
 
 function filterGoal(
   failed: boolean,
-  scope: string | undefined,
+  scope: Ast.decl | undefined,
 ): Filter<WP.goal, WP.goalsData> {
   return (goal: WP.goalsData): boolean => {
     if (failed && goal.passed) return false;
-    if (scope && scope && goal.fct !== scope) return false;
+    if (scope && goal.scope !== scope) return false;
     return true;
   };
 }
@@ -93,7 +94,7 @@ function filterGoal(
 /* -------------------------------------------------------------------------- */
 
 export interface GoalTableProps {
-  scope: string | undefined;
+  scope: Ast.decl | undefined;
   failed: boolean;
   onFilter: (goals: number, total: number) => void;
 }
@@ -101,16 +102,13 @@ export interface GoalTableProps {
 export function GoalTable(props: GoalTableProps): JSX.Element {
   const { scope, failed, onFilter } = props;
   const model = States.useSyncArrayModel(WP.goals);
-  const [_, updateAstSelection] = States.useSelection();
   const [wpoSelection, setWpoSelection] = React.useState(WP.goalDefault);
 
   const onWpoSelection = React.useCallback(
-    ({ wpo, property: marker, fct }: WP.goalsData) => {
-      const location = { fct, marker };
-      updateAstSelection({ location });
+    ({ wpo, property }: WP.goalsData) => {
+      States.gotoGlobalMarker(property);
       setWpoSelection(wpo);
-    }, [updateAstSelection],
-  );
+    }, []);
 
   React.useEffect(() => {
     if (failed || !!scope) {
diff --git a/ivette/src/frama-c/plugins/wp/index.tsx b/ivette/src/frama-c/plugins/wp/index.tsx
index ebc87c0fc249c5219cf44d959727876e1f2d15e1..24a6beb1cb16da6c2afe48f45e9ca9aa45d57c4f 100644
--- a/ivette/src/frama-c/plugins/wp/index.tsx
+++ b/ivette/src/frama-c/plugins/wp/index.tsx
@@ -43,15 +43,14 @@ import './style.css';
 function WPGoals(): JSX.Element {
   const [scoped, flipScoped] = Dome.useFlipSettings('frama-c.wp.goals.scoped');
   const [failed, flipFailed] = Dome.useFlipSettings('frama-c.wp.goals.failed');
-  const [selection] = States.useSelection();
+  const { decl } = States.useCurrent();
   const [goals, setGoals] = React.useState(0);
   const [total, setTotal] = React.useState(0);
   const onFilter = React.useCallback((goals, total) => {
     setGoals(goals);
     setTotal(total);
   }, [setGoals, setTotal]);
-  const fct = selection?.current?.fct;
-  const scope = scoped ? fct : undefined;
+  const scope = scoped ? decl : undefined;
     return (
       <>
         <Ivette.TitleBar>
@@ -60,7 +59,7 @@ function WPGoals(): JSX.Element {
           </Label>
           <Inset />
           <IconButton icon='COMPONENT' title='Current Scope Only'
-                      enabled={!!fct}
+                      enabled={!!scope}
                       selected={scoped} onClick={flipScoped} />
           <IconButton icon='CIRC.QUESTION' title='Unresolved Goals Only'
                       selected={failed} onClick={flipFailed} />
diff --git a/ivette/src/frama-c/states.ts b/ivette/src/frama-c/states.ts
index 1ff4fadc9ba31801b70fd96a2519b316b43cbc93..38f09f477e9f681ff6f49cac2d3c032dfe730a4f 100644
--- a/ivette/src/frama-c/states.ts
+++ b/ivette/src/frama-c/states.ts
@@ -38,7 +38,6 @@ import { Client, useModel } from 'dome/table/models';
 import { CompactModel } from 'dome/table/arrays';
 import * as Ast from 'frama-c/kernel/api/ast';
 import * as Server from './server';
-import * as Status from 'frama-c/kernel/Status';
 
 // --------------------------------------------------------------------------
 // --- Pretty Printing (Browser Console)
@@ -519,305 +518,136 @@ export function onSyncArray<K, A>(
 // --- Selection
 // --------------------------------------------------------------------------
 
-/** An AST location.
- *
- *  Properties [[function]] and [[marker]] are optional,
- *  but at least one of the two must be set.
- */
-export type Location = {
-  fct?: string;
-  marker?: Ast.marker;
-};
+/**
+   A global Ivette selection.
 
-export interface HistorySelection {
-  /** Previous locations with respect to the [[current]] one. */
-  prevSelections: Location[];
-  /** Next locations with respect to the [[current]] one. */
-  nextSelections: Location[];
-}
+   There is no expected relation between the current marker and declaration:
 
-/** Actions on history selections:
- * - `HISTORY_PREV` jumps to previous history location
- *   (first in [[prevSelections]]).
- * - `HISTORY_NEXT` jumps to next history location
- *   (first in [[nextSelections]]).
- */
-export type HistorySelectActions = 'HISTORY_PREV' | 'HISTORY_NEXT';
+   - current marker is used to synchronize selection in components;
+   - current declaration is used to synchronize filtering in components;
+   - some components might display markers from different declarations.
 
-/** A selection of multiple locations. */
-export interface MultipleSelection {
-  /** Name of the multiple selection.  */
-  name: string;
-  /** Explanatory description of the multiple selection.  */
-  title: string;
-  /** The index of the current selected location in [[allSelections]]. */
-  index: number;
-  /** All locations forming a multiple selection. */
-  allSelections: Location[];
-}
-
-/** A select action on multiple locations. */
-export interface MultipleSelect {
-  readonly name: string;
-  readonly title: string;
-  readonly index: number;
-  readonly locations: Location[];
-}
-
-/** Select the [[index]]-nth location of the current multiple selection. */
-export interface NthSelect {
-  readonly index: number;
-}
-
-/** Actions on multiple selections:
- * - [[MultipleSelect]].
- * - [[NthSelect]].
- * - `MULTIPLE_PREV` jumps to previous location of the multiple selections.
- * - `MULTIPLE_NEXT` jumps to next location of the multiple selections.
- * - `MULTIPLE_CYCLE` cycles between the multiple selections.
- * - `MULTIPLE_CLEAR` clears the multiple selection.
- */
-export type MultipleSelectActions =
-  MultipleSelect | NthSelect
-  | 'MULTIPLE_PREV' | 'MULTIPLE_NEXT' | 'MULTIPLE_CYCLE' | 'MULTIPLE_CLEAR';
+*/
+export interface Location {
+  decl?: Ast.decl;
+  marker?: Ast.marker;
+}
 
+/** Global current selection & history. */
 export interface Selection {
-  /** Current selection. May be one in [[history]] or [[multiple]]. */
-  current?: Location;
-  /** History of selections. */
-  history: HistorySelection;
-  /** Multiple selections at once. */
-  multiple: MultipleSelection;
+  curr: Location; // might be empty
+  prev: Location[]; // last first, no empty locs
+  next: Location[]; // next first, no empty locs
 }
 
-/** A select action on a location. */
-export interface SingleSelect {
-  readonly location: Location;
-}
+const emptySelection: Selection = { curr: {}, prev: [], next: [] };
+const isEmpty = (l: Location): boolean => (!l.decl && !l.marker);
+const pushLoc = (l: Location, ls: Location[]): Location[] =>
+  (isEmpty(l) ? ls : [l, ...ls]);
 
-/** Actions on selection:
- * - [[SingleSelect]].
- * - [[HistorySelectActions]].
- * - [[MultipleSelectActions]].
- */
-export type SelectionActions =
-  SingleSelect | HistorySelectActions | MultipleSelectActions;
+export type Hovered = Ast.marker | undefined
+export const MetaSelection = new Dome.Event<Location>('frama-c-meta-selection');
+export const GlobalHovered = new GlobalState<Hovered>(undefined);
+export const GlobalSelection = new GlobalState<Selection>(emptySelection);
 
-function isSingleSelect(a: SelectionActions): a is SingleSelect {
-  return (a as SingleSelect).location !== undefined;
-}
+Server.onShutdown(() => GlobalSelection.setValue(emptySelection));
 
-function isMultipleSelect(a: SelectionActions): a is MultipleSelect {
-  return (
-    (a as MultipleSelect).locations !== undefined &&
-    (a as MultipleSelect).index !== undefined
-  );
+export function setHovered(h: Hovered = undefined): void {
+  GlobalHovered.setValue(h);
 }
 
-function isNthSelect(a: SelectionActions): a is NthSelect {
-  return (a as NthSelect).index !== undefined;
+export function useHovered(): Hovered {
+  const [h] = useGlobalState(GlobalHovered);
+  return h;
 }
 
-/** Update selection to the given location. */
-function selectLocation(s: Selection, location: Location): Selection {
-  const [prevSelections, nextSelections] =
-    s.current && s.current.fct !== location.fct ?
-      [[s.current, ...s.history.prevSelections], []] :
-      [s.history.prevSelections, s.history.nextSelections];
-  return {
-    ...s,
-    current: location,
-    history: { prevSelections, nextSelections },
-  };
+export function useSelection(): Selection {
+  const [current] = useGlobalState(GlobalSelection);
+  return current;
 }
 
-/** Compute the next selection picking from the current history, depending on
- *  action.
- */
-function fromHistory(s: Selection, action: HistorySelectActions): Selection {
-  switch (action) {
-    case 'HISTORY_PREV': {
-      const [pS, ...prevS] = s.history.prevSelections;
-      return {
-        ...s,
-        current: pS,
-        history: {
-          prevSelections: prevS,
-          nextSelections:
-            [(s.current as Location), ...s.history.nextSelections],
-        },
-      };
-    }
-    case 'HISTORY_NEXT': {
-      const [nS, ...nextS] = s.history.nextSelections;
-      return {
-        ...s,
-        current: nS,
-        history: {
-          prevSelections:
-            [(s.current as Location), ...s.history.prevSelections],
-          nextSelections: nextS,
-        },
-      };
-    }
-    default:
-      return s;
-  }
+export function clearSelection(): void {
+  GlobalHovered.setValue(undefined);
+  GlobalSelection.setValue(emptySelection);
 }
 
-/** Compute the next selection picking from the current multiple, depending on
- *  action.
- */
-function fromMultipleSelections(
-  s: Selection,
-  a: 'MULTIPLE_PREV' | 'MULTIPLE_NEXT' | 'MULTIPLE_CYCLE' | 'MULTIPLE_CLEAR',
-): Selection {
-  switch (a) {
-    case 'MULTIPLE_PREV':
-    case 'MULTIPLE_NEXT':
-    case 'MULTIPLE_CYCLE': {
-      const idx =
-        a === 'MULTIPLE_PREV' ?
-          s.multiple.index - 1 :
-          s.multiple.index + 1;
-      const index =
-        a === 'MULTIPLE_CYCLE' && idx >= s.multiple.allSelections.length ?
-          0 :
-          idx;
-      if (0 <= index && index < s.multiple.allSelections.length) {
-        const multiple = { ...s.multiple, index };
-        return selectLocation(
-          { ...s, multiple },
-          s.multiple.allSelections[index],
-        );
-      }
-      return s;
-    }
-    case 'MULTIPLE_CLEAR':
-      return {
-        ...s,
-        multiple: {
-          name: '',
-          title: '',
-          index: 0,
-          allSelections: [],
-        },
-      };
-    default:
-      return s;
-  }
+export function getCurrent(): Location {
+  return GlobalSelection.getValue().curr;
 }
 
-/** Compute the next selection based on the current one and the given action. */
-function reducer(s: Selection, action: SelectionActions): Selection {
-  if (isSingleSelect(action)) {
-    return selectLocation(s, action.location);
-  }
-  if (isMultipleSelect(action)) {
-    const index = action.index > 0 ? action.index : 0;
-    const selection =
-      action.locations.length === 0 ? s :
-        selectLocation(s, action.locations[index]);
-    return {
-      ...selection,
-      multiple: {
-        name: action.name,
-        title: action.title,
-        allSelections: action.locations,
-        index,
-      },
-    };
-  }
-  if (isNthSelect(action)) {
-    const { index } = action;
-    if (0 <= index && index < s.multiple.allSelections.length) {
-      const location = s?.multiple.allSelections[index];
-      const selection = selectLocation(s, location);
-      const multiple = { ...selection.multiple, index };
-      return { ...selection, multiple };
-    }
-    return s;
-  }
-  switch (action) {
-    case 'HISTORY_PREV':
-    case 'HISTORY_NEXT':
-      return fromHistory(s, action);
-    case 'MULTIPLE_PREV':
-    case 'MULTIPLE_NEXT':
-    case 'MULTIPLE_CYCLE':
-    case 'MULTIPLE_CLEAR':
-      return fromMultipleSelections(s, action);
-    default:
-      return s;
-  }
+export function useCurrent(): Location {
+  const [{ curr }] = useGlobalState(GlobalSelection);
+  return curr;
 }
 
-/** The initial selection is empty. */
-const emptySelection = {
-  current: undefined,
-  history: {
-    prevSelections: [],
-    nextSelections: [],
-  },
-  multiple: {
-    name: '',
-    title: '',
-    index: 0,
-    allSelections: [],
-  },
-};
+/** Move both current declaration and marker. */
+export function setCurrent(l: Location, meta = false): void {
+  const s = GlobalSelection.getValue();
+  const empty = isEmpty(l);
+  GlobalSelection.setValue({
+    curr: l,
+    next: empty ? s.next : [],
+    prev: pushLoc(s.curr, s.prev)
+  });
+  if (meta && !empty) MetaSelection.emit(l);
+}
 
-export type Hovered = Location | undefined;
-export const MetaSelection = new Dome.Event<Location>('frama-c-meta-selection');
-export const GlobalHovered = new GlobalState<Hovered>(undefined);
-export const GlobalSelection = new GlobalState<Selection>(emptySelection);
+/** Move to marker in current declaration. */
+export function gotoLocalMarker(
+  marker : Ast.marker | undefined,
+  meta = false
+): void
+{
+  const s = GlobalSelection.getValue();
+  setCurrent({ decl: s.curr.decl, marker }, meta);
+}
 
-Server.onShutdown(() => GlobalSelection.setValue(emptySelection));
+/** Move to marker in its own declaration (if any). */
+export function gotoGlobalMarker(marker : Ast.marker, meta = false): void
+{
+  const st = currentSyncArray(Ast.markerAttributes);
+  const attr = st.model.getData(marker);
+  const decl = attr?.decl;
+  if (decl) setCurrent({ decl, marker }, meta);
+  else gotoLocalMarker(marker, meta);
+}
 
-export function setHovered(h: Hovered): void { GlobalHovered.setValue(h); }
-export function useHovered(): [Hovered, (h: Hovered) => void] {
-  return useGlobalState(GlobalHovered);
+/** Move to declaration. */
+export function gotoDeclaration(decl: Ast.decl | undefined): void
+{
+  setCurrent({ decl });
 }
 
-export function setSelection(location: Location, meta = false): void {
+/** Move forward in history. */
+export function gotoNext(): void {
   const s = GlobalSelection.getValue();
-  GlobalSelection.setValue(reducer(s, { location }));
-  if (meta) MetaSelection.emit(location);
-}
-
-/** Current selection. */
-export function useSelection(): [Selection, (a: SelectionActions) => void] {
-  const [current, setCurrent] = useGlobalState(GlobalSelection);
-  const callback = React.useCallback((action) => {
-    setCurrent(reducer(current, action));
-    if (isMultipleSelect(action)) {
-      const l = action.locations.length;
-      const markers =
-        (l > 1) ? `${l} markers selected, listed in the 'Locations' panel` :
-          (l === 1) ? `1 marker selected` : `no markers selected`;
-      const text = `${action.name}: ${markers}`;
-      Status.setMessage({ text, title: action.title, kind: 'success' });
-    }
-  }, [current, setCurrent]);
-  return [current, callback];
+  if (s.next.length > 0) {
+    const [curr, ...next] = s.next;
+    GlobalSelection.setValue({ curr, next, prev: pushLoc(s.curr, s.prev) });
+  }
 }
 
-/** Resets the selected locations. */
-export async function resetSelection(): Promise<void> {
-  GlobalSelection.setValue(emptySelection);
-  if (Server.isRunning()) {
-    try {
-      const main = await Server.send(Ast.getMainFunction, {});
-      // If the selection has already been modified, do not change it.
-      if (main && GlobalSelection.getValue() === emptySelection) {
-        GlobalSelection.setValue({ ...emptySelection, current: { fct: main } });
-      }
-    } catch (err) {
-      if (err) D.warn('Request error', err);
-    }
+/** Move backward in history. */
+export function gotoPrev(): void {
+  const s = GlobalSelection.getValue();
+  if (s.prev.length > 0) {
+    const [curr, ...prev] = s.prev;
+    GlobalSelection.setValue({ curr, next: pushLoc(s.curr, s.next), prev });
   }
 }
 
+// --------------------------------------------------------------------------
+// --- Declarations
+// --------------------------------------------------------------------------
+
+export type declaration = Ast.declAttributesData;
+
+/** Access the marker attributes from AST. */
+export function useDeclaration(decl: Ast.decl | undefined): declaration {
+  const data = useSyncArrayElt(Ast.declAttributes, decl);
+  return data ?? Ast.declAttributesDataDefault;
+}
+
 // --------------------------------------------------------------------------
 // --- Markers
 // --------------------------------------------------------------------------
@@ -826,8 +656,8 @@ export type attributes = Ast.markerAttributesData;
 
 /** Access the marker attributes from AST. */
 export function useMarker(marker: Ast.marker | undefined): attributes {
-  const marks = useSyncArrayElt(Ast.markerAttributes, marker);
-  return marks ?? Ast.markerAttributesDataDefault;
+  const data = useSyncArrayElt(Ast.markerAttributes, marker);
+  return data ?? Ast.markerAttributesDataDefault;
 }
 
 // --------------------------------------------------------------------------
@@ -835,8 +665,9 @@ export function useMarker(marker: Ast.marker | undefined): attributes {
 // --------------------------------------------------------------------------
 
 Server.onReady(() => {
-  if (GlobalSelection.getValue() === emptySelection)
-    resetSelection();
+  const s = GlobalSelection.getValue();
+  if (s.curr.decl || s.curr.marker || s.next.length > 0 || s.prev.length > 0)
+    clearSelection();
 });
 
 // --------------------------------------------------------------------------
diff --git a/src/plugins/callgraph/requests.ml b/src/plugins/callgraph/requests.ml
index cdef7f503d12adab16126d22d094cecf24057518..271a267483bab1b4fa452479bca6d5e675feec83 100644
--- a/src/plugins/callgraph/requests.ml
+++ b/src/plugins/callgraph/requests.ml
@@ -62,6 +62,8 @@ struct
 
   let kf = field "kf" (module Kernel_ast.Function)
       ~descr: "The function represented by the node"
+  let decl = field "decl" (module Kernel_ast.Decl)
+      ~descr: "The declaration tag of the function"
   let is_root = field "is_root" Data.jbool
       ~descr: "whether this node is the root of a service"
   let root = field "root" (module Kernel_ast.Function)
@@ -73,6 +75,7 @@ struct
   let to_json (v : Cil_types.kernel_function Service_graph.vertex) =
     default |>
     set kf v.node |>
+    set decl (Printer_tag.SFunction v.node) |>
     set is_root v.is_root |>
     set root v.root.node |>
     to_json
diff --git a/src/plugins/eva/api/general_requests.ml b/src/plugins/eva/api/general_requests.ml
index 253ac2362367a0b8341fda7800501bf2c22cbf7e..6e70920e0bbe1a7c14b8af476ca714e87559c369 100644
--- a/src/plugins/eva/api/general_requests.ml
+++ b/src/plugins/eva/api/general_requests.ml
@@ -726,10 +726,15 @@ let _computed_signal =
     ~add_hook:(Analysis.register_computation_hook ~on:Computed)
     ()
 
-let _array =
+let _functionStats =
   let open Summary in
   let model = States.model () in
 
+  States.column model ~name:"decl"
+    ~descr:(Markdown.plain "Function declaration tag")
+    ~data:(module Kernel_ast.Decl)
+    ~get:(fun (kf,_) -> Printer_tag.SFunction (Globals.Functions.get kf.svar));
+
   States.column model ~name:"coverage"
     ~descr:(Markdown.plain "Coverage of the Eva analysis")
     ~data:(module Coverage)
diff --git a/src/plugins/server/kernel_ast.ml b/src/plugins/server/kernel_ast.ml
index bb94478eefd95988adbd74d7673b9888bab12a08..cac14e656a3da1108eacc5de9499dad20c748474 100644
--- a/src/plugins/server/kernel_ast.ml
+++ b/src/plugins/server/kernel_ast.ml
@@ -674,6 +674,11 @@ struct
   let array : kernel_function States.array =
     begin
       let model = States.model () in
+      States.column model
+        ~name:"decl"
+        ~descr:(Md.plain "Declaration Tag")
+        ~data:(module Decl)
+        ~get:(fun kf -> Printer_tag.SFunction kf) ;
       States.column model
         ~name:"name"
         ~descr:(Md.plain "Name")
@@ -944,14 +949,12 @@ let () = Server_parameters.Debug.add_hook_on_update
 (* --- Marker at a position                                               --- *)
 (* -------------------------------------------------------------------------- *)
 
-let get_kf_marker (file, line, col) =
+let get_marker_at ~file ~line ~col =
   let pos_path = Filepath.Normalized.of_string file in
   let pos =
     Filepath.{ pos_path; pos_lnum = line; pos_cnum = col; pos_bol = 0; }
   in
-  let tag = Printer_tag.loc_to_localizable ~precise_col:true pos in
-  let kf = Option.bind tag Printer_tag.kf_of_localizable in
-  kf, tag
+  Printer_tag.loc_to_localizable ~precise_col:true pos
 
 let () =
   let descr =
@@ -959,11 +962,19 @@ let () =
       "Returns the marker and function at a source file position, if any. \
        Input: file path, line and column."
   in
-  Request.register
+  let signature = Request.signature
+      ~output:(module Joption(Marker)) () in
+  let get_file = Request.param signature
+      ~name:"file" ~descr:(Md.plain "File path") (module Jstring) in
+  let get_line = Request.param signature
+      ~name:"line" ~descr:(Md.plain "Line number") (module Jint) in
+  let get_col = Request.param signature
+      ~name:"column" ~descr:(Md.plain "Column number") (module Jint) in
+  Request.register_sig signature
     ~package ~descr ~kind:`GET ~name:"getMarkerAt"
-    ~input:(module Jtriple (Jstring) (Jint) (Jint))
-    ~output:(module Jpair (Joption (Function)) (Joption (Marker)))
-    get_kf_marker
+    ~signals:[ast_changed_signal]
+    (fun rq () ->
+       get_marker_at ~file:(get_file rq) ~line:(get_line rq) ~col:(get_col rq))
 
 (* -------------------------------------------------------------------------- *)
 (* --- Files                                                              --- *)
diff --git a/src/plugins/server/kernel_main.ml b/src/plugins/server/kernel_main.ml
index aacea649733aa9527bff607439a5a6fc0e809712..03757b20ba614f2a8e932b9b049cad901adb4254 100644
--- a/src/plugins/server/kernel_main.ml
+++ b/src/plugins/server/kernel_main.ml
@@ -152,18 +152,18 @@ let () = States.option model ~name:"source"
 let getMarker (evt, _id) =
   Option.bind evt.Log.evt_source Printer_tag.loc_to_localizable
 
-let getFunction t =
-  Option.bind (getMarker t) Printer_tag.kf_of_localizable
+let getDecl t =
+  Option.bind (getMarker t) Printer_tag.declaration_of_localizable
 
 let () = States.option model ~name:"marker"
     ~descr:(Md.plain "Marker at the message position (if any)")
     ~data:(module Kernel_ast.Marker)
     ~get:getMarker
 
-let () = States.option model ~name:"fct"
-    ~descr:(Md.plain "Function containing the message position (if any)")
-    ~data:(module Kernel_ast.Function)
-    ~get:getFunction
+let () = States.option model ~name:"decl"
+    ~descr:(Md.plain "Declaration containing the message position (if any)")
+    ~data:(module Kernel_ast.Decl)
+    ~get:getDecl
 
 let iter f = ignore (Messages.fold (fun i evt -> f (evt, i); succ i) 0)
 
diff --git a/src/plugins/wp/wpApi.ml b/src/plugins/wp/wpApi.ml
index 7f63a336980f8575a3242a71d7de1da0c6ba13d1..12fd4a74c07e9d55ff69f37261ed6a2308e9cba0 100644
--- a/src/plugins/wp/wpApi.ml
+++ b/src/plugins/wp/wpApi.ml
@@ -169,6 +169,10 @@ let () = R.register ~package ~kind:`GET ~name:"getAvailableProvers"
 
 let gmodel : Wpo.t S.model = S.model ()
 
+let get_decl g = match g.Wpo.po_idx with
+  | Function(kf,_) -> Some (Printer_tag.SFunction kf)
+  | Axiomatic _ -> None (* TODO *)
+
 let get_kf g = match g.Wpo.po_idx with
   | Function(kf,_) -> Some kf
   | Axiomatic _ -> None
@@ -192,6 +196,10 @@ let () = S.column gmodel ~name:"property"
     ~data:(module AST.Marker)
     ~get:(fun g -> Printer_tag.PIP (WpPropId.property_of_id g.Wpo.po_pid))
 
+let () = S.column gmodel ~name:"scope"
+    ~descr:(Md.plain "Associated declaration, if any")
+    ~data:(module D.Joption(AST.Decl)) ~get:get_decl
+
 let () = S.column gmodel ~name:"fct"
     ~descr:(Md.plain "Associated function, if any")
     ~data:(module D.Joption(AST.Function)) ~get:get_kf