diff --git a/src/cmd.js b/src/cmd.js index ca38072..3196f43 100644 --- a/src/cmd.js +++ b/src/cmd.js @@ -534,6 +534,13 @@ const change_html_file = (state, html_file) => { return {...state, html_file} } +const goto_location = (state, loc) => { + return { + state: move_cursor(set_location(state, loc), loc.index), + effects: {type: 'set_focus'}, + } +} + const goto_definition = (state, index) => { if(!state.parse_result.ok){ return {state, effects: {type: 'set_status', args: ['unresolved syntax errors']}} @@ -569,12 +576,7 @@ const goto_definition = (state, index) => { } else { loc = {module: state.current_module, index: d.index} } - return { - state: move_cursor( - {...state, current_module: loc.module}, - loc.index, - ) - } + return goto_location(state, loc) } } } @@ -950,6 +952,7 @@ export const COMMANDS = { change_current_module, change_entrypoint, change_html_file, + goto_location, goto_definition, goto_problem, move_cursor, diff --git a/src/editor/calltree.js b/src/editor/calltree.js index dd68f9d..ef60e75 100644 --- a/src/editor/calltree.js +++ b/src/editor/calltree.js @@ -1,19 +1,11 @@ import {exec} from '../index.js' -import {el, stringify, fn_link, scrollIntoViewIfNeeded} from './domutils.js' +import {el, scrollIntoViewIfNeeded, value_to_dom_el, join} from './domutils.js' import {stringify_for_header} from '../value_explorer_utils.js' import {find_node} from '../ast_utils.js' import {with_version_number} from '../runtime/runtime.js' import {is_expandable, root_calltree_node, get_deferred_calls, has_error} from '../calltree.js' -// TODO perf - quadratic difficulty -const join = arr => arr.reduce( - (acc, el) => acc.length == 0 - ? [el] - : [...acc, ',', el], - [], -) - export class CallTree { constructor(ui, container) { this.ui = ui @@ -111,11 +103,7 @@ export class CallTree { ...join( // for arguments, use n.version_number - last version before call with_version_number(this.state.rt_cxt, n.version_number, () => - n.args.map( - a => typeof(a) == 'function' - ? fn_link(a) - : stringify_for_header(a) - ) + n.args.map(a => value_to_dom_el(a)) ) ), ')' , @@ -124,7 +112,9 @@ export class CallTree { // for return value, use n.last_version_number - last version that was // created during call with_version_number(this.state.rt_cxt, n.last_version_number, () => - (n.ok ? stringify_for_header(n.value) : stringify_for_header(n.error)) + n.ok + ? value_to_dom_el(n.value) + : stringify_for_header(n.error) ) ), ), diff --git a/src/editor/domutils.js b/src/editor/domutils.js index 51ef427..a37b0ae 100644 --- a/src/editor/domutils.js +++ b/src/editor/domutils.js @@ -1,3 +1,6 @@ +import {exec} from '../index.js' +import {stringify_for_header} from '../value_explorer_utils.js' + export function el(tag, className, ...children){ const result = document.createElement(tag) if(typeof(className) == 'string'){ @@ -38,38 +41,40 @@ export function el(tag, className, ...children){ return result } -export function stringify(val){ - function fn_to_str(fn){ - // TODO if name is 'anonymous', then change name for code - return fn.__location == null - ? `${fn.name}` - : `fn ${fn.name}` - } - if(typeof(val) == 'undefined') { - return 'undefined' - } else if(typeof(val) == 'function'){ - return fn_to_str(val) - } else { - return JSON.stringify(val, (key, value) => { - if(typeof(value) == 'function'){ - return fn_to_str(value) - } else { - return value - } - }) - } -} - -export function fn_link(fn){ - const str = stringify(fn) +function fn_link(fn){ + // TODO if name is empty or 'anonymous', then show its source code instead + // of name + const str = fn.__location == null + ? `${fn.name}` + : `fn ${fn.name}` const c = document.createElement('div') c.innerHTML = str - return c.children[0] + const el = c.children[0] + if(fn.__location != null) { + el.addEventListener('click', e => { + e.stopPropagation() + exec('goto_location',fn.__location) + }) + } + return el } +export function value_to_dom_el(value) { + return typeof(value) == 'function' + ? fn_link(value) + : stringify_for_header(value) +} + +export function join(arr, separator = ', ') { + const result = [] + for(let i = 0; i < arr.length; i++) { + result.push(arr[i]) + if(i != arr.length - 1) { + result.push(separator) + } + } + return result +} // Idea is borrowed from: diff --git a/src/editor/editor.js b/src/editor/editor.js index 997e7b0..6a081ad 100644 --- a/src/editor/editor.js +++ b/src/editor/editor.js @@ -1,7 +1,7 @@ import {exec, get_state} from '../index.js' import {ValueExplorer} from './value_explorer.js' import {stringify_for_header} from '../value_explorer_utils.js' -import {el, stringify, fn_link} from './domutils.js' +import {el} from './domutils.js' import {version_number_symbol} from '../calltree.js' /* diff --git a/src/editor/io_trace.js b/src/editor/io_trace.js index d1a924e..f05d026 100644 --- a/src/editor/io_trace.js +++ b/src/editor/io_trace.js @@ -55,7 +55,6 @@ export class IO_Trace { + (is_used ? '' : 'native '), item.name, '(' , - // TODO fn_link, like in ./calltree.js item.args.map(a => header(a)).join(', '), '): ' , (item.ok ? stringify_for_header(item.value) : item.error.toString()) diff --git a/src/editor/logs.js b/src/editor/logs.js index 6dd068a..db3c1f6 100644 --- a/src/editor/logs.js +++ b/src/editor/logs.js @@ -1,4 +1,4 @@ -import {el, scrollIntoViewIfNeeded} from './domutils.js' +import {el, scrollIntoViewIfNeeded, value_to_dom_el, join} from './domutils.js' import {exec} from '../index.js' import {header} from '../value_explorer_utils.js' import {with_version_number_of_log} from '../cmd.js' @@ -72,10 +72,9 @@ export class Logs { + ':' ), ' ', - with_version_number_of_log(state, log, () => - // TODO fn_link, for function args, like in ./calltree.js - log.args.map(a => header(a)).join(', ') - ) + ...join(with_version_number_of_log(state, log, () => + log.args.map(a => value_to_dom_el(a)) + )) ) ) } diff --git a/src/editor/ui.js b/src/editor/ui.js index 4c543ce..b1ae0cd 100644 --- a/src/editor/ui.js +++ b/src/editor/ui.js @@ -171,21 +171,6 @@ export class UI { this.logs = new Logs(this, this.debugger.logs) this.io_trace = new IO_Trace(this, this.debugger.io_trace) - // TODO jump to another module - // TODO use exec - const jump_to_fn_location = (e) => { - let loc - if((loc = e.target.dataset.location) != null){ - loc = JSON.parse(loc) - this.editor.set_cursor_position(loc.index) - this.editor.focus() - } - } - - // TODO when click in calltree, do not jump to location, navigateCallTree - // instead - this.debugger.calltree.addEventListener('click', jump_to_fn_location) - this.render_current_module(state.current_module) this.set_active_tab('calltree', true) diff --git a/src/editor/value_explorer.js b/src/editor/value_explorer.js index d41a21d..d186add 100644 --- a/src/editor/value_explorer.js +++ b/src/editor/value_explorer.js @@ -3,7 +3,7 @@ // TODO fns as clickable links (jump to definition), both for header and for // content -import {el, stringify, scrollIntoViewIfNeeded} from './domutils.js' +import {el, scrollIntoViewIfNeeded} from './domutils.js' import {with_code_execution} from '../index.js' // TODO remove is_expandble, join with displayed entries import {header, short_header, is_expandable, displayed_entries} from '../value_explorer_utils.js'