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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user