This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
local CodeWriter = require 'u.codewriter'
|
||||
local Range = require 'u.range'
|
||||
local vim_repeat = require 'u.repeat'
|
||||
|
||||
@@ -8,8 +7,27 @@ local M = {}
|
||||
--- @param left string
|
||||
--- @param right string
|
||||
local function split(bracket_range, left, right)
|
||||
local code = CodeWriter.from_pos(bracket_range.start)
|
||||
code:write_raw(left)
|
||||
local bufnr = bracket_range.start.bufnr
|
||||
local first_line = Range.from_line(bufnr, bracket_range.start.lnum):text()
|
||||
local ws = first_line:match '^%s*'
|
||||
local expandtab = vim.bo[bufnr].expandtab
|
||||
local shiftwidth = vim.bo[bufnr].shiftwidth
|
||||
|
||||
local indent_str, base_indent
|
||||
if expandtab then
|
||||
indent_str = string.rep(' ', shiftwidth)
|
||||
base_indent = math.floor(#ws / shiftwidth)
|
||||
else
|
||||
indent_str = '\t'
|
||||
base_indent = #ws
|
||||
end
|
||||
|
||||
local lines = {}
|
||||
local function write(line, indent_offset)
|
||||
table.insert(lines, indent_str:rep(base_indent + (indent_offset or 0)) .. line)
|
||||
end
|
||||
|
||||
table.insert(lines, left)
|
||||
|
||||
local curr = bracket_range.start:next()
|
||||
if curr == nil then return end
|
||||
@@ -27,7 +45,7 @@ local function split(bracket_range, left, right)
|
||||
if vim.tbl_contains({ ',', ';' }, curr:char()) then
|
||||
-- accumulate item:
|
||||
local item = vim.trim(Range.new(last_start, curr):text())
|
||||
if item ~= '' then code:indent():write(item) end
|
||||
if item ~= '' then write(item, 1) end
|
||||
|
||||
local next_last_start = curr:next()
|
||||
if next_last_start == nil then break end
|
||||
@@ -45,11 +63,11 @@ local function split(bracket_range, left, right)
|
||||
local pos_before_right = bracket_range.stop:must_next(-1)
|
||||
if last_start < pos_before_right then
|
||||
local item = vim.trim(Range.new(last_start, pos_before_right):text())
|
||||
if item ~= '' then code:indent():write(item) end
|
||||
if item ~= '' then write(item, 1) end
|
||||
end
|
||||
|
||||
code:write(right)
|
||||
bracket_range:replace(code.lines)
|
||||
write(right)
|
||||
bracket_range:replace(lines)
|
||||
end
|
||||
|
||||
--- @param bracket_range u.Range
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
local CodeWriter = require 'u.codewriter'
|
||||
local Range = require 'u.range'
|
||||
local vim_repeat = require 'u.repeat'
|
||||
|
||||
@@ -20,47 +19,37 @@ local surrounds = {
|
||||
['`'] = { left = '`', right = '`' },
|
||||
}
|
||||
|
||||
--- @type { left: string, right: string } | nil
|
||||
--- @type { left: string; right: string } | nil
|
||||
local CACHED_BOUNDS = nil
|
||||
|
||||
--- @return { left: string, right: string }|nil
|
||||
--- @return { left: string; right: string }|nil
|
||||
local function prompt_for_bounds()
|
||||
if vim_repeat.is_repeating() then
|
||||
-- If we are repeating, we don't want to prompt for bounds, because
|
||||
-- we want to reuse the last bounds:
|
||||
return CACHED_BOUNDS
|
||||
end
|
||||
if vim_repeat.is_repeating() then return CACHED_BOUNDS end
|
||||
|
||||
local cn = vim.fn.getchar()
|
||||
-- Check for non-printable characters:
|
||||
if type(cn) ~= 'number' or cn < 32 or cn > 126 then return end
|
||||
local c = vim.fn.nr2char(cn)
|
||||
|
||||
if c == '<' then
|
||||
-- Surround with a tag:
|
||||
vim.keymap.set('c', '>', '><CR>')
|
||||
local tag = '<' .. vim.fn.input '<'
|
||||
if tag == '<' then return end
|
||||
vim.keymap.del('c', '>')
|
||||
local endtag = '</' .. tag:sub(2):match '[^ >]*' .. '>'
|
||||
-- selene: allow(global_usage)
|
||||
CACHED_BOUNDS = { left = tag, right = endtag }
|
||||
return CACHED_BOUNDS
|
||||
else
|
||||
-- Default surround:
|
||||
CACHED_BOUNDS = (surrounds)[c] or { left = c, right = c }
|
||||
CACHED_BOUNDS = surrounds[c] or { left = c, right = c }
|
||||
return CACHED_BOUNDS
|
||||
end
|
||||
end
|
||||
|
||||
--- @param range u.Range
|
||||
--- @param bounds { left: string, right: string }
|
||||
--- @param bounds { left: string; right: string }
|
||||
local function do_surround(range, bounds)
|
||||
local left = bounds.left
|
||||
local right = bounds.right
|
||||
if range.mode == 'V' then
|
||||
-- If we are surrounding multiple lines, we don't care about
|
||||
-- space-padding:
|
||||
left = vim.trim(left)
|
||||
right = vim.trim(right)
|
||||
end
|
||||
@@ -69,28 +58,34 @@ local function do_surround(range, bounds)
|
||||
range:replace(left .. range:text() .. right)
|
||||
elseif range.mode == 'V' then
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
local cw = CodeWriter.from_line(range.start:line(), bufnr)
|
||||
local first_line = Range.from_line(bufnr, range.start.lnum):text()
|
||||
local ws = first_line:match '^%s*'
|
||||
local expandtab = vim.bo[bufnr].expandtab
|
||||
local shiftwidth = vim.bo[bufnr].shiftwidth
|
||||
|
||||
-- write the left bound at the current indent level:
|
||||
cw:write(left)
|
||||
local indent_str, base_indent
|
||||
if expandtab then
|
||||
indent_str = string.rep(' ', shiftwidth)
|
||||
base_indent = math.floor(#ws / shiftwidth)
|
||||
else
|
||||
indent_str = '\t'
|
||||
base_indent = #ws
|
||||
end
|
||||
|
||||
local curr_ident_prefix = cw.indent_str:rep(cw.indent_level)
|
||||
cw:indent(function(cw2)
|
||||
for _, line in ipairs(range:lines()) do
|
||||
-- trim the current indent prefix from the line:
|
||||
if line:sub(1, #curr_ident_prefix) == curr_ident_prefix then
|
||||
--
|
||||
line = line:sub(#curr_ident_prefix + 1)
|
||||
end
|
||||
local lines = {}
|
||||
local function write(line, indent_offset)
|
||||
table.insert(lines, indent_str:rep(base_indent + (indent_offset or 0)) .. line)
|
||||
end
|
||||
|
||||
cw2:write(line)
|
||||
end
|
||||
end)
|
||||
write(left)
|
||||
local indent_prefix = indent_str:rep(base_indent)
|
||||
for _, line in ipairs(range:lines()) do
|
||||
if line:sub(1, #indent_prefix) == indent_prefix then line = line:sub(#indent_prefix + 1) end
|
||||
write(line, 1)
|
||||
end
|
||||
write(right)
|
||||
|
||||
-- write the right bound at the current indent level:
|
||||
cw:write(right)
|
||||
|
||||
range:replace(cw.lines)
|
||||
range:replace(lines)
|
||||
end
|
||||
|
||||
range.start:save_to_pos '.'
|
||||
@@ -193,6 +188,7 @@ function M.setup()
|
||||
local txt_obj = vim_repeat.is_repeating() and CACHED_DELETE_FROM or vim.fn.getcharstr()
|
||||
CACHED_DELETE_FROM = txt_obj
|
||||
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
local irange = Range.from_motion('i' .. txt_obj)
|
||||
local arange = Range.from_motion('a' .. txt_obj)
|
||||
if arange == nil or irange == nil then return end
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
--- @class u.CodeWriter
|
||||
--- @field lines string[]
|
||||
--- @field indent_level number
|
||||
--- @field indent_str string
|
||||
local CodeWriter = {}
|
||||
CodeWriter.__index = CodeWriter
|
||||
|
||||
--- @param indent_level? number
|
||||
--- @param indent_str? string
|
||||
--- @return u.CodeWriter
|
||||
function CodeWriter.new(indent_level, indent_str)
|
||||
if indent_level == nil then indent_level = 0 end
|
||||
if indent_str == nil then indent_str = ' ' end
|
||||
|
||||
local cw = {
|
||||
lines = {},
|
||||
indent_level = indent_level,
|
||||
indent_str = indent_str,
|
||||
}
|
||||
setmetatable(cw, CodeWriter)
|
||||
return cw
|
||||
end
|
||||
|
||||
--- @param p u.Pos
|
||||
function CodeWriter.from_pos(p)
|
||||
local Range = require 'u.range'
|
||||
local line = Range.from_line(p.bufnr, p.lnum):text()
|
||||
return CodeWriter.from_line(line, p.bufnr)
|
||||
end
|
||||
|
||||
--- @param line string
|
||||
--- @param bufnr? number
|
||||
function CodeWriter.from_line(line, bufnr)
|
||||
if bufnr == nil or bufnr == 0 then bufnr = vim.api.nvim_get_current_buf() end
|
||||
|
||||
local ws = line:match '^%s*'
|
||||
local expandtab = vim.api.nvim_get_option_value('expandtab', { buf = bufnr })
|
||||
local shiftwidth = vim.api.nvim_get_option_value('shiftwidth', { buf = bufnr })
|
||||
|
||||
--- @type number
|
||||
local indent_level
|
||||
local indent_str = ''
|
||||
if expandtab then
|
||||
while #indent_str < shiftwidth do
|
||||
indent_str = indent_str .. ' '
|
||||
end
|
||||
indent_level = #ws / shiftwidth
|
||||
else
|
||||
indent_str = '\t'
|
||||
indent_level = #ws
|
||||
end
|
||||
|
||||
return CodeWriter.new(indent_level, indent_str)
|
||||
end
|
||||
|
||||
--- @param line string
|
||||
function CodeWriter:write_raw(line)
|
||||
if line:find '\n' then error 'line contains newline character' end
|
||||
table.insert(self.lines, line)
|
||||
end
|
||||
|
||||
--- @param line string
|
||||
function CodeWriter:write(line) self:write_raw(self.indent_str:rep(self.indent_level) .. line) end
|
||||
|
||||
--- @param f? fun(cw: u.CodeWriter):any
|
||||
function CodeWriter:indent(f)
|
||||
local cw = {
|
||||
lines = self.lines,
|
||||
indent_level = self.indent_level + 1,
|
||||
indent_str = self.indent_str,
|
||||
}
|
||||
setmetatable(cw, { __index = CodeWriter })
|
||||
if f ~= nil then f(cw) end
|
||||
return cw
|
||||
end
|
||||
|
||||
return CodeWriter
|
||||
@@ -1,30 +0,0 @@
|
||||
--- @diagnostic disable: undefined-field, need-check-nil, need-check-nil
|
||||
local CodeWriter = require 'u.codewriter'
|
||||
|
||||
describe('CodeWriter', function()
|
||||
it('should write with indentation', function()
|
||||
local cw = CodeWriter.new()
|
||||
cw:write '{'
|
||||
cw:indent(function(cw2) cw2:write 'x: 123' end)
|
||||
cw:write '}'
|
||||
|
||||
assert.are.same(cw.lines, { '{', ' x: 123', '}' })
|
||||
end)
|
||||
|
||||
it('should keep relative indentation', function()
|
||||
local cw = CodeWriter.new()
|
||||
cw:write '{'
|
||||
cw:indent(function(cw2)
|
||||
cw2:write 'x: 123'
|
||||
cw2:write ' y: 123'
|
||||
end)
|
||||
cw:write '}'
|
||||
|
||||
assert.are.same(cw.lines, {
|
||||
'{',
|
||||
' x: 123',
|
||||
' y: 123',
|
||||
'}',
|
||||
})
|
||||
end)
|
||||
end)
|
||||
Reference in New Issue
Block a user