AmrEldib / cmder-powerline-prompt

Custom prompt for Cmder on Windows
715 stars 154 forks source link

How to show active python virtual environment in prompt? #5

Open Tset-Noitamotua opened 7 years ago

Tset-Noitamotua commented 7 years ago

I have used below code (in file C:\tools\cmder\config\pyenv_prompt_config.lua) to display which virtual python env is activated. It's not my code, I have found it on Github or Stackoverflow and I have no clue about Lua :(

Could it be combined somehow with your cmder-powerline-prompt?

local clink_path_lua_file = clink.get_env('CMDER_ROOT')..'\\vendor\\clink-completions\\modules\\path.lua'
dofile(clink_path_lua_file)

function get_virtual_env(env_var)
    env_path = clink.get_env(env_var)
    if env_path then
        basen = exports.basename(env_path)
        return basen
    end
    return false
end

---
 -- add python virutual env name 
---
function conda_prompt_filter()
    -- add in python virtual env name
    local python_env = get_virtual_env('DEFAULT_ENV')
    if python_env then
        clink.prompt.value = string.gsub(clink.prompt.value, "λ", "["..python_env.."] λ")
    end
end

---
 -- add virtual env name 
---
function venv_prompt_filter()
    -- add in virtual env name
    local venv = get_virtual_env('VIRTUAL_ENV')
    if venv then
        clink.prompt.value = string.gsub(clink.prompt.value, "λ", "["..venv.."] λ")
    end
end

Thats how it looked:

example_prompt

Cheers Tset

AmrEldib commented 7 years ago

Hi @Tset-Noitamotua This looks promising! You can register a function as a filter and assign it a rank, and Clink (which Cmder uses in the background) will apply this filter to the prompt. See this line in the powerline_prompt.lua file.

clink.prompt.register_filter(colorful_git_prompt_filter, 60)

Here colorful_git_prompt_filter is the function, and 60 is the rank. This way you can create one lua file for each group of filter that are related, and choose to apply them as you want. To activate a lua file, just place it in the %CMDER_ROOT%/config folder.

Tset-Noitamotua commented 7 years ago

I'll try it.

What is the rank for? Is it kind of a prioraty to which filter will apply?

Tset-Noitamotua commented 7 years ago

yep yep yep

image (... font is missing on my recent machine)

cmder-powerline-venv-prompt :)

powerline_venv_prompt.lua

-- Resets the prompt 
function lambda_prompt_filter()
    cwd = clink.get_cwd()
    prompt = "\x1b[37;44m{cwd} {git}{hg}\n\x1b[1;30;40m{lamb} \x1b[0m"
    new_value = string.gsub(prompt, "{cwd}", cwd)
    clink.prompt.value = string.gsub(new_value, "{lamb}", "λ")
end

--- copied from clink.lua
 -- Resolves closest directory location for specified directory.
 -- Navigates subsequently up one level and tries to find specified directory
 -- @param  {string} path    Path to directory will be checked. If not provided
 --                          current directory will be used
 -- @param  {string} dirname Directory name to search for
 -- @return {string} Path to specified directory or nil if such dir not found
local function get_dir_contains(path, dirname)

    -- return parent path for specified entry (either file or directory)
    local function pathname(path)
        local prefix = ""
        local i = path:find("[\\/:][^\\/:]*$")
        if i then
            prefix = path:sub(1, i-1)
        end
        return prefix
    end

    -- Navigates up one level
    local function up_one_level(path)
        if path == nil then path = '.' end
        if path == '.' then path = clink.get_cwd() end
        return pathname(path)
    end

    -- Checks if provided directory contains git directory
    local function has_specified_dir(path, specified_dir)
        if path == nil then path = '.' end
        local found_dirs = clink.find_dirs(path..'/'..specified_dir)
        if #found_dirs > 0 then return true end
        return false
    end

    -- Set default path to current directory
    if path == nil then path = '.' end

    -- If we're already have .git directory here, then return current path
    if has_specified_dir(path, dirname) then
        return path..'/'..dirname
    else
        -- Otherwise go up one level and make a recursive call
        local parent_path = up_one_level(path)
        if parent_path == path then
            return nil
        else
            return get_dir_contains(parent_path, dirname)
        end
    end
end

-- copied from clink.lua
-- clink.lua is saved under %CMDER_ROOT%\vendor
local function get_hg_dir(path)
    return get_dir_contains(path, '.hg')
end

-- adopted from clink.lua
-- clink.lua is saved under %CMDER_ROOT%\vendor
function colorful_hg_prompt_filter()

    -- Colors for mercurial status
    local colors = {
        clean = "\x1b[1;37;40m",
        dirty = "\x1b[31;1m",
    }

    if get_hg_dir() then
        -- if we're inside of mercurial repo then try to detect current branch
        local branch = get_hg_branch()
        if branch then
            -- Has branch => therefore it is a mercurial folder, now figure out status
            if get_hg_status() then
                color = colors.clean
            else
                color = colors.dirty
            end

            clink.prompt.value = string.gsub(clink.prompt.value, "{hg}", color.."("..branch..")")
            return false
        end
    end

    -- No mercurial present or not in mercurial file
    clink.prompt.value = string.gsub(clink.prompt.value, "{hg}", "")
    return false
end

-- copied from clink.lua
-- clink.lua is saved under %CMDER_ROOT%\vendor
local function get_git_dir(path)

    -- return parent path for specified entry (either file or directory)
    local function pathname(path)
        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.find_dirs(dir..'/.git') > 0 and dir..'/.git'
    end

    local function has_git_file(dir)
        local gitfile = io.open(dir..'/.git')
        if not gitfile then return false end

        local git_dir = gitfile:read():match('gitdir: (.*)')
        gitfile:close()

        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

---
 -- Get the status of working dir
 -- @return {bool}
---
function get_git_status()
    return os.execute("git diff --quiet --ignore-submodules HEAD")
end

-- adopted from clink.lua
-- Modified to add colors and arrow symbols
function colorful_git_prompt_filter()

    -- Colors for git status
    local colors = {
        clean = "\x1b[34;42m\x1b[37;42m ",
        dirty = "\x1b[34;43m\x1b[30;43m ",
    }

    local closingcolors = {
        clean = " \x1b[32;40m",
        dirty = "± \x1b[33;40m",
    }

    local git_dir = get_git_dir()
    if git_dir then
        -- if we're inside of git repo then try to detect current branch
        local branch = get_git_branch(git_dir)
        if branch then
            -- Has branch => therefore it is a git folder, now figure out status
            if get_git_status() then
                color = colors.clean
                closingcolor = closingcolors.clean
            else
                color = colors.dirty
                closingcolor = closingcolors.dirty
            end

            clink.prompt.value = string.gsub(clink.prompt.value, "{git}", color.."  "..branch..closingcolor)
            return false
        end
    end

    -- No git present or not in git file
    clink.prompt.value = string.gsub(clink.prompt.value, "{git}", "\x1b[34;40m")
    return false
end

---
 -- add virtual env name 
---
function venv_prompt_filter()
    -- add in virtual env name
    local venv = get_virtual_env('VIRTUAL_ENV')
    if venv then
        clink.prompt.value = string.gsub(clink.prompt.value, "λ", "["..venv.."] λ")
    end
end

-- override the built-in filters
clink.prompt.register_filter(lambda_prompt_filter, 55)
clink.prompt.register_filter(colorful_hg_prompt_filter, 60)
clink.prompt.register_filter(colorful_git_prompt_filter, 60)
clink.prompt.register_filter(venv_prompt_filter, 65)

Thanke You!

Cheers Tset

Tset-Noitamotua commented 7 years ago

... if I just could code as good as this looks - damn it!!! :-)))

Font name is (in case somebody cares) Roboto Mono Medium for Powerline, Size 14, Anti-aliasing=ClearType

image

AmrEldib commented 7 years ago

That's great @Tset-Noitamotua I suggest that this becomes its own plugin in a separate repo. This way, those who are interested in the Git integration get that only, and those interested in Python integration get that only. See the README for this project on how to easily install a plugin.

Tset-Noitamotua commented 7 years ago

I did a fork. New repo is here: cmder-powerline-venv-prompt

Cheers Tset

mattdkerr commented 6 years ago

@AmrEldib can we link out to their repository in the readme.md here?

JorgeGRC commented 6 years ago

Hi, I tried your solution @Tset-Noitamotua but unfortunately when I'm activating any of the virtualenvs I get this:

\powerline_venv_prompt.lua:193: attempt to index global 'exports' (a nil value)

image

image

Any idea what might be causing it?

1600 commented 6 years ago

Up with JorgeGRC, debugging right now.

Kbman99 commented 6 years ago

@JorgeGRC Did you ever figure out the cause? I can't seem to find the exports variable in any of the .lua files.

Kbman99 commented 6 years ago

Fixed my issue. For anyone who may be running into the attempt to index global 'exports' (a nil value) . It seems that exports.basename(path) is a global function created in the cmder/vendor/clink-completions/modules/path.lua. I simple copied the function and modified it to be a locally defined function within the cmder/config/powerline_venv_prompt.lua file. Below are the two solutions I have found:

Solution 1: Add local exports = require('path') into the cmder/config/powerline_venv_prompt.lua file.

Solution 2: Insert this code below in the the cmder/config/powerline_venv_prompt.lua file to create a locally defined basename function.

function basename(path)
    local prefix = path
    local i = path:find("[\\/:][^\\/:]*$")
    if i then
        prefix = path:sub(i + 1)
    end
    return prefix
end

Then modify the line which is set to access the function as written here in the same filebasen = exports.basename(env_path) and replace it with basen = basename(env_path).

I'm sure there is another solution to the issue which seems to be causing the script to not be able to access the path.lua script which sets the exports object, but either of these fixed the issue for myself.

steveoh commented 5 years ago

so if cmder supports showing env's by default, and powerline breaks this feature and this enhancement no longer works... what is the proper solution?

this is my solution for conda

local function get_py_env(env_var)
    env = clink.get_env(env_var)
    if env then
        return env
    end

    return false
end

local function get_conda_env_filter()
    local env = get_py_env("CONDA_DEFAULT_ENV")

    if env then
        env = addTextWithColor("", env.." ", ansiFgClrYellow, ansiBgClrBlack)
        clink.prompt.value = string.gsub(clink.prompt.value, plc_prompt_lambSymbol, env..plc_prompt_lambSymbol)
    end

    return false
end

clink.prompt.register_filter(get_conda_env_filter, 100)
xNinjaKittyx commented 5 years ago

Is there a way to add virtualenv next to the git as another prompt module?

Also anyone have a working solution for pipenv (without the hash values)

sn0rlacks commented 5 years ago

So, Is this something that could work with the portable version?

Tset-Noitamotua commented 5 years ago

I have not tested it with the portable version.

Josh Chrestman notifications@github.com schrieb am Di., 2. Apr. 2019, 17:30:

So, Is this something that could work with the portable version?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/AmrEldib/cmder-powerline-prompt/issues/5#issuecomment-479052160, or mute the thread https://github.com/notifications/unsubscribe-auth/AFX9hv681Pd-TCotBiGeafsyT6rIK641ks5vc3d0gaJpZM4LZ5xN .

sn0rlacks commented 5 years ago

@Tset-Noitamotua A simple creation of the pyenv_prompt_config.lua didn't do anything. No errors, and the env seems to have been activated, but theres no change in the console.

Im also using activate rather than workon... not sure if clink cares about that? Very unfamiliar with all this lua config.

Tset-Noitamotua commented 5 years ago

Me, too have no clue about lua :-) maybe the maintainer of the repo I forked from can help you.

Actually I switched to Linux cause I was tired of all the extra steps needed when it comes to setup all the cool stuff. It was always like: u see something cool on Mac or Linux then have to go and find a workaround to achive the same on Windows :-(

Josh Chrestman notifications@github.com schrieb am Di., 2. Apr. 2019, 17:42:

@Tset-Noitamotua https://github.com/Tset-Noitamotua A simple creation of the pyenv_prompt_config.lua didn't do anything. No errors, and the env seems to have been activated, but theres no change in the console.

Im also using activate rather than workon... not sure if clink cares about that? Very unfamiliar with all this lua config.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/AmrEldib/cmder-powerline-prompt/issues/5#issuecomment-479058769, or mute the thread https://github.com/notifications/unsubscribe-auth/AFX9hktI6EQEatqpAqU_0I-J6sbMxQcnks5vc3pwgaJpZM4LZ5xN .