#!/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 SQL=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\]" local ARROW_COLOR="35" (( ${LAST_EXIT_CODE} != 0 )) && ARROW_COLOR="32" local USER_PROMPT="\[\033[0;31m\]╰─\[\033[${ARROW_COLOR}m\]❯\[\033[0m\]" PS1="\n${HOUR} ${USER} ${FOLDER} ${INFO}\n${USER_PROMPT} " } PROMPT_COMMAND=gen_prompt