Skip to content

Commit

Permalink
support status symbols customization via symbols.csv (#256)
Browse files Browse the repository at this point in the history
* support status symbols customization via symbols.csv

* fix unit tests
  • Loading branch information
nosarthur authored Jul 17, 2023
1 parent 28ffaf6 commit 091cd91
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 37 deletions.
17 changes: 9 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -337,16 +337,17 @@ Here `branch` includes both branch name and status.

Another choice is to enable `spaceship_status`, which mimics
the symbols used in [spaceship-prompt](https://spaceship-prompt.sh/sections/git/#Git-status-git_status).
For example,

symbol | meaning
---|---
∅| local has no remote
(no string) | local is the same as remote
⇕ | local has diverged from remote
⇡| local is ahead of remote (good for push)
⇣| local is behind remote (good for merge)
To customize these symbols, add a file in `$XDG_CONFIG_HOME/gita/symbols.csv`.
The default `spaceship_status` settings corresponds to

```csv
dirty,staged,untracked,local_ahead,remote_ahead,diverged,in_sync,no_remote
!,+,?,↑,↓,⇕,,∅
```
Only the symbols to be overridden need to be defined.

You can search unicode symbols [here](https://www.compart.com/en/unicode/).

### customize git command flags

Expand Down
65 changes: 44 additions & 21 deletions gita/info.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import os
import csv
import subprocess
from enum import Enum
from pathlib import Path
from collections import namedtuple
from functools import lru_cache, partial
from typing import Tuple, List, Callable, Dict

Expand Down Expand Up @@ -41,11 +41,11 @@ def __str__(self):


default_colors = {
"no-remote": Color.white.name,
"in-sync": Color.green.name,
"no_remote": Color.white.name,
"in_sync": Color.green.name,
"diverged": Color.red.name,
"local-ahead": Color.purple.name,
"remote-ahead": Color.yellow.name,
"local_ahead": Color.purple.name,
"remote_ahead": Color.yellow.name,
}


Expand Down Expand Up @@ -196,31 +196,54 @@ def get_commit_time(prop: Dict[str, str]) -> str:
return f"({result.stdout.strip()})"


# better solution exists for python3.7+
# https://stackoverflow.com/questions/11351032/named-tuple-and-default-values-for-optional-keyword-arguments
Symbols = namedtuple(
"Symbols",
"dirty staged untracked local_ahead remote_ahead diverged in_sync no_remote",
)
Symbols.__new__.__defaults__ = ("",) * len(Symbols._fields)
default_symbols = Symbols("*", "+", "?")


@lru_cache()
def get_symbols(baseline: Symbols = default_symbols) -> Dict[str, str]:
"""
return status symbols with customization
"""
symbols = baseline._asdict()
symbols[""] = ""
custom = {}
csv_config = Path(common.get_config_fname("symbols.csv"))
if csv_config.is_file():
with open(csv_config, "r") as f:
reader = csv.DictReader(f)
custom = next(reader)
symbols.update(custom)
return symbols


def get_repo_status(prop: Dict[str, str], no_colors=False) -> str:
head = get_head(prop["path"])
colors = {situ: Color[name].value for situ, name in get_color_encoding().items()}
dirty, staged, untracked, situ = _get_repo_status(prop, skip_situ=no_colors)
info = f"{head:<10} [{dirty+staged+untracked}]"
symbols = get_symbols(default_symbols)
info = f"{head:<10} [{symbols[dirty]+symbols[staged]+symbols[untracked]}]"

if no_colors:
return f"{info:<17}"
color = colors[situ]
return f"{color}{info:<17}{Color.end}"


spaceship = {
"local-ahead": "⇡",
"remote-ahead": "⇣",
"diverged": "⇕",
"in-sync": "",
"no-remote": "∅",
}
spaceship = Symbols("!", "+", "?", "↑", "↓", "⇕", "", "∅")


def get_spaceship_status(prop: Dict[str, str]) -> str:
head = get_head(prop["path"])
dirty, staged, untracked, situ = _get_repo_status(prop)
info = f"{head:<10} [{dirty+staged+untracked+spaceship[situ]}]"
symbols = get_symbols(spaceship)
info = f"{head:<10} [{symbols[dirty]+symbols[staged]+symbols[untracked]+symbols[situ]}]"
return f"{info:<18}"


Expand All @@ -236,26 +259,26 @@ def _get_repo_status(
"""
path = prop["path"]
flags = prop["flags"]
dirty = "*" if run_quiet_diff(flags, [], path) else ""
staged = "+" if run_quiet_diff(flags, ["--cached"], path) else ""
untracked = "?" if has_untracked(flags, path) else ""
dirty = "dirty" if run_quiet_diff(flags, [], path) else ""
staged = "staged" if run_quiet_diff(flags, ["--cached"], path) else ""
untracked = "untracked" if has_untracked(flags, path) else ""

if skip_situ:
return dirty, staged, untracked, ""

diff_returncode = run_quiet_diff(flags, ["@{u}", "@{0}"], path)
if diff_returncode == 128:
situ = "no-remote"
situ = "no_remote"
elif diff_returncode == 0:
situ = "in-sync"
situ = "in_sync"
else:
common_commit = get_common_commit(path)
outdated = run_quiet_diff(flags, ["@{u}", common_commit], path)
if outdated:
diverged = run_quiet_diff(flags, ["@{0}", common_commit], path)
situ = "diverged" if diverged else "remote-ahead"
situ = "diverged" if diverged else "remote_ahead"
else: # local is ahead of remote
situ = "local-ahead"
situ = "local_ahead"
return dirty, staged, untracked, situ


Expand Down
19 changes: 11 additions & 8 deletions tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,18 +130,21 @@ def test_ls(self, monkeypatch, capfd):
[
(
PATH_FNAME,
"repo1 \x1b[31mmaster [dsu] \x1b[0m msg \nrepo2 \x1b[31mmaster [dsu] \x1b[0m msg \nxxx \x1b[31mmaster [dsu] \x1b[0m msg \n",
"repo1 \x1b[31mmaster [*+?] \x1b[0m msg \nrepo2 \x1b[31mmaster [*+?] \x1b[0m msg \nxxx \x1b[31mmaster [*+?] \x1b[0m msg \n",
),
(PATH_FNAME_EMPTY, ""),
(
PATH_FNAME_CLASH,
"repo1 \x1b[31mmaster [dsu] \x1b[0m msg \nrepo2 \x1b[31mmaster [dsu] \x1b[0m msg \n",
"repo1 \x1b[31mmaster [*+?] \x1b[0m msg \nrepo2 \x1b[31mmaster [*+?] \x1b[0m msg \n",
),
],
)
@patch("gita.utils.is_git", return_value=True)
@patch("gita.info.get_head", return_value="master")
@patch("gita.info._get_repo_status", return_value=("d", "s", "u", "diverged"))
@patch(
"gita.info._get_repo_status",
return_value=("dirty", "staged", "untracked", "diverged"),
)
@patch("gita.info.get_commit_msg", return_value="msg")
@patch("gita.info.get_commit_time", return_value="")
@patch("gita.common.get_config_fname")
Expand Down Expand Up @@ -567,7 +570,7 @@ def test_set_color(mock_get_fname, tmpdir):
args = argparse.Namespace()
args.color_cmd = "set"
args.color = "b_white"
args.situation = "no-remote"
args.situation = "no_remote"
with tmpdir.as_cwd():
csv_config = Path.cwd() / "colors.csv"
mock_get_fname.return_value = csv_config
Expand All @@ -577,11 +580,11 @@ def test_set_color(mock_get_fname, tmpdir):
items = info.get_color_encoding()
info.get_color_encoding.cache_clear() # avoid side effect
assert items == {
"no-remote": "b_white",
"in-sync": "green",
"no_remote": "b_white",
"in_sync": "green",
"diverged": "red",
"local-ahead": "purple",
"remote-ahead": "yellow",
"local_ahead": "purple",
"remote_ahead": "yellow",
}


Expand Down

0 comments on commit 091cd91

Please sign in to comment.