302 lines
No EOL
8 KiB
Lua
302 lines
No EOL
8 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
|
|
-- }}} |