1
0
mirror of https://gitlab.com/ceda_ei/wish synced 2025-10-22 22:40:05 +02:00

1 Commits

Author SHA1 Message Date
dfd0baaf6a Replace escape codes with tput 2019-03-02 12:25:03 +05:30
19 changed files with 89 additions and 860 deletions

3
.gitignore vendored
View File

@@ -1,3 +0,0 @@
__pycache__/
*.pyc
config.gie

100
README.md
View File

@@ -1,110 +1,24 @@
# Wish
A customizable, extensible, themable bash prompt.
![](https://webionite.com/wish/images/wish.png)
## Features
+ **Extensible**: Parts can be added using simple plugins.
+ **Themable**: Choose from an existing theme or create your own.
+ **Customizable**: The prompt can be customized via a simple config file.
+ **Right Prompt**: Allows for two prompts — one on left, one on right.
+ **Auto newline**: Automatically adds a newline if the last output doesn't end with one.
+ **Powerline support**: Supports powerline symbols in both prompts.
A customizable bash prompt.
# Installation
## One step Installation
```sh
curl https://gitlab.com/ceda_ei/wish/raw/master/install.sh | bash
```
`curl https://gitlab.com/ceda_ei/wish/raw/master/install.sh | bash`
## Manual Installation
+ `cd ~/.config/`
+ `git clone https://gitlab.com/ceda_ei/wish.git`
+ `cp wish/config.default.gie wish/config.gie`
# Customization
Customization of wish is done using a gINIe file (format specific to wish)
which is an extended INI config file. The default location for config file is
`~/.config/wish/config.gie`. The default config file is automatically copied
over. The location can be customized using `WISH_CONFIG_FILE` variable.
Multiple config files can be supplied as an array to `WISH_CONFIG_FILE`.
Comments start with a `#` or `;` and always exist on a line of their own.
Key value pairs are in the form of `key = value`. Keys cannot have whitespaces
or `=` in them. Values can have any character as part of them. Surrounding
spaces in values are stripped away. To keep surrounding spaces as a part of
the value. Although, keys are case-sensitive in gINIe, wish treats them case-
insensitively.
Block names are enclosed in `[]` (e.g. `[core]`). Block names are case
sensitive. All key value pairs after a block starts and before the next block
begins are considered a part of that block. All key value pairs must be in a
block.
Available Blocks:
+ `core`: Core block configures Wish itself. Available keys are:
- `auto_newline`: Automatically add a newline if last line of output
doesn't end in newline. (0 to disable, 1 to enable)
- `theme`: Wish theme.
- `powerline`: Enable / Disable powerline. (0 to disable, 1 to enable)
+ `plugin`: Adds a plugin to the section the block is added to. All config for
that plugin goes there. Check wiki for details on available keys for
plugins. The key "name" defines the plugin to use. Plugin blocks
outside a section are ignored.
Section names are enclosed in `||` (e.g. `|left|`). All blocks after a section
starts and before the next section begins are considered a part of that
section. Blocks don't necessarily need to be in a section.
Available sections are `left`, `right` for left prompt and right prompt
respectively.
Sample config file
+ Add the following lines to ~/.bashrc
```bash
WISH_PLUGINS=(exit_code_smiley bg_jobs date path newline vcs)
WISH_THEME=plain
source ~/.config/wish/wish.sh
```
[core]
auto_newline = 1
powerline = 1
theme = rgb_gradient
|left|
[plugin]
name = path_powerline
max_perc = 50
[plugin]
name = python_venv
[plugin]
name = newline
[plugin]
name = custom_text
text = ' λ '
[plugin]
name = space
|right|
[plugin]
name = date
format = %a %b %H:%M
[plugin]
name = hostname
```
Which results in the following prompt
![](https://webionite.com/wish/images/prompt.png)
# Wiki

View File

@@ -1,59 +0,0 @@
# vim: set ft=dosini:
#
# Comments start with a # or ; and always exist on a line of their own.
#
# Key value pairs are in the form of key = value. Keys cannot have whitespaces
# or = in them. Values can have any character as part of them. Surrounding
# spaces in values are stripped away. To keep surrounding spaces as a part of
# the value. Although, keys are case-sensitive in gINIe, wish treats them case-
# insensitively.
#
# Block names are enclosed in [] (e.g. [core]). Block names are case sensitive.
# All key value pairs after a block starts and before the next block begins are
# considered a part of that block. All key value pairs must be in a block.
#
# Available Blocks:
#
# core: Core block configures Wish itself. Available keys are:
# - auto_newline: Automatically add a newline if last line of output
# doesn't end in newline. (0 to disable, 1 to enable)
# - theme: Wish theme.
# - powerline: Enable / Disable powerline. (0 to disable, 1 to enable)
#
# plugin: Adds a plugin to the section the block is added to. All config for
# that plugin goes there. The key "name" defines the plugin to use.
# Plugin blocks outside a section are ignored.
#
# Section names are enclosed in || (e.g. |left|). All blocks after a section
# starts and before the next section begins are considered a part of that
# section. Blocks don't necessarily need to be in a section.
#
# Available sections are left, right for left prompt and right prompt
# respectively.
[core]
auto_newline = 1
powerline = 1
theme = plain
|left|
[plugin]
name = exit_code_smiley
[plugin]
name = bg_jobs
[plugin]
name = date
format = %d %b %H:%M
[plugin]
name = path
[plugin]
name = newline
[plugin]
name = vcs
|right|

135
ginie.py
View File

@@ -1,135 +0,0 @@
#!/usr/bin/env python3
"gINIe Parser for Wish"
import re
import sys
class GinieParseError(ValueError):
"Exception Class for errors in"
def loads(string):
"""
Deserialize `string` (a `str` instance containing a gINIe document)
into a Python list.
Return Value: List of `block` and `section` dicts
`block` dicts are of form
{
'type': 'block',
'name': 'name_of_block_as_in_config',
'config': {}
}
where `name` and `config` keys are based on the gINIe document.
`section` dicts are of form
{
'type': 'block',
'name': 'name_of_block_as_in_config',
'blocks': []
}
where `name` and `blocks` keys are based on the gINIe document and blocks
is a list of block dicts found in the section.
"""
config = []
section_re = re.compile(r"^\|(?P<section>.+)\|$")
block_re = re.compile(r"^\[(?P<block>[^\]]*)\]$")
line_re = re.compile(r"^\s*(?P<key>[^\s=]+)(\s*)=\s*?(?P<value>.*)$")
empty_re = re.compile(r"^\s*$")
comment_re = re.compile(r"^\s*(#|;)")
current_block = None
current_section = None
for idx, line in enumerate(string.splitlines()):
idx += 1 # Since line numbers begin with 1
# Skip comments and empty lines
if empty_re.match(line) or comment_re.match(line):
continue
# Section parsing
if line.startswith("|"):
match = section_re.match(line)
if match is None:
err = "Invalid line {}".format(idx)
raise GinieParseError(err)
current_section = {
"type": "section",
"name": match.group("section"),
"blocks": []
}
config.append(current_section)
continue
# Block Parsing
if line.startswith("["):
match = block_re.match(line)
if match is None:
err = "Invalid block name on line {}".format(idx)
raise GinieParseError(err)
current_block = {
"type": "block",
"name": match.group("block"),
"config": {}
}
if current_section is None:
config.append(current_block)
else:
current_section["blocks"].append(current_block)
continue
# If it is neither a comment, nor a section, nor a block, it has to be
# a key, value pair.
if current_block is None:
raise GinieParseError("Found lines outside a block")
match = line_re.match(line)
if match is None:
raise GinieParseError("Invalid line {}: {}".format(idx, line))
value = match.group('value').strip()
if value:
if value[0] == value[-1] == "'" or value[0] == value[-1] == '"':
value = value[1:-1]
current_block["config"][match.group('key').strip()] = value
return config
def load(file):
"""
Deserialize `file` (a file-like object containing a gINIe document)
into a Python list.
Return Value: List of `block` and `section` dicts
`block` dicts are of form
{
'type': 'block',
'name': 'name_of_block_as_in_config',
'config': {}
}
where `name` and `config` keys are based on the gINIe document.
`section` dicts are of form
{
'type': 'block',
'name': 'name_of_block_as_in_config',
'blocks': []
}
where `name` and `blocks` keys are based on the gINIe document and blocks
is a list of block dicts found in the section.
"""
return loads(file.read())
if __name__ == "__main__":
if len(sys.argv) == 1:
print("Usage: {} file.gie|-".format(sys.argv[0]))
elif sys.argv[1] == '-':
print(load(sys.stdin))
else:
with open(sys.argv[1]) as source:
print(load(source))

View File

@@ -1,20 +1,16 @@
#!/usr/bin/env bash
if hash git; then
git clone https://gitlab.com/ceda_ei/Wish.git $HOME/.config/wish
else
curl https://gitlab.com/ceda_ei/wish/-/archive/master/wish-master.tar -o /tmp/wish.tar
mkdir $HOME/.config 2> /dev/null
cd /tmp/
tar xf wish.tar
mv wish-master/ wish/
mv wish/ $HOME/.config/
fi
cp $HOME/.config/wish/config.default.gie $HOME/.config/wish/config.gie
curl https://gitlab.com/ceda_ei/wish/-/archive/master/wish-master.tar -o /tmp/wish.tar
cd /tmp/
tar xf wish.tar
mv wish-master/ wish/
mv wish/ $HOME/.config/
cat >> ~/.bashrc <<EOF
# Wish
WISH_CONFIG_FILE="$HOME/.config/wish/config.gie"
source $HOME/.config/wish/wish.sh
WISH_PLUGINS=(exit_code_smiley bg_jobs date path newline vcs)
WISH_THEME=plain
source ~/.config/wish/wish.sh
EOF

View File

@@ -16,17 +16,12 @@ function wish_battery_set_colors() {
function wish_battery_main() {
local path=/sys/class/power_supply/$WISH_BATTERY_ID
local battery
if [[ -f $path/charge_now ]]; then
battery=$(($(cat $path/charge_now) * 100 / $(cat $path/charge_full)))%
if [[ $(cat $path/status) == "Charging" ]] ||
[[ $(cat $path/status) == "Full" ]]; then
battery="$WISH_BATTERY_CHARGING $battery"
else
battery="$WISH_BATTERY_DISCHARGING $battery"
fi
local battery=$(($(cat $path/charge_now) * 100 / $(cat $path/charge_full)))%
if [[ $(cat $path/status) == "Charging" ]] ||
[[ $(cat $path/status) == "Full" ]]; then
battery="$WISH_BATTERY_CHARGING $battery"
else
battery="$WISH_BATTERY_ID not found"
battery="$WISH_BATTERY_DISCHARGING $battery"
fi
wish_append $WISH_BATTERY_BG $WISH_BATTERY_FG " $battery "
}

View File

@@ -9,10 +9,10 @@ function wish_custom_text_end() {
function wish_custom_text_set_colors() {
WISH_CUSTOM_TEXT_FG=${WISH_CUSTOM_TEXT_FG:-$WISH_DEFAULT_FG}
WISH_CUSTOM_TEXT_BG=${WISH_CUSTOM_TEXT_BG:-$WISH_DEFAULT_BG}
local default_text='To set custom text here, add text="your text" in your config'
WISH_CUSTOM_TEXT_TEXT=${WISH_CUSTOM_TEXT_TEXT:-$default_text}
local default_text='To set custom text here, add WISH_CUSTOM_TEXT="your text" in ~/.bashrc'
WISH_CUSTOM_TEXT=${WISH_CUSTOM_TEXT:-$default_text}
}
function wish_custom_text_main() {
wish_append $WISH_CUSTOM_TEXT_BG $WISH_CUSTOM_TEXT_FG "$WISH_CUSTOM_TEXT_TEXT"
wish_append $WISH_CUSTOM_TEXT_BG $WISH_CUSTOM_TEXT_FG "$WISH_CUSTOM_TEXT"
}

View File

@@ -11,14 +11,11 @@ function wish_exit_code_smiley_set_colors() {
WISH_EXIT_CODE_SMILEY_BG=${WISH_EXIT_CODE_SMILEY_BG:-$WISH_DEFAULT_BG}
WISH_EXIT_CODE_SMILEY_PASS=${WISH_EXIT_CODE_SMILEY_PASS:- :) }
WISH_EXIT_CODE_SMILEY_FAIL=${WISH_EXIT_CODE_SMILEY_FAIL:- :( }
WISH_EXIT_CODE_SMILEY_HIDE_ON_PASS=${WISH_EXIT_CODE_SMILEY_HIDE_ON_PASS:-0}
}
function wish_exit_code_smiley_main() {
if [[ $1 == 0 ]]; then
if [[ $WISH_EXIT_CODE_SMILEY_HIDE_ON_PASS == 0 ]]; then
wish_append $WISH_EXIT_CODE_SMILEY_BG $WISH_EXIT_CODE_SMILEY_FG "$WISH_EXIT_CODE_SMILEY_PASS"
fi
wish_append $WISH_EXIT_CODE_SMILEY_BG $WISH_EXIT_CODE_SMILEY_FG "$WISH_EXIT_CODE_SMILEY_PASS"
else
wish_append $WISH_EXIT_CODE_SMILEY_BG $WISH_EXIT_CODE_SMILEY_FG "$WISH_EXIT_CODE_SMILEY_FAIL"
fi

View File

@@ -1,59 +0,0 @@
function wish_path_powerline_start() {
return 0
}
function wish_path_powerline_end() {
return 0
}
function wish_path_powerline_set_colors() {
WISH_PATH_POWERLINE_FG=${WISH_PATH_POWERLINE_FG:-$WISH_DEFAULT_FG}
WISH_PATH_POWERLINE_BG=${WISH_PATH_POWERLINE_BG:-$WISH_DEFAULT_BG}
WISH_PATH_POWERLINE_NO_WRITE_SUFFIX=${WISH_PATH_POWERLINE_NO_WRITE_SUFFIX:-}
WISH_PATH_POWERLINE_MAX_PERC=${WISH_PATH_POWERLINE_MAX_PERC:-0}
}
function wish_path_lensum() {
sum=0
for i in $*; do
sum=$(($sum + ${#i}))
done
echo $sum
}
function wish_path_powerline_shrink() {
local IFS='/'
local path=( $1 )
local max=$(( $2 - 3 * ${#path[@]} ))
for ((i=0; i <$((${#path[@]} - 1)); i++)); do
path[$i]=${path[$i]:0:1}
if [[ $(wish_path_lensum ${path[@]}) -lt $max ]]; then
local short_path=""
for i in ${path[@]}; do
short_path+="$i/"
done
echo "${short_path%/}"
return
fi
done
local short_path=""
for i in ${path[@]}; do
short_path+="$i/"
done
echo "${short_path%/}"
}
function wish_path_powerline_main() {
local path="${PWD/#$HOME/\~}"
if [[ $WISH_PATH_POWERLINE_MAX_PERC -ne 0 && ${#path} -ge $WISH_PATH_POWERLINE_MAX_LEN ]] ; then
local max=$(($WISH_PATH_POWERLINE_MAX_PERC * $(wish_remaining_chars) / 100))
local path=$(wish_path_powerline_shrink "$path" "$max")
fi
local path="${path//\//  }"
if [[ -w $PWD ]]; then
local path=" $path "
else
local path=" $path$WISH_PATH_POWERLINE_NO_WRITE_SUFFIX "
fi
wish_append $WISH_PATH_POWERLINE_BG $WISH_PATH_POWERLINE_FG "$path"
}

View File

@@ -13,7 +13,7 @@ function wish_python_venv_set_colors() {
function wish_python_venv_main() {
if [[ -v VIRTUAL_ENV ]]; then
local venv=$(echo $VIRTUAL_ENV | sed ';s|/.venv$||;s|.*/||')
local venv=$(echo $VIRTUAL_ENV | sed 's|.*/||')
wish_append $WISH_PYTHON_VENV_BG $WISH_PYTHON_VENV_FG " $venv "
else
wish_append $WISH_PYTHON_VENV_BG $WISH_PYTHON_VENV_FG ""

View File

@@ -10,12 +10,12 @@ function wish_vcs_set_colors() {
WISH_VCS_FG=${WISH_VCS_FG:-$WISH_DEFAULT_FG}
WISH_VCS_BG=${WISH_VCS_BG:-$WISH_DEFAULT_BG}
WISH_VCS_GIT_SYMBOL=${WISH_VCS_GIT:-}
[[ -v WISH_VCS_DEFAULT ]] || WISH_VCS_DEFAULT="$"
WISH_VCS_DEFAULT=${WISH_VCS_DEFAULT:-$}
WISH_VCS_GIT_UNTRACKED_SYMBOL=${WISH_VCS_GIT_UNTRACKED_SYMBOL:-}
}
function wish_vcs_main() {
local op=$(git diff --numstat 2> /dev/null || echo -1)
local op=$(git diff --numstat HEAD 2> /dev/null || echo -1)
if [[ $op != "-1" ]]; then
local git
git="$git $WISH_VCS_GIT_SYMBOL "
@@ -32,7 +32,7 @@ function wish_vcs_main() {
fi
wish_append $WISH_VCS_BG $WISH_VCS_FG "$git"
else
wish_append $WISH_VCS_BG $WISH_VCS_FG "$WISH_VCS_DEFAULT"
wish_append $WISH_VCS_BG $WISH_VCS_FG " $WISH_VCS_DEFAULT "
fi
}

View File

@@ -1,27 +0,0 @@
#!/usr/bin/env bash
local i
local gradient=(226 118 37 66 60 237 233)
local fg_gradient=(16 16 16 16 16 15 15)
local j=0
for i in ${WISH_PLUGINS[@]}; do
[[ $i =~ newline$ ]] && j=0 && continue
eval WISH_${i^^}_BG=${gradient[$j]}
eval WISH_${i^^}_FG=${fg_gradient[$j]}
((j++))
if [[ $j -eq ${#gradient[@]} ]]; then
j=0
fi
done
j=0
for ((idx=$((${#WISH_RIGHT_PLUGINS[@]} - 1)); idx >= 0; idx--)); do
i=${WISH_RIGHT_PLUGINS[$idx]}
[[ $i =~ newline$ ]] && j=0 && continue
eval WISH_${i^^}_BG=${gradient[$j]}
eval WISH_${i^^}_FG=${fg_gradient[$j]}
((j++))
if [[ $j -eq ${#gradient[@]} ]]; then
j=0
fi
done

View File

@@ -1,32 +0,0 @@
#!/usr/bin/env bash
function wish_icy_candy_theme {
local i
local l_gradient=(f5a9b8 f6b7c3 f8c5cf fad4db fbe2e7 fdf0f3 ffffff)
local r_gradient=(5bcefa 76d6fa 91defb ade6fc c8eefd e3f6fe ffffff)
local fg_gradient=(000000 000000 000000 000000 000000 000000 000000)
local j=0
for i in "${WISH_PLUGINS[@]}"; do
[[ $i =~ newline$ ]] && j=0 && continue
eval "WISH_${i^^}_FG=${fg_gradient[$j]}"
eval "WISH_${i^^}_BG=${l_gradient[$j]}"
((j++))
if [[ $j -eq ${#l_gradient[@]} ]]; then
j=0
fi
done
j=0
for ((idx=$((${#WISH_RIGHT_PLUGINS[@]} - 1)); idx >= 0; idx--)); do
i=${WISH_RIGHT_PLUGINS[$idx]}
[[ $i =~ newline$ ]] && j=0 && continue
eval "WISH_${i^^}_BG=${r_gradient[$j]}"
eval "WISH_${i^^}_FG=${fg_gradient[$j]}"
((j++))
if [[ $j -eq ${#r_gradient[@]} ]]; then
j=0
fi
done
}
wish_icy_candy_theme

View File

@@ -1,27 +0,0 @@
#!/usr/bin/env bash
WISH_POWERLINE=0
local i
local gradient=(e7c547 c0e551 82e35a 62e177 6bdfb3 73d4dd 7aa6da)
local j=0
for i in ${WISH_PLUGINS[@]}; do
[[ $i =~ newline$ ]] && j=0 && continue
eval WISH_${i^^}_BG=-1
eval WISH_${i^^}_FG=${gradient[$j]}
((j++))
if [[ $j -eq ${#gradient[@]} ]]; then
j=0
fi
done
j=0
for ((idx=$((${#WISH_RIGHT_PLUGINS[@]} - 1)); idx >= 0; idx--)); do
i=${WISH_RIGHT_PLUGINS[$idx]}
[[ $i =~ newline$ ]] && j=0 && continue
eval WISH_${i^^}_BG=-1
eval WISH_${i^^}_FG=${gradient[$j]}
((j++))
if [[ $j -eq ${#gradient[@]} ]]; then
j=0
fi
done

View File

@@ -1,31 +0,0 @@
#!/usr/bin/env bash
function wish_luminous_theme {
local i
local gradient=(ef7126 f7894c fea16f ffb892 ffd0b5 ffe7da ffffff)
local fg_gradient=(000000 000000 000000 000000 000000 000000 000000)
local j=0
for i in "${WISH_PLUGINS[@]}"; do
[[ $i =~ newline$ ]] && j=0 && continue
eval "WISH_${i^^}_FG=${fg_gradient[$j]}"
eval "WISH_${i^^}_BG=${gradient[$j]}"
((j++))
if [[ $j -eq ${#gradient[@]} ]]; then
j=0
fi
done
j=0
for ((idx=$((${#WISH_RIGHT_PLUGINS[@]} - 1)); idx >= 0; idx--)); do
i=${WISH_RIGHT_PLUGINS[$idx]}
[[ $i =~ newline$ ]] && j=0 && continue
eval "WISH_${i^^}_BG=${gradient[$j]}"
eval "WISH_${i^^}_FG=${fg_gradient[$j]}"
((j++))
if [[ $j -eq ${#gradient[@]} ]]; then
j=0
fi
done
}
wish_luminous_theme

View File

@@ -1,7 +1,7 @@
WISH_DEFAULT_BG=-1
WISH_DEFAULT_FG=-1
WISH_POWERLINE=0
for i in ${WISH_PLUGINS[@]} ${WISH_RIGHT_PLUGINS[@]}; do
eval WISH_${i^^}_BG=-1
eval WISH_${i^^}_FG=-1
for i in ${WISH_PLUGINS[@]}; do
eval WISH_$(echo $i | tr '[:lower:]' '[:upper:]')_BG=-1
eval WISH_$(echo $i | tr '[:lower:]' '[:upper:]')_FG=-1
done

View File

@@ -3,23 +3,12 @@
local i
local gradient=(ffff5f 7ad767 66b097 5e7388 534d61 3a3338 121212)
local fg_gradient=(000000 000000 000000 000000 000000 ffffff ffffff)
# local gradient=(888888 999999)
local j=0
for i in ${WISH_PLUGINS[@]}; do
[[ $i =~ newline$ ]] && j=0 && continue
eval WISH_${i^^}_BG=${gradient[$j]}
eval WISH_${i^^}_FG=${fg_gradient[$j]}
((j++))
if [[ $j -eq ${#gradient[@]} ]]; then
j=0
fi
done
j=0
for ((idx=$((${#WISH_RIGHT_PLUGINS[@]} - 1)); idx >= 0; idx--)); do
i=${WISH_RIGHT_PLUGINS[$idx]}
[[ $i =~ newline$ ]] && j=0 && continue
eval WISH_${i^^}_BG=${gradient[$j]}
eval WISH_${i^^}_FG=${fg_gradient[$j]}
[[ $i == "newline" ]] && j=0 && continue
eval WISH_$(echo $i | tr '[:lower:]' '[:upper:]')_BG=${gradient[$j]}
eval WISH_$(echo $i | tr '[:lower:]' '[:upper:]')_FG=${fg_gradient[$j]}
((j++))
if [[ $j -eq ${#gradient[@]} ]]; then
j=0

140
wish.py
View File

@@ -1,140 +0,0 @@
#!/usr/bin/env python3
"""
Generate function wrappers from config for wish.
wish.py works by writing plugins which are isolated from the global plugins.
It assigns all the config to relevant variables inside a wrapper. e.g.
If the config file is
```
|left|
[plugin]
name = date
format = %F
```
wish.py creates a wrapper around date as 0_date where 0 is the index of plugin.
Inside the wrapper, in each function, it sets WISH_DATE_FORMAT="%F" and
WISH_DATE_BG=$WISH_0_DATE_BG (same for FG), so the plugin sees its config as
defined in the config file. After calling the plugin's relevant function, it
sets WISH_0_DATE_BG=$WISH_DATE_BG (same for FG) in case, the plugin has changed
it). This allows multiple instances of the same plugin to coexist with
different config and different themes.
"""
from os.path import isfile
import shlex
import sys
import ginie
def parse_core(core):
"Parses a core block"
config = core["config"]
for key in core["config"]:
print("WISH_{}={}".format(key.upper(), shlex.quote(config[key])))
def parse_plugin(plugin, plugin_idx):
"Parses a plugin and writes a wrapper around it"
config = plugin["config"]
try:
plugin_name = config["name"].strip().lower()
config["name"] = plugin_name
except KeyError:
return False
keys_from_source = ("BG", "FG")
for j in ("start", "end", "set_colors", "main"):
print("function wish_{}_{}_{}()".format(plugin_idx, plugin_name, j))
print("{")
for key in keys_from_source:
print("\tlocal WISH_{0}_{2}=$WISH_{1}_{0}_{2}".format(
plugin_name.upper(),
plugin_idx,
key.upper()
))
for key in config:
if key == "name":
continue
print("\tlocal WISH_{}_{}={}".format(plugin_name.upper(),
key.upper(),
shlex.quote(config[key])))
print("\twish_{}_{} $*".format(plugin_name, j))
print("\tlocal err=$?")
for key in keys_from_source:
print("\tWISH_{1}_{0}_{2}=$WISH_{0}_{2}".format(
plugin_name.upper(),
plugin_idx,
key.upper()
))
print("\treturn $err")
print("}")
return True
def print_plugin_list(name, array, always=False):
"Prints a list of plugins as a bash array"
if array or always:
print("{}=(".format(name))
for plugin in array:
print("\t" + plugin)
print(")")
def main():
"Parse a config file passed as first argument"
config_file = []
for file_name in sys.argv[1:]:
if isfile(file_name):
with open(file_name) as file:
config_file += ginie.load(file)
else:
print("echo Invalid config file: {}".format(
shlex.quote(file_name)))
plugin_idx = 0
plugins_to_source = []
left_plugins = []
right_plugins = []
for i in config_file:
kind = i["type"]
name = i["name"]
# Parse core blocks outside a section.
# No other blocks outside a section need to be parsed for now.
if kind == "block" and name == "core":
parse_core(i)
elif kind == "section":
# All sections other than left and right are skipped.
if name not in ("left", "right"):
continue
for block in i["blocks"]:
if block["name"] == "core":
parse_core(block)
elif block["name"] == "plugin" and parse_plugin(block,
plugin_idx):
plugin_name = "{}_{}".format(plugin_idx,
block["config"]["name"])
if name == "left":
left_plugins.append(plugin_name)
else:
right_plugins.append(plugin_name)
plugins_to_source.append(block["config"]["name"])
plugin_idx += 1
print_plugin_list("WISH_PLUGINS", left_plugins)
print_plugin_list("WISH_RIGHT_PLUGINS", right_plugins)
print_plugin_list("WISH_PLUGINS_SOURCE", plugins_to_source)
# Python 2 patch
# TODO: Write a better patch for Python 2
try:
shlex.quote
except AttributeError:
shlex.quote = lambda x: x
if __name__ == "__main__":
main()

253
wish.sh
View File

@@ -1,205 +1,74 @@
#!/usr/bin/env bash
# shellcheck disable=SC1090
# INTERNAL USE ONLY! Do not use this in plugins.
function wish_print_right_prompt() {
local idx=0
for i in "${WISH_RPL[@]}"; do
echo "\e[$((COLUMNS - i + 1))G${WISH_RIGHT_PS1[idx]}"
((idx++))
done
}
function wish_init() {
# Find default config file if WISH_CONFIG_FILE is unset
if [[ ! -v WISH_CONFIG_FILE ]]; then
for path in "$XDG_CONFIG_HOME" "/usr/share" "$HOME/.config"; do
if [[ -f "$path/wish/config.gie" ]]; then
WISH_CONFIG_FILE="$path/wish/config.gie"
break
fi
done
fi
# Source config files
for path in "$XDG_CONFIG_HOME" "/usr/share" "$HOME/.config"; do
if [[ -f "$path/wish/wish.py" ]]; then
source <($path/wish/wish.py "${WISH_CONFIG_FILE[@]}")
break
fi
done
# Set defaults for core if not found
: "${WISH_POWERLINE_LEFT:=}"
: "${WISH_POWERLINE_RIGHT=}"
# Source all plugins
# If WISH_CONFIG_FILE is not set, then assume that the user hasn't updated
# to a config file yet. Set WISH_PLUGINS_SOURCE=WISH_PLUGINS.
if [[ ! -v WISH_CONFIG_FILE ]]; then
WISH_PLUGINS_SOURCE=("${WISH_PLUGINS[@]}" "${WISH_RIGHT_PLUGINS[@]}")
fi
local plugin
local path
for plugin in "${WISH_PLUGINS_SOURCE[@]}"; do
if ! for path in "$XDG_CONFIG_HOME" "/usr/share" "$HOME/.config"; do
source "$path/wish/plugins/$plugin.sh" &> /dev/null && break
done; then
echo "Plugin $plugin not found." >&2
fi
local i
local j
for i in ${WISH_PLUGINS[@]}; do
for j in "$XDG_CONFIG_HOME" "/usr/share" "$HOME/.config"; do
source "$j/wish/plugins/$i.sh" &> /dev/null && break
done
[[ $? -ne 0 ]] && echo "Plugin $i not found." >&2
done
# Source theme
WISH_THEME=${WISH_THEME:-plain}
while :; do
for theme in "$XDG_CONFIG_HOME" "/usr/share" "$HOME/.config"; do
source "$theme/wish/themes/$WISH_THEME.sh" &> /dev/null && break
done
if [[ $? -eq 0 ]]; then
break
else
echo "Theme $WISH_THEME not found. Using theme plain." >&2
if [[ $WISH_THEME == "plain" ]]; then
break
else
WISH_THEME=plain
fi
fi
for i in "$XDG_CONFIG_HOME" "/usr/share" "$HOME/.config"; do
source "$i/wish/themes/$WISH_THEME.sh" &> /dev/null && break
done
[[ $? -ne 0 ]] && echo "Theme $WISH_THEME not found." >&2
# Call plugins to set colors
for plugin in "${WISH_PLUGINS[@]}" "${WISH_RIGHT_PLUGINS[@]}"; do
eval wish_${plugin}_set_colors $prev
for i in ${WISH_PLUGINS[@]}; do
eval wish_$(echo $i)_set_colors $prev
done
}
# Usage: color_to_escape_code [3|4] color
#
# Parameters:
# - [3|4]: Use 3 if escape code is for foreground, 4 for background
# - color: -1 to reset, 0-255 for terminal color codes. 6 digit hexadecimal
# value for true color.
#
# Return value: Prints escape code that sets the fg/bg as requested.
function color_to_escape_code() {
local choice=$1
local color=$2
if [[ $color == -1 ]]; then
echo "\[\033[0;5;0m\]"
else
if [[ ${#fg_code} -eq 6 ]]; then
local r=$(( 16#${color:0:2} ))
local g=$(( 16#${color:2:2} ))
local b=$(( 16#${color:4:2} ))
echo "\[\033[${choice}8;2;$r;$g;${b}m\]"
else
echo "\[\033[${choice}8;5;${fg_code}m\]"
fi
fi
}
# INTERNAL USE ONLY! Do not use this in plugins.
# Usage: wish_append_left escape_codes text
function wish_append_left() {
local text="$2"
local colors="$1"
local prompt_text="${text@P}"
if [[ $text == "\n" ]]; then
((WISH_LPLINE++))
WISH_LPL=(${WISH_LPL[@]} 0)
WISH_LEFT_PS1="$WISH_LEFT_PS1$colors$text"
else
if [[ $((${WISH_LPL[$WISH_LPLINE]} + ${#prompt_text})) -lt $COLUMNS ]]; then
WISH_LEFT_PS1="$WISH_LEFT_PS1$colors$text"
WISH_LPL[$WISH_LPLINE]=$((${WISH_LPL[$WISH_LPLINE]} + ${#prompt_text}))
fi
fi
}
# INTERNAL USE ONLY! Do not use this in plugins.
# Usage: wish_append_right escape_codes text
function wish_append_right() {
local text="$2"
local colors="$1"
local prompt_text="${text@P}"
if [[ $text == "\n" ]]; then
((WISH_RPLINE++))
WISH_RIGHT_PS1=("${WISH_RIGHT_PS1[@]}" "")
WISH_RPL=(${WISH_RPL[@]} 0)
elif [[ $((${WISH_LPL[$WISH_RPLINE]} + ${WISH_RPL[$WISH_RPLINE]} + ${#prompt_text})) -lt $COLUMNS ]]; then
WISH_RIGHT_PS1[$WISH_RPLINE]="${WISH_RIGHT_PS1[$WISH_RPLINE]}$colors$text"
WISH_RPL[$WISH_RPLINE]=$((${WISH_RPL[$WISH_RPLINE]} + ${#prompt_text}))
fi
}
# Public API
# Usage: wish_append bg fg text
#
# Parameters:
# - fg, bg: -1 to reset, 0-255 for terminal color codes. 6 digit hexadecimal
# value for true color.
# - text: Text of the plugin
#
# Return value: None
function wish_append() {
local bg_code=$1
local fg_code=$2
local text=$3
local fg=$(color_to_escape_code 3 $fg_code)
local bg=$(color_to_escape_code 4 $bg_code)
if [[ $fg_code == -1 ]]; then
local fg=$(tput sgr0)
else
if [[ ${#fg_code} -eq 6 ]]; then
local color=($(echo $fg_code | grep -o .))
local r=$(( 16#${color[0]}${color[1]} ))
local g=$(( 16#${color[2]}${color[3]} ))
local b=$(( 16#${color[4]}${color[5]} ))
local fg="\[\033[38;2;$r;$g;${b}m\]"
else
local fg=$(tput setaf $fg_code)
fi
fi
if [[ $bg_code == -1 ]]; then
local bg=$(tput sgr0)
else
if [[ ${#bg_code} -eq 6 ]]; then
local color=($(echo $bg_code | grep -o .))
local r=$(( 16#${color[0]}${color[1]} ))
local g=$(( 16#${color[2]}${color[3]} ))
local b=$(( 16#${color[4]}${color[5]} ))
local bg="\[\033[48;2;$r;$g;${b}m\]"
else
local bg=$(tput setab $bg_code)
fi
fi
if [[ $fg_code == -1 ]]; then
case $WISH_STATE in
0)
wish_append_left "$fg$bg" "$text"
;;
1)
wish_append_right "$fg$bg" "$text"
;;
esac
PS1="$PS1$fg${bg}$text"
else
case $WISH_STATE in
0)
wish_append_left "$bg$fg" "$text"
;;
1)
wish_append_right "$bg$fg" "$text"
;;
esac
PS1="$PS1$bg${fg}$text"
fi
}
# Public API
# Usage: wish_remaining_chars
# Parameters: None
# Return Value: Capture stdout to get the remaining characters available in the
# line.
wish_remaining_chars() {
if [[ $WISH_STATE -eq 0 ]]; then
echo "$(( $COLUMNS - ${WISH_LPL[$WISH_LPLINE]} ))"
else
echo "$(( $COLUMNS - ${WISH_LPL[$WISH_RPLINE]} - ${WISH_RPL[$WISH_RPLINE]} ))"
fi
}
function wish_main() {
local prev=$?
# Set local IFS to avoid being affected by shell's IFS
local IFS=$' \n'
PS1=""
WISH_LEFT_PS1=""
WISH_RIGHT_PS1=("")
WISH_LPL=(0)
WISH_RPL=(0)
WISH_RPLINE=0
WISH_LPLINE=0
local i
# Set newline
if [[ $WISH_AUTO_NEWLINE != 0 ]]; then
echo -ne "\033[6n" ; read -s -d ';'; read -s -d R WISH_CURSOR_POSITION
if [[ $WISH_CURSOR_POSITION != "1" ]]; then
PS1="\n"
fi
fi
# Generate left prompt.
WISH_STATE=0 # 0 = left prompt, 1 = right prompt
for i in $(seq 0 $((${#WISH_PLUGINS[@]} - 1))); do
wish_${WISH_PLUGINS[i]}_main $prev
if [[ -v WISH_POWERLINE ]] && [[ $WISH_POWERLINE != 0 ]]; then
@@ -208,43 +77,25 @@ function wish_main() {
if wish_${WISH_PLUGINS[$(($i + 1))]}_start $prev; then
local plugin=${WISH_PLUGINS[$i]}
local next_plugin=${WISH_PLUGINS[$(($i+1))]}
local fg_name="WISH_${plugin^^}_BG"
local bg_name="WISH_${next_plugin^^}_BG"
wish_append ${!bg_name} ${!fg_name} "${WISH_POWERLINE_LEFT}"
local fg=$(eval echo \$WISH_$(echo $plugin |
tr '[:lower:]' '[:upper:]')_BG)
local bg=$(eval echo \$WISH_$(echo $next_plugin |
tr '[:lower:]' '[:upper:]')_BG)
wish_append $bg $fg
fi
else
local plugin=${WISH_PLUGINS[$i]}
local fg_name="WISH_${plugin^^}_BG"
wish_append -1 ${!fg_name} "${WISH_POWERLINE_LEFT}"
local fg=$(eval echo \$WISH_$(echo $plugin |
tr '[:lower:]' '[:upper:]')_BG)
wish_append -1 $fg
fi
fi
fi
done
# Generate Right prompt.
WISH_STATE=1
for i in $(seq 0 $((${#WISH_RIGHT_PLUGINS[@]} - 1))); do
if [[ -v WISH_POWERLINE ]] && [[ $WISH_POWERLINE != 0 ]]; then
if wish_${WISH_RIGHT_PLUGINS[$i]}_end $prev; then
if [[ $i == 0 ]]; then
local plugin=${WISH_RIGHT_PLUGINS[$i]}
local fg_name="WISH_${plugin^^}_BG"
wish_append -1 ${!fg_name} "${WISH_POWERLINE_RIGHT}"
elif wish_${WISH_RIGHT_PLUGINS[$(($i - 1))]}_start $prev; then
local plugin=${WISH_RIGHT_PLUGINS[$i]}
local prev_plugin=${WISH_RIGHT_PLUGINS[$(($i-1))]}
local fg_name="WISH_${plugin^^}_BG"
local bg_name="WISH_${prev_plugin^^}_BG"
wish_append ${!bg_name} ${!fg_name} "${WISH_POWERLINE_RIGHT}"
fi
fi
if [[ $i -eq $((${#WISH_PLUGINS[@]} - 1)) ]]; then
PS1="$PS1\[\033[0;5;0m\]"
fi
wish_${WISH_RIGHT_PLUGINS[$i]}_main $prev
done
# Save cursor position, print right prompt, restore cursor position,
# print left prompt, reset terminal
PS1=$PS1"\[\e7"
PS1="$PS1$(wish_print_right_prompt)"
PS1="$PS1\e8\]$WISH_LEFT_PS1\[\033[0;5;0m\]"
}
wish_init