phase 2: remove CodeWriter
Some checks failed
ci / ci (push) Failing after 3m7s

This commit is contained in:
2026-04-04 17:05:56 -06:00
parent b00d8d2fa7
commit f12928749b
4 changed files with 55 additions and 148 deletions

View File

@@ -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

View File

@@ -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)
local lines = {}
local function write(line, indent_offset)
table.insert(lines, indent_str:rep(base_indent + (indent_offset or 0)) .. line)
end
write(left)
local indent_prefix = indent_str:rep(base_indent)
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)
if line:sub(1, #indent_prefix) == indent_prefix then line = line:sub(#indent_prefix + 1) end
write(line, 1)
end
write(right)
cw2:write(line)
end
end)
-- 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

View File

@@ -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

View File

@@ -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)