mirror of
https://github.com/leporello-js/leporello-js
synced 2026-01-13 21:14:28 -08:00
find_call eagerly
This commit is contained in:
111
src/calltree.js
111
src/calltree.js
@@ -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,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
70
src/cmd.js
70
src/cmd.js
@@ -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},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
4
src/effects.js
vendored
@@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
23
src/eval.js
23
src/eval.js
@@ -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(
|
||||||
|
|||||||
141
src/runtime.js
141
src/runtime.js
@@ -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
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
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
|
cxt.children = null
|
||||||
|
|
||||||
const do_find = node => {
|
if(node.fn.__location != null) {
|
||||||
if(node.children != null) {
|
// fn is hosted, it created call, this time with children
|
||||||
for(let c of node.children) {
|
const result = children[0]
|
||||||
const result = do_find(c)
|
result.id = node.id
|
||||||
if(result != null) {
|
result.children = cxt.prev_children
|
||||||
|
result.has_more_children = false
|
||||||
return result
|
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 {
|
} else {
|
||||||
node.fn.apply(node.context, node.args)
|
// fn is native, it did not created call, only its child did
|
||||||
|
return {...node,
|
||||||
|
children: children,
|
||||||
|
has_more_children: false,
|
||||||
}
|
}
|
||||||
} 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,
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
57
test/test.js
57
test/test.js
@@ -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',
|
||||||
|
|||||||
@@ -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
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user