Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow escaping with $$ #40

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ jobs:
git clone --depth 1 https://github.com/nvim-treesitter/nvim-treesitter.git ~/.local/share/nvim/site/pack/vendor/start/nvim-treesitter
git clone --depth 1 https://github.com/nvim-lua/plenary.nvim ~/.local/share/nvim/site/pack/vendor/start/plenary.nvim
ln -s $(pwd) ~/.local/share/nvim/site/pack/vendor/start
nvim --headless -c 'TSInstallSync python javascript lua rust go' -c 'q'
nvim --headless -c 'TSInstallSync python javascript lua rust go bash' -c 'q'

- name: Run tests
run: |
nvim --version
Expand Down
20 changes: 19 additions & 1 deletion lua/ssr/parse.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,24 @@ local wildcard_prefix = require("ssr.search").wildcard_prefix

local M = {}

---Substituts $var using the provided function, and unescapes $$.
---@param pattern string
---@param replace fun(string):string a function that takes the variable name and returns the substitution result
local function substitute_variables(pattern, replace)
return pattern:gsub("%$(%$?[_%a%d]*)", function(match)
if match == "" then
-- single $, not a valid variable
return "$"
end
if string.match(match, "^%$") then
-- this is an escaped $
return match
end

return replace(match)
end)
end

---@class ParseContext
---@field lang string
---@field before string
Expand Down Expand Up @@ -68,7 +86,7 @@ end
---@return TSNode?, string
function ParseContext:parse(pattern)
-- Replace named wildcard $name to identifier __ssr_var_name to avoid syntax error.
pattern = pattern:gsub("%$([_%a%d]+)", wildcard_prefix .. "%1")
pattern = substitute_variables(pattern, function(var) return wildcard_prefix .. var end)
local context_text = self.before .. pattern .. self.after
local root = ts.get_string_parser(context_text, self.lang):parse()[1]:root()
local lines = vim.split(pattern, "\n")
Expand Down
29 changes: 26 additions & 3 deletions lua/ssr/search.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,24 @@ local u = require "ssr.utils"

local M = {}

---Substituts $var using the provided function, and unescapes $$.
---@param pattern string
---@param replace fun(integer, string):string a function that takes the position, variable name and returns the substitution result
local function substitute_variables(pattern, replace)
return pattern:gsub("()%$(%$?[_%a%d]*)", function(pos, match)
if match == "" then
-- single $, not a valid variable
return "$"
end
if string.match(match, "^%$") then
-- this is an escaped $
return match
end

return replace(pos, match)
end)
end

M.wildcard_prefix = "__ssr_var_"

---@class Match
Expand Down Expand Up @@ -178,7 +196,7 @@ function M.search(buf, node, source, ns)
return true
end
return (start_row1 > start_row2 or (start_row1 == start_row2 and start_col1 > start_col2))
and (end_row1 < end_row2 or (end_row1 == end_row2 and end_col1 <= end_col2))
and (end_row1 < end_row2 or (end_row1 == end_row2 and end_col1 <= end_col2))
end)

return matches
Expand All @@ -190,8 +208,12 @@ end
---@param template string
function M.replace(buf, match, template)
-- Render templates with captured nodes.
local replace = template:gsub("()%$([_%a%d]+)", function(pos, var)
local start_row, start_col, end_row, end_col = match.captures[var]:get()
local replace = substitute_variables(template, function(pos, var)
local capture = match.captures[var]
if capture == nil then
return "$" .. var
end
local start_row, start_col, end_row, end_col = capture:get()
local lines = api.nvim_buf_get_text(buf, start_row, start_col, end_row, end_col, {})
u.remove_indent(lines, u.get_indent(buf, start_row))
local var_lines = vim.split(template:sub(1, pos), "\n")
Expand All @@ -200,6 +222,7 @@ function M.replace(buf, match, template)
u.add_indent(lines, template_indent)
return table.concat(lines, "\n")
end)

replace = vim.split(replace, "\n")
local start_row, start_col, end_row, end_col = match.range:get()
u.add_indent(replace, u.get_indent(buf, start_row))
Expand Down
16 changes: 16 additions & 0 deletions tests/ssr_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,22 @@ x
local a = vim.api
]]

t [[ bash escape dollar sign in pattern
<$FOO=$$BAR>
====
$$FOO=$$$$BAR ==>> foo
====
foo
]]

t [[ bash escape dollar sign in template
<${FOO:-bar}>
====
$${FOO:-$b} ==>> $b$$b$$$b
====
bar$b$bar
]]

describe("", function()
-- Plenary runs nvim with `--noplugin` argument.
-- Make sure nvim-treesitter is loaded, which populates vim.treesitter's ft_to_lang table.
Expand Down
Loading