Skip to content

Commit

Permalink
fix!: query down, not up
Browse files Browse the repository at this point in the history
fix: lookaround narratives

fix: don't debounce

refactor: remove last global state

refactor: destructure apis

BREAKING CHANGES:
- require tree-sitter-regexp 0.21
- require nvim 0.10
- replace `narrative.separator` with `narrative.indentation_string`
- change `component.depth` to `component.capture_depth` in `narrative.indentation_string` function
  • Loading branch information
bennypowers committed May 28, 2024
1 parent 3a7303d commit 41c1b90
Show file tree
Hide file tree
Showing 17 changed files with 614 additions and 942 deletions.
36 changes: 14 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ require'regexplainer'.setup {
},

-- Whether to log debug messages
debug = false,
debug = false,

-- 'split', 'popup'
display = 'popup',
Expand All @@ -73,7 +73,7 @@ require'regexplainer'.setup {
},

narrative = {
separator = '\n',
indendation_string = '> ', -- default ' '
},
}
```
Expand Down Expand Up @@ -122,41 +122,33 @@ your editor.

### Render Options

`narrative.separator` can also be a function taking the current component and
returning a string clause separator. For example, to separate clauses by a new
line, followed by `> ` for each level of capture-group depth, define the
following function:
`narrative.indendation_string` can be a function taking the current component and
returning an indendation indicator string. For example, to show the capture group on each line:

```lua
narrative = {
separator = function(component)
local sep = '\n';
if component.depth > 0 then
for _ = 1, component.depth do
sep = sep .. '> '
end
end
return sep
indentation_string = function(component)
return component.capture_depth .. '> '
end
},
```

Input:

```javascript
/zero(one(two(?<inner>three)))/;
/zero(one(two(three)))/;
```

Output:

```markdown
`zero`
capture group 1:
> `one`
> capture group 2:
> > `two`
> > named capture group 3 `inner`:
> > > `three`
`zero`
capture group 1:
1> `one`
1> capture group 2:
1> 2> `two`
1> 2> capture group 3:
1> 2> 3> `three`
```

## Yank
Expand Down
114 changes: 48 additions & 66 deletions lua/regexplainer.lua
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
local component = require 'regexplainer.component'
local tree = require 'regexplainer.utils.treesitter'
local utils = require 'regexplainer.utils'
local buffers = require 'regexplainer.buffers'
local defer = require 'regexplainer.utils.defer'
local Buffers = require 'regexplainer.buffers'

local get_node_text = vim.treesitter.get_node_text
local deep_extend = vim.tbl_deep_extend
local map = vim.tbl_map
local buf_delete = vim.api.nvim_buf_delete
local ag = vim.api.nvim_create_augroup
local au = vim.api.nvim_create_autocmd

---@class RegexplainerOptions
---@field mode? 'narrative' # TODO: 'ascii', 'graphical'
---@field mode? 'narrative'|'debug' # TODO: 'ascii', 'graphical'
---@field auto? boolean # Automatically display when cursor enters a regexp
---@field filetypes? string[] # Filetypes (extensions) to automatically show regexplainer.
---@field debug? boolean # Notify debug logs
Expand All @@ -15,10 +21,10 @@ local defer = require 'regexplainer.utils.defer'
---@field popup? NuiPopupBufferOptions # options for the popup buffer
---@field split? NuiSplitBufferOptions # options for the split buffer

---@class RegexplainerRenderOptions : RegexplainerOptions
---@class RegexplainerRenderOptions: RegexplainerOptions
---@field register "*"|"+"|'"'|":"|"."|"%"|"/"|"#"|"0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9"

---@class RegexplainerYankOptions : RegexplainerOptions
---@class RegexplainerYankOptions: RegexplainerOptions
---@field register "*"|"+"|'"'|":"|"."|"%"|"/"|"#"|"0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9"

---@class RegexplainerMappings
Expand All @@ -29,15 +35,12 @@ local defer = require 'regexplainer.utils.defer'
---@field show_split? string # shows regexplainer in a split window
---@field show_popup? string # shows regexplainer in a popup window

local get_node_text = vim.treesitter.get_node_text or vim.treesitter.query.get_node_text

---Maps config.mappings keys to vim command names and descriptions
--
local config_command_map = {
show = { 'RegexplainerShow', 'Show Regexplainer' },
hide = { 'RegexplainerHide', 'Hide Regexplainer' },
toggle = { 'RegexplainerToggle', 'Toggle Regexplainer' },
yank = { 'RegexplainerYank', 'Yank Regexplainer' },
toggle = { 'RegexplainerToggle', 'Toggle Regexplainer' }, yank = { 'RegexplainerYank', 'Yank Regexplainer' },
show_split = { 'RegexplainerShowSplit', 'Show Regexplainer in a split Window' },
show_popup = { 'RegexplainerShowPopup', 'Show Regexplainer in a popup' },
}
Expand All @@ -51,90 +54,73 @@ local default_config = {
auto = false,
filetypes = {
'html',
'js',
'cjs',
'mjs',
'ts',
'jsx',
'tsx',
'cjsx',
'mjsx',
'js', 'javascript', 'cjs', 'mjs',
'ts', 'typescript', 'cts', 'mts',
'tsx', 'typescriptreact', 'ctsx', 'mtsx',
'jsx', 'javascriptreact', 'cjsx', 'mjsx',
},
debug = false,
display = 'popup',
mappings = {
toggle = 'gR',
},
narrative = {
separator = '\n',
indentation_string = ' ',
},
}

--- A deep copy of the default config.
--- During setup(), any user-provided config will be folded in
---@type RegexplainerOptions
--
local local_config = vim.tbl_deep_extend('keep', default_config, {})
local local_config = deep_extend('keep', default_config, {})

--- Show the explainer for the regexp under the cursor
---@param options? RegexplainerOptions overrides for this call
---@return nil
---@return nil|number bufnr the bufnr of the regexplaination
--
local function show(options)
options = vim.tbl_deep_extend('force', local_config, options or {})
local node, error = tree.get_regexp_pattern_at_cursor(options)
local function show_for_real(options)
options = deep_extend('force', local_config, options or {})
local node, scratchnr, error = tree.get_regexp_pattern_at_cursor()

if error and options.debug then
utils.notify('Rexexplainer: ' .. error, 'debug')
elseif node then
-- in the case of a pattern node, we need to get the first child 🤷
if node:type() == 'pattern' and node:child_count() == 1 then
node = node:child(0)
end

---@type RegexplainerRenderer
local renderer
elseif node and scratchnr then
---@type boolean, RegexplainerRenderer
local can_render, _renderer = pcall(require, 'regexplainer.renderers.' .. options.mode)
local can_render, renderer = pcall(require, 'regexplainer.renderers.' .. options.mode)

if can_render then
renderer = _renderer
else
if not can_render then
utils.notify(options.mode .. ' is not a valid renderer', 'warning')
utils.notify(renderer, 'error')
renderer = require 'regexplainer.renderers.narrative'
end

local components = component.make_components(node, nil, node)
local components = component.make_components(scratchnr, node, nil, node)

local buffer = buffers.get_buffer(options)
local buffer = Buffers.get_buffer(options)

if not buffer and options.debug then
local Debug = require 'regexplainer.renderers.debug'
return Debug.render(options, components)
renderer = require'regexplainer.renderers.debug'
end

buffers.render(buffer, renderer, components, options, {
full_regexp_text = get_node_text(node, 0),
})
local state = { full_regexp_text = get_node_text(node, scratchnr) }

Buffers.render(buffer, renderer, components, options, state)
buf_delete(scratchnr, { force = true })
else
buffers.hide_all()
Buffers.hide_all()
end
end

local disable_auto = false

local show_debounced_trailing, timer_trailing = defer.debounce_trailing(show, 5)

buffers.register_timer(timer_trailing)

local M = {}

--- Show the explainer for the regexp under the cursor
---@param options? RegexplainerOptions
function M.show(options)
disable_auto = true
show(options)
show_for_real(options)
disable_auto = false
end

Expand All @@ -145,11 +131,7 @@ function M.yank(options)
if type(options) == 'string' then
options = { register = options }
end
show(vim.tbl_deep_extend(
'force',
options,
{ display = 'register' }
))
show_for_real(deep_extend('force', options, { display = 'register' }))
disable_auto = false
end

Expand All @@ -158,16 +140,16 @@ end
---@return nil
--
function M.setup(config)
local_config = vim.tbl_deep_extend('keep', config or {}, default_config)
local_config = deep_extend('keep', config or {}, default_config)

-- bind keys from config
local has_which_key = pcall(require, 'which-key')
for cmd, binding in pairs(local_config.mappings) do
local command = ':' .. config_command_map[cmd][1] .. '<CR>'
for cmdmap, binding in pairs(local_config.mappings) do
local cmd, description = unpack(config_command_map[cmdmap])
local command = ':' .. cmd .. '<CR>'

if has_which_key then
local wk = require 'which-key'
local description = config_command_map[cmd][2]
wk.register({ [binding] = { command, description } }, { mode = 'n' })
else
utils.map('n', binding, command)
Expand All @@ -176,13 +158,13 @@ function M.setup(config)

-- setup autocommand
if local_config.auto then
vim.api.nvim_create_augroup(augroup_name, { clear = true })
vim.api.nvim_create_autocmd('CursorMoved', {
ag(augroup_name, { clear = true })
au('CursorMoved', {
group = 'Regexplainer',
pattern = vim.tbl_map(function(x) return '*.' .. x end, local_config.filetypes),
pattern = map(function(x) return '*.' .. x end, local_config.filetypes),
callback = function()
if not disable_auto then
show_debounced_trailing()
if tree.has_regexp_at_cursor() and not disable_auto then
show_for_real()
end
end,
})
Expand All @@ -194,13 +176,13 @@ end
--- Hide any displayed regexplainer buffers
--
function M.hide()
buffers.hide_all()
Buffers.hide_all()
end

--- Toggle Regexplainer
--
function M.toggle()
if buffers.is_open() then
if Buffers.is_open() then
M.hide()
else
M.show()
Expand All @@ -211,7 +193,7 @@ end
--
function M.teardown()
local_config = vim.tbl_deep_extend('keep', {}, default_config)
buffers.clear_timers()
Buffers.clear_timers()
pcall(vim.api.nvim_del_augroup_by_name, augroup_name)
end

Expand All @@ -220,7 +202,7 @@ end
function M.debug_components()
---@type any
local mode = 'debug'
show({ auto = false, display = 'split', mode = mode })
show_for_real({ auto = false, display = 'split', mode = mode })
end

return M
Loading

0 comments on commit 41c1b90

Please sign in to comment.