From f93287abd0d3556fa16b3fbf0ca87c35e7a07a1b Mon Sep 17 00:00:00 2001 From: Dmitry Vasilev Date: Thu, 26 Oct 2023 09:41:17 +0800 Subject: [PATCH] hotkey to select error origin --- src/ast_utils.js | 5 +++-- src/calltree.js | 37 +++++++++++++++++++++++++++++++++++++ src/editor/calltree.js | 4 ++++ src/editor/ui.js | 1 + test/test.js | 18 ++++++++++++++++++ 5 files changed, 63 insertions(+), 2 deletions(-) diff --git a/src/ast_utils.js b/src/ast_utils.js index 7b20b40..af1bad6 100644 --- a/src/ast_utils.js +++ b/src/ast_utils.js @@ -145,8 +145,9 @@ export const find_node = (node, pred) => { ) } -// TODO refactor, have explicit information if node is error origin, without -// guessing. See also color.js +// TODO refactor, at eval.js we have explicit information if node is error +// origin, without guessing. See also color.js +// TODO result is ok, but value is rejected promise // TODO check if return result is null and throw early export const find_error_origin_node = node => find_node( diff --git a/src/calltree.js b/src/calltree.js index 36f0b8c..387a73a 100644 --- a/src/calltree.js +++ b/src/calltree.js @@ -836,6 +836,42 @@ const select_arguments = (state, with_focus = true) => { } } +const select_error = state => { + const node = [ + state.current_calltree_node, + state.active_calltree_node, + root_calltree_node(state), + // TODO deferred calls??? + ].find(n => n != null && !n.ok) + + if(node == null) { + return {state, effects: [{type: 'set_status', args: ['no error found']}]} + } + + const error_origin = find_node(node, n => + !n.ok + && ( + n.children == null || n.children.every(c => c.ok) + ) + ) + + if(error_origin == null) { + throw new Error('illegal state: error origin not found') + } + + const next = expand_path(add_frame(state, error_origin), error_origin) + const frame = active_frame(next) + const error_node = find_error_origin_node(frame) + + return { + state: set_location(next, { + module: calltree_node_loc(error_origin).module, + index: error_node.index + }), + effects: {type: 'set_focus'} + } +} + const navigate_logs_increment = (state, increment) => { if(state.logs.logs.length == 0) { return {state} @@ -875,6 +911,7 @@ export const calltree_commands = { click, select_return_value, select_arguments, + select_error, navigate_logs_position, navigate_logs_increment, } diff --git a/src/editor/calltree.js b/src/editor/calltree.js index 9224250..9d46162 100644 --- a/src/editor/calltree.js +++ b/src/editor/calltree.js @@ -39,6 +39,10 @@ export class CallTree { exec('calltree.select_arguments') } + if(e.key == 'e') { + exec('calltree.select_error') + } + if(e.key == 'r' || e.key == 'Enter') { exec('calltree.select_return_value') } diff --git a/src/editor/ui.js b/src/editor/ui.js index ec2687a..44b9911 100644 --- a/src/editor/ui.js +++ b/src/editor/ui.js @@ -307,6 +307,7 @@ export class UI { ['Step out of call', 'Ctrl-o', '\\o'], ['When in call tree view, jump to return statement', 'Enter'], ['When in call tree view, jump to function arguments', 'a'], + ['When in call tree view, jump to error origin', 'e'], ['Clear IO trace', 'F6'], ['(Re)open run window (F7)', 'F7'], ] diff --git a/test/test.js b/test/test.js index ae3c0dc..41e5b64 100644 --- a/test/test.js +++ b/test/test.js @@ -2375,6 +2375,24 @@ const y = x()` assert_equal(s3.value_explorer.result, {ok: true, value: ["1"]}) }), + test('select_error', () => { + const code = ` + const deep = x => { + if(x == 10) { + throw new Error() + } else { + deep(x + 1) + } + } + + deep(0) + ` + const i = test_initial_state(code, code.indexOf('deep(x + 1)')) + const {state: found_err_state, effects} = COMMANDS.calltree.select_error(i) + assert_equal(found_err_state.active_calltree_node.args, [10]) + assert_equal(current_cursor_position(found_err_state), code.indexOf('throw')) + }), + test('move_cursor arguments', () => { const code = ` const x = (a, b) => { }