JanDeDobbeleer / oh-my-posh

The most customisable and low-latency cross platform/shell prompt renderer
https://ohmyposh.dev
MIT License
16.79k stars 2.35k forks source link

Oh-My-Posh fails to start with cmd init #2969

Closed kevinnansoncx closed 1 year ago

kevinnansoncx commented 1 year ago

Code of Conduct

What happened?

I recently received a new PC and was setting up oh-my-posh and was unable to get the prompt to update in cmd. I am using Windows Terminal (and cmd) with Clink and it reports a error on load,

Theme

atomic and default theme.

What OS are you seeing the problem on?

Windows

Which shell are you using?

other (please specify)

Log output

Microsoft Windows [Version 10.0.19044.2130]
(c) Microsoft Corporation. All rights reserved.
Clink v0.4.9 [git:2fd2c2] Copyright (c) 2012-2016 Martin Ridgers
http://mridgers.github.io/clink

[string "-- Helper functions..."]:38: attempt to call field 'setenv' (a nil value)

Output of file:

c:\git>oh-my-posh init cmd --config "$env:POSH_THEMES_PATH\atomic.omp.json"
-- Helper functions

function get_priority_number(name, default)
        local value = os.getenv(name)
        if os.envmap ~= nil and type(os.envmap) == 'table' then
                local t = os.envmap[name]
                value = (t ~= nil and type(t) == 'string') and t or value
        end
        if type(default) == 'number' then
                value = tonumber(value)
                if value == nil then
                        return default
                else
                        return value
                end
        else
        return default
        end
end

-- Duration functions

local endedit_time = 0
local last_duration = 0
local tip
local tooltips_enabled = true
local tooltip_active = false
local cached_prompt = {}

local function omp_exe()
    return '"'..'C:/Users/kevin.nanson/AppData/Local/Programs/oh-my-posh/bin/oh-my-posh.exe'..'"'
end

local function omp_config()
    return '"'..'c:\\git\\$env:POSH_THEMES_PATH\\atomic.omp.json'..'"'
end

os.setenv("POSH_THEME", 'c:\\git\\$env:POSH_THEMES_PATH\\atomic.omp.json')

local function can_async()
    if (clink.version_encoded or 0) >= 10030001 then
        return settings.get("prompt.async")
    end
end

local function run_posh_command(command)
    command = '"'..command..'"'
    local _,ismain = coroutine.running()
    if ismain then
        output = io.popen(command):read("*a")
    else
        output = io.popenyield(command):read("*a")
    end
    return output
end

local function os_clock_millis()
    -- Clink v1.2.30 has a fix for Lua's os.clock() implementation failing after
    -- the program has been running more than 24 days.  In older versions, call
    -- OMP to get the time in milliseconds.
    if (clink.version_encoded or 0) >= 10020030 then
        return math.floor(os.clock() * 1000)
    else
        local prompt_exe = string.format('%s get millis --shell=cmd', omp_exe())
        return run_posh_command(prompt_exe)
    end
end

local function duration_onbeginedit()
    last_duration = 0
    if endedit_time ~= 0 then
        local beginedit_time = os_clock_millis()
        local elapsed = beginedit_time - endedit_time
        if elapsed >= 0 then
            last_duration = elapsed
        end
    end
end

local function duration_onendedit(input)
    endedit_time = 0
    -- For an empty command, the execution time should not be evaluated.
    if string.gsub(input, "^%s*(.-)%s*$", "%1") ~= "" then
        endedit_time = os_clock_millis()
    end
end

-- Prompt functions

local function execution_time_option()
    if last_duration ~= nil then
        return "--execution-time "..last_duration
    end
    return ""
end

local function error_level_option()
    if os.geterrorlevel ~= nil and settings.get("cmd.get_errorlevel") then
        return "--error "..os.geterrorlevel()
    end
    return ""
end

local function get_posh_prompt(rprompt)
    local prompt = "primary"
    if rprompt then
        prompt = "right"
    end
    local prompt_exe = string.format('%s print %s --shell=cmd --config=%s %s %s', omp_exe(), prompt, omp_config(), execution_time_option(), error_level_option(), rprompt)
    return run_posh_command(prompt_exe)
end

local function set_posh_tooltip(command)
    if command == nil then
        return
    end

    -- escape special characters properly, if any
    command = string.gsub(command, '(\\+)"', '%1%1"')
    command = string.gsub(command, '(\\+)$', '%1%1')
    command = string.gsub(command, '"', '\\"')
    command = string.gsub(command, '([&<>%(%)@%^|])', '^%1')

    local prompt_exe = string.format('%s print tooltip --shell=cmd %s --config=%s --command="%s"', omp_exe(), error_level_option(), omp_config(), command)
    local tooltip = run_posh_command(prompt_exe)
    if tooltip ~= "" then
        tooltip_active = true
        cached_prompt.right = tooltip
    end
end

-- set priority lower than z.lua
-- https://github.com/skywind3000/z.lua/pull/125/commits/48a77adf3575952b2e951aa820a1ce11ed4ce56b
local zl_prompt_priority = get_priority_number('_ZL_CLINK_PROMPT_PRIORITY', 0)
local p = clink.promptfilter(zl_prompt_priority + 1)
function p:filter(prompt)
    if cached_prompt.left and cached_prompt.tip_space then
        -- Use the cached left prompt when updating the rprompt (tooltip) in
        -- response to the Spacebar.  This allows typing to stay responsive.
    else
        -- Generate the left prompt normally.
        cached_prompt.left = get_posh_prompt(false)
    end
    return cached_prompt.left
end
function p:rightfilter(prompt)
    if cached_prompt.tip_space and can_async() then
        -- Generate tooltip asynchronously in response to Spacebar.
        if cached_prompt.coroutine then
            -- Coroutine is already in progress.  The cached right prompt will
            -- be used until the coroutine finishes.
        else
            -- Create coroutine to generate tooltip rprompt.
            cached_prompt.coroutine = coroutine.create(function ()
                set_posh_tooltip(tip)
                cached_prompt.tip_done = true
                -- Refresh the prompt once the tooltip is generated.
                clink.refilterprompt()
            end)
        end
        if cached_prompt.tip_done then
            -- Once the tooltip is ready, clear the Spacebar flag so that if the
            -- tip changes and the Spacebar is pressed again, we can
            -- generate a new tooltip.
            cached_prompt.tip_done = nil
            cached_prompt.tip_space = nil
            cached_prompt.coroutine = nil
        end
    else
        -- Tooltip is needed, but not in response to Spacebar, so refresh it
        -- immediately.
        set_posh_tooltip(tip)
    end
    if not tooltip_active then
        -- Tooltip is not active, generate rprompt normally.
        cached_prompt.right = get_posh_prompt(true)
    end
    return cached_prompt.right, false
end
function p:transientfilter(prompt)
    local prompt_exe = string.format('%s print transient --shell=cmd --config=%s %s', omp_exe(), omp_config(), error_level_option())
    prompt = run_posh_command(prompt_exe)
    if prompt == "" then
        prompt = nil
    end
    return prompt
end
function p:transientrightfilter(prompt)
    return "", false
end

-- Event handlers

local function builtin_modules_onbeginedit()
    _cached_state = {}
    duration_onbeginedit()
end

local function builtin_modules_onendedit(input)
    duration_onendedit(input)
end

if clink.onbeginedit ~= nil and clink.onendedit ~= nil then
    clink.onbeginedit(builtin_modules_onbeginedit)
    clink.onendedit(builtin_modules_onendedit)
end

-- Tooltips

function ohmyposh_space(rl_buffer)
    local new_tip = string.gsub(rl_buffer:getbuffer(), "^%s*(.-)%s*$", "%1")
    rl_buffer:insert(" ")
    if new_tip ~= tip then
        tip = new_tip -- remember the tip for use when filtering the prompt
        cached_prompt.tip_space = can_async()
        clink.refilterprompt() -- invoke the prompt filters so OMP can update the prompt per the tip
    end
end

if tooltips_enabled and rl.setbinding then
    clink.onbeginedit(function () tip = nil cached_prompt = {} end)
    rl.setbinding(' ', [["luafunc:ohmyposh_space"]], 'emacs')
end
JanDeDobbeleer commented 1 year ago

@kevinnansoncx the docs state that commands need to be added to a specific file and not ran directly in your terminal. Please follow the guide correctly to get things to work.

As you're the second person to do this, allow me to ask, how come you came to the conclusion that command needs to be run this way?

kevinnansoncx commented 1 year ago

Sorry Jan, I ran that exact command (and others) as a part of the oh-my-posh-cmd.lua file as directed by the guide. Each time it resulted in the

[string "-- Helper functions..."]:38: attempt to call field 'setenv' (a nil value) error.
In an attempt to help you troubleshoot, I included the output of running that file directly in the command window, which may have confused the issue. I could not get the debug output to show from the command window, so I thought that was the next best thing. Based on the error message. I think that the line "os.setenv("POSH_THEME", 'c:\git\$env:POSH_THEMES_PATH\atomic.omp.json')" is the culprit (it lines up with line 38). Does that help?

JanDeDobbeleer commented 1 year ago

@kevinnansoncx ah OK, thanks makes sense now. So, you're mixing PowerShell and CMD logic here by using --config "$env:POSH_THEMES_PATH\atomic.omp.json" as the path. Lua can't resolve that environment variable so you'll need to add the actual path to the json file.

AND, you're using the wrong version of clink. Needs to be this one.

kevinnansoncx commented 1 year ago

Thanks so much Jan. It looks like the 'correct' version of Clink resolved it.

github-actions[bot] commented 9 months ago

This issue has been automatically locked since there has not been any recent activity (i.e. last half year) after it was closed. It helps our maintainers focus on the active issues. If you have found a problem that seems similar, please open a discussion first, complete the body with all the details necessary to reproduce, and mention this issue as reference.