This commit is contained in:
@@ -1,4 +1,3 @@
|
|||||||
local CodeWriter = require 'u.codewriter'
|
|
||||||
local Range = require 'u.range'
|
local Range = require 'u.range'
|
||||||
local vim_repeat = require 'u.repeat'
|
local vim_repeat = require 'u.repeat'
|
||||||
|
|
||||||
@@ -8,8 +7,27 @@ local M = {}
|
|||||||
--- @param left string
|
--- @param left string
|
||||||
--- @param right string
|
--- @param right string
|
||||||
local function split(bracket_range, left, right)
|
local function split(bracket_range, left, right)
|
||||||
local code = CodeWriter.from_pos(bracket_range.start)
|
local bufnr = bracket_range.start.bufnr
|
||||||
code:write_raw(left)
|
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()
|
local curr = bracket_range.start:next()
|
||||||
if curr == nil then return end
|
if curr == nil then return end
|
||||||
@@ -27,7 +45,7 @@ local function split(bracket_range, left, right)
|
|||||||
if vim.tbl_contains({ ',', ';' }, curr:char()) then
|
if vim.tbl_contains({ ',', ';' }, curr:char()) then
|
||||||
-- accumulate item:
|
-- accumulate item:
|
||||||
local item = vim.trim(Range.new(last_start, curr):text())
|
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()
|
local next_last_start = curr:next()
|
||||||
if next_last_start == nil then break end
|
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)
|
local pos_before_right = bracket_range.stop:must_next(-1)
|
||||||
if last_start < pos_before_right then
|
if last_start < pos_before_right then
|
||||||
local item = vim.trim(Range.new(last_start, pos_before_right):text())
|
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
|
end
|
||||||
|
|
||||||
code:write(right)
|
write(right)
|
||||||
bracket_range:replace(code.lines)
|
bracket_range:replace(lines)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param bracket_range u.Range
|
--- @param bracket_range u.Range
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
local CodeWriter = require 'u.codewriter'
|
|
||||||
local Range = require 'u.range'
|
local Range = require 'u.range'
|
||||||
local vim_repeat = require 'u.repeat'
|
local vim_repeat = require 'u.repeat'
|
||||||
|
|
||||||
@@ -20,47 +19,37 @@ local surrounds = {
|
|||||||
['`'] = { left = '`', right = '`' },
|
['`'] = { left = '`', right = '`' },
|
||||||
}
|
}
|
||||||
|
|
||||||
--- @type { left: string, right: string } | nil
|
--- @type { left: string; right: string } | nil
|
||||||
local CACHED_BOUNDS = nil
|
local CACHED_BOUNDS = nil
|
||||||
|
|
||||||
--- @return { left: string, right: string }|nil
|
--- @return { left: string; right: string }|nil
|
||||||
local function prompt_for_bounds()
|
local function prompt_for_bounds()
|
||||||
if vim_repeat.is_repeating() then
|
if vim_repeat.is_repeating() then return CACHED_BOUNDS end
|
||||||
-- If we are repeating, we don't want to prompt for bounds, because
|
|
||||||
-- we want to reuse the last bounds:
|
|
||||||
return CACHED_BOUNDS
|
|
||||||
end
|
|
||||||
|
|
||||||
local cn = vim.fn.getchar()
|
local cn = vim.fn.getchar()
|
||||||
-- Check for non-printable characters:
|
|
||||||
if type(cn) ~= 'number' or cn < 32 or cn > 126 then return end
|
if type(cn) ~= 'number' or cn < 32 or cn > 126 then return end
|
||||||
local c = vim.fn.nr2char(cn)
|
local c = vim.fn.nr2char(cn)
|
||||||
|
|
||||||
if c == '<' then
|
if c == '<' then
|
||||||
-- Surround with a tag:
|
|
||||||
vim.keymap.set('c', '>', '><CR>')
|
vim.keymap.set('c', '>', '><CR>')
|
||||||
local tag = '<' .. vim.fn.input '<'
|
local tag = '<' .. vim.fn.input '<'
|
||||||
if tag == '<' then return end
|
if tag == '<' then return end
|
||||||
vim.keymap.del('c', '>')
|
vim.keymap.del('c', '>')
|
||||||
local endtag = '</' .. tag:sub(2):match '[^ >]*' .. '>'
|
local endtag = '</' .. tag:sub(2):match '[^ >]*' .. '>'
|
||||||
-- selene: allow(global_usage)
|
|
||||||
CACHED_BOUNDS = { left = tag, right = endtag }
|
CACHED_BOUNDS = { left = tag, right = endtag }
|
||||||
return CACHED_BOUNDS
|
return CACHED_BOUNDS
|
||||||
else
|
else
|
||||||
-- Default surround:
|
CACHED_BOUNDS = surrounds[c] or { left = c, right = c }
|
||||||
CACHED_BOUNDS = (surrounds)[c] or { left = c, right = c }
|
|
||||||
return CACHED_BOUNDS
|
return CACHED_BOUNDS
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param range u.Range
|
--- @param range u.Range
|
||||||
--- @param bounds { left: string, right: string }
|
--- @param bounds { left: string; right: string }
|
||||||
local function do_surround(range, bounds)
|
local function do_surround(range, bounds)
|
||||||
local left = bounds.left
|
local left = bounds.left
|
||||||
local right = bounds.right
|
local right = bounds.right
|
||||||
if range.mode == 'V' then
|
if range.mode == 'V' then
|
||||||
-- If we are surrounding multiple lines, we don't care about
|
|
||||||
-- space-padding:
|
|
||||||
left = vim.trim(left)
|
left = vim.trim(left)
|
||||||
right = vim.trim(right)
|
right = vim.trim(right)
|
||||||
end
|
end
|
||||||
@@ -69,28 +58,34 @@ local function do_surround(range, bounds)
|
|||||||
range:replace(left .. range:text() .. right)
|
range:replace(left .. range:text() .. right)
|
||||||
elseif range.mode == 'V' then
|
elseif range.mode == 'V' then
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
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:
|
local indent_str, base_indent
|
||||||
cw:write(left)
|
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)
|
local lines = {}
|
||||||
cw:indent(function(cw2)
|
local function write(line, indent_offset)
|
||||||
for _, line in ipairs(range:lines()) do
|
table.insert(lines, indent_str:rep(base_indent + (indent_offset or 0)) .. line)
|
||||||
-- trim the current indent prefix from the line:
|
end
|
||||||
if line:sub(1, #curr_ident_prefix) == curr_ident_prefix then
|
|
||||||
--
|
|
||||||
line = line:sub(#curr_ident_prefix + 1)
|
|
||||||
end
|
|
||||||
|
|
||||||
cw2:write(line)
|
write(left)
|
||||||
end
|
local indent_prefix = indent_str:rep(base_indent)
|
||||||
end)
|
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:
|
range:replace(lines)
|
||||||
cw:write(right)
|
|
||||||
|
|
||||||
range:replace(cw.lines)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
range.start:save_to_pos '.'
|
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()
|
local txt_obj = vim_repeat.is_repeating() and CACHED_DELETE_FROM or vim.fn.getcharstr()
|
||||||
CACHED_DELETE_FROM = txt_obj
|
CACHED_DELETE_FROM = txt_obj
|
||||||
|
|
||||||
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
local irange = Range.from_motion('i' .. txt_obj)
|
local irange = Range.from_motion('i' .. txt_obj)
|
||||||
local arange = Range.from_motion('a' .. txt_obj)
|
local arange = Range.from_motion('a' .. txt_obj)
|
||||||
if arange == nil or irange == nil then return end
|
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