From 8ec4789e3528a9424e4ebfc2ba9a99907301a90c Mon Sep 17 00:00:00 2001 From: Chris Antos Date: Thu, 4 Sep 2025 10:41:08 -0700 Subject: [PATCH 1/2] Fix cmderdev/cmder#3020; hg prompt is slow. Cmder's hg prompt didn't use async prompt filtering yet. Cmder's svn prompt only used async prompt filtering if a special config variable was set (the commit which contributed it seems to have misunderstood the git config settings for the git async prompt). This commit makes the following changes: 1. Adds async prompt filtering for hg. 2. Makes async prompt filtering for svn the default. 3. Removes the prompt_overrideSvnStatusOptIn variable. 4. Fixes a bug where any errors during `svn status` in the svn prompt accidentally show up in the terminal. 5. Fixes a bug where any errors during `hg branch` in the hg prompt turn into Lua errors. 6. Simplifies the code for colors in the hg and svn prompts. 7. Clean up the svn prompt code and make it consistent with the git and hg prompt code. --- vendor/clink.lua | 152 ++++++++++++++----------- vendor/cmder_prompt_config.lua.default | 5 - 2 files changed, 83 insertions(+), 74 deletions(-) diff --git a/vendor/clink.lua b/vendor/clink.lua index 0593ae135..824c39594 100644 --- a/vendor/clink.lua +++ b/vendor/clink.lua @@ -7,7 +7,7 @@ -- luacheck: globals uah_color cwd_color lamb_color clean_color dirty_color conflict_color unknown_color -- luacheck: globals prompt_homeSymbol prompt_lambSymbol prompt_type prompt_useHomeSymbol prompt_useUserAtHost -- luacheck: globals prompt_singleLine prompt_includeVersionControl --- luacheck: globals prompt_overrideGitStatusOptIn prompt_overrideSvnStatusOptIn +-- luacheck: globals prompt_overrideGitStatusOptIn -- luacheck: globals clink io.popenyield os.isdir settings.get -- At first, load the original clink.lua file @@ -350,13 +350,8 @@ end -- @return {false|mercurial branch information} --- local function get_hg_branch() - -- Return the branch information. The default is to get just the - -- branch name, but you could e.g. use the "hg-prompt" extension to - -- get more information, such as any applied mq patches. Here's an - -- example of that: - -- local cmd = "hg prompt \"{branch}{status}{|{patch}}{update}\"" - local cmd = "hg branch 2>nul" - local file = io.popen(cmd) + -- Return the branch information. + local file = io.popen("hg branch 2>nul") if not file then return false end @@ -424,12 +419,33 @@ local function get_git_status() return { status = is_status, conflict = conflict_found } end +--- +-- Get the status of working dir +-- @return {bool} +--- +local function get_hg_status() + -- The default is to just use the branch name, but you could e.g. use the + -- "hg-prompt" extension to get more information, such as any applied mq + -- patches. Here's an example of that: + -- "hg prompt \"{branch}{status}{|{patch}}{update}\"" + local pipe = io_popenyield("hg status -amrd 2>&1") + if not pipe then + return { error = true } + end + + local output = pipe:read('*all') + pipe:close() + + local dirty = (output ~= nil and output ~= "") + return { clean = not dirty } +end + --- -- Get the status of working dir -- @return {bool} --- local function get_svn_status() - local file = io_popenyield("svn status -q") + local file = io_popenyield("svn status -q 2>nul") if not file then return { error = true } end @@ -520,14 +536,6 @@ local function git_prompt_filter() return false end - -- Colors for git status - local colors = { - clean = get_clean_color(), - dirty = get_dirty_color(), - conflict = get_conflict_color(), - nostatus = get_unknown_color() - } - local git_dir = get_git_dir() local color if git_dir then @@ -547,18 +555,19 @@ local function git_prompt_filter() local gitConflict = gitInfo.conflict if gitStatus == nil then - color = colors.nostatus + color = get_unknown_color() elseif gitStatus then - color = colors.clean + color = get_clean_color() else - color = colors.dirty + color = get_dirty_color() end if gitConflict then - color = colors.conflict + color = get_conflict_color() end - clink.prompt.value = gsub_plain(clink.prompt.value, "{git}", " "..color.."("..branch..")") + local result = " "..color.."("..branch..")" + clink.prompt.value = gsub_plain(clink.prompt.value, "{git}", result) return false end end @@ -568,6 +577,18 @@ local function git_prompt_filter() return false end +local function get_hg_info_table() + local info = clink_promptcoroutine(function () + return get_hg_status() or {} + end) + if not info then + info = cached_info.hg_info or {} + else + cached_info.hg_info = info + end + return info +end + local function hg_prompt_filter() -- Don't do any hg processing if the prompt doesn't want to show hg info. @@ -577,33 +598,34 @@ local function hg_prompt_filter() local hg_dir = get_hg_dir() if hg_dir then - -- Colors for mercurial status - local colors = { - clean = get_clean_color(), - dirty = get_dirty_color(), - nostatus = get_unknown_color() - } - local output = get_hg_branch() - - -- strip the trailing newline from the branch name - local n = #output - while n > 0 and output:find("^%s", n) do n = n - 1 end - local branch = output:sub(1, n) + local output = get_hg_branch() or "" + + -- strip the trailing spaces and newline from the branch name + local branch = output:gsub("%s+$", "") if branch ~= nil and string.sub(branch,1,7) ~= "abort: " and -- not an HG working copy (not string.find(branch, "is not recognized")) then -- 'hg' not in path - local color = colors.clean + -- If in a different repo or branch than last time, discard cached info + if cached_info.hg_dir ~= hg_dir or cached_info.hg_branch ~= branch then + cached_info.hg_info = nil + cached_info.hg_dir = hg_dir + cached_info.hg_branch = branch + end - local pipe = io.popen("hg status -amrd 2>&1") - if pipe then - output = pipe:read('*all') - pipe:close() - if output ~= nil and output ~= "" then color = colors.dirty end + local hgInfo = get_hg_info_table() + + local color + if not hgInfo or hgInfo.error then + color = get_unknown_color() + elseif hgInfo.clean then + color = get_clean_color() + else + color = get_dirty_color() end - local result = color .. "(" .. branch .. ")" - clink.prompt.value = gsub_plain(clink.prompt.value, "{hg}", " "..result) + local result = " "..color.."("..branch..")" + clink.prompt.value = gsub_plain(clink.prompt.value, "{hg}", result) return false end end @@ -612,6 +634,18 @@ local function hg_prompt_filter() clink.prompt.value = gsub_plain(clink.prompt.value, "{hg}", "") end +local function get_svn_info_table() + local info = clink_promptcoroutine(function () + return get_svn_status() or {} + end) + if not info then + info = cached_info.svn_info or {} + else + cached_info.svn_info = info + end + return info +end + local function svn_prompt_filter() -- Don't do any svn processing if the prompt doesn't want to show svn info. @@ -619,13 +653,6 @@ local function svn_prompt_filter() return false end - -- Colors for svn status - local colors = { - clean = get_clean_color(), - dirty = get_dirty_color(), - nostatus = get_unknown_color() - } - local svn_dir = get_svn_dir() if svn_dir then -- if we're inside of svn repo then try to detect current branch @@ -637,29 +664,16 @@ local function svn_prompt_filter() cached_info.svn_dir = svn_dir cached_info.svn_branch = branch end - -- Get the svn status using coroutine if available and option is enabled. Otherwise use a blocking call - local svnStatus - if clink.promptcoroutine and io.popenyield and settings.get("prompt.async") and prompt_overrideSvnStatusOptIn then -- luacheck: no max line length - svnStatus = clink_promptcoroutine(function () - return get_svn_status() - end) - -- If the status result is pending, use the cached version instead, otherwise store it to the cache - if svnStatus == nil then - svnStatus = cached_info.svn_info - else - cached_info.svn_info = svnStatus - end - else - svnStatus = get_svn_status() - end + + local svnInfo = get_svn_info_table() local color - if not svnStatus or svnStatus.error then - color = colors.nostatus - elseif svnStatus.clean then - color = colors.clean + if not svnInfo or svnInfo.error then + color = get_unknown_color() + elseif svnInfo.clean then + color = get_clean_color() else - color = colors.dirty + color = get_dirty_color() end clink.prompt.value = gsub_plain(clink.prompt.value, "{svn}", " "..color.."("..branch..")") diff --git a/vendor/cmder_prompt_config.lua.default b/vendor/cmder_prompt_config.lua.default index d6c0a8cec..b7dc1f610 100644 --- a/vendor/cmder_prompt_config.lua.default +++ b/vendor/cmder_prompt_config.lua.default @@ -38,11 +38,6 @@ prompt_includeVersionControl = true -- NOTE: This only takes effect if using Clink v1.2.10 or higher. prompt_overrideGitStatusOptIn = false --- OPTIONAL. If true then always ignore the cmder.status and cmder.cmdstatus svn config settings and run the svn prompt commands in the background. - -- default is false - -- NOTE: This only takes effect if using Clink v1.2.10 or higher. -prompt_overrideSvnStatusOptIn = false - -- Prompt Attributes -- -- Colors: https://github.com/cmderdev/cmder/wiki/Customization#list-of-colors From 173b86fbc8673518642ee2346763e5282d439b80 Mon Sep 17 00:00:00 2001 From: Chris Antos Date: Sun, 14 Sep 2025 10:34:23 -0700 Subject: [PATCH 2/2] Fix another pre-existing bug in the hg prompt. If the hg program is not found, then there used to be a Lua script error. I fixed that in the earlier commit in this PR. But the fix was incomplete, and the hg prompt still appended " ()" instead of not appending anything (which is how the svn prompt behaves when the svn program is not found). --- vendor/clink.lua | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/vendor/clink.lua b/vendor/clink.lua index 824c39594..81b4d1eef 100644 --- a/vendor/clink.lua +++ b/vendor/clink.lua @@ -598,12 +598,8 @@ local function hg_prompt_filter() local hg_dir = get_hg_dir() if hg_dir then - local output = get_hg_branch() or "" - - -- strip the trailing spaces and newline from the branch name - local branch = output:gsub("%s+$", "") - - if branch ~= nil and + local branch = get_hg_branch() + if branch and string.sub(branch,1,7) ~= "abort: " and -- not an HG working copy (not string.find(branch, "is not recognized")) then -- 'hg' not in path -- If in a different repo or branch than last time, discard cached info