dotfiles-pub/windows/home/AppData/Local/clink/init.lua

302 lines
8.0 KiB
Lua

-- luacheck: globals clink
local CLINK_HOME = clink.get_env("LOCALAPPDATA") .. "/clink/"
-- {{{ prompt
local colors = {
black = 0,
red = 1,
green = 2,
yellow = 3,
blue = 4,
magenta = 5,
cyan = 6,
white = 7,
reset = 9,
}
local function c(color, bright)
bright = bright == nil and false or bright
return "\x1b[" .. (bright and "9" or "3") .. tostring(colors[color] or 9) .. "m"
end
local function escape_gsub_find_arg(text)
return text and text:gsub("([-+*?.%%()%[%]$^])", "%%%1") or ""
end
local function escape_gsub_replace_arg(text)
return text and text:gsub("%%", "%%%%") or ""
end
local function gsub_plain(str, find, replace)
return string.gsub(str, escape_gsub_find_arg(find), escape_gsub_replace_arg(replace))
end
local function get_folder_name(path)
local reversePath = string.reverse(path)
local slashIndex = string.find(reversePath, "\\")
if slashIndex == nil then return path end
local folder = string.sub(path, string.len(path) - slashIndex + 2)
if folder == "" then
return path
else
return folder
end
end
local old_prompt = ""
local old = clink.promptfilter(0)
function old:filter(prompt)
old_prompt = prompt
end
local lambda = clink.promptfilter(10)
function lambda:filter(prompt)
local code = tonumber(os.geterrorlevel())
return " " .. c(code == 0 and "magenta" or "red") .. "λ" .. c("reset") .. " "
end
local cwd_prompt = clink.promptfilter(20)
function cwd_prompt:filter(prompt)
local cwd = old_prompt:match('.*(.:[^>]*)>')
if cwd == nil then cwd = clink.get_cwd() end
if string.find(cwd, clink.get_env("HOME")) then
cwd = gsub_plain(cwd, clink.get_env("HOME"), "~")
end
cwd = get_folder_name(cwd)
return prompt .. " " .. cwd .. " "
end
local function get_git_dir(path)
-- return parent path for specified entry (either file or directory)
local function pathname(path) -- luacheck: ignore 432
local prefix = ""
local i = path:find("[\\/:][^\\/:]*$")
if i then
prefix = path:sub(1, i-1)
end
return prefix
end
-- Checks if provided directory contains git directory
local function has_git_dir(dir)
return clink.is_dir(dir..'/.git') and dir..'/.git'
end
local function has_git_file(dir)
local gitfile = io.open(dir..'/.git')
if not gitfile then return false end
local line = gitfile:read() or ''
local git_dir = line:match('gitdir: (.*)')
gitfile:close()
if os.isdir then -- only available in Clink v1.0.0 and higher
if git_dir and os.isdir(git_dir) then
return git_dir
end
end
return git_dir and dir..'/'..git_dir
end
-- Set default path to current directory
if not path or path == '.' then path = clink.get_cwd() end
-- Calculate parent path now otherwise we won't be
-- able to do that inside of logical operator
local parent_path = pathname(path)
return has_git_dir(path)
or has_git_file(path)
-- Otherwise go up one level and make a recursive call
or (parent_path ~= path and get_git_dir(parent_path) or nil)
end
local function get_git_branch(git_dir)
git_dir = git_dir or get_git_dir()
-- If git directory not found then we're probably outside of repo
-- or something went wrong. The same is when head_file is nil
local head_file = git_dir and io.open(git_dir..'/HEAD')
if not head_file then return end
local HEAD = head_file:read()
head_file:close()
-- If HEAD is missing, something is wrong.
if not HEAD then return end
-- if HEAD matches branch expression, then we're on named branch
-- otherwise it is a detached commit
local branch_name = HEAD:match('ref: refs/heads/(.+)')
return branch_name or 'HEAD detached at '..HEAD:sub(1, 7)
end
local io_popenyield
local clink_promptcoroutine
local cached_info = {}
if clink.promptcoroutine and io.popenyield then
io_popenyield = io.popenyield
clink_promptcoroutine = clink.promptcoroutine
else
io_popenyield = io.popen
clink_promptcoroutine = function (func)
return func(false)
end
end
local function get_git_status()
local file = io_popenyield("git --no-optional-locks status --porcelain 2>nul")
if not file then
return {}
end
local conflict_found = false
local is_status = true
for line in file:lines() do
local code = line:sub(1, 2)
-- print (string.format("code: %s, line: %s", code, line))
if code == "DD" or code == "AU" or code == "UD" or code == "UA" or code == "DU" or code == "AA" or code == "UU" then -- luacheck: no max line length
is_status = false
conflict_found = true
break
-- unversioned files are ignored, comment out 'code ~= "!!"' to unignore them
elseif code ~= "!!" and code ~= "??" then
is_status = false
end
end
file:close()
return { status = is_status, conflict = conflict_found }
end
local function get_git_info_table()
local info = clink_promptcoroutine(function ()
return get_git_status()
end)
if not info then
info = cached_info.git_info or {}
else
cached_info.git_info = info
end
return info
end
local git_colors = {
clean = c("green"),
dirty = c("yellow"),
conflict = c("red"),
nostatus = c("white"),
}
local git = clink.promptfilter(30)
function git:filter(prompt)
local git_dir = get_git_dir()
local color
if git_dir then
local branch = get_git_branch(git_dir)
if branch then
-- If in a different repo or branch than last time, discard cached info.
if cached_info.git_dir ~= git_dir or cached_info.git_branch ~= branch then
cached_info.git_info = nil
cached_info.git_dir = git_dir
cached_info.git_branch = branch
end
-- If we're inside of git repo then try to detect current branch
-- Has branch => therefore it is a git folder, now figure out status
local gitInfo = get_git_info_table()
local gitStatus = gitInfo.status
local gitConflict = gitInfo.conflict
if gitStatus == nil then
color = git_colors.nostatus
elseif gitStatus then
color = git_colors.clean
else
color = git_colors.dirty
end
if gitConflict then
color = git_colors.conflict
end
return prompt .. " " .. c("cyan") .. branch .. " " .. color .. "" .. c("reset") .. " "
end
end
return prompt
end
local clock = clink.promptfilter(40)
function clock:filter(prompt) end
function clock:rightfilter(prompt)
return c("white") .. " " .. os.date("%H:%M:%S") .. " "
end
local last_run = os.clock()
local function format_time(time)
local out = ""
if time >= 3600 then
out = out .. math.floor(time / 3600) .. "h"
end
if time >= 60 then
out = out .. math.floor(time % 3600 / 60) .. "m"
end
out = out .. math.floor(time % 60) .. "s"
return out
end
local exectime = clink.promptfilter(50)
function exectime:filter(prompt) end
function exectime:rightfilter(prompt)
local delta = os.clock() - last_run
if delta < 3 then
return prompt
end
return c("yellow") .. " " .. format_time(delta) .. " " .. prompt
end
local exitcode = clink.promptfilter(60)
function exitcode:filter(prompt) end
function exitcode:rightfilter(prompt)
local code = tonumber(os.geterrorlevel())
if code == 0 then
return prompt
else
return c("red") .. " " .. code .. " " .. prompt
end
end
local finish = clink.promptfilter(100)
function finish:filter(prompt)
last_run = os.clock()
return prompt .. " ", false
end
-- }}}
-- {{{ completions
local completions_dir = CLINK_HOME .. "clink-completions/"
dofile(completions_dir .. ".init.lua")
for _,lua_module in ipairs(clink.find_files(completions_dir.."*.lua")) do
-- Skip files that starts with _. This could be useful if some files should be ignored
if not string.match(lua_module, "^_.*") then
local filename = completions_dir..lua_module
-- use dofile instead of require because require caches loaded modules
dofile(filename)
end
end
-- }}}