mirror of
https://github.com/leporello-js/leporello-js
synced 2026-01-13 13:04:30 -08:00
finish record io
This commit is contained in:
@@ -41,6 +41,17 @@ const is_stackoverflow = node =>
|
||||
// Firefox
|
||||
node.error?.message == "too much recursion"
|
||||
|
||||
export const has_error = n =>
|
||||
!n.ok
|
||||
||
|
||||
(
|
||||
n.value instanceof globalThis.run_window.Promise
|
||||
&&
|
||||
n.value.status != null
|
||||
&&
|
||||
!n.value.status.ok
|
||||
)
|
||||
|
||||
export const calltree_node_loc = node => node.toplevel
|
||||
? {module: node.module}
|
||||
: node.fn.__location
|
||||
|
||||
@@ -3,7 +3,8 @@ import {el, stringify, fn_link, scrollIntoViewIfNeeded} from './domutils.js'
|
||||
import {FLAGS} from '../feature_flags.js'
|
||||
import {stringify_for_header} from './value_explorer.js'
|
||||
import {find_node} from '../ast_utils.js'
|
||||
import {is_expandable, root_calltree_node, get_deferred_calls} from '../calltree.js'
|
||||
import {is_expandable, root_calltree_node, get_deferred_calls, has_error}
|
||||
from '../calltree.js'
|
||||
|
||||
// TODO perf - quadratic difficulty
|
||||
const join = arr => arr.reduce(
|
||||
@@ -13,17 +14,6 @@ const join = arr => arr.reduce(
|
||||
[],
|
||||
)
|
||||
|
||||
const is_error = n =>
|
||||
!n.ok
|
||||
||
|
||||
(
|
||||
n.value instanceof globalThis.run_window.Promise
|
||||
&&
|
||||
n.value.status != null
|
||||
&&
|
||||
!n.value.status.ok
|
||||
)
|
||||
|
||||
export class CallTree {
|
||||
constructor(ui, container) {
|
||||
this.ui = ui
|
||||
@@ -46,9 +36,11 @@ export class CallTree {
|
||||
this.ui.editor.focus()
|
||||
}
|
||||
|
||||
/* TODO test
|
||||
if(e.key == 'F3') {
|
||||
this.ui.set_active_tab('logs')
|
||||
}
|
||||
*/
|
||||
|
||||
if(e.key == 'a') {
|
||||
if(FLAGS.embed_value_explorer) {
|
||||
@@ -119,7 +111,7 @@ export class CallTree {
|
||||
)
|
||||
: el('span',
|
||||
'call_header '
|
||||
+ (is_error(n) ? 'error' : '')
|
||||
+ (has_error(n) ? 'error' : '')
|
||||
+ (n.fn.__location == null ? ' native' : '')
|
||||
,
|
||||
// TODO show `this` argument
|
||||
|
||||
@@ -297,7 +297,7 @@ export class Editor {
|
||||
this.ace_editor.commands.removeCommand('goToNextError')
|
||||
|
||||
|
||||
this.ace_editor.commands.bindKey("F4", "goto_definition");
|
||||
this.ace_editor.commands.bindKey("F5", "goto_definition");
|
||||
VimApi._mapCommand({
|
||||
keys: 'gd',
|
||||
type: 'action',
|
||||
|
||||
45
src/editor/io_cache.js
Normal file
45
src/editor/io_cache.js
Normal file
@@ -0,0 +1,45 @@
|
||||
import {header, stringify_for_header} from './value_explorer.js'
|
||||
import {el} from './domutils.js'
|
||||
import {has_error} from '../calltree.js'
|
||||
|
||||
// TODO render grey items there were not used in run
|
||||
|
||||
export class IO_Cache {
|
||||
constructor(ui, el) {
|
||||
this.el = el
|
||||
this.ui = ui
|
||||
|
||||
this.el.addEventListener('keydown', (e) => {
|
||||
|
||||
if(e.key == 'Escape') {
|
||||
this.ui.editor.focus()
|
||||
}
|
||||
|
||||
if(e.key == 'F4') {
|
||||
this.ui.editor.focus()
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
render_io_cache(items) {
|
||||
this.el.innerHTML = ''
|
||||
for(let item of items) {
|
||||
if(item.type == 'resolution') {
|
||||
continue
|
||||
}
|
||||
this.el.appendChild(
|
||||
el('div',
|
||||
'call_header ' + (has_error(item) ? 'error' : ''),
|
||||
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())
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,9 +22,11 @@ export class Logs {
|
||||
this.ui.editor.focus_value_explorer(this.el)
|
||||
}
|
||||
|
||||
/* TODO test
|
||||
if(e.key == 'F2') {
|
||||
this.ui.set_active_tab('calltree')
|
||||
}
|
||||
*/
|
||||
|
||||
if(e.key == 'F3') {
|
||||
this.ui.editor.focus()
|
||||
@@ -75,6 +77,7 @@ export class Logs {
|
||||
+ ':'
|
||||
),
|
||||
' ',
|
||||
// TODO fn_link, for function args, like in ./calltree.js
|
||||
log.args.map(a => header(a)).join(', ')
|
||||
)
|
||||
)
|
||||
|
||||
@@ -3,6 +3,7 @@ import {Editor} from './editor.js'
|
||||
import {Files} from './files.js'
|
||||
import {CallTree} from './calltree.js'
|
||||
import {Logs} from './logs.js'
|
||||
import {IO_Cache} from './io_cache.js'
|
||||
import {Eval} from './eval.js'
|
||||
import {el} from './domutils.js'
|
||||
import {FLAGS} from '../feature_flags.js'
|
||||
@@ -41,6 +42,12 @@ export class UI {
|
||||
href: 'javascript: void(0)',
|
||||
}, 'Logs (F3)')
|
||||
),
|
||||
this.tabs.io_cache = el('div', 'tab',
|
||||
el('a', {
|
||||
click: () => this.set_active_tab('io_cache'),
|
||||
href: 'javascript: void(0)',
|
||||
}, 'IO cache (F4)')
|
||||
),
|
||||
this.entrypoint_select = el('div', 'entrypoint_select')
|
||||
),
|
||||
this.debugger.calltree = el('div', {
|
||||
@@ -51,6 +58,10 @@ export class UI {
|
||||
'class': 'tab_content logs',
|
||||
tabindex: 0,
|
||||
}),
|
||||
this.debugger.io_cache = el('div', {
|
||||
'class': 'tab_content io_cache',
|
||||
tabindex: 0,
|
||||
}),
|
||||
),
|
||||
this.debugger_loading = el('div', 'debugger_wrapper')
|
||||
),
|
||||
@@ -74,11 +85,19 @@ export class UI {
|
||||
*/
|
||||
|
||||
el('a', {
|
||||
'class': 'open_run_window',
|
||||
'class': 'statusbar_action first',
|
||||
href: 'javascript: void(0)',
|
||||
click: () => exec('clear_io_cache')
|
||||
},
|
||||
'Clear IO cache (F6)'
|
||||
),
|
||||
|
||||
el('a', {
|
||||
'class': 'statusbar_action',
|
||||
href: 'javascript: void(0)',
|
||||
click: this.open_run_window,
|
||||
},
|
||||
'(Re)open run window (F6)'
|
||||
'(Re)open run window (F7)'
|
||||
),
|
||||
|
||||
this.options = el('div', 'options',
|
||||
@@ -137,13 +156,21 @@ export class UI {
|
||||
this.set_active_tab('logs')
|
||||
}
|
||||
|
||||
if(e.key == 'F5'){
|
||||
this.fullscreen_editor()
|
||||
if(e.key == 'F4'){
|
||||
this.set_active_tab('io_cache')
|
||||
}
|
||||
|
||||
if(e.key == 'F6'){
|
||||
exec('clear_io_cache')
|
||||
}
|
||||
|
||||
if(e.key == 'F7'){
|
||||
this.open_run_window()
|
||||
}
|
||||
|
||||
if(e.key == 'F8'){
|
||||
this.fullscreen_editor()
|
||||
}
|
||||
})
|
||||
|
||||
if(!FLAGS.embed_value_explorer) {
|
||||
@@ -161,6 +188,7 @@ export class UI {
|
||||
|
||||
this.calltree = new CallTree(this, this.debugger.calltree)
|
||||
this.logs = new Logs(this, this.debugger.logs)
|
||||
this.io_cache = new IO_Cache(this, this.debugger.io_cache)
|
||||
|
||||
// TODO jump to another module
|
||||
// TODO use exec
|
||||
@@ -184,6 +212,7 @@ export class UI {
|
||||
}
|
||||
|
||||
set_active_tab(tab_id, skip_focus = false) {
|
||||
this.active_tab = tab_id
|
||||
Object.values(this.tabs).forEach(el => el.classList.remove('active'))
|
||||
this.tabs[tab_id].classList.add('active')
|
||||
Object.values(this.debugger).forEach(el => el.style.display = 'none')
|
||||
@@ -272,8 +301,15 @@ export class UI {
|
||||
|
||||
this.debugger_loading.style = 'display: none'
|
||||
this.debugger_loaded.style = ''
|
||||
|
||||
this.calltree.render_calltree(state)
|
||||
this.logs.render_logs(null, state.logs)
|
||||
|
||||
// render lazily
|
||||
// TODO
|
||||
//if(this.active_tab == 'io_cache') {
|
||||
this.io_cache.render_io_cache(state.io_cache)
|
||||
//}
|
||||
}
|
||||
|
||||
render_problems(problems) {
|
||||
@@ -327,14 +363,18 @@ export class UI {
|
||||
['Focus console logs', 'F3'],
|
||||
['Navigate console logs', '↑ ↓ or jk'],
|
||||
['Leave console logs', 'F3 or Esc'],
|
||||
['Jump to definition', 'F4', 'gd'],
|
||||
['Focus IO cache', 'F4'],
|
||||
['Leave IO cache', 'F4 or Esc'],
|
||||
['Jump to definition', 'F5', 'gd'],
|
||||
['Expand selection to eval expression', 'Ctrl-↓ or Ctrl-j'],
|
||||
['Collapse selection', 'Ctrl-↑ or Ctrl-k'],
|
||||
['Step into call', 'Ctrl-i', '\\i'],
|
||||
['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'],
|
||||
['Expand/collapse editor to fullscreen', 'F5'],
|
||||
['Clear IO cache', 'F6'],
|
||||
['(Re)open run window (F7)', 'F7'],
|
||||
['Expand/collapse editor to fullscreen', 'F8'],
|
||||
]
|
||||
return el('dialog', 'help_dialog',
|
||||
el('table', 'help',
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// TODO show Errors in red
|
||||
// TODO fns as clickable links (jump to definition), both for header and for
|
||||
// content
|
||||
// TODO show constructor.name in header?
|
||||
|
||||
import {el, stringify, scrollIntoViewIfNeeded} from './domutils.js'
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import {set_record_call} from './runtime.js'
|
||||
|
||||
// TODO remove all console.log
|
||||
|
||||
const get_object_to_patch = (cxt, path) => {
|
||||
let obj = cxt.window
|
||||
for(let i = 0; i < path.length - 1; i++) {
|
||||
@@ -25,6 +27,7 @@ const io_patch = (cxt, path, use_context = false) => {
|
||||
// previous run ASAP
|
||||
|
||||
// TODO remove
|
||||
/*
|
||||
console.error('patched method', name, {
|
||||
io_cache_is_recording: cxt.io_cache_is_recording,
|
||||
io_cache_is_replay_aborted: cxt.io_cache_is_replay_aborted,
|
||||
@@ -32,11 +35,7 @@ const io_patch = (cxt, path, use_context = false) => {
|
||||
? cxt.io_cache.length
|
||||
: cxt.io_cache_index
|
||||
})
|
||||
|
||||
// sanity check
|
||||
if(cxt.searched_location != null) {
|
||||
throw new Error('illegal state')
|
||||
}
|
||||
*/
|
||||
|
||||
if(cxt.io_cache_is_replay_aborted) {
|
||||
// Try to finish fast
|
||||
@@ -70,7 +69,8 @@ const io_patch = (cxt, path, use_context = false) => {
|
||||
? new original(...args)
|
||||
: original.apply(this, args)
|
||||
|
||||
console.log('value', value)
|
||||
// TODO remove
|
||||
//console.log('value', value)
|
||||
|
||||
if(value instanceof cxt.window.Promise) {
|
||||
// TODO use cxt.promise_then, not finally which calls
|
||||
@@ -134,12 +134,12 @@ const io_patch = (cxt, path, use_context = false) => {
|
||||
JSON.stringify(call.args) != JSON.stringify(args)
|
||||
)
|
||||
){
|
||||
console.error('DISCARD cache', call)
|
||||
//TODO remove console.error('DISCARD cache', call)
|
||||
cxt.io_cache_is_replay_aborted = true
|
||||
// Try to finish fast
|
||||
throw new Error('io replay aborted')
|
||||
} else {
|
||||
console.log('cached call found', call)
|
||||
// TODO remove console.log('cached call found', call)
|
||||
const next_resolution = cxt.io_cache.find((e, i) =>
|
||||
e.type == 'resolution' && i > cxt.io_cache_index
|
||||
)
|
||||
@@ -190,7 +190,7 @@ const io_patch = (cxt, path, use_context = false) => {
|
||||
} else {
|
||||
resolver(cxt.io_cache[resolution.index].value)
|
||||
}
|
||||
console.log('RESOLVE', cxt.io_cache_index, resolution.index)
|
||||
// TODO remove console.log('RESOLVE', cxt.io_cache_index, resolution.index)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user