find_call eagerly

This commit is contained in:
Dmitry Vasilev
2023-08-02 05:59:49 +03:00
parent 800c301cc1
commit adbca074c2
9 changed files with 144 additions and 304 deletions

View File

@@ -2,7 +2,7 @@ import {map_accum, map_find, map_object, stringify, findLast} from './utils.js'
import {is_eq, find_error_origin_node} from './ast_utils.js' import {is_eq, find_error_origin_node} from './ast_utils.js'
import {find_node, find_leaf, ancestry_inc} from './ast_utils.js' import {find_node, find_leaf, ancestry_inc} from './ast_utils.js'
import {color} from './color.js' import {color} from './color.js'
import {eval_frame, eval_expand_calltree_node, eval_find_call} from './eval.js' import {eval_frame, eval_expand_calltree_node} from './eval.js'
export const pp_calltree = tree => ({ export const pp_calltree = tree => ({
id: tree.id, id: tree.id,
@@ -82,8 +82,17 @@ export const is_native_fn = calltree_node =>
export const active_frame = state => export const active_frame = state =>
state.frames[state.active_calltree_node.id] state.frames[state.active_calltree_node.id]
const get_calltree_node_by_loc = (state, node) => export const get_calltree_node_by_loc = (state, index) => {
state.calltree_node_by_loc const nodes_of_module = state.calltree_node_by_loc.get(state.current_module)
if(nodes_of_module == null) {
return null
} else {
return nodes_of_module.get(index)
}
}
const get_selected_calltree_node_by_loc = (state, node) =>
state.selected_calltree_node_by_loc
?.[state.current_module] ?.[state.current_module]
?.[ ?.[
state.parse_result.modules[state.current_module] == node state.parse_result.modules[state.current_module] == node
@@ -93,13 +102,13 @@ const get_calltree_node_by_loc = (state, node) =>
: node.index : node.index
] ]
const add_calltree_node_by_loc = (state, loc, node_id) => { const add_selected_calltree_node_by_loc = (state, loc, node_id) => {
return { return {
...state, ...state,
calltree_node_by_loc: selected_calltree_node_by_loc:
{...state.calltree_node_by_loc, {...state.selected_calltree_node_by_loc,
[loc.module]: { [loc.module]: {
...state.calltree_node_by_loc?.[loc.module], ...state.selected_calltree_node_by_loc?.[loc.module],
[loc.index ?? -1]: node_id [loc.index ?? -1]: node_id
} }
} }
@@ -111,18 +120,11 @@ export const set_active_calltree_node = (
active_calltree_node, active_calltree_node,
current_calltree_node = state.current_calltree_node, current_calltree_node = state.current_calltree_node,
) => { ) => {
const result = { return {
...state, ...state,
active_calltree_node, active_calltree_node,
current_calltree_node, current_calltree_node,
} }
// TODO currently commented, required to implement livecoding second and
// subsequent fn calls
/*
// Record last_good_state every time active_calltree_node changes
return {...result, last_good_state: result}
*/
return result
} }
export const add_frame = ( export const add_frame = (
@@ -142,7 +144,8 @@ export const add_frame = (
} else { } else {
with_frame = state with_frame = state
} }
const result = add_calltree_node_by_loc( // TODO only add if it is not the same
const result = add_selected_calltree_node_by_loc(
with_frame, with_frame,
calltree_node_loc(active_calltree_node), calltree_node_loc(active_calltree_node),
active_calltree_node.id, active_calltree_node.id,
@@ -571,18 +574,21 @@ export const find_call = (state, index) => {
return state return state
} }
const ct_node_id = get_calltree_node_by_loc(state, node) const selected_ct_node_id = get_selected_calltree_node_by_loc(state, node)
if(ct_node_id === null) { /*
TODO remove because it interferes with find_call deferred calls
if(selected_ct_node_id === null) {
// strict compare (===) with null, to check if we put null earlier to // strict compare (===) with null, to check if we put null earlier to
// designate that fn is not reachable // designate that fn is not reachable
return set_active_calltree_node(state, null) return set_active_calltree_node(state, null)
} }
*/
if(ct_node_id != null) { if(selected_ct_node_id != null) {
const ct_node = find_node( const ct_node = find_node(
state.calltree, state.calltree,
n => n.id == ct_node_id n => n.id == selected_ct_node_id
) )
if(ct_node == null) { if(ct_node == null) {
throw new Error('illegal state') throw new Error('illegal state')
@@ -604,69 +610,22 @@ export const find_call = (state, index) => {
return state return state
} }
const loc = {index: node.index, module: state.current_module} const ct_node_id = get_calltree_node_by_loc(state, node.index)
// First try to find node among existing calltree nodes if(ct_node_id == null) {
const call = find_node(state.calltree, node => return set_active_calltree_node(state, null)
true
&& node.fn != null
&& node.fn.__location != null
&& node.fn.__location.index == loc.index
&& node.fn.__location.module == loc.module
)
let next_calltree, active_calltree_node
if(call != null) {
if(call.has_more_children) {
active_calltree_node = eval_expand_calltree_node(
// TODO copy eval_cxt?
state.eval_cxt,
state.parse_result,
call
)
next_calltree = replace_calltree_node(
state.calltree,
call,
active_calltree_node
)
} else {
active_calltree_node = call
next_calltree = state.calltree
}
} else {
const find_result = eval_find_call(
// TODO copy eval_cxt?
state.eval_cxt,
state.parse_result,
state.calltree,
loc
)
if(find_result == null) {
return add_calltree_node_by_loc(
// Remove active_calltree_node
// current_calltree_node may stay not null, because it is calltree node
// explicitly selected by user in calltree view
set_active_calltree_node(state, null),
loc,
null
)
} }
active_calltree_node = find_result.call const ct_node = find_node(
next_calltree = replace_calltree_node(
state.calltree, state.calltree,
find_node(state.calltree, n => n.id == find_result.node.id), n => n.id == ct_node_id
find_result.node,
) )
if(ct_node == null) {
throw new Error('illegal state')
} }
return add_frame( return add_frame(
expand_path( expand_path(state, ct_node),
{...state, calltree: next_calltree}, ct_node,
active_calltree_node
),
active_calltree_node,
) )
} }

View File

@@ -11,10 +11,10 @@ import {
root_calltree_node, root_calltree_module, make_calltree, root_calltree_node, root_calltree_module, make_calltree,
get_deferred_calls, get_deferred_calls,
calltree_commands, calltree_commands,
add_frame, calltree_node_loc, expand_path, add_frame, calltree_node_loc, get_calltree_node_by_loc, expand_path,
initial_calltree_node, default_expand_path, toggle_expanded, active_frame, initial_calltree_node, default_expand_path, toggle_expanded, active_frame,
find_call, find_call_node, set_active_calltree_node, find_call, find_call_node, set_active_calltree_node,
set_cursor_position, current_cursor_position, set_location set_cursor_position, current_cursor_position, set_location,
} from './calltree.js' } from './calltree.js'
const collect_logs = (logs, call) => { const collect_logs = (logs, call) => {
@@ -45,6 +45,8 @@ const apply_eval_result = (state, eval_result) => {
return { return {
...state, ...state,
calltree: make_calltree(eval_result.calltree, null), calltree: make_calltree(eval_result.calltree, null),
calltree_node_by_loc: eval_result.calltree_node_by_loc,
// TODO copy eval_cxt?
eval_cxt: eval_result.eval_cxt, eval_cxt: eval_result.eval_cxt,
logs: { logs: {
logs: collect_logs(eval_result.logs, eval_result.calltree), logs: collect_logs(eval_result.logs, eval_result.calltree),
@@ -95,6 +97,7 @@ const run_code = (s, dirty_files) => {
calltree_node_is_expanded: null, calltree_node_is_expanded: null,
frames: null, frames: null,
calltree_node_by_loc: null, calltree_node_by_loc: null,
selected_calltree_node_by_loc: null,
selection_state: null, selection_state: null,
loading_external_imports_state: null, loading_external_imports_state: null,
value_explorer: null, value_explorer: null,
@@ -185,75 +188,38 @@ const external_imports_loaded = (
} }
} }
const node = find_call_node(state, current_cursor_position(state))
let toplevel, result
if(
// edit module that is not imported (maybe recursively by state.entrypoint)
// TODO if module not imported, then do not run code on edit at all // TODO if module not imported, then do not run code on edit at all
node == null const result = eval_modules(
||
node.type == 'do' /* toplevel AST node */
) {
result = eval_modules(
state.parse_result, state.parse_result,
external_imports, external_imports,
state.on_deferred_call, state.on_deferred_call,
state.calltree_changed_token, state.calltree_changed_token,
state.io_trace, state.io_trace,
) )
toplevel = true
} else {
result = eval_modules(
state.parse_result,
external_imports,
state.on_deferred_call,
state.calltree_changed_token,
state.io_trace,
{index: node.index, module: state.current_module},
)
toplevel = false
}
if(result.then != null) { if(result.then != null) {
return {...state, return {...state,
eval_modules_state: { eval_modules_state: { promise: result }
promise: result, node, toplevel,
}
} }
} else { } else {
return eval_modules_finished(state, state, result, node, toplevel) return eval_modules_finished(state, state, result)
} }
} }
const eval_modules_finished = (state, prev_state, result, node, toplevel) => { const eval_modules_finished = (state, prev_state, result) => {
if(state.calltree_changed_token != prev_state.calltree_changed_token) { if(state.calltree_changed_token != prev_state.calltree_changed_token) {
// code was modified after prev vesion of code was executed, discard // code was modified after prev vesion of code was executed, discard
return state return state
} }
const next = apply_eval_result(state, result)
let active_calltree_node const next = find_call(
apply_eval_result(state, result),
if(toplevel) { current_cursor_position(state)
if(node == state.parse_result.modules[root_calltree_module(next)]) { )
active_calltree_node = root_calltree_node(next)
} else {
active_calltree_node = null
}
} else {
if(result.call == null) {
// Unreachable call
active_calltree_node = null
} else {
active_calltree_node = result.call
}
}
let result_state let result_state
if(active_calltree_node == null) { if(next.active_calltree_node == null) {
const {node, state: next2} = initial_calltree_node(next) const {node, state: next2} = initial_calltree_node(next)
result_state = set_active_calltree_node(next2, null, node) result_state = set_active_calltree_node(next2, null, node)
} else { } else {
@@ -261,10 +227,10 @@ const eval_modules_finished = (state, prev_state, result, node, toplevel) => {
default_expand_path( default_expand_path(
expand_path( expand_path(
next, next,
active_calltree_node next.active_calltree_node
) )
), ),
active_calltree_node, next.active_calltree_node,
) )
} }
@@ -883,7 +849,7 @@ const open_app_window = (state, globals) => {
}) })
} }
const get_initial_state = (state, entrypoint_settings) => { const get_initial_state = (state, entrypoint_settings, cursor_pos = 0) => {
const with_files = state.project_dir == null const with_files = state.project_dir == null
? state ? state
: load_files(state, state.project_dir) : load_files(state, state.project_dir)
@@ -892,7 +858,7 @@ const get_initial_state = (state, entrypoint_settings) => {
return { return {
...with_settings, ...with_settings,
cursor_position_by_file: {[with_settings.current_module]: 0}, cursor_position_by_file: {[with_settings.current_module]: cursor_pos},
} }
} }

View File

@@ -209,7 +209,7 @@ export const color = frame => {
export const color_file = (state, file) => export const color_file = (state, file) =>
Object Object
.values(state.calltree_node_by_loc?.[file] ?? {}) .values(state.selected_calltree_node_by_loc?.[file] ?? {})
// node_id == null means it is unreachable, so do not color // node_id == null means it is unreachable, so do not color
.filter(node_id => node_id != null) .filter(node_id => node_id != null)
.map(node_id => state.frames[node_id].coloring) .map(node_id => state.frames[node_id].coloring)

4
src/effects.js vendored
View File

@@ -185,8 +185,6 @@ export const apply_side_effects = (prev, next, command, ui) => {
exec('eval_modules_finished', exec('eval_modules_finished',
next, /* becomes prev_state */ next, /* becomes prev_state */
result, result,
s.node,
s.toplevel
) )
}) })
} }
@@ -251,7 +249,7 @@ export const apply_side_effects = (prev, next, command, ui) => {
ui.calltree.render_select_node(prev, next) ui.calltree.render_select_node(prev, next)
} }
if(prev.calltree_node_by_loc != next.calltree_node_by_loc) { if(prev.selected_calltree_node_by_loc != next.selected_calltree_node_by_loc) {
render_coloring(ui, next) render_coloring(ui, next)
} }

View File

@@ -17,7 +17,7 @@ import {has_toplevel_await} from './find_definitions.js'
// import runtime as external because it has non-functional code // import runtime as external because it has non-functional code
// external // external
import {run, do_eval_expand_calltree_node, do_eval_find_call} from './runtime.js' import {run, do_eval_expand_calltree_node} from './runtime.js'
// TODO: fix error messages. For example, "__fn is not a function" // TODO: fix error messages. For example, "__fn is not a function"
@@ -331,9 +331,6 @@ export const eval_modules = (
// TODO use native array for stack for perf? stack contains booleans // TODO use native array for stack for perf? stack contains booleans
stack: new Array(), stack: new Array(),
searched_location: location,
found_call: null,
is_recording_deferred_calls: false, is_recording_deferred_calls: false,
on_deferred_call: (call, calltree_changed_token, logs) => { on_deferred_call: (call, calltree_changed_token, logs) => {
return on_deferred_call( return on_deferred_call(
@@ -352,15 +349,12 @@ export const eval_modules = (
const make_result = result => { const make_result = result => {
const calltree = assign_code(parse_result.modules, result.calltree) const calltree = assign_code(parse_result.modules, result.calltree)
const call = result.call == null
? null
: find_node(calltree, node => node.id == result.call.id)
return { return {
modules: result.modules, modules: result.modules,
logs: result.logs, logs: result.logs,
eval_cxt: result.eval_cxt, eval_cxt: result.eval_cxt,
calltree, calltree,
call, calltree_node_by_loc: result.eval_cxt.calltree_node_by_loc,
io_trace: result.eval_cxt.io_trace, io_trace: result.eval_cxt.io_trace,
} }
} }
@@ -372,19 +366,6 @@ export const eval_modules = (
} }
} }
export const eval_find_call = (cxt, parse_result, calltree, location) => {
const result = do_eval_find_call(cxt, calltree, location)
if(result == null) {
return null
}
const {node, call} = result
const node_with_code = assign_code(parse_result.modules, node)
const call_with_code = find_node(node_with_code, n => n.id == call.id)
return {
node: node_with_code,
call: call_with_code,
}
}
export const eval_expand_calltree_node = (cxt, parse_result, node) => { export const eval_expand_calltree_node = (cxt, parse_result, node) => {
return assign_code( return assign_code(

View File

@@ -45,11 +45,13 @@ const do_run = function*(module_fns, cxt, io_trace){
// TODO group all io_trace_ properties to single object? // TODO group all io_trace_ properties to single object?
? {...cxt, ? {...cxt,
logs: [], logs: [],
calltree_node_by_loc: new Map(),
io_trace_is_recording: true, io_trace_is_recording: true,
io_trace: [], io_trace: [],
} }
: {...cxt, : {...cxt,
logs: [], logs: [],
calltree_node_by_loc: new Map(),
io_trace_is_recording: false, io_trace_is_recording: false,
io_trace, io_trace,
io_trace_is_replay_aborted: false, io_trace_is_replay_aborted: false,
@@ -63,13 +65,16 @@ const do_run = function*(module_fns, cxt, io_trace){
apply_promise_patch(cxt) apply_promise_patch(cxt)
set_current_context(cxt) set_current_context(cxt)
for(let {module, fn} of module_fns) { for(let i = 0; i < module_fns.length; i++) {
cxt.found_call = null const {module, fn} = module_fns[i]
cxt.is_entrypoint = i == module_fns.length - 1
cxt.children = null cxt.children = null
calltree = { calltree = {
toplevel: true, toplevel: true,
module, module,
id: cxt.call_counter++ id: ++cxt.call_counter,
} }
try { try {
@@ -98,14 +103,9 @@ const do_run = function*(module_fns, cxt, io_trace){
remove_promise_patch(cxt) remove_promise_patch(cxt)
cxt.searched_location = null
const call = cxt.found_call
cxt.found_call = null
return { return {
modules: cxt.modules, modules: cxt.modules,
calltree, calltree,
call,
logs: _logs, logs: _logs,
eval_cxt: cxt, eval_cxt: cxt,
} }
@@ -170,23 +170,6 @@ export const set_record_call = cxt => {
} }
} }
const do_expand_calltree_node = (cxt, node) => {
if(node.fn.__location != null) {
// fn is hosted, it created call, this time with children
const result = cxt.children[0]
result.id = node.id
result.children = cxt.prev_children
result.has_more_children = false
return result
} else {
// fn is native, it did not created call, only its child did
return {...node,
children: cxt.children,
has_more_children: false,
}
}
}
export const do_eval_expand_calltree_node = (cxt, node) => { export const do_eval_expand_calltree_node = (cxt, node) => {
cxt.is_recording_deferred_calls = false cxt.is_recording_deferred_calls = false
cxt.children = null cxt.children = null
@@ -199,79 +182,28 @@ export const do_eval_expand_calltree_node = (cxt, node) => {
} catch(e) { } catch(e) {
// do nothing. Exception was caught and recorded inside '__trace' // do nothing. Exception was caught and recorded inside '__trace'
} }
cxt.is_recording_deferred_calls = true cxt.is_recording_deferred_calls = true
const result = do_expand_calltree_node(cxt, node) const children = cxt.children
cxt.children = null cxt.children = null
if(node.fn.__location != null) {
// fn is hosted, it created call, this time with children
const result = children[0]
result.id = node.id
result.children = cxt.prev_children
result.has_more_children = false
return result return result
} else {
// fn is native, it did not created call, only its child did
return {...node,
children: children,
has_more_children: false,
}
}
} }
/*
Try to find call of function with given 'location'
Function is synchronous, because we recorded calltree nodes for all async
function calls. Here we walk over calltree, find leaves that have
'has_more_children' set to true, and rerunning fns in these leaves with
'searched_location' being set, until we find find call or no children
left.
We dont rerun entire execution because we want find_call to be
synchronous for simplicity
*/
export const do_eval_find_call = (cxt, calltree, location) => {
// TODO always cleanup children when work finished, not before it started
cxt.children = null
const do_find = node => {
if(node.children != null) {
for(let c of node.children) {
const result = do_find(c)
if(result != null) {
return result
}
}
// call was not find in children, return null
return null
}
if(node.has_more_children) {
try {
if(node.is_new) {
new node.fn(...node.args)
} else {
node.fn.apply(node.context, node.args)
}
} catch(e) {
// do nothing. Exception was caught and recorded inside '__trace'
}
if(cxt.found_call != null) {
return {
node: do_expand_calltree_node(cxt, node),
call: cxt.found_call,
}
} else {
cxt.children = null
}
}
// node has no children, return null
return null
}
cxt.is_recording_deferred_calls = false
cxt.searched_location = location
const result = do_find(calltree)
cxt.children = null
cxt.searched_location = null
cxt.found_call = null
cxt.is_recording_deferred_calls = true
return result
}
const __do_await = async (cxt, value) => { const __do_await = async (cxt, value) => {
// children is an array of child calls for current function call. But it // children is an array of child calls for current function call. But it
@@ -309,19 +241,19 @@ const __trace = (cxt, fn, name, argscount, __location, get_closure) => {
cxt.children = null cxt.children = null
cxt.stack.push(false) cxt.stack.push(false)
const is_found_call = const call_id = ++cxt.call_counter
(cxt.searched_location != null && cxt.found_call == null)
&&
(
__location.index == cxt.searched_location.index
&&
__location.module == cxt.searched_location.module
)
if(is_found_call) { // populate calltree_node_by_loc only for entrypoint module
// Assign temporary value to prevent nested calls from populating if(cxt.is_entrypoint) {
// found_call let nodes_of_module = cxt.calltree_node_by_loc.get(__location.module)
cxt.found_call = {} if(nodes_of_module == null) {
nodes_of_module = new Map()
cxt.calltree_node_by_loc.set(__location.module, nodes_of_module)
}
if(nodes_of_module.get(__location.index) == null) {
set_record_call(cxt)
nodes_of_module.set(__location.index, call_id)
}
} }
let ok, value, error let ok, value, error
@@ -351,7 +283,7 @@ const __trace = (cxt, fn, name, argscount, __location, get_closure) => {
cxt.prev_children = cxt.children cxt.prev_children = cxt.children
const call = { const call = {
id: cxt.call_counter++, id: call_id,
ok, ok,
value, value,
error, error,
@@ -362,11 +294,6 @@ const __trace = (cxt, fn, name, argscount, __location, get_closure) => {
: args.slice(0, argscount), : args.slice(0, argscount),
} }
if(is_found_call) {
cxt.found_call = call
set_record_call(cxt)
}
const should_record_call = cxt.stack.pop() const should_record_call = cxt.stack.pop()
if(should_record_call) { if(should_record_call) {
@@ -453,7 +380,7 @@ const __trace_call = (cxt, fn, context, args, errormessage, is_new = false) => {
cxt.prev_children = cxt.children cxt.prev_children = cxt.children
const call = { const call = {
id: cxt.call_counter++, id: ++cxt.call_counter,
ok, ok,
value, value,
error, error,

View File

@@ -69,8 +69,9 @@ console.time('run')
const i = test_initial_state( const i = test_initial_state(
{}, // files {}, // files
undefined,
{project_dir: dir},
{entrypoint: 'test/run.js'}, {entrypoint: 'test/run.js'},
{project_dir: dir}
) )
assert_equal(i.loading_external_imports_state != null, true) assert_equal(i.loading_external_imports_state != null, true)

View File

@@ -1426,7 +1426,8 @@ export const tests = [
] ]
) )
const step_into = COMMANDS.calltree.click(initial, 1) const x_call = root_calltree_node(initial).children[0]
const step_into = COMMANDS.calltree.click(initial, x_call.id)
assert_equal( assert_equal(
color_file(step_into, '').sort((a,b) => a.index - b.index), color_file(step_into, '').sort((a,b) => a.index - b.index),
[ [
@@ -1479,7 +1480,8 @@ export const tests = [
x()` x()`
const initial = test_initial_state(code) const initial = test_initial_state(code)
const step_into = COMMANDS.calltree.click(initial, 1) const x_call = root_calltree_node(initial).children[0]
const step_into = COMMANDS.calltree.click(initial, x_call.id)
assert_equal( assert_equal(
color_file(step_into, '').sort((c1, c2) => c1.index - c2.index), color_file(step_into, '').sort((c1, c2) => c1.index - c2.index),
@@ -1563,6 +1565,20 @@ const y = x()`
) )
}), }),
test('coloring function body after move inside', () => {
const code = `
const x = () => {
1
}
x()
`
const i = test_initial_state(code)
const moved = COMMANDS.move_cursor(i, code.indexOf('1'))
const coloring = color_file(moved, '')
const color_body = coloring.find(c => c.index == code.indexOf('('))
assert_equal(color_body.result.ok, true)
}),
test('better parse errors', () => { test('better parse errors', () => {
const code = ` const code = `
const x = z => { const x = z => {
@@ -1634,7 +1650,7 @@ const y = x()`
assert_equal(res.result.value, 6) assert_equal(res.result.value, 6)
assert_equal( assert_equal(
n.calltree_node_by_loc[''][edited.indexOf('foo =>')] == null, n.calltree_node_by_loc.get('').get(edited.indexOf('foo =>')) == null,
false false
) )
}), }),
@@ -1786,8 +1802,8 @@ const y = x()`
countdown(10) countdown(10)
`) `)
const first = root_calltree_node(s).children[0] const first = root_calltree_node(s).children[0]
assert_equal(first.children, undefined) assert_equal(first.children[0].children, undefined)
assert_equal(first.has_more_children, true) assert_equal(first.children[0].has_more_children, true)
assert_equal(first.value, 10) assert_equal(first.value, 10)
const s2 = COMMANDS.calltree.click(s, first.id) const s2 = COMMANDS.calltree.click(s, first.id)
const first2 = root_calltree_node(s2).children[0] const first2 = root_calltree_node(s2).children[0]
@@ -1816,7 +1832,6 @@ const y = x()`
test('expand_calltree_node native', () => { test('expand_calltree_node native', () => {
const s = test_initial_state(`[1,2,3].map(x => x + 1)`) const s = test_initial_state(`[1,2,3].map(x => x + 1)`)
const map = root_calltree_node(s).children[0] const map = root_calltree_node(s).children[0]
assert_equal(map.children, null)
const s2 = COMMANDS.calltree.click(s, map.id) const s2 = COMMANDS.calltree.click(s, map.id)
const map_expanded = root_calltree_node(s2).children[0] const map_expanded = root_calltree_node(s2).children[0]
assert_equal(map_expanded.children.length, 3) assert_equal(map_expanded.children.length, 3)
@@ -1854,18 +1869,11 @@ const y = x()`
y([1,2,3]) y([1,2,3])
` `
const assert_loc = (s, substring, is_assert_node_by_loc) => { const assert_loc = (s, substring) => {
const state = COMMANDS.calltree.arrow_right(s) const state = COMMANDS.calltree.arrow_right(s)
const index = code.indexOf(substring) const index = code.indexOf(substring)
assert_equal(current_cursor_position(state), index) assert_equal(current_cursor_position(state), index)
if(is_assert_node_by_loc) {
assert_equal(
state.calltree_node_by_loc[''][index] == null,
false
)
}
assert_equal(active_frame(state) != null, true) assert_equal(active_frame(state) != null, true)
return state return state
} }
@@ -1876,7 +1884,7 @@ const y = x()`
const s2 = assert_loc(s1, 'y([') const s2 = assert_loc(s1, 'y([')
// Expand call of `y()` // Expand call of `y()`
const s3 = assert_loc(s2, 'arr =>', true) const s3 = assert_loc(s2, 'arr =>')
// Select call of arr.map // Select call of arr.map
const s4 = assert_loc(s3, 'arr.map') const s4 = assert_loc(s3, 'arr.map')
@@ -1886,8 +1894,7 @@ const y = x()`
const s5 = assert_loc(s4, 'arr.map') const s5 = assert_loc(s4, 'arr.map')
// Select call of x // Select call of x
const s6 = assert_loc(s5, 'foo =>', true) const s6 = assert_loc(s5, 'foo =>')
}), }),
test('jump_calltree select callsite', () => { test('jump_calltree select callsite', () => {
@@ -1918,6 +1925,14 @@ const y = x()`
assert_equal(good.code.index, code.indexOf('() => {/*good')) assert_equal(good.code.index, code.indexOf('() => {/*good'))
}), }),
test('jump_calltree select another call of the same fn', () => {
const code = '[1,2].map(x => x*10)'
const i = test_initial_state(code, code.indexOf('10'))
assert_equal(i.value_explorer.result.value, 10)
const second_iter = COMMANDS.calltree.arrow_down(i)
const moved = COMMANDS.move_cursor(second_iter, code.indexOf('x*10'))
assert_equal(moved.value_explorer.result.value, 20)
}),
test('unwind_stack', () => { test('unwind_stack', () => {
const s = test_initial_state(` const s = test_initial_state(`
@@ -2079,10 +2094,6 @@ const y = x()`
assert_equal(node.children.length, 3) assert_equal(node.children.length, 3)
assert_equal(node.code != null, true) assert_equal(node.code != null, true)
// Siblings are not expanded
assert_equal(node.children[0].has_more_children, true)
assert_equal(node.children[2].has_more_children, true)
return find_target(node.children[1], i + 1) return find_target(node.children[1], i + 1)
} }
@@ -2091,8 +2102,6 @@ const y = x()`
assert_equal(target.args, [10]) assert_equal(target.args, [10])
const target2 = target.children[0] const target2 = target.children[0]
// Target is expanded, but only one level deep
assert_equal(target2.has_more_children, true)
}), }),
test('find_call error', () => { test('find_call error', () => {
@@ -2567,6 +2576,8 @@ const y = x()`
'' : `import {x} from 'x'; x()`, '' : `import {x} from 'x'; x()`,
'x' : `export const x = () => 1; x()`, 'x' : `export const x = () => 1; x()`,
}, },
undefined,
undefined,
{ {
entrypoint: '', entrypoint: '',
current_module: 'x', current_module: 'x',

View File

@@ -102,7 +102,7 @@ export const assert_code_error_async = async (codestring, error) => {
assert_equal(result.error, error) assert_equal(result.error, error)
} }
export const test_initial_state = (code, entrypoint_settings, other) => { export const test_initial_state = (code, cursor_pos, other, entrypoint_settings) => {
return COMMANDS.open_app_window( return COMMANDS.open_app_window(
COMMANDS.get_initial_state( COMMANDS.get_initial_state(
{ {
@@ -113,7 +113,8 @@ export const test_initial_state = (code, entrypoint_settings, other) => {
entrypoint: '', entrypoint: '',
current_module: '', current_module: '',
...entrypoint_settings, ...entrypoint_settings,
} },
cursor_pos
), ),
new Set(Object.getOwnPropertyNames(globalThis.app_window)) new Set(Object.getOwnPropertyNames(globalThis.app_window))
) )
@@ -127,8 +128,6 @@ export const test_initial_state_async = async code => {
s, s,
s, s,
result, result,
s.eval_modules_state.node,
s.eval_modules_state.toplevel
) )
} }
@@ -139,8 +138,6 @@ export const command_input_async = async (...args) => {
after_input, after_input,
after_input, after_input,
result, result,
after_input.eval_modules_state.node,
after_input.eval_modules_state.toplevel
) )
} }