flow: Add text reflow plugin

This commit is contained in:
Sasha Koshka 2025-01-09 21:41:43 -05:00
parent 48b4c97e3e
commit f23876f07b
2 changed files with 173 additions and 0 deletions

168
flow/go.lua Normal file
View File

@ -0,0 +1,168 @@
VERSION = "0.1.0"
PLUGIN_NAME = "flow"
local micro = import("micro")
local config = import("micro/config")
local util = import("micro/util")
local buffer = import("micro/buffer")
function init()
config.MakeCommand(PLUGIN_NAME, flowIndent, config.NoComplete)
config.MakeCommand(PLUGIN_NAME .. ".left", flowLeft, config.NoComplete)
config.MakeCommand(PLUGIN_NAME .. ".right", flowRight, config.NoComplete)
config.MakeCommand(PLUGIN_NAME .. ".center", flowCenter, config.NoComplete)
-- config.MakeCommand(PLUGIN_NAME .. ".justify", flowJustify, config.NoComplete)
end
function flowIndent(bufferPane)
return flow(bufferPane, "indent")
end
function flowLeft(bufferPane)
return flow(bufferPane, "left")
end
function flowRight(bufferPane)
return flow(bufferPane, "right")
end
function flowCenter(bufferPane)
return flow(bufferPane, "center")
end
function flowJustify(bufferPane)
return flow(bufferPane, "justify")
end
function flow(bufferPane, align)
local cursor = bufferPane.Buf:GetActiveCursor()
if not (cursor and cursor:HasSelection()) then return end
-- TODO: if there is no selection, operate on the start and end of
-- the currently selected paragraph, which is demarcated by double line
-- breaks/beginning/end of file
local sstart, send = nil, nil
if cursor.CurSelection[1]:GreaterThan(-cursor.CurSelection[2]) then
sstart, send = cursor.CurSelection[2], cursor.CurSelection[1]
else
sstart, send = cursor.CurSelection[1], cursor.CurSelection[2]
end
sstart = buffer.Loc(sstart.X, sstart.Y)
send = buffer.Loc(send.X, send.Y)
local selection = util.String(cursor:GetSelection())
selection = reflow(selection, align, colorColumn())
bufferPane.Buf:Replace(sstart, send, selection)
end
function reflow(text, alignment, columns)
-- get indentation information
local firstLineIndent, middleLineIndent, inputLines = splitIndentation(text)
firstLineIndent = firstLineIndent or ""
middleLineIndent = middleLineIndent or ""
-- tokenize
local tokens = { }
for _, line in ipairs(inputLines) do
tokens = append(tokens, tokenize(line))
end
-- format tokens
local result = ""
local line = ""
local currentColumns = columns - measure(firstLineIndent)
function addLine()
local first = false
if result == "" then
first = true
else
result = result .. "\n"
end
if alignment == "indent" then
if first then
line = firstLineIndent .. line
else
line = middleLineIndent .. line
end
else
line = align(line, alignment, columns)
end
result = result .. line
currentColumns = columns - measure(middleLineIndent)
end
for _, token in ipairs(tokens) do
local forecast = #line + 1 + #token
if forecast > currentColumns then
addLine()
line = ""
end
if line ~= "" then line = line .. " " end
line = line .. token
end
addLine()
return result
end
-- assumes the text begins directly at the start of the file or directly after
-- a line break. returns the indentation for the first line, lines in the
-- middle, and the last line.
function splitIndentation(text)
local indents = { }
local lines = { }
for line in text:gmatch("([^\n]*)\n?") do
local indent = line:match("^[ \t/#*-]+") or ""
table.insert(indents, indent)
table.insert(lines, line:sub(#indent + 1))
end
if #indents < 1 then
return "", "", "", lines
end
local firstLineIndent = indents[1] or ""
local middleLineIndent = indents[2] or ""
return firstLineIndent, middleLineIndent, lines
end
function align(line, alignment, columns)
local gap = columns - #line
if alignment == "right" then
return string.rep(" ", gap) .. line
elseif alignment == "center" then
return string.rep(" ", math.floor(gap / 2)) .. line
else
return line
end
end
function tokenize(text)
local tokens = { }
for token in text:gmatch("%S+") do
table.insert(tokens, token)
end
return tokens
end
function measure(text)
if not text then return 0 end
local width = 0
for index = 1, #text do
if text:sub(index, index) == "\t" then
width = math.ceil((width + 1) / 8) * 8
else
width = width + 1
end
end
return width
end
function append(dest, src)
for index = 1, #src do
dest[#dest + 1] = src[index]
end
return dest
end
function colorColumn()
return config.GetGlobalOption("colorcolumn")
end

5
flow/repo.json Normal file
View File

@ -0,0 +1,5 @@
[{
"Name": "case",
"Description": "Change the case of the selection",
"License": "GPLv3",
}]