Skip to content

Commit d922f58

Browse files
committed
feat: allow evaluating whole buffer
feat(attach_to_buffer): add user command
1 parent c9084be commit d922f58

13 files changed

Lines changed: 458 additions & 86 deletions

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ watch = '*.lua'
99

1010
.PHONY: api_documentation luacheck stylua test
1111

12-
all: test luacheck
12+
all: test luacheck stylua
1313

1414
api_documentation:
1515
nvim -u scripts/make_api_documentation/init.lua -l scripts/make_api_documentation/main.lua

README.md

Lines changed: 132 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
**lua-console.nvim** - is a handy scratch pad / REPL / debug console for Lua development and Neovim exploration and configuration.
44
Acts as a user friendly replacement of command mode - messages loop and as a handy scratch pad to store and test your code gists.
55

6-
<img src="doc/demo.gif">
6+
***Update: Although it originated as a tool for Lua development, it has now evolved into supporting other languages too. See [`evaluating other languages`](#evaluating-other-languages).***
7+
8+
<br/><img src="doc/demo.gif">
79

810
## 💡 Motivation
911

@@ -13,15 +15,15 @@ syntax error and retyping the whole thing again, copying the paths from error st
1315

1416
## ✨ Features
1517

16-
- Evaluate single line expressions
17-
- Evaluate visually selected lines of code
18+
- Evaluate single line expressions and statements, visually selected lines of code or the whole buffer
1819
- Pretty print Lua objects, including function details and their source paths
19-
- Show normal and error output in the console, including output of `print()`, errors and stacktraces.
20+
- Show normal and error output in the console/buffer, including output of `print()`, errors and stacktraces.
2021
- Syntax highlighting and autocompletion
2122
- Load Neovim’s messages into console for inspection and copy/paste
2223
- Open links from stacktraces and function sources
23-
- Save / Load console session
24-
- Use as scratch pad for code gists
24+
- Save / Load / Autosave console session
25+
- Use as a scratch pad for code gists
26+
- Attach code evaluators to any buffer
2527

2628

2729
## 📦 Installation
@@ -35,43 +37,48 @@ return {
3537
}
3638
```
3739
otherwise, install with your favorite package manager and add
38-
`require('lua-console').setup({ your_custom_options })` somewhere in your config.
40+
`require('lua-console').setup { custom_options }` somewhere in your config.
3941

4042

4143
## ⚙️ Configuration
4244

4345
> [!NOTE]
44-
> All settings are very straight forward, but please read below about [`preserve_context`](#-notes-on-globals-locals-and-preserving-execution-context) option.
46+
> All settings are self explanatory, but please read below about [`preserve_context`](#-notes-on-globals-locals-and-preserving-execution-context) option.
4547
46-
Mappings are local to the console, except the one for toggling, which is - `` ` `` by default.
48+
Mappings are local to the console, except the ones for toggling the console - `` ` `` and attaching to a buffer - ``<Leader>` ``. All mappings can be overridden in your custom
49+
config. If you want to delete a mapping - set its value to `false`.
4750

4851
<details><summary>Default Settings</summary>
4952

5053
<!-- config:start -->
51-
54+
`config.lua`
5255
```lua
5356
opts = {
5457
buffer = {
55-
prepend_result_with = '=> ',
58+
result_prefix = '=> ',
5659
save_path = vim.fn.stdpath('state') .. '/lua-console.lua',
57-
load_on_start = true, -- load saved session on first entry
58-
preserve_context = true -- preserve context between executions
60+
autosave = true, -- autosave on console hide / close
61+
load_on_start = true, -- load saved session on start
62+
preserve_context = true, -- preserve results between evaluations
5963
},
6064
window = {
61-
border = 'double', -- single|double|rounded
65+
border = 'double', -- single|double|rounded
6266
height = 0.6, -- percentage of main window
6367
},
6468
mappings = {
6569
toggle = '`',
70+
attach = '<Leader>`',
6671
quit = 'q',
6772
eval = '<CR>',
73+
eval_buffer = '<S-CR>',
74+
open = 'gf',
6875
messages = 'M',
6976
save = 'S',
7077
load = 'L',
7178
resize_up = '<C-Up>',
7279
resize_down = '<C-Down>',
7380
help = '?'
74-
}
81+
},
7582
}
7683
```
7784
<!-- config:end -->
@@ -84,7 +91,7 @@ opts = {
8491
- Install, press the mapped key `` ` `` and start exploring.
8592
- Enter code as normal, in insert mode.
8693
- Hit `Enter` in normal mode to evaluate a variable, statement or an expression in the current line.
87-
- Visually select a range of lines and press `Enter` to evaluate the code in the range.
94+
- Visually select a range of lines and press `Enter` to evaluate the code in the range or use `<S-Enter>` to evaluate the whole console.
8895
- The evaluation of the last line is returned and printed, so no `return` is needed in most cases.
8996
To avoid noise, if the return of your execution is `nil`, e.g. from a loop or a function without return, it will not be printed, but shown as virtual text.
9097
The result of assignments on the last line will be also shown as virtual text.
@@ -97,22 +104,23 @@ opts = {
97104
example, you can jump to its current definition, while Lsp/tags would take you to the original one.
98105

99106
- Press `M` to load Neovim messages into the console.
100-
- Use `S` and `L` to save / load the console session to preserve history of your hacking.
107+
- Use `S` and `L` to save / load the console session to preserve history of your hacking. If the `autosave` option is on, the contents of the console will be
108+
saved whenever it is toggled or closed.
101109
- You can resize the console with `<C-Up>` and `<C-Down>`.
102110

103111

104112
## 📓 Notes on globals, locals and preserving execution context
105113

106114
> [!IMPORTANT]
107-
> By default, the option `preserve_context` is on, which means that the context is preserved between executions.
115+
> By default, the option `preserve_context` is on, which means that the execution context is preserved between evaluations.
108116
109-
All the code executed in the console is evaluated in isolated environment. This means that any variables you declare will not be persisted in Neovim's global
110-
environment, although all global variables are accessible. If you want purposefully to alter the global state, use `_G.My_variable = ..`.
117+
All the code executed in the console is evaluated in isolated environment. This means that any variables you declare without the `local` keyword will not be persisted
118+
in Neovim's global environment, although all global variables are accessible. If you want purposefully to alter the global state, use `_G.My_variable = ..`.
111119

112-
The option `preserve_context` means that if you assign variables without `local`, they will be stored in console's local context and preserved between separate executions.
120+
The option `preserve_context` means that although you declare variables without `local`, they will be stored in console's local context and preserved between separate executions.
113121
So, if you first execute `a = 1`, then `a = a + 1` and then `a` - you will get `2`. Variables with `local` are not preserved.
114122

115-
If you want a classic REPL experience, when the context is cleared on every execution, set `preserve_context = false`.
123+
If you want the context to be cleared before every execution, set `preserve_context = false`.
116124

117125
There are two functions available within the console:
118126

@@ -124,27 +132,117 @@ There are two functions available within the console:
124132

125133
### Attaching code evaluator to other buffers
126134

127-
- The evaluator behind the console can be attached to any buffer by calling or mapping `require('lua-console.utils).attach(buf_number)` where `buf_number` can be omitted for current buffer.
128-
You will be able to evaluate the code as in the console and follow links. The evaluators and their contexts are isolated for each attached buffer.
129-
- You can also setup external code runners for languages other than lua.
130-
135+
- The evaluator behind the console can be attached/detached to any buffer by pressing ``<Leader>` `` or executing command `LuaConsole AttachToggle`.
136+
You will be able to evaluate the code in the buffer as in the console and follow links. The evaluators and their contexts are isolated for each attached buffer.
131137

132138
### Evaluating other languages
133139

134-
- It is possible to setup external code executors for other languages. There are examples for `ruby, racket` in the `exev_config.lua`. `Python, go, rust` - are WIP.
135-
- The language is determined either from the buffer type, which you can set manually with `vim.bo.filetype = 'lang'` or by prefixing your code with `===lang` on the line above.
136-
The prefix can be changed in the config, e.g.
140+
#### Setting up
141+
142+
- It is possible to setup external code executors for other languages. Evaluators for `ruby` and `racket` are working out of the box, support for other languages is coming.
143+
Meanwhile, you can easily setup your own language.
144+
- Below is the default configuration which can be overridden or extended by your custom config (`default_process_opts` will be
145+
replaced by language specific opts), e.g. a possible config for `python` could be:
146+
147+
```lua
148+
require('lua-console').setup {
149+
external_evaluators = {
150+
python = {
151+
cmd = { 'python3', '-c' },
152+
env = { PYTHONPATH = '~/projects' },
153+
timeout = 100000,
154+
formatter = function(result) do_something; return result end,
155+
},
156+
}
157+
}
158+
```
159+
160+
- You can also setup a custom formatter to format the evaluator output before appending results to the console or buffer. Example is in the config.
161+
162+
<details><summary>Default External Evaluators Settings</summary>
163+
164+
<!-- config:start -->
165+
`exev_config.lua`
166+
```lua
167+
---Formats the output of external evaluator
168+
---@param result string[]
169+
---@return string[]
170+
local function generic_formatter(result)
171+
local width = vim.o.columns
172+
local sep_start = ('='):rep(width)
173+
local sep_end = ('='):rep(width)
174+
175+
table.insert(result, 1, sep_start)
176+
table.insert(result, sep_end)
177+
178+
return result
179+
end
180+
181+
local external_evaluators = {
182+
lang_prefix = '===',
183+
default_process_opts = {
184+
cwd = nil,
185+
env = { EMPTY = '' },
186+
clear_env = false,
187+
stdin = false,
188+
stdout = false,
189+
stderr = false,
190+
text = true,
191+
timeout = nil,
192+
detach = false,
193+
on_exit = nil,
194+
},
195+
196+
ruby = {
197+
cmd = { 'ruby', '-e' },
198+
env = { RUBY_VERSION = '3.3.0' },
199+
code_prefix = '$stdout.sync = true;',
200+
formatter = generic_formatter,
201+
},
202+
203+
racket = {
204+
cmd = { 'racket', '-e' },
205+
formatter = generic_formatter,
206+
},
207+
}
137208

209+
return external_evaluators
138210
```
211+
<!-- config:end -->
212+
213+
</details>
214+
215+
#### Usage
216+
217+
- The language evaluator is determined either from (in order of precedence):
218+
219+
- The code prefix `===lang` on the line above your code snippet, in which case it only applies to the snippet directly below and it should be included in the selection
220+
for evaluation. The prefix can be changed in the config.
221+
- The code prefix on the top line of the console/buffer, in which case it applies to the whole buffer.
222+
- The file type of the buffer.
223+
<br/>
224+
225+
```racket
226+
===racket
227+
228+
229+
(define (log str)
230+
(displayln (format "~v" str)))
231+
232+
233+
(define (fact n)
234+
(if (= n 0)
235+
1
236+
(* n (fact (- n 1)))))
237+
238+
(displayln (fact 111))
239+
```
240+
241+
```ruby
139242
===ruby
140243
5.times { puts 'Hey' }
141244
```
142245

143-
- You can also setup a custom formatter to format the executor output before appending results to the console or buffer. Example is in the config.
144-
- Unlike lua, the context with external evaluators is not preserved yet, but it is WIP.
145-
u
146-
147-
148246
## Alternatives and comparison
149247

150248
There are a number of alternatives available, notably:

lua/lua-console.lua

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ local get_or_create_buffer = function()
2323
vim.diagnostic.enable(false, { bufnr = buf })
2424

2525
mappings.set_console_mappings(buf)
26-
mappings.set_evaluator_mappings(buf)
26+
mappings.set_evaluator_mappings(buf, true)
2727
mappings.set_console_autocommands(buf)
2828

2929
if config.buffer.load_on_start then utils.load_saved_console(buf) end
@@ -78,10 +78,8 @@ local setup = function(opts)
7878
mappings = require('lua-console.mappings')
7979
utils = require('lua-console.utils')
8080

81-
vim.keymap.set('n', config.mappings.toggle, '', {
82-
callback = toggle_console,
83-
desc = 'Toggle Lua console',
84-
})
81+
mappings.set_global_mappings()
82+
mappings.set_console_commands()
8583

8684
return config
8785
end

lua/lua-console/config.lua

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ local default_config = {
44
buffer = {
55
result_prefix = '=> ',
66
save_path = vim.fn.stdpath('state') .. '/lua-console.lua',
7-
autosave = true,
7+
autosave = true, -- autosave on console hide / close
88
load_on_start = true, -- load saved session on start
9-
preserve_context = true,
9+
preserve_context = true, -- preserve results between evaluations
1010
},
1111
window = {
1212
relative = 'editor',
@@ -20,8 +20,10 @@ local default_config = {
2020
},
2121
mappings = {
2222
toggle = '`',
23+
attach = '<Leader>`',
2324
quit = 'q',
2425
eval = '<CR>',
26+
eval_buffer = '<S-CR>',
2527
open = 'gf',
2628
messages = 'M',
2729
save = 'S',

lua/lua-console/exev_config.lua

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
---@param result string[]
33
---@return string[]
44
local function generic_formatter(result)
5-
local sep_start = ('='):rep(vim.o.columns)
6-
local sep_end = ('='):rep(vim.o.columns)
5+
local width = vim.o.columns
6+
local sep_start = ('='):rep(width)
7+
local sep_end = ('='):rep(width)
78

89
table.insert(result, 1, sep_start)
910
table.insert(result, sep_end)
@@ -37,6 +38,11 @@ local external_evaluators = {
3738
cmd = { 'racket', '-e' },
3839
formatter = generic_formatter,
3940
},
41+
42+
python = {
43+
cmd = { 'python3', '-c' },
44+
formatter = generic_formatter,
45+
},
4046
}
4147

4248
return external_evaluators

0 commit comments

Comments
 (0)