flow: Add text reflow plugin
This commit is contained in:
parent
48b4c97e3e
commit
f23876f07b
168
flow/go.lua
Normal file
168
flow/go.lua
Normal 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
5
flow/repo.json
Normal file
@ -0,0 +1,5 @@
|
||||
[{
|
||||
"Name": "case",
|
||||
"Description": "Change the case of the selection",
|
||||
"License": "GPLv3",
|
||||
}]
|
Loading…
Reference in New Issue
Block a user