From a4a1267552d53aca57dbf78d81e2e1ecae29c42c Mon Sep 17 00:00:00 2001 From: amtrivedi91 Date: Tue, 30 Aug 2016 23:43:47 +0530 Subject: [PATCH 1/2] Moved all repo stats code from git.py to the base When all of the new code to provide more interesting stats was added to git.py, it was not very re-usable. A few pull requests could use it though. In particular, #105 adds more info to the svn segment and #210 does the same for mercurial. I would rather not create inconsistencies among these segments or have them duplicate code. Moving it into the base file creates a place where each segment can access it and have a consistent behavior. --- powerline_shell_base.py | 59 ++++++++++++++++++++++++++++++++++ segments/git.py | 48 +++++++-------------------- test/repo_stats_test.py | 22 +++++++++++++ test/segments_test/git_test.py | 5 ++- 4 files changed, 96 insertions(+), 38 deletions(-) create mode 100644 test/repo_stats_test.py diff --git a/powerline_shell_base.py b/powerline_shell_base.py index 8b95fb3a..6a51c0de 100755 --- a/powerline_shell_base.py +++ b/powerline_shell_base.py @@ -90,6 +90,65 @@ def draw_segment(self, idx): self.fgcolor(segment[4]), segment[3])) + +class RepoStats: + symbols = { + 'detached': u'\u2693', + 'ahead': u'\u2B06', + 'behind': u'\u2B07', + 'staged': u'\u2714', + 'not_staged': u'\u270E', + 'untracked': u'\u2753', + 'conflicted': u'\u273C' + } + + def __init__(self): + self.ahead = 0 + self.behind = 0 + self.untracked = 0 + self.not_staged = 0 + self.staged = 0 + self.conflicted = 0 + + @property + def dirty(self): + qualifiers = [ + self.untracked, + self.not_staged, + self.staged, + self.conflicted, + ] + return (True if sum(qualifiers) > 0 else False) + + def __getitem__(self, _key): + return getattr(self, _key) + + def n_or_empty(self, _key): + """Given a string name of one of the properties of this class, returns + the value of the property as a string when the value is greater than + 1. When it is not greater than one, returns an empty string. + + As an example, if you want to show an icon for untracked files, but you + only want a number to appear next to the icon when there are more than + one untracked files, you can do: + + segment = repo_stats.n_or_empty("untracked") + icon_string + """ + return unicode(self[_key]) if int(self[_key]) > 1 else u'' + + def add_to_powerline(self, powerline, color): + def add(_key, fg, bg): + if self[_key]: + s = u" {}{} ".format(self.n_or_empty(_key), self.symbols[_key]) + powerline.append(s, fg, bg) + add('ahead', color.GIT_AHEAD_FG, color.GIT_AHEAD_BG) + add('behind', color.GIT_BEHIND_FG, color.GIT_BEHIND_BG) + add('staged', color.GIT_STAGED_FG, color.GIT_STAGED_BG) + add('not_staged', color.GIT_NOTSTAGED_FG, color.GIT_NOTSTAGED_BG) + add('untracked', color.GIT_UNTRACKED_FG, color.GIT_UNTRACKED_BG) + add('conflicted', color.GIT_CONFLICTED_FG, color.GIT_CONFLICTED_BG) + + def get_valid_cwd(): """ We check if the current working directory is valid or not. Typically happens when you checkout a different branch on git that doesn't have diff --git a/segments/git.py b/segments/git.py index d33a9d39..0da777f0 100644 --- a/segments/git.py +++ b/segments/git.py @@ -2,16 +2,6 @@ import subprocess import os -GIT_SYMBOLS = { - 'detached': u'\u2693', - 'ahead': u'\u2B06', - 'behind': u'\u2B07', - 'staged': u'\u2714', - 'notstaged': u'\u270E', - 'untracked': u'\u2753', - 'conflicted': u'\u273C' -} - def get_PATH(): """Normally gets the PATH from the OS. This function exists to enable easily mocking the PATH in tests. @@ -43,33 +33,29 @@ def _get_git_detached_branch(): env=git_subprocess_env()) detached_ref = p.communicate()[0].decode("utf-8").rstrip('\n') if p.returncode == 0: - branch = u'{} {}'.format(GIT_SYMBOLS['detached'], detached_ref) + branch = u'{} {}'.format(RepoStats.symbols['detached'], detached_ref) else: branch = 'Big Bang' return branch def parse_git_stats(status): - stats = {'untracked': 0, 'notstaged': 0, 'staged': 0, 'conflicted': 0} + stats = RepoStats() for statusline in status[1:]: code = statusline[:2] if code == '??': - stats['untracked'] += 1 + stats.untracked += 1 elif code in ('DD', 'AU', 'UD', 'UA', 'DU', 'AA', 'UU'): - stats['conflicted'] += 1 + stats.conflicted += 1 else: if code[1] != ' ': - stats['notstaged'] += 1 + stats.not_staged += 1 if code[0] != ' ': - stats['staged'] += 1 + stats.staged += 1 return stats -def _n_or_empty(_dict, _key): - return _dict[_key] if int(_dict[_key]) > 1 else u'' - - def add_git_segment(powerline): try: p = subprocess.Popen(['git', 'status', '--porcelain', '-b'], @@ -84,33 +70,21 @@ def add_git_segment(powerline): return status = pdata[0].decode("utf-8").splitlines() - - branch_info = parse_git_branch_info(status) stats = parse_git_stats(status) - dirty = (True if sum(stats.values()) > 0 else False) + branch_info = parse_git_branch_info(status) if branch_info: + stats.ahead = branch_info["ahead"] + stats.behind = branch_info["behind"] branch = branch_info['local'] else: branch = _get_git_detached_branch() bg = Color.REPO_CLEAN_BG fg = Color.REPO_CLEAN_FG - if dirty: + if stats.dirty: bg = Color.REPO_DIRTY_BG fg = Color.REPO_DIRTY_FG powerline.append(' %s ' % branch, fg, bg) - - def _add(_dict, _key, fg, bg): - if _dict[_key]: - _str = u' {}{} '.format(_n_or_empty(_dict, _key), GIT_SYMBOLS[_key]) - powerline.append(_str, fg, bg) - - if branch_info: - _add(branch_info, 'ahead', Color.GIT_AHEAD_FG, Color.GIT_AHEAD_BG) - _add(branch_info, 'behind', Color.GIT_BEHIND_FG, Color.GIT_BEHIND_BG) - _add(stats, 'staged', Color.GIT_STAGED_FG, Color.GIT_STAGED_BG) - _add(stats, 'notstaged', Color.GIT_NOTSTAGED_FG, Color.GIT_NOTSTAGED_BG) - _add(stats, 'untracked', Color.GIT_UNTRACKED_FG, Color.GIT_UNTRACKED_BG) - _add(stats, 'conflicted', Color.GIT_CONFLICTED_FG, Color.GIT_CONFLICTED_BG) + stats.add_to_powerline(powerline, Color) diff --git a/test/repo_stats_test.py b/test/repo_stats_test.py new file mode 100644 index 00000000..bcd206a6 --- /dev/null +++ b/test/repo_stats_test.py @@ -0,0 +1,22 @@ +import unittest +import powerline_shell_base as p + + +class RepoStatsTest(unittest.TestCase): + + def setUp(self): + self.repo_stats = p.RepoStats() + self.repo_stats.not_staged = 1 + self.repo_stats.conflicted = 4 + + def test_simple(self): + self.assertEqual(self.repo_stats.untracked, 0) + + def test_n_or_empty__empty(self): + self.assertEqual(self.repo_stats.n_or_empty("not_staged"), u"") + + def test_n_or_empty__n(self): + self.assertEqual(self.repo_stats.n_or_empty("conflicted"), u"4") + + def test_index(self): + self.assertEqual(self.repo_stats["not_staged"], 1) diff --git a/test/segments_test/git_test.py b/test/segments_test/git_test.py index 982289e3..7ca344ab 100644 --- a/test/segments_test/git_test.py +++ b/test/segments_test/git_test.py @@ -3,14 +3,17 @@ import tempfile import shutil import sh +import powerline_shell_base as p import segments.git as git +git.Color = mock.MagicMock() +git.RepoStats = p.RepoStats + class GitTest(unittest.TestCase): def setUp(self): self.powerline = mock.MagicMock() - git.Color = mock.MagicMock() self.dirname = tempfile.mkdtemp() sh.cd(self.dirname) From 50225858f2e9502d31cc69c5ff95349d3191b6ab Mon Sep 17 00:00:00 2001 From: amtrivedi91 Date: Tue, 30 Aug 2016 23:43:47 +0530 Subject: [PATCH 2/2] no need for a ternary --- powerline_shell_base.py | 2 +- test/repo_stats_test.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/powerline_shell_base.py b/powerline_shell_base.py index 6a51c0de..896693a5 100755 --- a/powerline_shell_base.py +++ b/powerline_shell_base.py @@ -118,7 +118,7 @@ def dirty(self): self.staged, self.conflicted, ] - return (True if sum(qualifiers) > 0 else False) + return sum(qualifiers) > 0 def __getitem__(self, _key): return getattr(self, _key) diff --git a/test/repo_stats_test.py b/test/repo_stats_test.py index bcd206a6..c97a089d 100644 --- a/test/repo_stats_test.py +++ b/test/repo_stats_test.py @@ -9,6 +9,9 @@ def setUp(self): self.repo_stats.not_staged = 1 self.repo_stats.conflicted = 4 + def test_dirty(self): + self.assertTrue(self.repo_stats.dirty) + def test_simple(self): self.assertEqual(self.repo_stats.untracked, 0)