Skip to content
Snippets Groups Projects
Commit f0631932 authored by Michele Alberti's avatar Michele Alberti
Browse files

[ivette] Reproduce the 'Go to caller' functionality of the current GUI.

parent 58b71ec1
No related branches found
No related tags found
No related merge requests found
......@@ -501,6 +501,7 @@ export interface MultipleSelection {
/** A select action on multiple locations. */
export interface MultipleSelect {
readonly index: number;
readonly locations: Location[];
}
......@@ -546,7 +547,10 @@ function isSingleSelect(a: SelectionActions): a is SingleSelect {
}
function isMultipleSelect(a: SelectionActions): a is MultipleSelect {
return (a as MultipleSelect).locations !== undefined;
return (
(a as MultipleSelect).locations !== undefined &&
(a as MultipleSelect).index !== undefined
);
}
function isNthSelect(a: SelectionActions): a is NthSelect {
......@@ -644,12 +648,13 @@ function reducer(s: Selection, action: SelectionActions): Selection {
if (isMultipleSelect(action)) {
if (action.locations.length === 0)
return s;
const selection = selectLocation(s, action.locations[0]);
const index = action.index > 0 ? action.index : 0;
const selection = selectLocation(s, action.locations[index]);
return {
...selection,
multiple: {
allSelections: action.locations,
index: 0,
index,
},
};
}
......
......@@ -3,6 +3,7 @@
// --------------------------------------------------------------------------
import React from 'react';
import _ from 'lodash';
import * as Server from 'frama-c/server';
import * as States from 'frama-c/states';
......@@ -57,8 +58,8 @@ async function loadAST(
});
} catch (err) {
PP.error(
'Fail to retrieve the AST of function', theFunction,
'marker:', theMarker, err,
`Fail to retrieve the AST of function '${theFunction}' ` +
`and marker '${theMarker}':`, err,
);
}
})();
......@@ -72,7 +73,7 @@ async function functionCallers(functionName: string) {
const locations = data.map(([fct, marker]) => ({ function: fct, marker }));
return locations;
} catch (err) {
PP.error(`Fail to retrieve callers of function ${functionName}.`, err);
PP.error(`Fail to retrieve callers of function '${functionName}':`, err);
return [];
}
}
......@@ -91,7 +92,7 @@ const ASTview = () => {
const [theme, setTheme] = Dome.useGlobalSetting('ASTview.theme', 'default');
const [fontSize, setFontSize] = Dome.useGlobalSetting('ASTview.fontSize', 12);
const [wrapText, setWrapText] = Dome.useSwitch('ASTview.wrapText', false);
const markers = States.useSyncArray(markerInfo);
const markersInfo = States.useSyncArray(markerInfo);
const theFunction = selection?.current?.function;
const theMarker = selection?.current?.marker;
......@@ -129,26 +130,42 @@ const ASTview = () => {
}
}
function onContextMenu(id: key<'#markerInfo'>) {
async function onContextMenu(id: key<'#markerInfo'>) {
const items = [];
const marker = markers.find((e) => e.key === id);
if (marker?.kind === 'function') {
items.push({
label: `Go to definition of ${marker.name}`,
onClick: () => {
const location = { function: marker.name };
updateSelection({ location });
},
});
}
if (marker?.kind === 'declaration' && marker?.name) {
items.push({
label: 'Go to callers',
onClick: async () => {
const locations = await functionCallers(marker.name);
updateSelection({ locations });
},
});
const selectedMarkerInfo = markersInfo.find((e) => e.key === id);
switch (selectedMarkerInfo?.kind) {
case 'function': {
items.push({
label: `Go to definition of ${selectedMarkerInfo.name}`,
onClick: () => {
const location = { function: selectedMarkerInfo.name };
updateSelection({ location });
},
});
break;
}
case 'declaration': {
if (selectedMarkerInfo?.name) {
const locations = await functionCallers(selectedMarkerInfo.name);
const locationsByFunction = _.groupBy(locations, (e) => e.function);
_.forEach(locationsByFunction,
(e) => {
const callerName = e[0].function;
items.push({
label:
`Go to caller ${callerName} ` +
`${e.length > 1 ? `(${e.length} call sites)` : ''}`,
onClick: () => updateSelection({
locations,
index: locations.findIndex((l) => l.function === callerName),
}),
});
});
}
break;
}
default:
break;
}
if (items.length > 0)
Dome.popupMenu(items);
......
......@@ -25,18 +25,18 @@ const LocationsTable = () => {
const model = React.useMemo(() => (
new CompactModel<number, LocationId>(({ id }: LocationId) => id)
), []);
const multiple: States.MultipleSelection = selection?.multiple;
const numberOfSelections = multiple?.allSelections?.length;
const multipleSelections = selection?.multiple;
const numberOfSelections = multipleSelections?.allSelections?.length;
// Updates [[model]] with the current multiple selection.
// Updates [[model]] with the current multiple selections.
React.useEffect(() => {
if (numberOfSelections > 0) {
const data: LocationId[] =
multiple.allSelections.map((d, i) => ({ ...d, id: i }));
multipleSelections.allSelections.map((d, i) => ({ ...d, id: i }));
model.replaceAllDataWith(data);
} else
model.clear();
}, [numberOfSelections, multiple, model]);
}, [numberOfSelections, multipleSelections, model]);
// Callbacks
const onTableSelection = React.useCallback(
......@@ -45,7 +45,7 @@ const LocationsTable = () => {
);
const reload = () => {
const location = multiple.allSelections[multiple.index];
const location = multipleSelections.allSelections[multipleSelections.index];
updateSelection({ location });
};
......@@ -62,7 +62,7 @@ const LocationsTable = () => {
<IconButton
icon="ANGLE.LEFT"
onClick={() => updateSelection('MULTIPLE_PREV')}
enabled={numberOfSelections > 1 && multiple?.index > 0}
enabled={numberOfSelections > 1 && multipleSelections?.index > 0}
title="Previous location"
/>
<IconButton
......@@ -70,7 +70,7 @@ const LocationsTable = () => {
onClick={() => updateSelection('MULTIPLE_NEXT')}
enabled={
numberOfSelections > 1 &&
multiple?.index < numberOfSelections - 1
multipleSelections?.index < numberOfSelections - 1
}
title="Next location"
/>
......@@ -82,8 +82,9 @@ const LocationsTable = () => {
`location${numberOfSelections > 1 ? 's' : ''}`
}
>
{multiple?.allSelections.length === 0 ?
'0 / 0' : `${multiple?.index + 1} / ${numberOfSelections}`}
{multipleSelections?.allSelections.length === 0 ?
'0 / 0' :
`${multipleSelections?.index + 1} / ${numberOfSelections}`}
</Label>
<Space />
<IconButton
......@@ -95,7 +96,7 @@ const LocationsTable = () => {
</TitleBar>
<Table
model={model}
selection={multiple?.index}
selection={multipleSelections?.index}
onSelection={onTableSelection}
>
<Column
......@@ -118,9 +119,9 @@ const LocationsTable = () => {
export default () => (
<Component
id="frama-c.selection"
id="frama-c.locations"
label="Locations"
title="Browse a selection of multiple locations"
title="Browse multiple locations"
>
<LocationsTable />
</Component>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment