Skip to content

Commit 0746f35

Browse files
committed
refactor(watches): try to be more coroutine friendly
Previous implementation would rely on a callback to keep the memory up to date with the view. This is an attempt to avoid having the refresh tied to the eval method (usually the refresh is called separately). Additionally, this implements #120
1 parent 2036cd6 commit 0746f35

File tree

8 files changed

+91
-33
lines changed

8 files changed

+91
-33
lines changed

docs/src/routes/api/+page.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,11 @@ Calls `require("dap-view").open()` if there's no views window. Else, behaves lik
3434

3535
```lua
3636
---@param expr? string
37-
require("dap-view").add_expr(expr)
37+
---@param default_expanded? boolean
38+
require("dap-view").add_expr(expr, default_expanded)
3839
```
3940

40-
In normal mode, adds the expression under the cursor to the watch list (see [caveats](faq#Why-is-DapViewWatch-not-adding-the-whole-variable-)). In visual mode, adds the selection to the watch list. If `expr` is specified, adds the expression directly, overriding previous conditions.
41+
In normal mode, adds the expression under the cursor to the watch list (see [caveats](faq#Why-is-DapViewWatch-not-adding-the-whole-variable-)). In visual mode, adds the selection to the watch list. If `expr` is specified, adds the expression directly, overriding previous conditions. Expressions are expanded (non recursively). This behavior can be overridden by setting `default_expanded` to false.
4142

4243
## Jump To View
4344

lua/dap-view.lua

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@ M.toggle = function(hide_terminal)
2626
end
2727

2828
---@param expr? string
29-
M.add_expr = function(expr)
30-
actions.add_expr(expr)
29+
---@param default_expanded? boolean
30+
M.add_expr = function(expr, default_expanded)
31+
actions.add_expr(expr, default_expanded == nil or default_expanded)
3132
end
3233

3334
---@param view dapview.Section

lua/dap-view/actions.lua

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,15 @@ M.open = function(hide_terminal)
115115
end
116116

117117
---@param expr? string
118-
M.add_expr = function(expr)
119-
local final_expr = expr or require("dap-view.util.exprs").get_current_expr()
120-
if require("dap-view.watches.actions").add_watch_expr(final_expr) then
121-
require("dap-view.views").switch_to_view("watches")
122-
end
118+
---@param default_expanded boolean
119+
M.add_expr = function(expr, default_expanded)
120+
local expr_ = expr or require("dap-view.util.exprs").get_current_expr()
121+
coroutine.wrap(function()
122+
local co = coroutine.running()
123+
if require("dap-view.watches.actions").add_watch_expr(expr_, default_expanded, co) then
124+
require("dap-view.views").switch_to_view("watches")
125+
end
126+
end)()
123127
end
124128

125129
---@param view dapview.Section|string

lua/dap-view/listeners.lua

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ local scopes = require("dap-view.scopes.view")
66
local sessions = require("dap-view.sessions.view")
77
local util = require("dap-view.util")
88
local term = require("dap-view.console.view")
9-
local eval = require("dap-view.watches.eval")
109
local setup = require("dap-view.setup")
1110
local refresher = require("dap-view.refresher")
1211
local winbar = require("dap-view.options.winbar")
@@ -48,7 +47,7 @@ dap.listeners.on_session[SUBSCRIPTION_ID] = function(_, new)
4847
end
4948
end
5049
if new.stopped_thread_id then
51-
eval.reevaluate_all_expressions()
50+
refresher.refresh_all_expressions()
5251
end
5352
end
5453
else
@@ -118,7 +117,7 @@ dap.listeners.after.scopes[SUBSCRIPTION_ID] = function(session)
118117

119118
-- Do not use `event_stopped`
120119
-- It may cause race conditions
121-
eval.reevaluate_all_expressions()
120+
refresher.refresh_all_expressions()
122121
end
123122

124123
local continue = { "event_continued", "continue" }
@@ -167,7 +166,7 @@ end
167166
local reeval = { "setExpression", "setVariable" }
168167

169168
for _, listener in ipairs(reeval) do
170-
dap.listeners.after[listener][SUBSCRIPTION_ID] = eval.reevaluate_all_expressions
169+
dap.listeners.after[listener][SUBSCRIPTION_ID] = refresher.refresh_all_expressions
171170
end
172171

173172
dap.listeners.after.initialize[SUBSCRIPTION_ID] = function(session)

lua/dap-view/refresher.lua

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ local threads = require("dap-view.threads.view")
33
local scopes = require("dap-view.scopes.view")
44
local sessions = require("dap-view.sessions.view")
55
local exceptions = require("dap-view.exceptions.view")
6+
local eval = require("dap-view.watches.eval")
67
local util = require("dap-view.util")
78

89
local M = {}
@@ -31,4 +32,15 @@ M.refresh_session_based_views = function()
3132
end
3233
end
3334

35+
M.refresh_all_expressions = function()
36+
coroutine.wrap(function()
37+
local co = coroutine.running()
38+
eval.reevaluate_all_expressions(co)
39+
40+
if state.current_section == "watches" then
41+
require("dap-view.views").switch_to_view("watches")
42+
end
43+
end)()
44+
end
45+
3446
return M

lua/dap-view/views/keymaps/views.lua

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,13 @@ M.views_keymaps = function()
2121
elseif state.current_section == "scopes" or state.current_section == "sessions" then
2222
require("dap.ui").trigger_actions({ mode = "first" })
2323
elseif state.current_section == "watches" then
24-
watches_actions.expand_or_collapse(cursor_line)
24+
coroutine.wrap(function()
25+
local co = coroutine.running()
26+
27+
if watches_actions.expand_or_collapse(cursor_line, co) then
28+
require("dap-view.views").switch_to_view("watches")
29+
end
30+
end)()
2531
end
2632

2733
-- Selecting a session triggers a full redraw
@@ -81,7 +87,13 @@ M.views_keymaps = function()
8187
if state.current_section == "watches" then
8288
vim.ui.input({ prompt = "Expression: " }, function(input)
8389
if input then
84-
watches_actions.add_watch_expr(input)
90+
coroutine.wrap(function()
91+
local co = coroutine.running()
92+
93+
if watches_actions.add_watch_expr(input, true, co) then
94+
require("dap-view.views").switch_to_view("watches")
95+
end
96+
end)()
8597
end
8698
end)
8799
end
@@ -109,7 +121,13 @@ M.views_keymaps = function()
109121
if expression_view then
110122
vim.ui.input({ prompt = "Expression: ", default = expression_view.expression }, function(input)
111123
if input then
112-
watches_actions.edit_watch_expr(input, cursor_line)
124+
coroutine.wrap(function()
125+
local co = coroutine.running()
126+
127+
if watches_actions.edit_watch_expr(input, cursor_line, co) then
128+
require("dap-view.views").switch_to_view("watches")
129+
end
130+
end)()
113131
end
114132
end)
115133
end

lua/dap-view/watches/actions.lua

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,16 @@ local set = require("dap-view.watches.set")
66
local M = {}
77

88
---@param expr string
9-
---@return boolean
10-
M.add_watch_expr = function(expr)
9+
---@param default_expanded boolean
10+
---@param co thread
11+
M.add_watch_expr = function(expr, default_expanded, co)
1112
if #expr == 0 or not guard.expect_session() then
1213
return false
1314
end
1415

15-
eval.evaluate_expression(expr)
16+
eval.evaluate_expression(expr, default_expanded, co)
17+
18+
coroutine.yield(co)
1619

1720
state.expr_count = state.expr_count + 1
1821

@@ -25,6 +28,8 @@ M.remove_watch_expr = function(line)
2528

2629
if expression_view then
2730
state.watched_expressions[expression_view.expression] = nil
31+
32+
return expression_view
2833
else
2934
vim.notify("No expression under the under cursor")
3035
end
@@ -113,19 +118,29 @@ end
113118

114119
---@param expr string
115120
---@param line number
116-
M.edit_watch_expr = function(expr, line)
121+
---@param co thread
122+
M.edit_watch_expr = function(expr, line, co)
117123
if #expr == 0 or not guard.expect_session() then
118-
return
124+
return false
119125
end
120126

121127
-- The easiest way to edit is to delete and insert again
122-
M.remove_watch_expr(line)
128+
local expression_view = M.remove_watch_expr(line)
129+
130+
if expression_view == nil then
131+
return false
132+
end
133+
134+
eval.evaluate_expression(expr, expression_view.view.expanded, co)
135+
136+
coroutine.yield(co)
123137

124-
eval.evaluate_expression(expr)
138+
return true
125139
end
126140

127141
---@param line number
128-
M.expand_or_collapse = function(line)
142+
---@param co thread
143+
M.expand_or_collapse = function(line, co)
129144
if not guard.expect_session() then
130145
return
131146
end
@@ -135,7 +150,11 @@ M.expand_or_collapse = function(line)
135150
if expression_view then
136151
expression_view.view.expanded = not expression_view.view.expanded
137152

138-
eval.evaluate_expression(expression_view.expression)
153+
eval.evaluate_expression(expression_view.expression, true, co)
154+
155+
coroutine.yield(co)
156+
157+
return true
139158
else
140159
local variable_reference = state.variable_views_by_line[line]
141160

lua/dap-view/watches/eval.lua

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ local state = require("dap-view.state")
33
local M = {}
44

55
---@param expression string
6-
---@param skip_redraw boolean?
7-
M.evaluate_expression = function(expression, skip_redraw)
6+
---@param default_expanded boolean
7+
---@param co thread?
8+
M.evaluate_expression = function(expression, default_expanded, co)
89
local session = assert(require("dap").session(), "has active session")
910

1011
coroutine.wrap(function()
@@ -35,7 +36,7 @@ M.evaluate_expression = function(expression, skip_redraw)
3536
response = response,
3637
err = err,
3738
updated = false,
38-
expanded = true,
39+
expanded = default_expanded,
3940
children = nil,
4041
}
4142

@@ -59,8 +60,8 @@ M.evaluate_expression = function(expression, skip_redraw)
5960

6061
state.watched_expressions[expression] = new_expression_view
6162

62-
if state.current_section == "watches" and not skip_redraw then
63-
require("dap-view.views").switch_to_view("watches")
63+
if co then
64+
coroutine.resume(co)
6465
end
6566
end)()
6667
end
@@ -161,16 +162,19 @@ M.expand_variable = function(variables_reference, previous_expansion_result)
161162
return #varible_views > 0 and varible_views or nil, err
162163
end
163164

164-
M.reevaluate_all_expressions = function()
165+
---@param co thread?
166+
M.reevaluate_all_expressions = function(co)
165167
local i = 0
166168
local size = vim.tbl_count(state.watched_expressions)
167169

168170
for expr, _ in pairs(state.watched_expressions) do
169171
i = i + 1
170172

171-
-- Avoid needless redrawing by waiting for the last expression
172-
M.evaluate_expression(expr, i < size)
173+
-- This assumes that the last evaluation will be the last one to fnish, but I'm not sure that's the case?
174+
M.evaluate_expression(expr, true, i == size and co or nil)
173175
end
176+
177+
coroutine.yield(co)
174178
end
175179

176180
return M

0 commit comments

Comments
 (0)