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

[ivette] Add source code component.

parent 3ccbb876
No related branches found
No related tags found
No related merge requests found
...@@ -129,8 +129,8 @@ export interface markerInfoData { ...@@ -129,8 +129,8 @@ export interface markerInfoData {
name: string; name: string;
/** Marker declaration or description */ /** Marker declaration or description */
descr: string; descr: string;
/** Marker position */ /** Source location */
position: source; sloc: source;
} }
/** Loose decoder for `markerInfoData` */ /** Loose decoder for `markerInfoData` */
...@@ -142,7 +142,7 @@ export const jMarkerInfoData: Json.Loose<markerInfoData> = ...@@ -142,7 +142,7 @@ export const jMarkerInfoData: Json.Loose<markerInfoData> =
var: jMarkerVarSafe, var: jMarkerVarSafe,
name: Json.jFail(Json.jString,'String expected'), name: Json.jFail(Json.jString,'String expected'),
descr: Json.jFail(Json.jString,'String expected'), descr: Json.jFail(Json.jString,'String expected'),
position: jSourceSafe, sloc: jSourceSafe,
}); });
/** Safe decoder for `markerInfoData` */ /** Safe decoder for `markerInfoData` */
...@@ -153,13 +153,13 @@ export const jMarkerInfoDataSafe: Json.Safe<markerInfoData> = ...@@ -153,13 +153,13 @@ export const jMarkerInfoDataSafe: Json.Safe<markerInfoData> =
export const byMarkerInfoData: Compare.Order<markerInfoData> = export const byMarkerInfoData: Compare.Order<markerInfoData> =
Compare.byFields Compare.byFields
<{ key: Json.key<'#markerInfo'>, kind: markerKind, var: markerVar, <{ key: Json.key<'#markerInfo'>, kind: markerKind, var: markerVar,
name: string, descr: string, position: source }>({ name: string, descr: string, sloc: source }>({
key: Compare.string, key: Compare.string,
kind: byMarkerKind, kind: byMarkerKind,
var: byMarkerVar, var: byMarkerVar,
name: Compare.alpha, name: Compare.alpha,
descr: Compare.string, descr: Compare.string,
position: bySource, sloc: bySource,
}); });
/** Signal for array [`markerInfo`](#markerinfo) */ /** Signal for array [`markerInfo`](#markerinfo) */
...@@ -302,6 +302,8 @@ export interface functionsData { ...@@ -302,6 +302,8 @@ export interface functionsData {
builtin?: boolean; builtin?: boolean;
/** Has the function been analyzed by Eva */ /** Has the function been analyzed by Eva */
eva_analyzed?: boolean; eva_analyzed?: boolean;
/** Source location */
sloc: source;
} }
/** Loose decoder for `functionsData` */ /** Loose decoder for `functionsData` */
...@@ -316,6 +318,7 @@ export const jFunctionsData: Json.Loose<functionsData> = ...@@ -316,6 +318,7 @@ export const jFunctionsData: Json.Loose<functionsData> =
stdlib: Json.jBoolean, stdlib: Json.jBoolean,
builtin: Json.jBoolean, builtin: Json.jBoolean,
eva_analyzed: Json.jBoolean, eva_analyzed: Json.jBoolean,
sloc: jSourceSafe,
}); });
/** Safe decoder for `functionsData` */ /** Safe decoder for `functionsData` */
...@@ -327,7 +330,7 @@ export const byFunctionsData: Compare.Order<functionsData> = ...@@ -327,7 +330,7 @@ export const byFunctionsData: Compare.Order<functionsData> =
Compare.byFields Compare.byFields
<{ key: Json.key<'#functions'>, name: string, signature: string, <{ key: Json.key<'#functions'>, name: string, signature: string,
main?: boolean, defined?: boolean, stdlib?: boolean, main?: boolean, defined?: boolean, stdlib?: boolean,
builtin?: boolean, eva_analyzed?: boolean }>({ builtin?: boolean, eva_analyzed?: boolean, sloc: source }>({
key: Compare.string, key: Compare.string,
name: Compare.alpha, name: Compare.alpha,
signature: Compare.string, signature: Compare.string,
...@@ -336,6 +339,7 @@ export const byFunctionsData: Compare.Order<functionsData> = ...@@ -336,6 +339,7 @@ export const byFunctionsData: Compare.Order<functionsData> =
stdlib: Compare.defined(Compare.boolean), stdlib: Compare.defined(Compare.boolean),
builtin: Compare.defined(Compare.boolean), builtin: Compare.defined(Compare.boolean),
eva_analyzed: Compare.defined(Compare.boolean), eva_analyzed: Compare.defined(Compare.boolean),
sloc: bySource,
}); });
/** Signal for array [`functions`](#functions) */ /** Signal for array [`functions`](#functions) */
......
...@@ -24,14 +24,7 @@ import 'codemirror/mode/clike/clike'; ...@@ -24,14 +24,7 @@ import 'codemirror/mode/clike/clike';
import 'codemirror/theme/ambiance.css'; import 'codemirror/theme/ambiance.css';
import 'codemirror/theme/solarized.css'; import 'codemirror/theme/solarized.css';
import { Theme, FontSize } from './Preferences'; import { THEMES, ThemeASTview, FontSizeASTview } from './Preferences';
const THEMES = [
{ id: 'default', label: 'Default' },
{ id: 'ambiance', label: 'Ambiance' },
{ id: 'solarized light', label: 'Solarized Light' },
{ id: 'solarized dark', label: 'Solarized Dark' },
];
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// --- Pretty Printing (Browser Console) // --- Pretty Printing (Browser Console)
...@@ -110,8 +103,8 @@ const ASTview = () => { ...@@ -110,8 +103,8 @@ const ASTview = () => {
const printed = React.useRef<string | undefined>(); const printed = React.useRef<string | undefined>();
const [selection, updateSelection] = States.useSelection(); const [selection, updateSelection] = States.useSelection();
const multipleSelections = selection?.multiple.allSelections; const multipleSelections = selection?.multiple.allSelections;
const [theme, setTheme] = Settings.useGlobalSettings(Theme); const [theme, setTheme] = Settings.useGlobalSettings(ThemeASTview);
const [fontSize, setFontSize] = Settings.useGlobalSettings(FontSize); const [fontSize, setFontSize] = Settings.useGlobalSettings(FontSizeASTview);
const [wrapText, flipWrapText] = Dome.useFlipSettings('ASTview.wrapText'); const [wrapText, flipWrapText] = Dome.useFlipSettings('ASTview.wrapText');
const markersInfo = States.useSyncArray(markerInfo); const markersInfo = States.useSyncArray(markerInfo);
......
...@@ -23,6 +23,7 @@ import Globals, { GlobalHint, useHints } from './Globals'; ...@@ -23,6 +23,7 @@ import Globals, { GlobalHint, useHints } from './Globals';
import Properties from './Properties'; import Properties from './Properties';
import Locations from './Locations'; import Locations from './Locations';
import Values from './Values'; import Values from './Values';
import SourceCode from './SourceCode';
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// --- Selection Controls // --- Selection Controls
...@@ -129,6 +130,7 @@ export default (() => { ...@@ -129,6 +130,7 @@ export default (() => {
<Group id="frama-c" label="Frama-C" title="Frama-C Kernel Components"> <Group id="frama-c" label="Frama-C" title="Frama-C Kernel Components">
<Controller.Console /> <Controller.Console />
<Properties /> <Properties />
<SourceCode />
<ASTview /> <ASTview />
<ASTinfo /> <ASTinfo />
<Locations /> <Locations />
......
...@@ -16,16 +16,27 @@ import React from 'react'; ...@@ -16,16 +16,27 @@ import React from 'react';
import * as Settings from 'dome/data/settings'; import * as Settings from 'dome/data/settings';
import * as Forms from 'dome/layout/forms'; import * as Forms from 'dome/layout/forms';
export const Theme = new Settings.GString('ASTview.theme', 'default'); export const THEMES = [
export const FontSize = new Settings.GNumber('ASTview.fontSize', 12); { id: 'default', label: 'Default' },
{ id: 'ambiance', label: 'Ambiance' },
{ id: 'solarized light', label: 'Solarized Light' },
{ id: 'solarized dark', label: 'Solarized Dark' },
];
// --------------------------------------------------------------------------
// --- AST View Preferences
// --------------------------------------------------------------------------
export const ThemeASTview = new Settings.GString('ASTview.theme', 'default');
export const FontSizeASTview = new Settings.GNumber('ASTview.fontSize', 12);
const ASTviewPrefs = () => { const ASTviewPrefs = () => {
const theme = Forms.useDefined(Forms.useValid( const theme = Forms.useDefined(Forms.useValid(
Settings.useGlobalSettings(Theme), Settings.useGlobalSettings(ThemeASTview),
)); ));
const font = Forms.useValid( const font = Forms.useValid(
Settings.useGlobalSettings(FontSize), Settings.useGlobalSettings(FontSizeASTview),
); );
return ( return (
...@@ -54,6 +65,55 @@ const ASTviewPrefs = () => { ...@@ -54,6 +65,55 @@ const ASTviewPrefs = () => {
); );
}; };
// --------------------------------------------------------------------------
// --- Source Code Preferences
// --------------------------------------------------------------------------
export const ThemeSC = new Settings.GString('SourceCode.theme', 'default');
export const FontSizeSC = new Settings.GNumber('SourceCode.fontSize', 12);
const SourceCodePrefs = () => {
const theme = Forms.useDefined(Forms.useValid(
Settings.useGlobalSettings(ThemeSC),
));
const font = Forms.useValid(
Settings.useGlobalSettings(FontSizeSC),
);
return (
<Forms.Page>
<Forms.Section label="Source Code" unfold>
<Forms.SelectField
state={theme}
label="Theme"
title="Set the color theme of the source code"
>
<option value="default" label="Default" />
<option value="ambiance" label="Ambiance" />
<option value="solarized light" label="Solarized light" />
<option value="solarized dark" label="Solarized dark" />
</Forms.SelectField>
<Forms.SliderField
state={font}
label="Font Size"
title="Set the font size of the source code"
min={8}
max={32}
step={2}
/>
</Forms.Section>
</Forms.Page>
);
};
// --------------------------------------------------------------------------
// --- Export Components
// --------------------------------------------------------------------------
export default (() => ( export default (() => (
<ASTviewPrefs /> <>
<ASTviewPrefs />
<SourceCodePrefs />
</>
)); ));
// --------------------------------------------------------------------------
// --- Source Code
// --------------------------------------------------------------------------
import React from 'react';
import _ from 'lodash';
import * as States from 'frama-c/states';
import * as Dome from 'dome';
import { readFile } from 'dome/system';
import * as Json from 'dome/data/json';
import * as Settings from 'dome/data/settings';
import { RichTextBuffer } from 'dome/text/buffers';
import { Text } from 'dome/text/editors';
import { IconButton } from 'dome/controls/buttons';
import { Component, TitleBar } from 'frama-c/LabViews';
import { functions, markerInfo } from 'frama-c/api/kernel/ast';
import { source } from 'frama-c/api/kernel/services';
import 'codemirror/mode/clike/clike';
import 'codemirror/theme/ambiance.css';
import 'codemirror/theme/solarized.css';
import 'codemirror/addon/selection/active-line';
import 'codemirror/addon/dialog/dialog.css';
import 'codemirror/addon/dialog/dialog';
import 'codemirror/addon/search/searchcursor';
import 'codemirror/addon/search/search';
import 'codemirror/addon/search/jump-to-line';
import { THEMES, ThemeSC, FontSizeSC } from './Preferences';
// --------------------------------------------------------------------------
// --- Pretty Printing (Browser Console)
// --------------------------------------------------------------------------
const D = new Dome.Debug('Source Code');
// --------------------------------------------------------------------------
// --- Rich Text Printer
// --------------------------------------------------------------------------
async function loadSourceCode(buffer: RichTextBuffer, sloc: source) {
const { file, line } = sloc;
try {
const content = await readFile(file);
buffer.setValue(content);
buffer.scroll(line);
buffer.getDoc().setCursor(line);
} catch (err) {
D.error(`Fail to load source code file ${file}.`);
}
}
// --------------------------------------------------------------------------
// --- Source Code Printer
// --------------------------------------------------------------------------
const SourceCode = () => {
// Hooks
const buffer = React.useMemo(() => new RichTextBuffer(), []);
const [selection] = States.useSelection();
const [theme, setTheme] = Settings.useGlobalSettings(ThemeSC);
const [fontSize, setFontSize] = Settings.useGlobalSettings(FontSizeSC);
const [wrapText, flipWrapText] = Dome.useFlipSettings('SourceCode.wrapText');
const markersInfo = States.useSyncArray(markerInfo);
const fcts = States.useSyncArray(functions).getArray();
const theFunction = selection?.current?.function;
const currentFunction = React.useRef<string | undefined>();
const theMarker = selection?.current?.marker;
const currentMarker = React.useRef<string | undefined>();
// Hook: async loading
React.useEffect(() => {
if (theMarker && currentMarker.current !== theMarker) {
currentMarker.current = theMarker;
const markerId = (theMarker as Json.key<'#markerInfo'>);
const markerIdInfo = markersInfo.getData(markerId);
if (markerIdInfo) {
loadSourceCode(buffer, markerIdInfo.sloc);
}
} else if (theFunction && currentFunction.current !== theFunction) {
currentFunction.current = theFunction;
const currentFunctionData = _.find(fcts, (e) => e.name === theFunction);
if (currentFunctionData) {
loadSourceCode(buffer, currentFunctionData.sloc);
}
} else
buffer.clear();
}, [buffer, fcts, markersInfo, theFunction, theMarker]);
// Callbacks
const zoomIn = () => fontSize < 48 && setFontSize(fontSize + 2);
const zoomOut = () => fontSize > 4 && setFontSize(fontSize - 2);
// Theme Popup
const selectTheme = (id?: string) => id && setTheme(id);
const themeItem = (th: { id: string; label: string }) => (
{ checked: th.id === theme, ...th }
);
const themePopup = () => Dome.popupMenu(THEMES.map(themeItem), selectTheme);
// Component
return (
<>
<TitleBar>
<IconButton
icon="ZOOM.OUT"
onClick={zoomOut}
disabled={!theFunction}
title="Decrease font size"
/>
<IconButton
icon="ZOOM.IN"
onClick={zoomIn}
disabled={!theFunction}
title="Increase font size"
/>
<IconButton
icon="PAINTBRUSH"
onClick={themePopup}
title="Choose theme"
/>
<IconButton
icon="WRAPTEXT"
selected={wrapText}
onClick={flipWrapText}
title="Wrap text"
/>
</TitleBar>
<Text
buffer={buffer}
mode="text/x-csrc"
theme={theme}
fontSize={fontSize}
lineWrapping={wrapText}
selection={theMarker}
lineNumbers={!!theFunction}
readOnly
styleActiveLine={!!theFunction}
extraKeys={{ 'Alt-F': 'findPersistent' }}
/>
</>
);
};
// --------------------------------------------------------------------------
// --- Export Component
// --------------------------------------------------------------------------
export default () => (
<Component
id="frama-c.sourcecode"
label="Source Code"
title="Original source code"
>
<SourceCode />
</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