Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clink async prompt #2556

Merged
merged 18 commits into from
Jul 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 122 additions & 27 deletions vendor/clink.lua
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ local function get_conflict_color()
return conflict_color or "\x1b[31;1m"
end

local function get_unknown_color()
return unknown_color or "\x1b[37;1m"
end

---
-- Makes a string safe to use as the replacement in string.gsub
---
Expand All @@ -59,6 +63,32 @@ local function get_folder_name(path)
end


---
-- Forward/backward compatibility for Clink asynchronous prompt filtering.
-- With Clink v1.2.10 and higher this lets git status run in the background and
-- refresh the prompt when it finishes, to eliminate waits in large git repos.
---
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


---
-- Global variable so other Lua scripts can detect whether they're in a Cmder
-- shell session.
---
CMDER_SESSION = true


---
-- Setting the prompt in clink means that commands which rewrite the prompt do
-- not destroy our own prompt. It also means that started cmds (or batch files
Expand Down Expand Up @@ -109,6 +139,10 @@ local function set_prompt_filter()
prompt_singleLine = false
end

if prompt_includeVersionControl == nil then
prompt_includeVersionControl = true
end

if prompt_type == 'folder' then
cwd = get_folder_name(cwd)
end
Expand All @@ -129,7 +163,12 @@ local function set_prompt_filter()

if env ~= nil then env = "("..env..") " else env = "" end

prompt = get_uah_color() .. "{uah}" .. get_cwd_color() .. "{cwd}{git}{hg}{svn}" .. get_lamb_color() .. cr .. "{lamb} \x1b[0m"
if uah ~= '' then uah = get_uah_color() .. uah end
if cwd ~= '' then cwd = get_cwd_color() .. cwd end

local version_control = prompt_includeVersionControl and "{git}{hg}{svn}" or ""

prompt = "{uah}{cwd}" .. version_control .. get_lamb_color() .. cr .. "{lamb} \x1b[0m"
prompt = string.gsub(prompt, "{uah}", uah)
prompt = string.gsub(prompt, "{cwd}", cwd)
prompt = string.gsub(prompt, "{env}", env)
Expand Down Expand Up @@ -304,7 +343,7 @@ end
-- @return {bool}
---
local function get_git_status()
local file = io.popen("git --no-optional-locks status --porcelain 2>nul")
local file = io_popenyield("git --no-optional-locks status --porcelain 2>nul")
for line in file:lines() do
file:close()
return false
Expand All @@ -319,7 +358,7 @@ end
-- @return {bool} indicating true for conflict, false for no conflicts
---
function get_git_conflict()
local file = io.popen("git diff --name-only --diff-filter=U 2>nul")
local file = io_popenyield("git diff --name-only --diff-filter=U 2>nul")
for line in file:lines() do
file:close()
return true;
Expand Down Expand Up @@ -359,11 +398,36 @@ local function get_svn_status()
return true
end

---
-- Use a prompt coroutine to get git status in the background.
-- Cache the info so we can reuse it next time to reduce flicker.
---
local function get_git_info_table()
local info = clink_promptcoroutine(function ()
return { status=get_git_status(), conflict=get_git_conflict() }
end)
if not info then
info = cached_info.git_info or {}
else
cached_info.git_info = info
end
return info
end

---
-- Get the status of working dir
-- @return {bool}
---
local function get_git_status_setting()
-- When async prompt filtering is available, check the
-- prompt_overrideGitStatusOptIn config setting for whether to ignore the
-- cmder.status and cmder.cmdstatus git config opt-in settings.
if clink.promptcoroutine and io.popenyield and settings.get("prompt.async") then
if prompt_overrideGitStatusOptIn then
return true
end
end

local gitStatusConfig = io.popen("git --no-pager config cmder.status 2>nul")

for line in gitStatusConfig:lines() do
Expand All @@ -388,38 +452,56 @@ end

local function git_prompt_filter()

-- Don't do any git processing if the prompt doesn't want to show git info.
if not clink.prompt.value:find("{git}") then
return false
end

-- Colors for git status
local colors = {
clean = get_clean_color(),
dirty = get_dirty_color(),
conflict = get_conflict_color()
conflict = get_conflict_color(),
nostatus = get_unknown_color()
}

local git_dir = get_git_dir()
local color
cmderGitStatusOptIn = get_git_status_setting()
if cmderGitStatusOptIn then
if git_dir then
-- if we're inside of git repo then try to detect current branch
local branch = get_git_branch(git_dir)
local color
if branch then
-- Has branch => therefore it is a git folder, now figure out status
local gitStatus = get_git_status()
local gitConflict = get_git_conflict()

color = colors.dirty
if gitStatus then
color = colors.clean
end

if gitConflict then
color = colors.conflict
end

clink.prompt.value = string.gsub(clink.prompt.value, "{git}", color.."("..verbatim(branch)..")")
return false
end
end
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
-- Use git status if allowed.
if cmderGitStatusOptIn then
-- 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 = colors.nostatus
elseif gitStatus then
color = colors.clean
else
color = colors.dirty
end

if gitConflict then
color = colors.conflict
end
else
color = colors.nostatus
end
clink.prompt.value = string.gsub(clink.prompt.value, "{git}", color.."("..verbatim(branch)..")")
return false
end
end

-- No git present or not in git file
Expand All @@ -429,6 +511,11 @@ end

local function hg_prompt_filter()

-- Don't do any hg processing if the prompt doesn't want to show hg info.
if not clink.prompt.value:find("{hg}") then
return false
end

local result = ""

local hg_dir = get_hg_dir()
Expand All @@ -437,6 +524,7 @@ local function hg_prompt_filter()
local colors = {
clean = get_clean_color(),
dirty = get_dirty_color(),
nostatus = get_unknown_color()
}

local pipe = io.popen("hg branch 2>&1")
Expand Down Expand Up @@ -467,10 +555,17 @@ local function hg_prompt_filter()
end

local function svn_prompt_filter()

-- Don't do any svn processing if the prompt doesn't want to show svn info.
if not clink.prompt.value:find("{svn}") then
return false
end

-- Colors for svn status
local colors = {
clean = get_clean_color(),
dirty = get_dirty_color(),
nostatus = get_unknown_color()
}

if get_svn_dir() then
Expand Down
10 changes: 10 additions & 0 deletions vendor/cmder_prompt_config.lua.default
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ prompt_useUserAtHost = false
-- default is false
prompt_singleLine = false

-- OPTIONAL. If true then always ignore the cmder.status and cmder.cmdstatus git config settings and run the git prompt commands in the background.
-- default is false
-- NOTE: This only takes effect if using Clink v1.2.10 or higher.
prompt_overrideGitStatusOptIn = false

-- OPTIONAL. If true then Cmder includes git, mercurial, and subversion status in the prompt.
-- default is true
prompt_includeVersionControl = true

-- Prompt Attributes
--
-- Colors
Expand All @@ -43,3 +52,4 @@ lamb_color = "\x1b[1;30;40m" -- Light Grey = Lambda Color
clean_color = "\x1b[1;37;40m"
dirty_color = "\x1b[33;3m"
conflict_color = "\x1b[31;1m"
unknown_color = "\x1b[37;1m" -- White = No VCS Status Branch Color
18 changes: 18 additions & 0 deletions vendor/git-prompt.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,21 @@ function getGitStatusSetting() {
fi
}

function getSimpleGitBranch() {
gitDir=$(git rev-parse --git-dir 2>/dev/null)
if [ -z "$gitDir" ]; then
return 0
fi

headContent=$(< "$gitDir/HEAD")
if [[ "$headContent" == "ref: refs/heads/"* ]]
then
echo " (${headContent:16})"
else
echo " (HEAD detached at ${headContent:0:7})"
fi
}

if test -f /etc/profile.d/git-sdk.sh
then
TITLEPREFIX=SDK-${MSYSTEM#MINGW}
Expand Down Expand Up @@ -45,6 +60,9 @@ else
. "$COMPLETION_PATH/git-prompt.sh"
PS1="$PS1"'\[\033[36m\]' # change color to cyan
PS1="$PS1"'`__git_ps1`' # bash function
else
PS1="$PS1"'\[\033[37;1m\]' # change color to white
PS1="$PS1"'`getSimpleGitBranch`'
fi
fi
fi
Expand Down
8 changes: 8 additions & 0 deletions vendor/psmodules/Cmder.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ function checkGit($Path) {

if (getGitStatusSetting -eq $true) {
Write-VcsStatus
} else {
$headContent = Get-Content (Join-Path $Path '.git/HEAD')
if ($headContent -like "ref: refs/heads/*") {
$branchName = $headContent.Substring(16)
} else {
$branchName = "HEAD detached at $($headContent.Substring(0, 7))"
}
Write-Host " [$branchName]" -NoNewline -ForegroundColor White
}

return
Expand Down