diff --git a/Makefile b/Makefile
index f9e8f06..149de5c 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,6 @@
set_lua_paths = eval $$(luarocks path --lua-version 5.1 --bin)
busted = $$(find /usr/local/lib/luarocks/*/busted/* -name busted)
+set_luals_path = PATH="$$PATH:/home/yaro/.local/share/nvim/mason/bin/lua-language-server"
test_unit = busted --run=unit
test_nvim = nvim --headless -i NONE -n -u spec/minimal_init.lua -l $(busted) --run=unit
@@ -15,7 +16,7 @@ api_documentation:
nvim -u scripts/make_api_documentation/init.lua -l scripts/make_api_documentation/main.lua
llscheck:
- llscheck --configpath .luarc.json .
+ @$(set_luals_path) && llscheck --configpath .luarc.json .
luacheck:
luacheck lua spec scripts
diff --git a/README.md b/README.md
index fbd8585..3d5fb36 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,8 @@
**lua-console.nvim** - is a handy scratch pad / REPL / debug console for Lua development and Neovim exploration and configuration.
Acts as a user friendly replacement of command mode - messages loop and as a handy scratch pad to store and test your code gists.
-***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).***
+
+***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).***
@@ -52,6 +53,7 @@ config. If you want to delete a mapping - set its value to `false`.
`config.lua`
+
```lua
opts = {
buffer = {
@@ -139,10 +141,10 @@ There are two functions available within the console:
#### Setting up
-- 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.
+- It is possible to setup external code executors for other languages. Evaluators for `ruby`,`racket` and `python` are working out of the box, support for other languages is coming.
Meanwhile, you can easily setup your own language.
-- Below is the default configuration which can be overridden or extended by your custom config (`default_process_opts` will be
- replaced by language specific opts), e.g. a possible config for `python` could be:
+- Below is the default configuration, which can be overridden or extended by your custom config, where `default_process_opts` will be
+ replaced by language specific opts, e.g. a possible config for `python` could be:
```lua
require('lua-console').setup {
@@ -157,12 +159,11 @@ There are two functions available within the console:
}
```
-- 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.
-
Default External Evaluators Settings
`exev_config.lua`
+
```lua
---Formats the output of external evaluator
---@param result string[]
@@ -209,23 +210,24 @@ There are two functions available within the console:
return external_evaluators
```
-
+- 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.
+
+
#### Usage
- The language evaluator is determined either from (in order of precedence):
- - 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
- for evaluation. The prefix can be changed in the config.
+ - The code prefix `===lang` on the line above your code snippet, in which case it only applies to the snippet directly below. The prefix can be changed in the config.
- The code prefix on the top line of the console/buffer, in which case it applies to the whole buffer.
- The file type of the buffer.
+
```racket
===racket
-
(define (log str)
(displayln (format "~v" str)))
@@ -243,6 +245,18 @@ There are two functions available within the console:
5.times { puts 'Hey' }
```
+- Code inside Lua comments will be sytax highlighted.
+
+
+ ```python
+ [[===python
+ list = [1, 3, 5, 7, 9]
+
+ for val in a:
+ print(list)
+ ]]
+ ```
+
## Alternatives and comparison
There are a number of alternatives available, notably:
diff --git a/lua/lua-console.lua b/lua/lua-console.lua
index 47be54d..9cda634 100644
--- a/lua/lua-console.lua
+++ b/lua/lua-console.lua
@@ -1,4 +1,4 @@
-local config, mappings, utils
+local config, mappings, utils, injections
local get_or_create_buffer = function()
--- @type number
@@ -19,6 +19,7 @@ local get_or_create_buffer = function()
vim.api.nvim_buf_set_name(buf, buf_name) -- the name is only needed so the buffer is picked up by Lsp with correct root
+ injections.set_highlighting()
vim.api.nvim_set_option_value('filetype', 'lua', { buf = buf })
vim.diagnostic.enable(false, { bufnr = buf })
@@ -77,6 +78,7 @@ local setup = function(opts)
config = require('lua-console.config').setup(opts)
mappings = require('lua-console.mappings')
utils = require('lua-console.utils')
+ injections = require('lua-console.injections')
mappings.set_global_mappings()
mappings.set_console_commands()
diff --git a/lua/lua-console/injections.lua b/lua/lua-console/injections.lua
new file mode 100644
index 0000000..9c9b478
--- /dev/null
+++ b/lua/lua-console/injections.lua
@@ -0,0 +1,29 @@
+local M = {}
+
+---Allow syntax highlighting for languages embedded in Lua comments
+M.set_highlighting = function()
+ local config = require('lua-console.config')
+ local lang_prefix = config.external_evaluators.lang_prefix
+ local lang_pattern = ('^%s([^\\n]-)\\n.+$'):format(lang_prefix)
+
+ vim.treesitter.query.add_directive('indent!', function(_, _, _, predicate, metadata)
+ local capture_id = predicate[2]
+ if not metadata[capture_id].range then return end
+
+ metadata[capture_id].range[2] = tonumber(predicate[3]) -- set indent col to 0
+ end, { all = true, force = true })
+
+ local query = ([[ ;query
+ ; extends
+ (string
+ content: (string_content) @injection.language @injection.content
+ (#lua-match? @injection.language "^@1")
+ (#gsub! @injection.language "@2" "%1")
+ (#offset! @injection.content 1 0 0 0)
+ (#indent! @injection.content 0))
+ ]]):gsub('@1', lang_prefix):gsub('@2', lang_pattern)
+
+ vim.treesitter.query.set('lua', 'injections', query)
+end
+
+return M
diff --git a/lua/lua-console/utils.lua b/lua/lua-console/utils.lua
index 27acc27..8d44217 100644
--- a/lua/lua-console/utils.lua
+++ b/lua/lua-console/utils.lua
@@ -12,6 +12,7 @@ local to_string = function(tbl, sep, trim)
for _, pat in ipairs(patterns) do
line = line:gsub(pat, '')
end
+ -- compact strings by removing redundant spaces
line = line:gsub('(["\'])%s+', '%1'):gsub('%s+(["\'])', '%1'):gsub('%s%s+', ' ')
end
@@ -22,6 +23,13 @@ local to_table = function(str)
return vim.split(str or '', '\n', { trimempty = true })
end
+local function remove_indentation(tbl)
+ local indent = tbl[1]:match('(%s*)%w') or tbl[1]:match('(\t*)%w')
+ return vim.tbl_map(function(line)
+ return line:sub(#indent + 1)
+ end, tbl)
+end
+
---Shows virtual text in the buffer
---@param buf number buffer
---@param id number namespace id
@@ -60,9 +68,9 @@ local toggle_help = function(buf)
vim.api.nvim_buf_del_extmark(buf, ns, 1)
message =
- [[%s - eval a line or selection, %s - open file, %s - load messages, %s - save console, %s - load console, %s/%s - resize window, %s - toggle help]]
+ [[%s - eval a line or selection, %s - eval buffer, %s - open file, %s - load messages, %s - save console, %s - load console, %s/%s - resize window, %s - toggle help]]
message =
- string.format(message, cm.eval, cm.open, cm.messages, cm.save, cm.load, cm.resize_up, cm.resize_down, cm.help)
+ string.format(message, cm.eval, cm.eval_buffer, cm.open, cm.messages, cm.save, cm.load, cm.resize_up, cm.resize_down, cm.help)
local visible_line = vim.fn.line('w0')
show_virtual_text(buf, 2, message, visible_line - 1, 'overlay', 'Comment')
@@ -131,7 +139,7 @@ local print_buffer = {}
local append_current_buffer = function(buf, lines)
if not lines or #lines == 0 then return end
- local lnum = vim.fn.line('.', vim.fn.bufwinid(buf))
+ local lnum = vim.fn.line('.')
local prefix = config.buffer.result_prefix
local virtual_text
@@ -331,7 +339,7 @@ local get_external_evaluator = function(buf, lang)
return function(lines)
local cmd = vim.tbl_extend('force', {}, lang_config.cmd)
- local code = (lang_config.code_prefix or '') .. to_string(lines)
+ local code = (lang_config.code_prefix or '') .. to_string(remove_indentation(lines)) -- some languages, like python are concerned with indentation
table.insert(cmd, code)
local status, id = pcall(vim.system, cmd, opts, opts.on_exit)
@@ -346,42 +354,33 @@ end
---Determines the language of the code/console/buffer
---mutates lines array to remove the lang_prefix
---@param buf number
----@param lines string[]
+---@param range number[]
---@return string
-local function get_lang(buf, lines)
- local pattern = '^.*' .. config.external_evaluators.lang_prefix .. '(.-)%s*$'
+local function get_lang(buf, range)
+ local pattern = ('^.*' .. config.external_evaluators.lang_prefix .. '(.-)%s*$')
local line, lang
- line = lines[1]
+ line = vim.api.nvim_buf_get_lines(buf, math.max(0, range[1] - 2), range[2], false)[1]
lang = line:match(pattern)
- if lang then
- table.remove(lines, 1)
- return lang
- end
+ if lang then return lang end
- line = vim.fn.getbufline(buf, 1)[1]
+ line = vim.api.nvim_buf_get_lines(buf, 0, 1, false)[1]
lang = line:match(pattern)
if lang then return lang end
return vim.bo[buf].filetype
end
-local get_evaluator = function(buf, lines)
- local evaluator, lang
- lang = get_lang(buf, lines)
+local get_evaluator = function(buf, range)
+ local lang = get_lang(buf, range)
if lang == '' then
vim.notify('Plese specify the language to evaluate or set the filetype', vim.log.levels.WARN)
- return
- end
-
- if lang == 'lua' then
- evaluator = lua_evaluator
+ elseif lang == 'lua' then
+ return lua_evaluator
else
- evaluator = get_external_evaluator(buf, lang)
+ return get_external_evaluator(buf, lang)
end
-
- return evaluator
end
---Evaluates code in the current line or visual selection and appends to buffer
@@ -409,7 +408,7 @@ local eval_code_in_buffer = function(buf, full)
lines = remove_empty_lines(lines)
if #lines == 0 then return end
- local evaluator = get_evaluator(buf, lines)
+ local evaluator = get_evaluator(buf, { v_start, v_end })
if not evaluator then return end
local result = evaluator(lines)
diff --git a/spec/spec_helper.lua b/spec/spec_helper.lua
index e04a472..b06b7e6 100644
--- a/spec/spec_helper.lua
+++ b/spec/spec_helper.lua
@@ -155,6 +155,7 @@ local function compare_strings(str_1, str_2)
if char_1 ~= char_2 then break end
end
+ if not pos then return '' end
pos = pos + 1
local sub_1 = str_1:sub(pos - 5, pos - 1) .. '<< ' .. str_1:sub(pos, pos) .. ' >>' .. str_1:sub(pos + 1, pos + 5)
diff --git a/spec/unit/external_evals_spec.lua b/spec/unit/external_evals_spec.lua
index b4b8ad1..883653c 100644
--- a/spec/unit/external_evals_spec.lua
+++ b/spec/unit/external_evals_spec.lua
@@ -80,7 +80,7 @@ describe('external evaluators', function()
h.set_buffer(buf, content)
- h.send_keys('4gg')
+ h.send_keys('5gg')
h.send_keys('Vj')
utils.eval_code_in_buffer()
@@ -232,6 +232,37 @@ describe('external evaluators', function()
end)
end)
+ it('uses removes indentation from code', function()
+ config.setup {
+ external_evaluators = { ruby = { code_prefix = '' }, }, }
+
+ content = {
+ ' a = [1, 3, 5, 7, 9]',
+ ' for val in a:',
+ ' print(val)'
+ }
+
+ expected = {
+ 'a = [1, 3, 5, 7, 9]',
+ ' for val in a:',
+ ' print(val)'
+ }
+
+ h.set_buffer(buf, content)
+
+ h.send_keys('VG')
+ utils.eval_code_in_buffer()
+
+ assert.stub(vim.system).was.called_with(
+ match.assert_arg(function(arg)
+ result = h.to_table(arg[3])
+ assert.is_same(result, expected)
+ end),
+ _,
+ _
+ )
+ end)
+
it('uses custom formatter to process results', function()
vim.system = vim_system
vim.g._wait_for_spec = false
diff --git a/spec/unit/lua-console_spec.lua b/spec/unit/lua-console_spec.lua
index 9385f3f..67db4d1 100644
--- a/spec/unit/lua-console_spec.lua
+++ b/spec/unit/lua-console_spec.lua
@@ -23,7 +23,7 @@ describe('lua-console.nvim', function()
it('sets up with custom config', function()
config = {
buffer = {
- result_prefix = '$$ ',
+ result_prefix = '$$ '
},
window = {
border = 'single',