commit 14423a6dd15fcde59cd1afd65c4122e7813f19cd Author: Ulysse Cura Date: Tue Oct 28 01:16:13 2025 +0100 Initial commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..7ab6131 --- /dev/null +++ b/README.md @@ -0,0 +1,42 @@ +# CommandPrompt + +## Description + +This CommandPrompt is an *highly* customised prompt for bash + +It show the time, user, current directory and an info. +The info is an icon if pwd is in the configured special folders (see more below). +However if pwd is in the directory or sub-directory of a project, it will find by probabilities +where the project root is and show the project's folder name. +It also detect the majority language in the current directory and displays an icon depending on it ! + +For the moment the supported language are : + - C + - C++ + - Rust + - Go + - Python + - Java + - Ruby + - Javascript + - PHP + - C# + - HTML/CSS (considered same language) + +## How to install + +You can install this prompt by copying the file to any directory you want (for example ~/.config/bash/) +and calling with **source** the file at the end of your .bashrc or .profile depending on your system. + +If your system is in a different language you can change the special folder names direcly at the beginning +of the file. + +## Internal commands + +This command prompt include 2 internal command that you can call manually if you want to +manipulate cache or debug the prompt. +These are : + - find_project_root_dir + - find_dir_majority_language + +For more info call them with --help. diff --git a/media/prompt showcase.gif b/media/prompt showcase.gif new file mode 100644 index 0000000..dc5082d Binary files /dev/null and b/media/prompt showcase.gif differ diff --git a/prompt b/prompt new file mode 100644 index 0000000..be0d5c1 --- /dev/null +++ b/prompt @@ -0,0 +1,324 @@ +#!/bin/env bash + +# folder names, change it depending on your language +documents="Documents" +music="Music" +pictures="Pictures" +videos="Videos" +downloads="Downloads" + +declare -A PROJECT_ROOT_CACHE +export FIND_PROJECT_ROOT_DEBUG=0 + +find_project_root() { + local args=${@} + local cache_key="cache_$(echo -n $PWD | md5sum | cut -d' ' -f1)" + + # args detection + for arg in ${args}; do + if [[ "${arg:0:2}" = "--" ]]; then + arg="${arg:2}" + + case "${arg}" in + "help") + echo -e "Usage : find_project_root [OPTION(S)]\n" + echo "Find the folder that could be a project root." + echo "It is automatically called by the prompt generator but you can call it if you want to access or modify the cache.\n" + echo "The result is stocked in the env variable PROJECT_ROOT. The exit value is 0 when project root found or 1 if not." + echo "Options :" + echo " --print-cache print project root cache" + echo " --clear-cache clear project root cache ; WARNING : the cache is wiped automatically in new bash instances." + echo " --toggle-debug toggle debug mode" + echo " --help display this help" + return 0 ;; + + "print-cache") + for key in ${!PROJECT_ROOT_CACHE[@]}; do + echo "[${key}]=${PROJECT_ROOT_CACHE[$key]}" + done ;; + + "clear-cache") + PROJECT_ROOT_CACHE=() ;; + + "toggle-debug") + FIND_PROJECT_ROOT_DEBUG=$((! FIND_PROJECT_ROOT_DEBUG)) ;; + + *) + echo -e "Error : unrecognised parameter : --${arg}\n Try typing --help" ;; + esac + else + echo -e "Error : unrecognised parameter : ${arg}\n Try typing --help" + fi + done + + # search cache for existing data + if [[ -n "${PROJECT_ROOT_CACHE[$cache_key]}" ]]; then + if [[ "${PROJECT_ROOT_CACHE[$cache_key]}" != "." ]]; then + PROJECT_ROOT="${PROJECT_ROOT_CACHE[$cache_key]}" + return 0 + fi + + PROJECT_ROOT="" + return 1 + fi + + # estimate probability of root dir + local depth=0 + local current_dir="$PWD" + local current_max_score=0 + local current_dir_score=0 + local project_root="." + + while (( depth < 10 )) && [[ "${current_dir}" != "/" ]]; do + current_dir_score=0 + + # 100% root + for path in README.md README.rst README LICENSE \ + Dockerfile docker-compose.yml \ + .git; do + if [[ -e "${current_dir}/${path}" ]]; then + (( FIND_PROJECT_ROOT_DEBUG )) && echo "${path}" + ((current_dir_score += 100)) + fi + done + + # 90% root + for path in CMakeLists.txt \ + Cargo.toml Cargo.lock \ + go.mod go.sum \ + pyproject.toml \ + pom.xml \ + package.json \ + .csproj .sln; do + if [[ -e "${current_dir}/${path}" ]]; then + (( FIND_PROJECT_ROOT_DEBUG )) && echo "${path}" + ((current_dir_score += 90)) + fi + done + + # 75% root + for path in Makefile \ + requirements.txt \ + build.gradle \ + Gemfile \ + tsconfig.json \ + composer.json \ + .gitignore; do + if [[ -e "${current_dir}/${path}" ]]; then + (( FIND_PROJECT_ROOT_DEBUG )) && echo "${path}" + ((current_dir_score += 75)) + fi + done + + # 50% root + for path in webpack.config.js vite.config.js babel.config.js .babelrc rollup.config.js \ + lib include config scripts bin src third_party 3rd_party vendor \ + .env .env.local \ + .dockerignore; do + if [[ -e "${current_dir}/${path}" ]]; then + (( FIND_PROJECT_ROOT_DEBUG )) && echo "${path}" + ((current_dir_score += 50)) + fi + done + + # 25% root + for path in configure \ + Gopkg.toml \ + Pipfile Pipfile.lock setup.py tox.ini .python-version \ + build.xml settings.gradle \ + packages.config \ + Directory.Build.props \ + .htmlhintrc .stylelintrc \ + Gemfile.lock \ + composer.lock \ + Rakefile; do + if [[ -e "${current_dir}/${path}" ]]; then + (( FIND_PROJECT_ROOT_DEBUG )) && echo "${path}" + ((current_dir_score += 25)) + fi + done + + if (( current_dir_score >= current_max_score )) && (( current_dir_score > 50 )); then + current_max_score=${current_dir_score} + project_root=${current_dir} + fi + + (( FIND_PROJECT_ROOT_DEBUG )) && echo -e "${depth} : ${current_dir}\n score : ${current_dir_score}" + + current_dir=$(dirname "$current_dir") + ((depth++)) + done + + # save data to cache + PROJECT_ROOT_CACHE[${cache_key}]="${project_root}" + + if [[ "${project_root}" != "." ]]; then + PROJECT_ROOT="${project_root}" + return 0 + fi + + PROJECT_ROOT="" + return 1 +} + +declare -A DIR_MAJORITY_LANGUAGE_CACHE +export FIND_DIR_MAJORITY_LANGUAGE_DEBUG=0 + +find_dir_majority_language() { + local args=${@} + local cache_key="cache_$(echo -n $PWD | md5sum | cut -d' ' -f1)" + + # args detection + for arg in ${args}; do + if [[ "${arg:0:2}" = "--" ]]; then + arg="${arg:2}" + + case "${arg}" in + "help") + echo -e "Usage : find_dir_majority_language [OPTION(S)]\n" + echo "Find the majoritary language in the current and sub-folders." + echo "It is automatically called by the prompt generator but you can call it if you want to access or modify the cache.\n" + echo "The result is stocked in the env variable DIR_MAJORITY_LANGUAGE. The exit value is 0 when project root found or 1 if not." + echo "Options :" + echo " --print-cache print majority language cache" + echo " --clear-cache clear majority language cache ; WARNING : the cache is wiped automatically in new bash instances." + echo " --toggle-debug toggle debug mode" + echo " --help display this help" + return 0 ;; + + "print-cache") + for key in ${!DIR_MAJORITY_LANGUAGE_CACHE[@]}; do + echo "[${key}]=${DIR_MAJORITY_LANGUAGE_CACHE[$key]}" + done ;; + + "clear-cache") + DIR_MAJORITY_LANGUAGE_CACHE=() ;; + + "toggle-debug") + FIND_DIR_MAJORITY_LANGUAGE_DEBUG=$((! FIND_DIR_MAJORITY_LANGUAGE_DEBUG)) ;; + + *) + echo -e "Error : unrecognised parameter : --${arg}\n Try typing --help" ;; + esac + else + echo -e "Error : unrecognised parameter : ${arg}\n Try typing --help" + fi + done + + # search cache for existing data + if [[ -n "${DIR_MAJORITY_LANGUAGE_CACHE[$cache_key]}" ]]; then + DIR_MAJORITY_LANGUAGE="${DIR_MAJORITY_LANGUAGE_CACHE[$cache_key]}" + return + fi + + # in case of equal quantity of per-language file, the majoritary language is arbitrary selected + local C=0 Cpp=0 Rust=0 Go=0 Python=0 Java=0 Ruby=0 Javascript=0 PHP=0 CS=0 HTMLCSS=0 + local ext="" + + for file in "$PWD"/*.* "$PWD"/*/*.* "$PWD"/*/*/*.*; do + #[[ -f "$file" ]] || continue + local ext="${file##*.}" + [[ "${ext}" = "${file}" ]] && continue + + case "${ext}" in + c|h) ((C++)) ;; + cpp|cxx|cc|hpp|hxx|hh|h) ((Cpp++)) ;; + rs|rlib) ((Rust++)) ;; + go) ((Go++)) ;; + py|pyw|pyc|pyo) ((Python++)) ;; + java|class|jar|war) ((Java++)) ;; + rb|erb|rake) ((Ruby++)) ;; + js|jsx|ts|tsx|mjs|cjs) ((Javascript++)) ;; + php|phtml) ((PHP++)) ;; + cs|csproj|sln) ((CS++)) ;; + html|htm|css|scss|sass|less)((HTMLCSS++)) ;; + esac + done + + if (( FIND_DIR_MAJORITY_LANGUAGE_DEBUG )); then + echo "C: ${C}" + echo "C++: ${Cpp}" + echo "Rust: ${Rust}" + echo "Go: ${Go}" + echo "Python: ${Python}" + echo "Java: ${Java}" + echo "Ruby: ${Ruby}" + echo "Javascript: ${Javascript}" + echo "PHP: ${PHP}" + echo "C#: ${CS}" + echo "HTML/CSS: ${HTMLCSS}" + fi + + local max_count=0 + DIR_MAJORITY_LANGUAGE="" + + (( C > max_count )) && max_count=${C} && DIR_MAJORITY_LANGUAGE="C" + (( Cpp > max_count )) && max_count=${Cpp} && DIR_MAJORITY_LANGUAGE="C++" + (( Rust > max_count )) && max_count=${Rust} && DIR_MAJORITY_LANGUAGE="Rust" + (( Go > max_count )) && max_count=${Go} && DIR_MAJORITY_LANGUAGE="Go" + (( Python > max_count )) && max_count=${Python} && DIR_MAJORITY_LANGUAGE="Python" + (( Java > max_count )) && max_count=${Java} && DIR_MAJORITY_LANGUAGE="Java" + (( Ruby > max_count )) && max_count=${Ruby} && DIR_MAJORITY_LANGUAGE="Ruby" + (( Javascript > max_count )) && max_count=${Javascript} && DIR_MAJORITY_LANGUAGE="Javascript" + (( PHP > max_count )) && max_count=${PHP} && DIR_MAJORITY_LANGUAGE="PHP" + (( CS > max_count )) && max_count=${CS} && DIR_MAJORITY_LANGUAGE="C#" + (( HTMLCSS > max_count )) && max_count=${HTMLCSS} && DIR_MAJORITY_LANGUAGE="HTML/CSS" + + # save data to cache + DIR_MAJORITY_LANGUAGE_CACHE[${cache_key}]="${DIR_MAJORITY_LANGUAGE}" +} + +gen_prompt() { + local LAST_EXIT_CODE=$? + + local HOUR="\[\033[0;31m\]╭─\[\033[7;47m\]\A" + local USER="\[\033[45m\]\[\033[1;27;30m\]\u\[\033[7m\]" + local FOLDER="\[\033[0;36m\]\[\033[1;7m\]\w" + + local INFO=" " + if find_project_root; then + find_dir_majority_language + local language_icon="󰡯 " + case "$DIR_MAJORITY_LANGUAGE" in + "C") language_icon=" " ;; + "C++") language_icon=" " ;; + "Rust") language_icon=" " ;; + "Go") language_icon="󰟓 " ;; + "Python") language_icon=" " ;; + "Java") language_icon=" " ;; + "Ruby") language_icon=" " ;; + "Javascript") language_icon=" " ;; + "PHP") language_icon="󰌟 " ;; + "C#") language_icon="󰌛 " ;; + "HTML/CSS") language_icon=" " ;; + esac + INFO="${language_icon}$(basename "${PROJECT_ROOT}")" + elif [ "$HOME" = "$PWD" ]; then + INFO="󱂵 " + elif [ "$HOME/${documents}" = "$PWD" ]; then + INFO="󱧶 " + elif [ "$HOME/${music}" = "$PWD" ]; then + INFO="󱍙 " + elif [ "$HOME/${pictures}" = "$PWD" ]; then + INFO="󰉏 " + elif [ "$HOME/${videos}" = "$PWD" ]; then + INFO="󰉏 " + elif [ "$HOME/${downloads}" = "$PWD" ]; then + INFO="󰉍 " + elif [ "$HOME/.config" = "$PWD" ]; then + INFO="󱧼 " + elif [ "$HOME/.local" = "$PWD" ]; then + INFO="󰉌 " + elif mount | grep -q "$PWD"; then + INFO="󰉉 " + fi + INFO="\[\033[45m\]\[\033[1;27;30m\]${INFO}\[\033[7m\]\033[m" + + local ARROW_COLOR="35" + (( ${LAST_EXIT_CODE} != 0 )) && ARROW_COLOR="32" + local USER_PROMPT="\[\033[31m\]╰─\[\033[${ARROW_COLOR}m\]❯\[\033[0m\]" + + PS1="\n${HOUR} ${USER} ${FOLDER} ${INFO}\n${USER_PROMPT} " +} + +PROMPT_COMMAND=gen_prompt