1
0
mirror of https://github.com/SpaceVim/SpaceVim.git synced 2025-01-24 09:40:06 +08:00
SpaceVim/bundle/plenary.nvim/lua/plenary/strings.lua
2022-05-16 22:20:10 +08:00

189 lines
4.7 KiB
Lua
Vendored

local path = require("plenary.path").path
local M = {}
M.strdisplaywidth = (function()
if jit and path.sep ~= [[\]] then
local ffi = require "ffi"
ffi.cdef [[
typedef unsigned char char_u;
int linetabsize_col(int startcol, char_u *s);
]]
return function(str, col)
str = tostring(str)
local startcol = col or 0
local s = ffi.new("char[?]", #str + 1)
ffi.copy(s, str)
return ffi.C.linetabsize_col(startcol, s) - startcol
end
else
return function(str, col)
str = tostring(str)
if vim.in_fast_event() then
return #str - (col or 0)
end
return vim.fn.strdisplaywidth(str, col)
end
end
end)()
M.strcharpart = (function()
if jit and path.sep ~= [[\]] then
local ffi = require "ffi"
ffi.cdef [[
typedef unsigned char char_u;
int utf_ptr2len(const char_u *const p);
]]
local function utf_ptr2len(str)
local c_str = ffi.new("char[?]", #str + 1)
ffi.copy(c_str, str)
return ffi.C.utf_ptr2len(c_str)
end
return function(str, nchar, charlen)
local nbyte = 0
if nchar > 0 then
while nchar > 0 and nbyte < #str do
nbyte = nbyte + utf_ptr2len(str:sub(nbyte + 1))
nchar = nchar - 1
end
else
nbyte = nchar
end
local len = 0
if charlen then
while charlen > 0 and nbyte + len < #str do
local off = nbyte + len
if off < 0 then
len = len + 1
else
len = len + utf_ptr2len(str:sub(off + 1))
end
charlen = charlen - 1
end
else
len = #str - nbyte
end
if nbyte < 0 then
len = len + nbyte
nbyte = 0
elseif nbyte > #str then
nbyte = #str
end
if len < 0 then
len = 0
elseif nbyte + len > #str then
len = #str - nbyte
end
return str:sub(nbyte + 1, nbyte + len)
end
else
return function(str, nchar, charlen)
if vim.in_fast_event() then
return str:sub(nchar + 1, charlen)
end
return vim.fn.strcharpart(str, nchar, charlen)
end
end
end)()
local truncate = function(str, len, dots, direction)
if M.strdisplaywidth(str) <= len then
return str
end
local start = direction > 0 and 0 or str:len()
local current = 0
local result = ""
local len_of_dots = M.strdisplaywidth(dots)
local concat = function(a, b, dir)
if dir > 0 then
return a .. b
else
return b .. a
end
end
while true do
local part = M.strcharpart(str, start, 1)
current = current + M.strdisplaywidth(part)
if (current + len_of_dots) > len then
result = concat(result, dots, direction)
break
end
result = concat(result, part, direction)
start = start + direction
end
return result
end
M.truncate = function(str, len, dots, direction)
str = tostring(str) -- We need to make sure its an actually a string and not a number
dots = dots or ""
direction = direction or 1
if direction ~= 0 then
return truncate(str, len, dots, direction)
else
if M.strdisplaywidth(str) <= len then
return str
end
local len1 = math.floor((len + M.strdisplaywidth(dots)) / 2)
local s1 = truncate(str, len1, dots, 1)
local len2 = len - M.strdisplaywidth(s1) + M.strdisplaywidth(dots)
local s2 = truncate(str, len2, dots, -1)
return s1 .. s2:sub(dots:len() + 1)
end
end
M.align_str = function(string, width, right_justify)
local str_len = M.strdisplaywidth(string)
return right_justify and string.rep(" ", width - str_len) .. string or string .. string.rep(" ", width - str_len)
end
M.dedent = function(str, leave_indent)
-- Check each line and detect the minimum indent.
local indent
local info = {}
for line in str:gmatch "[^\n]*\n?" do
-- It matches '' for the last line.
if line ~= "" then
local chars, width
local line_indent = line:match "^[ \t]+"
if line_indent then
chars = #line_indent
width = M.strdisplaywidth(line_indent)
if not indent or width < indent then
indent = width
end
-- Ignore empty lines
elseif line ~= "\n" then
indent = 0
end
table.insert(info, { line = line, chars = chars, width = width })
end
end
-- Build up the result
leave_indent = leave_indent or 0
local result = {}
for _, i in ipairs(info) do
local line
if i.chars then
local content = i.line:sub(i.chars + 1)
local indent_width = i.width - indent + leave_indent
line = (" "):rep(indent_width) .. content
elseif i.line == "\n" then
line = "\n"
else
line = (" "):rep(leave_indent) .. i.line
end
table.insert(result, line)
end
return table.concat(result)
end
return M