Merge remote-tracking branch 'origin/master' into bryangrimes-aws_profile

Conflicts:
	.gitignore
	README.md
	powerline_shell/themes/default.py
This commit is contained in:
Buck Ryan 2017-09-30 17:47:01 -04:00
commit d469133a33
72 changed files with 1499 additions and 782 deletions

7
.gitignore vendored
View file

@ -1,6 +1,11 @@
powerline-shell.py
*.py[co]
config.py
powerline_shell.egg-info/
/build/
/dist/
tags
config.json
powerline-shell.json
.DS_Store
*sublime*

5
.travis.yml Normal file
View file

@ -0,0 +1,5 @@
language: python
script: |
./setup.py install
pip install -r requirements-dev.txt
nosetests

157
CHANGELOG.md Normal file
View file

@ -0,0 +1,157 @@
# Changes
Unreleased
* Fix username segment's background color after "su" command
([@Fak3](https://github.com/banga/powerline-shell/pull/175))
* New `battery` segment which shows the percentage your battery is charged and
an icon when your battery is charging.
([@wattengard](https://github.com/banga/powerline-shell/pull/204))
2017-09-30 (version 0.3.0)
* Redo Fossil segment to be consistent with git, svn, etc.
([@emansije](https://github.com/banga/powerline-shell/pull/286))
* Fix subshell execution in bash described by
[pw3nage](https://github.com/njhartwell/pw3nage)
([@b-ryan](https://github.com/banga/powerline-shell/pull/282))
* Change SSH segment to just use the text `SSH` instead of showing a lock
symbol. Closes [#287](https://github.com/banga/powerline-shell/issues/287).
2017-09-18 (version 0.2.2)
* Fix python3 issue in uptime segment. Fixes
[#291](https://github.com/banga/powerline-shell/issues/291).
2017-09-16 (version 0.2.1)
* Fix issues preventing fish shell from rendering.
2017-09-13 (version 0.2.0)
* Add Bazaar segment
([@emansije](https://github.com/banga/powerline-shell/pull/283))
* And rename properties of RepoStats for clarity
([@emansije](https://github.com/banga/powerline-shell/pull/284))
* Rewrite SVN segment to be consistent with git
* Remove duplicate function in colortrans.py
([@jmtd](https://github.com/banga/powerline-shell/pull/273))
* Make python 3 check compatible with older Python versions
* New theme! `solarized_light`
([@ruturajv](https://github.com/banga/powerline-shell/pull/143)
2017-09-10
* Complete overhaul of the project
([@b-ryan](https://github.com/banga/powerline-shell/pull/280))
* There is now a PyPi package
* It's significantly faster now
* Configuration and installation is brand new. See README.md
2017-06-21
* Add `rbenv` segment
([@dogo](https://github.com/banga/powerline-shell/pull/260))
* Fix path segment so that current directory is emphasized
([@inamiy](https://github.com/banga/powerline-shell/pull/235))
2017-06-20
* Add `newline` segment
([@ffried](https://github.com/banga/powerline-shell/pull/266))
* Add `npm_version` segment
([@WileESpaghetti](https://github.com/banga/powerline-shell/pull/265))
* Fix issue with conda environments
([@drorata](https://github.com/banga/powerline-shell/pull/257))
* Fix jobs segment for Cygwin
([@themiwi](https://github.com/banga/powerline-shell/pull/256))
2017-05-15
* Fix the `set_term_title` segment for ZSH
([@themiwi](https://github.com/banga/powerline-shell/pull/255))
2016-04-16
* Fix issue around unicode function for python 3
2016-04-01
* Refactor of the way the git segment manages data about git's state.
([@b-ryan](https://github.com/milkbikis/powerline-shell/pull/221))
2015-12-26
* Beginnings of unit testing for segments. Included in this change was a
refactor of the way segments are added to powerline. Now, instead of looking
for a global `powerline` object, `powerline` is passed into the function to
add the segment. Segments will also no longer add the segments by calling the
`add` function themselves.
([@b-ryan](https://github.com/milkbikis/powerline-shell/pull/212))
* Python3 fixes for `lib/color_compliment.py`.
([@ceholden](https://github.com/milkbikis/powerline-shell/pull/220))
2015-11-25
* `virtual_env` segment now supports environments made with `conda`
([@ceholden](https://github.com/milkbikis/powerline-shell/pull/198))
2015-11-21
* Fixes for Python 3 compatibility
([@b-ryan](https://github.com/milkbikis/powerline-shell/pull/211))
2015-11-18
* The git segment has gotten a makeover
([@MartinWetterwald](https://github.com/milkbikis/powerline-shell/pull/136))
* Fix git segment when git is not on the standard PATH
([@andrejgl](https://github.com/milkbikis/powerline-shell/pull/153))
* Fix `--cwd-max-depth` showing duplicates when it's <= 2
([@b-ryan](https://github.com/milkbikis/powerline-shell/pull/209))
* Add padding around `exit_code` segment
([@phatblat](https://github.com/milkbikis/powerline-shell/pull/205))
2015-10-02
* New option (`--cwd-max-dir-size`) which allows you to limit each directory
that is displayed to a number of characters. This currently does not apply
if you are using `--cwd-mode plain`.
([@mart-e](https://github.com/milkbikis/powerline-shell/pull/127))
2015-08-26
* New `plain` mode of displaying the current working directory which can be
used by adding `--cwd-only plain` to `powerline-shell.py`.
This deprecates the `--cwd-only` option. `--cwd-mode dironly` can be used
instead. ([@paol](https://github.com/milkbikis/powerline-shell/pull/156))
2015-08-18
* New `time` segment
([@filipebarros](https://github.com/milkbikis/powerline-shell/pull/107))
2015-08-01
* Use `print` function for some python3 compatibility
([@strycore](https://github.com/milkbikis/powerline-shell/pull/195))
2015-07-31
* The current working directory no longer follows symbolic links
* New `exit_code` segment
([@disruptek](https://github.com/milkbikis/powerline-shell/pull/129))
2015-07-30
* Fix ZSH root indicator
([@nkcfan](https://github.com/milkbikis/powerline-shell/pull/150))
* Add uptime segment
([@marcioAlmada](https://github.com/milkbikis/powerline-shell/pull/139))
2015-07-27
* Use `python2` instead of `python` in hashbangs
([@Undeterminant](https://github.com/milkbikis/powerline-shell/pull/100))
* Add `node_version` segment
([@mmilleruva](https://github.com/milkbikis/powerline-shell/pull/189))

338
README.md
View file

@ -1,93 +1,90 @@
A Powerline style prompt for your shell
=======================================
# A Powerline style prompt for your shell
A [Powerline](https://github.com/Lokaltog/vim-powerline) like prompt for Bash, ZSH and Fish:
A [Powerline](https://github.com/Lokaltog/vim-powerline) like prompt for Bash,
ZSH and Fish:
![MacVim+Solarized+Powerline+CtrlP](https://raw.github.com/milkbikis/dotfiles-mac/master/bash-powerline-screenshot.png)
![MacVim+Solarized+Powerline+CtrlP](https://raw.github.com/banga/powerline-shell/master/bash-powerline-screenshot.png)
* Shows some important details about the git/svn/hg/fossil branch (see below)
* Changes color if the last command exited with a failure code
* If you're too deep into a directory tree, shortens the displayed path with an ellipsis
* Shows the current Python [virtualenv](http://www.virtualenv.org/) environment
* It's easy to customize and extend. See below for details.
- Shows some important details about the git/svn/hg/fossil branch (see below)
- Changes color if the last command exited with a failure code
- If you're too deep into a directory tree, shortens the displayed path with an ellipsis
- Shows the current Python [virtualenv](http://www.virtualenv.org/) environment
- It's easy to customize and extend. See below for details.
### Version Control
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
- [Version Control](#version-control)
- [Setup](#setup)
- [Bash](#bash)
- [ZSH](#zsh)
- [Fish](#fish)
- [Customization](#customization)
- [Config File](#config-file)
- [Adding, Removing and Re-arranging segments](#adding-removing-and-re-arranging-segments)
- [Segment Separator](#segment-separator)
- [Themes](#themes)
- [Segment Configuration](#segment-configuration)
- [Contributing new types of segments](#contributing-new-types-of-segments)
- [Troubleshooting](#troubleshooting)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
## Version Control
All of the version control systems supported by powerline shell give you a
quick look into the state of your repo:
* The current branch is displayed and changes background color when the
- The current branch is displayed and changes background color when the
branch is dirty.
* When the local branch differs from the remote, the difference in number
- When the local branch differs from the remote, the difference in number
of commits is shown along with `⇡` or `⇣` indicating whether a git push
or pull is pending
In addition, git has a few extra symbols:
* `✎` -- a file has been modified, but not staged for commit
* `✔` -- a file is staged for commit
* `✼` -- a file has conflicts
- `✎` -- a file has been modified, but not staged for commit
- `✔` -- a file is staged for commit
- `✼` -- a file has conflicts
FIXME
* A `+` appears when untracked files are present (except for git, which
uses `?` instead)
- A `+` appears when untracked files are present (except for git, which uses
`?` instead)
Each of these will have a number next to it if more than one file matches.
# Setup
## Setup
This script uses ANSI color codes to display colors in a terminal. These are
notoriously non-portable, so may not work for you out of the box, but try
setting your $TERM to `xterm-256color`, because that works for me.
* Patch the font you use for your terminal: see https://github.com/Lokaltog/powerline-fonts
- Patch the font you use for your terminal: see
[powerline-fonts](https://github.com/Lokaltog/powerline-fonts)
- If you struggle too much to get working fonts in your terminal, you can use
"compatible" mode.
- If you're using old patched fonts, you have to use the older symbols.
Basically reverse [this
commit](https://github.com/milkbikis/powerline-shell/commit/2a84ecc) in
your copy
* If you struggle too much to get working fonts in your terminal, you can use "compatible" mode.
* If you're using old patched fonts, you have to use the older symbols. Basically reverse [this commit](https://github.com/milkbikis/powerline-shell/commit/2a84ecc) in your copy
* Clone this repository somewhere:
git clone https://github.com/milkbikis/powerline-shell
* Copy `config.py.dist` to `config.py` and edit it to configure the segments you want. Then run
./install.py
* This will generate `powerline-shell.py`
* (optional) Create a symlink to this python script in your home:
ln -s <path/to/powerline-shell.py> ~/powerline-shell.py
* If you don't want the symlink, just modify the path in the commands below
* For python2.6 you have to install argparse
pip install argparse
### All Shells:
There are a few optional arguments which can be seen by running `powerline-shell.py --help`.
- Install using pip:
```
--cwd-mode {fancy,plain,dironly}
How to display the current directory
--cwd-max-depth CWD_MAX_DEPTH
Maximum number of directories to show in path
--cwd-max-dir-size CWD_MAX_DIR_SIZE
Maximum number of letters displayed for each directory
in the path
--colorize-hostname Colorize the hostname based on a hash of itself.
--mode {patched,compatible,flat}
The characters used to make separators between
segments
pip install powerline-shell
```
### Bash:
- Setup your shell prompt using the instructions for your shell below.
### Bash
Add the following to your `.bashrc` (or `.profile` on Mac):
```
function _update_ps1() {
PS1="$(~/powerline-shell.py $? 2> /dev/null)"
PS1="$(powerline-shell $?)"
}
if [ "$TERM" != "linux" ]; then
@ -95,12 +92,13 @@ if [ "$TERM" != "linux" ]; then
fi
```
### ZSH:
### ZSH
Add the following to your `.zshrc`:
```
function powerline_precmd() {
PS1="$(~/powerline-shell.py $? --shell zsh 2> /dev/null)"
PS1="$(powerline-shell --shell zsh $?)"
}
function install_powerline_precmd() {
@ -117,32 +115,126 @@ if [ "$TERM" != "linux" ]; then
fi
```
### Fish:
### Fish
Redefine `fish_prompt` in ~/.config/fish/config.fish:
```
function fish_prompt
~/powerline-shell.py $status --shell bare ^/dev/null
powerline-shell --shell bare $status
end
```
# Customization
## Customization
### Config File
Powerline-shell is customizable through the use of a config file. This file is
expected to be located at `~/.powerline-shell.json`. You can generate the
default config at this location using:
```
powerline-shell --generate-config > ~/.powerline-shell.json
```
(You can see an example config file
[here](https://github.com/b-ryan/dotfiles/blob/master/home/powerline-shell.json))
### Adding, Removing and Re-arranging segments
The `config.py` file defines which segments are drawn and in which order. Simply
comment out and rearrange segment names to get your desired arrangement. Every
time you change `config.py`, run `install.py`, which will generate a new
`powerline-shell.py` customized to your configuration. You should see the new
prompt immediately.
Once you have generated your config file, you can now start adding or removing
"segments" - the building blocks of your shell. The list of segments available
are:
- `bzr` - Details about the current Bazaar repo.
- `cwd` - Shows your current working directory. See [Segment
Configuration](#segment-configuration) for some options.
- `exit_code` - When the previous command ends in a non-zero status, shows the
value of the exist status in red.
- `fossil` - Details about the current Fossil repo.
- `git` - Details about the current Git repo.
- `hg` - Details about the current Mercurial repo.
- `hostname` - Current machine's hostname.
- `jobs` - Number of background jobs currently running.
- `newline` - Inserts a newline into the prompt.
- `node_version` - `node --version`
- `npm_version` - `npm --version`
- `php_version` - Version of php on the machine
- `rbenv` - `rbenv local`
- `read_only` - Shows a lock icon if the current directory is read-only.
- `root` - Shows a `#` if logged in as root, `$` otherwise.
- `ruby_version` - `ruby --version`
- `set_term_title` - If able, sets the title of your terminal to include some
useful info.
- `ssh` - If logged into over SSH, shows a network icon.
- `svn` - Details about the current SVN repo.
- `time` - Shows the current time
- `uptime` - Uptime of the current machine
- `username` - Name of the logged-in user
- `virtual_env` - Shows the name of the current virtual env or conda env.
### Segment Separator
By default, a unicode character (resembling the > symbol) is used to separate
each segment. This can be changed by changing the "mode" option in the config
file. The available modes are:
- `patched` - The default
- `compatible` - Attempts to use characters that may already be available using
your chosen font.
- `flat` - No separator is used between segments, giving each segment a
rectangular appearance (and also saves space).
### Themes
The `powerline_shell/themes` directory stores themes for your prompt, which are
basically color values used by segments. The `default.py` defines a default
theme which can be used standalone, and every other theme falls back to it if
they miss colors for any segments. Create new themes by copying any other
existing theme and changing the values. To use a theme, set the `theme`
variable in `~/.powerline-shell.json` to the name of your theme.
A script for testing color combinations is provided at `colortest.py`. Note
that the colors you see may vary depending on your terminal. When designing a
theme, please test your theme on multiple terminals, especially with default
settings.
### Segment Configuration
Some segments support additional configuration. The options for the segment are
nested under the name of the segment itself. For example, all of the options
for the `cwd` segment are set in `~/.powerline-shell.py` like:
```
{
"segments": [...],
"cwd": {
options go here
}
}
```
The options for the `cwd` segment are:
- `mode`: If "plain" then simple text will be used to show the cwd. If
"dironly," only the current directory will be shown. Otherwise expands the
cwd into individual directories.
- `max_depth`: Maximum number of directories to show in path
- `max_dir_size`: Maximum number of characters displayed for each directory in
the path
The `hostname` segment provides one option:
- `colorize`: If true, the hostname will be colorized based on a hash of
itself.
### Contributing new types of segments
The `segments` directory contains python scripts which are injected as is into
a single file `powerline_shell_base.py`. Each segment script defines a function
that inserts one or more segments into the prompt. If you want to add a new
segment, simply create a new file in the segments directory and add its name to
the `config.py` file at the appropriate location.
The `powerline_shell/segments` directory contains python scripts which are
injected as is into a single file `powerline_shell_base.py`. Each segment
script defines a function that inserts one or more segments into the prompt. If
you want to add a new segment, simply create a new file in the segments
directory.
Make sure that your script does not introduce new globals which might conflict
with other scripts. Your script should fail silently and run quickly in any
@ -153,102 +245,10 @@ segment you create. Test your segment with this theme first.
You should add tests for your segment as best you are able. Unit and
integration tests are both welcome. Run your tests with the `nosetests` command
after install the requirements in `dev_requirements.txt`.
after install the requirements in `requirements-dev.txt`.
### Themes
## Troubleshooting
The `themes` directory stores themes for your prompt, which are basically color
values used by segments. The `default.py` defines a default theme which can be
used standalone, and every other theme falls back to it if they miss colors for
any segments. Create new themes by copying any other existing theme and
changing the values. To use a theme, set the `THEME` variable in `config.py` to
the name of your theme.
A script for testing color combinations is provided at `themes/colortest.py`.
Note that the colors you see may vary depending on your terminal. When designing
a theme, please test your theme on multiple terminals, especially with default
settings.
# Changes
2016-01-14
* AWS profle support added per the `AWS_DEFAULT_PROFILE` setup described in [the AWS docs](http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html)
* `$ export AWS_DEFAULT_PROFILE=<NAME>`
2015-12-26
* Beginnings of unit testing for segments. Included in this change was a
refactor of the way segments are added to powerline. Now, instead of looking
for a global `powerline` object, `powerline` is passed into the function to
add the segment. Segments will also no longer add the segments by calling the
`add` function themselves.
([@b-ryan](https://github.com/milkbikis/powerline-shell/pull/212))
* Python3 fixes for `lib/color_compliment.py`.
([@ceholden](https://github.com/milkbikis/powerline-shell/pull/220))
2015-11-25
* `virtual_env` segment now supports environments made with `conda`
([@ceholden](https://github.com/milkbikis/powerline-shell/pull/198))
2015-11-21
* Fixes for Python 3 compatibility
([@b-ryan](https://github.com/milkbikis/powerline-shell/pull/211))
2015-11-18
* The git segment has gotten a makeover
([@MartinWetterwald](https://github.com/milkbikis/powerline-shell/pull/136))
* Fix git segment when git is not on the standard PATH
([@andrejgl](https://github.com/milkbikis/powerline-shell/pull/153))
* Fix `--cwd-max-depth` showing duplicates when it's <= 2
([@b-ryan](https://github.com/milkbikis/powerline-shell/pull/209))
* Add padding around `exit_code` segment
([@phatblat](https://github.com/milkbikis/powerline-shell/pull/205))
2015-10-02
* New option (`--cwd-max-dir-size`) which allows you to limit each directory
that is displayed to a number of characters. This currently does not apply
if you are using `--cwd-mode plain`.
([@mart-e](https://github.com/milkbikis/powerline-shell/pull/127))
2015-08-26
* New `plain` mode of displaying the current working directory which can be
used by adding `--cwd-only plain` to `powerline-shell.py`.
This deprecates the `--cwd-only` option. `--cwd-mode dironly` can be used
instead. ([@paol](https://github.com/milkbikis/powerline-shell/pull/156))
2015-08-18
* New `time` segment
([@filipebarros](https://github.com/milkbikis/powerline-shell/pull/107))
2015-08-01
* Use `print` function for some python3 compatibility
([@strycore](https://github.com/milkbikis/powerline-shell/pull/195))
2015-07-31
* The current working directory no longer follows symbolic links
* New `exit_code` segment
([@disruptek](https://github.com/milkbikis/powerline-shell/pull/129))
2015-07-30
* Fix ZSH root indicator
([@nkcfan](https://github.com/milkbikis/powerline-shell/pull/150))
* Add uptime segment
([@marcioAlmada](https://github.com/milkbikis/powerline-shell/pull/139))
2015-07-27
* Use `python2` instead of `python` in hashbangs
([@Undeterminant](https://github.com/milkbikis/powerline-shell/pull/100))
* Add `node_version` segment
([@mmilleruva](https://github.com/milkbikis/powerline-shell/pull/189))
See the [FAQ](https://github.com/banga/powerline-shell/wiki/FAQ). If you
continue to have issues, please open an
[issue](https://github.com/banga/powerline-shell/issues/new).

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View file

@ -1,5 +0,0 @@
dependencies:
pre:
- sudo pip install -r dev_requirements.txt
- git config --global user.email "tester@example.com"
- git config --global user.name "Tester McGee"

View file

@ -1,57 +0,0 @@
# This is the configuration file for your powerline-shell prompt
# Every time you make a change to this file, run install.py to apply changes
#
# For instructions on how to use the powerline-shell.py script, see the README
# Add, remove or rearrange these segments to customize what you see on the shell
# prompt. Any segment you add must be present in the segments/ directory
SEGMENTS = [
# Set the terminal window title to user@host:dir
# 'set_term_title',
# Show current virtual environment (see http://www.virtualenv.org/)
'virtual_env',
# Show the current user's username as in ordinary prompts
'username',
# Show the machine's hostname. Mostly used when ssh-ing into other machines
'hostname',
# Show a padlock when ssh-ing from another machine
'ssh',
# Show the current directory. If the path is too long, the middle part is
# replaced with ellipsis ('...')
'cwd',
# Show a padlock if the current user has no write access to the current
# directory
'read_only',
# Show the current git branch and status
'git',
# Show the current mercurial branch and status
'hg',
# Show the current svn branch and status
'svn',
# Show the current fossil branch and status
'fossil',
# Show number of running jobs
'jobs',
# Show the last command's exit code if it was non-zero
# 'exit_code',
# Shows a '#' if the current user is root, '$' otherwise
# Also, changes color if the last command exited with a non-zero error code
'root',
]
# Change the colors used to draw individual segments in your prompt
THEME = 'default'

View file

@ -1,49 +0,0 @@
#!/usr/bin/env python
from __future__ import print_function
import os
import stat
try:
import config
except ImportError:
print('Created personal config.py for your customizations')
import shutil
shutil.copyfile('config.py.dist', 'config.py')
import config
TEMPLATE_FILE = 'powerline_shell_base.py'
OUTPUT_FILE = 'powerline-shell.py'
SEGMENTS_DIR = 'segments'
THEMES_DIR = 'themes'
def load_source(srcfile):
try:
return ''.join(open(srcfile).readlines()) + '\n\n'
except IOError:
print('Could not open', srcfile)
return ''
if __name__ == "__main__":
source = load_source(TEMPLATE_FILE)
source += load_source(os.path.join(THEMES_DIR, 'default.py'))
if config.THEME != 'default':
source += load_source(os.path.join(THEMES_DIR, config.THEME + '.py'))
for segment in config.SEGMENTS:
source += load_source(os.path.join(SEGMENTS_DIR, segment + '.py'))
# assumes each segment file will have a function called
# add_segment__[segment] that accepts the powerline object
source += 'add_{}_segment(powerline)\n'.format(segment)
source += 'sys.stdout.write(powerline.draw())\n'
try:
open(OUTPUT_FILE, 'w').write(source)
st = os.stat(OUTPUT_FILE)
os.chmod(OUTPUT_FILE, st.st_mode | stat.S_IEXEC)
print(OUTPUT_FILE, 'saved successfully')
except IOError:
print('ERROR: Could not write to powerline-shell.py. Make sure it is writable')
exit(1)

251
powerline_shell_base.py → powerline_shell/__init__.py Executable file → Normal file
View file

@ -1,94 +1,14 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import print_function
import argparse
import os
import sys
import importlib
import json
from .utils import warn, py3
import re
py3 = sys.version_info.major == 3
def warn(msg):
print('[powerline-bash] ', msg)
class Powerline:
symbols = {
'compatible': {
'lock': 'RO',
'network': 'SSH',
'separator': u'\u25B6',
'separator_thin': u'\u276F'
},
'patched': {
'lock': u'\uE0A2',
'network': u'\uE0A2',
'separator': u'\uE0B0',
'separator_thin': u'\uE0B1'
},
'flat': {
'lock': '',
'network': '',
'separator': '',
'separator_thin': ''
},
}
color_templates = {
'bash': '\\[\\e%s\\]',
'zsh': '%%{%s%%}',
'bare': '%s',
}
def __init__(self, args, cwd):
self.args = args
self.cwd = cwd
mode, shell = args.mode, args.shell
self.color_template = self.color_templates[shell]
self.reset = self.color_template % '[0m'
self.lock = Powerline.symbols[mode]['lock']
self.network = Powerline.symbols[mode]['network']
self.separator = Powerline.symbols[mode]['separator']
self.separator_thin = Powerline.symbols[mode]['separator_thin']
self.segments = []
def color(self, prefix, code):
if code is None:
return ''
else:
return self.color_template % ('[%s;5;%sm' % (prefix, code))
def fgcolor(self, code):
return self.color('38', code)
def bgcolor(self, code):
return self.color('48', code)
def append(self, content, fg, bg, separator=None, separator_fg=None):
self.segments.append((content, fg, bg,
separator if separator is not None else self.separator,
separator_fg if separator_fg is not None else bg))
def draw(self):
text = (''.join(self.draw_segment(i) for i in range(len(self.segments)))
+ self.reset) + ' '
if py3:
return text
else:
return text.encode('utf-8')
def draw_segment(self, idx):
segment = self.segments[idx]
next_segment = self.segments[idx + 1] if idx < len(self.segments)-1 else None
return ''.join((
self.fgcolor(segment[1]),
self.bgcolor(segment[2]),
segment[0],
self.bgcolor(next_segment[2]) if next_segment else self.reset,
self.fgcolor(segment[4]),
segment[3]))
def get_valid_cwd():
""" We check if the current working directory is valid or not. Typically
@ -120,26 +40,153 @@ def get_valid_cwd():
return cwd
if __name__ == "__main__":
class Powerline(object):
symbols = {
'compatible': {
'lock': 'RO',
'network': 'SSH',
'separator': u'\u25B6',
'separator_thin': u'\u276F'
},
'patched': {
'lock': u'\uE0A2',
'network': 'SSH',
'separator': u'\uE0B0',
'separator_thin': u'\uE0B1'
},
'flat': {
'lock': u'\uE0A2',
'network': 'SSH',
'separator': '',
'separator_thin': ''
},
}
color_templates = {
'bash': '\\[\\e%s\\]',
'zsh': '%%{%s%%}',
'bare': '%s',
}
def __init__(self, args, config, theme):
self.args = args
self.config = config
self.theme = theme
self.cwd = get_valid_cwd()
mode = config.get("mode", "patched")
self.color_template = self.color_templates[args.shell]
self.reset = self.color_template % '[0m'
self.lock = Powerline.symbols[mode]['lock']
self.network = Powerline.symbols[mode]['network']
self.separator = Powerline.symbols[mode]['separator']
self.separator_thin = Powerline.symbols[mode]['separator_thin']
self.segments = []
def segment_conf(self, seg_name, key, default=None):
return self.config.get(seg_name, {}).get(key, default)
def color(self, prefix, code):
if code is None:
return ''
elif code == self.theme.RESET:
return self.reset
else:
return self.color_template % ('[%s;5;%sm' % (prefix, code))
def fgcolor(self, code):
return self.color('38', code)
def bgcolor(self, code):
return self.color('48', code)
def append(self, content, fg, bg, separator=None, separator_fg=None):
self.segments.append((content, fg, bg,
separator if separator is not None else self.separator,
separator_fg if separator_fg is not None else bg))
def draw(self):
text = (''.join(self.draw_segment(i) for i in range(len(self.segments)))
+ self.reset) + ' '
if py3:
return text
else:
return text.encode('utf-8')
def draw_segment(self, idx):
segment = self.segments[idx]
if self.args.shell == "bash":
sanitized = re.sub(r"([`$])", r"\\\1", segment[0])
else:
sanitized = segment[0]
next_segment = self.segments[idx + 1] if idx < len(self.segments)-1 else None
return ''.join((
self.fgcolor(segment[1]),
self.bgcolor(segment[2]),
sanitized,
self.bgcolor(next_segment[2]) if next_segment else self.reset,
self.fgcolor(segment[4]),
segment[3]))
def find_config():
for location in [
"powerline-shell.json",
"~/.powerline-shell.json",
]:
full = os.path.expanduser(location)
if os.path.exists(full):
return full
DEFAULT_CONFIG = {
"segments": [
'virtual_env',
'username',
'hostname',
'ssh',
'cwd',
'git',
'hg',
'jobs',
'root',
]
}
def main():
arg_parser = argparse.ArgumentParser()
arg_parser.add_argument('--cwd-mode', action='store',
help='How to display the current directory', default='fancy',
choices=['fancy', 'plain', 'dironly'])
arg_parser.add_argument('--cwd-only', action='store_true',
help='Deprecated. Use --cwd-mode=dironly')
arg_parser.add_argument('--cwd-max-depth', action='store', type=int,
default=5, help='Maximum number of directories to show in path')
arg_parser.add_argument('--cwd-max-dir-size', action='store', type=int,
help='Maximum number of letters displayed for each directory in the path')
arg_parser.add_argument('--colorize-hostname', action='store_true',
help='Colorize the hostname based on a hash of itself.')
arg_parser.add_argument('--mode', action='store', default='patched',
help='The characters used to make separators between segments',
choices=['patched', 'compatible', 'flat'])
arg_parser.add_argument('--generate-config', action='store_true',
help='Generate the default config and print it to stdout')
arg_parser.add_argument('--shell', action='store', default='bash',
help='Set this to your shell type', choices=['bash', 'zsh', 'bare'])
help='Set this to your shell type',
choices=['bash', 'zsh', 'bare'])
arg_parser.add_argument('prev_error', nargs='?', type=int, default=0,
help='Error code returned by the last command')
help='Error code returned by the last command')
args = arg_parser.parse_args()
powerline = Powerline(args, get_valid_cwd())
if args.generate_config:
print(json.dumps(DEFAULT_CONFIG, indent=2))
return 0
config_path = find_config()
if config_path:
with open(config_path) as f:
config = json.loads(f.read())
else:
config = DEFAULT_CONFIG
theme_name = config.get("theme", "default")
mod = importlib.import_module("powerline_shell.themes." + theme_name)
theme = getattr(mod, "Color")
powerline = Powerline(args, config, theme)
segments = []
for seg_name in config["segments"]:
mod = importlib.import_module("powerline_shell.segments." + seg_name)
segment = getattr(mod, "Segment")(powerline)
segment.start()
segments.append(segment)
for segment in segments:
segment.add_to_powerline()
sys.stdout.write(powerline.draw())
return 0

View file

@ -1,4 +1,3 @@
#! /usr/bin/env python
from colorsys import hls_to_rgb, rgb_to_hls
# md5 deprecated since Python 2.5
try:
@ -6,19 +5,13 @@ try:
except ImportError:
from hashlib import md5
import sys
# Original, non-relative import errors on Python3
from .colortrans import *
py3 = sys.version_info[0] == 3
from .utils import py3
def getOppositeColor(r,g,b):
hls = rgb_to_hls(r,g,b)
#print "hls is"
#print hls
opp = list(hls[:])
#opp[0] = (opp[0]+0.5)%1 # reverse hue (a.k.a. color), reversing tends to be jarring
opp[0] = (opp[0]+0.2)%1 # shift hue (a.k.a. color)
if opp[1] > 255/2: # for level you want to make sure they
opp[1] -= 255/2 # are quite different so easily readable
@ -26,7 +19,6 @@ def getOppositeColor(r,g,b):
opp[1] += 255/2
if opp[2] > -0.5: # if saturation is low on first color increase second's
opp[2] -= 0.5
#print opp
opp = hls_to_rgb(*opp)
m = max(opp)
if m > 255: #colorsys module doesn't give caps to their conversions
@ -34,8 +26,6 @@ def getOppositeColor(r,g,b):
return tuple([ int(x) for x in opp])
def stringToHashToColorAndOpposite(string):
# Python3: Unicode string must be encoded before digest
# Python2.7: works either way, but check in case breaks earlier py2
if py3:
string = string.encode('utf-8')
string = md5(string).hexdigest()[:6] # get a random color

View file

@ -281,10 +281,6 @@ RGB2SHORT_DICT = {
(255, 255, 215): 230,
(255, 255, 255): 231}
def hexstr2num(hexstr):
return int(hexstr, 16)
def rgb2short(r, g, b):
""" Find the closest xterm-256 approximation to the given RGB value.
@param r,g,b: each is a number between 0-255 for the Red, Green, and Blue values

View file

@ -0,0 +1,31 @@
from ..utils import BasicSegment, warn
import os
LOW_BATTERY_THRESHOLD = 20
# See discussion in https://github.com/banga/powerline-shell/pull/204 regarding
# the directory where battery info is saved
DIR_OPTIONS = ["/sys/class/power_supply/BAT0",
"/sys/class/power_supply/BAT1"]
class Segment(BasicSegment):
def add_to_powerline(self):
if os.path.exists("/sys/class/power_supply/BAT0"):
dir_ = "/sys/class/power_supply/BAT0"
elif os.path.exists("/sys/class/power_supply/BAT1"):
dir_ = "/sys/class/power_supply/BAT1"
else:
warn("battery directory could not be found")
return
with open(os.path.join(dir_, "capacity")) as f:
cap = f.read().strip()
with open(os.path.join(dir_, "status")) as f:
status = f.read().strip()
pwr = u" \u26A1 " if status == "Charging" else u" "
if int(cap) < LOW_BATTERY_THRESHOLD:
bg = self.powerline.theme.BATTERY_LOW_BG
fg = self.powerline.theme.BATTERY_LOW_FG
else:
bg = self.powerline.theme.BATTERY_NORMAL_BG
fg = self.powerline.theme.BATTERY_NORMAL_FG
self.powerline.append(" " + cap + "%" + pwr, fg, bg)

View file

@ -0,0 +1,72 @@
import os
import subprocess
from ..utils import RepoStats, ThreadedSegment
def get_PATH():
"""Normally gets the PATH from the OS. This function exists to enable
easily mocking the PATH in tests.
"""
return os.getenv("PATH")
def bzr_subprocess_env():
return {"PATH": get_PATH()}
def _get_bzr_branch():
p = subprocess.Popen(['bzr', 'nick'],
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
env=bzr_subprocess_env())
branch = p.communicate()[0].decode("utf-8").rstrip('\n')
return branch
def parse_bzr_stats(status):
stats = RepoStats()
statustype = "changed"
for statusline in status:
if statusline[:2] == " ":
setattr(stats, statustype, getattr(stats, statustype) + 1)
elif statusline == "added:":
statustype = "staged"
elif statusline == "unknown:":
statustype = "new"
else: # removed, missing, renamed, modified or kind changed
statustype = "changed"
return stats
def build_stats():
try:
p = subprocess.Popen(['bzr', 'status'],
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
env=bzr_subprocess_env())
except OSError:
# Popen will throw an OSError if bzr is not found
return (None, None)
pdata = p.communicate()
if p.returncode != 0:
return (None, None)
status = pdata[0].decode("utf-8").splitlines()
stats = parse_bzr_stats(status)
branch = _get_bzr_branch()
return stats, branch
class Segment(ThreadedSegment):
def run(self):
self.stats, self.branch = build_stats()
def add_to_powerline(self):
self.join()
if not self.stats:
return
bg = self.powerline.theme.REPO_CLEAN_BG
fg = self.powerline.theme.REPO_CLEAN_FG
if self.stats.dirty:
bg = self.powerline.theme.REPO_DIRTY_BG
fg = self.powerline.theme.REPO_DIRTY_FG
self.powerline.append(" " + self.branch + " ", fg, bg)
self.stats.add_to_powerline(self.powerline)

View file

@ -1,4 +1,6 @@
import os
import sys
from ..utils import warn, py3, BasicSegment
ELLIPSIS = u'\u2026'
@ -22,27 +24,32 @@ def split_path_into_names(cwd):
return names
def requires_special_home_display(name):
def requires_special_home_display(powerline, name):
"""Returns true if the given directory name matches the home indicator and
the chosen theme should use a special home indicator display."""
return (name == '~' and Color.HOME_SPECIAL_DISPLAY)
return (name == '~' and powerline.theme.HOME_SPECIAL_DISPLAY)
def maybe_shorten_name(powerline, name):
"""If the user has asked for each directory name to be shortened, will
return the name up to their specified length. Otherwise returns the full
name."""
if powerline.args.cwd_max_dir_size:
return name[:powerline.args.cwd_max_dir_size]
max_size = powerline.segment_conf("cwd", "max_dir_size")
if max_size:
return name[:max_size]
return name
def get_fg_bg(name):
def get_fg_bg(powerline, name, is_last_dir):
"""Returns the foreground and background color to use for the given name.
"""
if requires_special_home_display(name):
return (Color.HOME_FG, Color.HOME_BG,)
return (Color.PATH_FG, Color.PATH_BG,)
if requires_special_home_display(powerline, name):
return (powerline.theme.HOME_FG, powerline.theme.HOME_BG,)
if is_last_dir:
return (powerline.theme.CWD_FG, powerline.theme.PATH_BG,)
else:
return (powerline.theme.PATH_FG, powerline.theme.PATH_BG,)
def add_cwd_segment(powerline):
@ -51,15 +58,15 @@ def add_cwd_segment(powerline):
cwd = cwd.decode("utf-8")
cwd = replace_home_dir(cwd)
if powerline.args.cwd_mode == 'plain':
powerline.append(' %s ' % (cwd,), Color.CWD_FG, Color.PATH_BG)
if powerline.segment_conf("cwd", "mode") == 'plain':
powerline.append(' %s ' % (cwd,), powerline.theme.CWD_FG, powerline.theme.PATH_BG)
return
names = split_path_into_names(cwd)
max_depth = powerline.args.cwd_max_depth
max_depth = powerline.segment_conf("cwd", "max_depth", 5)
if max_depth <= 0:
warn("Ignoring --cwd-max-depth argument since it's not greater than 0")
warn("Ignoring cwd.max_depth option since it's not greater than 0")
elif len(names) > max_depth:
# https://github.com/milkbikis/powerline-shell/issues/148
# n_before is the number is the number of directories to put before the
@ -71,20 +78,25 @@ def add_cwd_segment(powerline):
n_before = 2 if max_depth > 2 else max_depth - 1
names = names[:n_before] + [ELLIPSIS] + names[n_before - max_depth:]
if (powerline.args.cwd_mode == 'dironly' or powerline.args.cwd_only):
if powerline.segment_conf("cwd", "mode") == "dironly":
# The user has indicated they only want the current directory to be
# displayed, so chop everything else off
names = names[-1:]
for i, name in enumerate(names):
fg, bg = get_fg_bg(name)
is_last_dir = (i == len(names) - 1)
fg, bg = get_fg_bg(powerline, name, is_last_dir)
separator = powerline.separator_thin
separator_fg = Color.SEPARATOR_FG
is_last_dir = (i == len(names) - 1)
if requires_special_home_display(name) or is_last_dir:
separator_fg = powerline.theme.SEPARATOR_FG
if requires_special_home_display(powerline, name) or is_last_dir:
separator = None
separator_fg = None
powerline.append(' %s ' % maybe_shorten_name(powerline, name), fg, bg,
separator, separator_fg)
class Segment(BasicSegment):
def add_to_powerline(self):
add_cwd_segment(self.powerline)

View file

@ -0,0 +1,10 @@
from ..utils import BasicSegment
class Segment(BasicSegment):
def add_to_powerline(self):
if self.powerline.args.prev_error == 0:
return
fg = self.powerline.theme.CMD_FAILED_FG
bg = self.powerline.theme.CMD_FAILED_BG
self.powerline.append(' %s ' % str(self.powerline.args.prev_error), fg, bg)

View file

@ -0,0 +1,79 @@
import os
import subprocess
from ..utils import RepoStats, ThreadedSegment
def get_PATH():
"""Normally gets the PATH from the OS. This function exists to enable
easily mocking the PATH in tests.
"""
return os.getenv("PATH")
def fossil_subprocess_env():
return {"PATH": get_PATH()}
def _get_fossil_branch():
branches = os.popen("fossil branch 2>/dev/null").read().strip().split("\n")
return ''.join([
i.replace('*','').strip()
for i in branches
if i.startswith('*')
])
def parse_fossil_stats(status):
stats = RepoStats()
for line in status:
if line.startswith("ADDED"):
stats.staged += 1
elif line.startswith("EXTRA"):
stats.new += 1
elif line.startswith("CONFLICT"):
stats.conflicted += 1
else:
stats.changed += 1
return stats
def _get_fossil_status():
changes = os.popen("fossil changes 2>/dev/null").read().strip().split("\n")
extra = os.popen("fossil extras 2>/dev/null").read().strip().split("\n")
extra = ["EXTRA " + filename for filename in extra if filename != ""]
status = [line for line in changes + extra if line != '']
return status
def build_stats():
try:
subprocess.Popen(['fossil'], stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=fossil_subprocess_env()).communicate()
except OSError:
# Popen will throw an OSError if fossil is not found
return (None, None)
branch = _get_fossil_branch()
if branch == "":
return (None, None)
status = _get_fossil_status()
if status == []:
return (RepoStats(), branch)
stats = parse_fossil_stats(status)
return stats, branch
class Segment(ThreadedSegment):
def add_to_powerline(self):
self.stats, self.branch = build_stats()
if not self.stats:
return
bg = self.powerline.theme.REPO_CLEAN_BG
fg = self.powerline.theme.REPO_CLEAN_FG
if self.stats.dirty:
bg = self.powerline.theme.REPO_DIRTY_BG
fg = self.powerline.theme.REPO_DIRTY_FG
self.powerline.append(" " + self.branch + " ", fg, bg)
self.stats.add_to_powerline(self.powerline)

View file

@ -1,16 +1,8 @@
import re
import subprocess
import os
from ..utils import RepoStats, ThreadedSegment
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
@ -18,6 +10,7 @@ def get_PATH():
"""
return os.getenv("PATH")
def git_subprocess_env():
return {
# LANG is specified to ensure git always uses a language we are expecting.
@ -43,74 +36,68 @@ 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.new += 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.changed += 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):
def build_stats():
try:
p = subprocess.Popen(['git', 'status', '--porcelain', '-b'],
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
env=git_subprocess_env())
except OSError:
# Popen will throw an OSError if git is not found
return
return (None, None)
pdata = p.communicate()
if p.returncode != 0:
return
return (None, None)
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()
return stats, branch
bg = Color.REPO_CLEAN_BG
fg = Color.REPO_CLEAN_FG
if dirty:
bg = Color.REPO_DIRTY_BG
fg = Color.REPO_DIRTY_FG
powerline.append(' %s ' % branch, fg, bg)
class Segment(ThreadedSegment):
def run(self):
self.stats, self.branch = build_stats()
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)
def add_to_powerline(self):
self.join()
if not self.stats:
return
bg = self.powerline.theme.REPO_CLEAN_BG
fg = self.powerline.theme.REPO_CLEAN_FG
if self.stats.dirty:
bg = self.powerline.theme.REPO_DIRTY_BG
fg = self.powerline.theme.REPO_DIRTY_FG
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)
self.powerline.append(" " + self.branch + " ", fg, bg)
self.stats.add_to_powerline(self.powerline)

View file

@ -0,0 +1,79 @@
import os
import subprocess
from ..utils import ThreadedSegment
import subprocess
def get_PATH():
"""Normally gets the PATH from the OS. This function exists to enable
easily mocking the PATH in tests.
"""
return os.getenv("PATH")
def _subprocess_env():
return {"PATH": get_PATH()}
def get_hg_status():
has_modified_files = False
has_untracked_files = False
has_missing_files = False
p = subprocess.Popen(["hg", "status"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=_subprocess_env())
output = p.communicate()[0].decode("utf-8")
for line in output.split("\n"):
if line == "":
continue
elif line[0] == "?":
has_untracked_files = True
elif line[0] == "!":
has_missing_files = True
else:
has_modified_files = True
return has_modified_files, has_untracked_files, has_missing_files
def build_stats():
try:
p = subprocess.Popen(["hg", "branch"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=_subprocess_env())
except OSError:
# Will be thrown if hg cannot be found
return None, None
pdata = p.communicate()
if p.returncode != 0:
return None, None
branch = pdata[0].decode("utf-8").strip()
return branch, get_hg_status()
class Segment(ThreadedSegment):
def run(self):
self.branch, self.status = build_stats()
def add_to_powerline(self):
self.join()
if not self.branch or not self.status:
return
bg = self.powerline.theme.REPO_CLEAN_BG
fg = self.powerline.theme.REPO_CLEAN_FG
has_modified, has_untracked, has_missing = self.status
if has_modified or has_untracked or has_missing:
bg = self.powerline.theme.REPO_DIRTY_BG
fg = self.powerline.theme.REPO_DIRTY_FG
extra = ""
if has_untracked:
extra += "+"
if has_missing:
extra += "!"
self.branch += " " + extra
return self.powerline.append(" %s " % self.branch, fg, bg)

View file

@ -0,0 +1,25 @@
from ..utils import BasicSegment
from ..color_compliment import stringToHashToColorAndOpposite
from ..colortrans import rgb2short
from socket import gethostname
class Segment(BasicSegment):
def add_to_powerline(self):
powerline = self.powerline
if powerline.segment_conf("hostname", "colorize"):
hostname = gethostname()
FG, BG = stringToHashToColorAndOpposite(hostname)
FG, BG = (rgb2short(*color) for color in [FG, BG])
host_prompt = " %s " % hostname.split(".")[0]
powerline.append(host_prompt, FG, BG)
else:
if powerline.args.shell == "bash":
host_prompt = r" \h "
elif powerline.args.shell == "zsh":
host_prompt = " %m "
else:
host_prompt = " %s " % gethostname().split(".")[0]
powerline.append(host_prompt,
powerline.theme.HOSTNAME_FG,
powerline.theme.HOSTNAME_BG)

View file

@ -0,0 +1,31 @@
import os
import re
import subprocess
import platform
from ..utils import ThreadedSegment
class Segment(ThreadedSegment):
def run(self):
self.num_jobs = 0
if platform.system().startswith('CYGWIN'):
# cygwin ps is a special snowflake...
output_proc = subprocess.Popen(['ps', '-af'], stdout=subprocess.PIPE)
output = map(lambda l: int(l.split()[2].strip()),
output_proc.communicate()[0].decode("utf-8").splitlines()[1:])
self.num_jobs = output.count(os.getppid()) - 1
else:
pppid_proc = subprocess.Popen(['ps', '-p', str(os.getppid()), '-oppid='],
stdout=subprocess.PIPE)
pppid = pppid_proc.communicate()[0].decode("utf-8").strip()
output_proc = subprocess.Popen(['ps', '-a', '-o', 'ppid'],
stdout=subprocess.PIPE)
output = output_proc.communicate()[0].decode("utf-8")
self.num_jobs = len(re.findall(str(pppid), output)) - 1
def add_to_powerline(self):
self.join()
if self.num_jobs > 0:
self.powerline.append(' %d ' % self.num_jobs,
self.powerline.theme.JOBS_FG,
self.powerline.theme.JOBS_BG)

View file

@ -0,0 +1,9 @@
from ..utils import BasicSegment
class Segment(BasicSegment):
def add_to_powerline(self):
self.powerline.append("\n",
self.powerline.theme.RESET,
self.powerline.theme.RESET,
separator="")

View file

@ -0,0 +1,18 @@
import subprocess
from ..utils import ThreadedSegment
class Segment(ThreadedSegment):
def run(self):
try:
p1 = subprocess.Popen(["node", "--version"], stdout=subprocess.PIPE)
self.version = p1.communicate()[0].decode("utf-8").rstrip()
except OSError:
self.version = None
def add_to_powerline(self):
self.join()
if not self.version:
return
# FIXME no hard-coded colors
self.powerline.append("node " + self.version, 15, 18)

View file

@ -0,0 +1,17 @@
import subprocess
from ..utils import ThreadedSegment
class Segment(ThreadedSegment):
def run(self):
try:
p1 = subprocess.Popen(["npm", "--version"], stdout=subprocess.PIPE)
self.version = p1.communicate()[0].decode("utf-8").rstrip()
except OSError:
self.version = None
def add_to_powerline(self):
self.join()
if self.version:
# FIXME no hard-coded colors
self.powerline.append("npm " + self.version, 15, 18)

View file

@ -0,0 +1,19 @@
import subprocess
from ..utils import ThreadedSegment
class Segment(ThreadedSegment):
def run(self):
try:
output = subprocess.check_output(['php', '-r', 'echo PHP_VERSION;'],
stderr=subprocess.STDOUT)
self.version = output.split('-')[0] if '-' in output else output
except OSError:
self.version = None
def add_to_powerline(self):
self.join()
if not self.version:
return
# FIXME no hard-coded colors
self.powerline.append(" " + self.version + " ", 15, 4)

View file

@ -0,0 +1,17 @@
import subprocess
from ..utils import BasicSegment
class Segment(BasicSegment):
def add_to_powerline(self):
powerline = self.powerline
try:
p1 = subprocess.Popen(["rbenv", "local"], stdout=subprocess.PIPE)
version = p1.communicate()[0].decode("utf-8").rstrip()
if len(version) <= 0:
return
powerline.append(' %s ' % version,
powerline.theme.VIRTUAL_ENV_FG,
powerline.theme.VIRTUAL_ENV_BG)
except OSError:
return

View file

@ -0,0 +1,12 @@
import os
from ..utils import BasicSegment
class Segment(BasicSegment):
def add_to_powerline(self):
powerline = self.powerline
cwd = powerline.cwd or os.getenv('PWD')
if not os.access(cwd, os.W_OK):
powerline.append(' %s ' % powerline.lock,
powerline.theme.READONLY_FG,
powerline.theme.READONLY_BG)

View file

@ -0,0 +1,17 @@
from ..utils import BasicSegment
class Segment(BasicSegment):
def add_to_powerline(self):
powerline = self.powerline
root_indicators = {
'bash': ' \\$ ',
'zsh': ' %# ',
'bare': ' $ ',
}
bg = powerline.theme.CMD_PASSED_BG
fg = powerline.theme.CMD_PASSED_FG
if powerline.args.prev_error != 0:
fg = powerline.theme.CMD_FAILED_FG
bg = powerline.theme.CMD_FAILED_BG
powerline.append(root_indicators[powerline.args.shell], fg, bg)

View file

@ -0,0 +1,19 @@
import os
import subprocess
from ..utils import BasicSegment
class Segment(BasicSegment):
def add_to_powerline(self):
powerline = self.powerline
try:
p1 = subprocess.Popen(["ruby", "-v"], stdout=subprocess.PIPE)
p2 = subprocess.Popen(["sed", "s/ (.*//"], stdin=p1.stdout, stdout=subprocess.PIPE)
version = p2.communicate()[0].decode("utf-8").rstrip()
if os.environ.has_key("GEM_HOME"):
gem = os.environ["GEM_HOME"].split("@")
if len(gem) > 1:
version += " " + gem[1]
powerline.append(version, 15, 1)
except OSError:
return

View file

@ -0,0 +1,22 @@
import os
import socket
from ..utils import BasicSegment
class Segment(BasicSegment):
def add_to_powerline(self):
powerline = self.powerline
term = os.getenv('TERM')
if not (('xterm' in term) or ('rxvt' in term)):
return
if powerline.args.shell == 'bash':
set_title = '\\[\\e]0;\\u@\\h: \\w\\a\\]'
elif powerline.args.shell == 'zsh':
set_title = '%{\033]0;%n@%m: %~\007%}'
else:
set_title = '\033]0;%s@%s: %s\007' % (
os.getenv('USER'),
socket.gethostname().split('.')[0],
powerline.cwd or os.getenv('PWD'),
)
powerline.append(set_title, None, None, '')

View file

@ -0,0 +1,11 @@
import os
from ..utils import BasicSegment
class Segment(BasicSegment):
def add_to_powerline(self):
if os.getenv('SSH_CLIENT'):
powerline = self.powerline
powerline.append(' %s ' % powerline.network,
powerline.theme.SSH_FG,
powerline.theme.SSH_BG)

View file

@ -0,0 +1,29 @@
import subprocess
from ..utils import BasicSegment, RepoStats
class Segment(BasicSegment):
def add_to_powerline(self):
is_svn = subprocess.Popen(["svn", "status"],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
is_svn_output = is_svn.communicate()[1].decode("utf-8").strip()
if len(is_svn_output) != 0:
return
try:
p1 = subprocess.Popen(["svn", "status"], stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
except OSError:
return
stdout = p1.communicate()[0]
stats = RepoStats()
for line in stdout.splitlines():
if line[0] == "?":
stats.new += 1
elif line[0] == "C":
stats.conflicted += 1
elif line[0] in ["A", "D", "I", "M", "R", "!", "~"]:
stats.changed += 1
stats.add_to_powerline(self.powerline)

View file

@ -0,0 +1,17 @@
from __future__ import absolute_import
from ..utils import BasicSegment
import time
class Segment(BasicSegment):
def add_to_powerline(self):
powerline = self.powerline
if powerline.args.shell == 'bash':
time_ = ' \\t '
elif powerline.args.shell == 'zsh':
time_ = ' %* '
else:
time_ = ' %s ' % time.strftime('%H:%M:%S')
powerline.append(time_,
powerline.theme.HOSTNAME_FG,
powerline.theme.HOSTNAME_BG)

View file

@ -0,0 +1,20 @@
import subprocess
import re
from ..utils import BasicSegment
class Segment(BasicSegment):
def add_to_powerline(self):
powerline = self.powerline
try:
output = subprocess.check_output(['uptime'], stderr=subprocess.STDOUT).decode("utf-8")
raw_uptime = re.search('(?<=up).+(?=,\s+\d+\s+user)', output).group(0)
day_search = re.search('\d+(?=\s+day)', output)
days = '' if not day_search else '%sd ' % day_search.group(0)
hour_search = re.search('\d{1,2}(?=\:)', raw_uptime)
hours = '' if not hour_search else '%sh ' % hour_search.group(0)
minutes = re.search('(?<=\:)\d{1,2}|\d{1,2}(?=\s+min)', raw_uptime).group(0)
uptime = u' %s%s%sm \u2191 ' % (days, hours, minutes)
powerline.append(uptime, powerline.theme.CWD_FG, powerline.theme.PATH_BG)
except OSError:
return

View file

@ -0,0 +1,21 @@
from ..utils import BasicSegment
import os
import pwd
class Segment(BasicSegment):
def add_to_powerline(self):
powerline = self.powerline
if powerline.args.shell == "bash":
user_prompt = r" \u "
elif powerline.args.shell == "zsh":
user_prompt = " %n "
else:
user_prompt = " %s " % os.getenv("USER")
if pwd.getpwuid(os.getuid())[0] == "root":
bgcolor = powerline.theme.USERNAME_ROOT_BG
else:
bgcolor = powerline.theme.USERNAME_BG
powerline.append(user_prompt, powerline.theme.USERNAME_FG, bgcolor)

View file

@ -0,0 +1,15 @@
import os
from ..utils import BasicSegment
class Segment(BasicSegment):
def add_to_powerline(self):
env = os.getenv('VIRTUAL_ENV') \
or os.getenv('CONDA_ENV_PATH') \
or os.getenv('CONDA_DEFAULT_ENV')
if not env:
return
env_name = os.path.basename(env)
bg = self.powerline.theme.VIRTUAL_ENV_BG
fg = self.powerline.theme.VIRTUAL_ENV_FG
self.powerline.append(" " + env_name + " ", fg, bg)

View file

View file

@ -1,6 +1,8 @@
# Basic theme which only uses colors in 0-15 range
from .default import DefaultColor
class Color(DefaultColor):
"""Basic theme which only uses colors in 0-15 range"""
USERNAME_FG = 8
USERNAME_BG = 15
USERNAME_ROOT_BG = 1

View file

@ -1,8 +1,13 @@
class DefaultColor:
class DefaultColor(object):
"""
This class should have the default colors for every segment.
Please test every new segment with this theme first.
"""
# RESET is not a real color code. It is used as in indicator
# within the code that any foreground / background color should
# be cleared
RESET = -1
USERNAME_FG = 250
USERNAME_BG = 240
USERNAME_ROOT_BG = 124
@ -56,10 +61,14 @@ class DefaultColor:
VIRTUAL_ENV_BG = 35 # a mid-tone green
VIRTUAL_ENV_FG = 00
BATTERY_NORMAL_BG = 22
BATTERY_NORMAL_FG = 7
BATTERY_LOW_BG = 196
BATTERY_LOW_FG = 7
AWS_PROFILE_FG = 39
AWS_PROFILE_BG = 238
class Color(DefaultColor):
"""
This subclass is required when the user chooses to use 'default' theme.

View file

@ -1,3 +1,6 @@
from .default import DefaultColor
class Color(DefaultColor):
USERNAME_FG = 15
USERNAME_BG = 4

View file

@ -0,0 +1,38 @@
from .default import DefaultColor
class Color(DefaultColor):
USERNAME_FG = 15
USERNAME_BG = 4
USERNAME_ROOT_BG = 1
HOSTNAME_FG = 15
HOSTNAME_BG = 10
HOME_SPECIAL_DISPLAY = False
PATH_FG = 10
PATH_BG = 7
CWD_FG = 0
SEPARATOR_FG = 14
READONLY_BG = 1
READONLY_FG = 7
REPO_CLEAN_FG = 0
REPO_CLEAN_BG = 15
REPO_DIRTY_FG = 1
REPO_DIRTY_BG = 15
JOBS_FG = 4
JOBS_BG = 7
CMD_PASSED_FG = 15
CMD_PASSED_BG = 2
CMD_FAILED_FG = 15
CMD_FAILED_BG = 1
SVN_CHANGES_FG = REPO_DIRTY_FG
SVN_CHANGES_BG = REPO_DIRTY_BG
VIRTUAL_ENV_BG = 15
VIRTUAL_ENV_FG = 2

View file

@ -1,3 +1,6 @@
from .default import DefaultColor
class Color(DefaultColor):
USERNAME_FG = 8
USERNAME_BG = 251

95
powerline_shell/utils.py Normal file
View file

@ -0,0 +1,95 @@
import sys
import threading
py3 = sys.version_info[0] == 3
if py3:
def unicode(x):
return str(x)
class RepoStats(object):
symbols = {
'detached': u'\u2693',
'ahead': u'\u2B06',
'behind': u'\u2B07',
'staged': u'\u2714',
'changed': u'\u270E',
'new': u'\u2753',
'conflicted': u'\u273C'
}
def __init__(self, ahead=0, behind=0, new=0, changed=0, staged=0, conflicted=0):
self.ahead = ahead
self.behind = behind
self.new = new
self.changed = changed
self.staged = staged
self.conflicted = conflicted
def __eq__(self, other):
return (
self.ahead == other.ahead and
self.behind == other.behind and
self.new == other.new and
self.changed == other.changed and
self.staged == other.staged and
self.conflicted == other.conflicted
)
@property
def dirty(self):
qualifiers = [
self.new,
self.changed,
self.staged,
self.conflicted,
]
return sum(qualifiers) > 0
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 new files, but you only
want a number to appear next to the icon when there are more than one
new file, you can do:
segment = repo_stats.n_or_empty("new") + icon_string
"""
return unicode(self[_key]) if int(self[_key]) > 1 else u''
def add_to_powerline(self, powerline):
def add(_key, fg, bg):
if self[_key]:
s = u" {}{} ".format(self.n_or_empty(_key), self.symbols[_key])
powerline.append(s, fg, bg)
color = powerline.theme
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('changed', color.GIT_NOTSTAGED_FG, color.GIT_NOTSTAGED_BG)
add('new', color.GIT_UNTRACKED_FG, color.GIT_UNTRACKED_BG)
add('conflicted', color.GIT_CONFLICTED_FG, color.GIT_CONFLICTED_BG)
def warn(msg):
print('[powerline-bash] ', msg)
class BasicSegment(object):
def __init__(self, powerline):
self.powerline = powerline
def start(self):
pass
class ThreadedSegment(threading.Thread):
def __init__(self, powerline):
super(ThreadedSegment, self).__init__()
self.powerline = powerline

View file

@ -1,6 +0,0 @@
def add_exit_code_segment(powerline):
if powerline.args.prev_error == 0:
return
fg = Color.CMD_FAILED_FG
bg = Color.CMD_FAILED_BG
powerline.append(' %s ' % str(powerline.args.prev_error), fg, bg)

View file

@ -1,50 +0,0 @@
import os
import subprocess
def get_fossil_status():
has_modified_files = False
has_untracked_files = False
has_missing_files = False
output = os.popen('fossil changes 2>/dev/null').read().strip()
has_untracked_files = True if os.popen("fossil extras 2>/dev/null").read().strip() else False
has_missing_files = 'MISSING' in output
has_modified_files = 'EDITED' in output
return has_modified_files, has_untracked_files, has_missing_files
def _add_fossil_segment(powerline):
subprocess.Popen(['fossil'], stdout=subprocess.PIPE).communicate()[0]
branch = ''.join([i.replace('*','').strip() for i in os.popen("fossil branch 2> /dev/null").read().strip().split("\n") if i.startswith('*')])
if len(branch) == 0:
return
bg = Color.REPO_CLEAN_BG
fg = Color.REPO_CLEAN_FG
has_modified_files, has_untracked_files, has_missing_files = get_fossil_status()
if has_modified_files or has_untracked_files or has_missing_files:
bg = Color.REPO_DIRTY_BG
fg = Color.REPO_DIRTY_FG
extra = ''
if has_untracked_files:
extra += '+'
if has_missing_files:
extra += '!'
branch += (' ' + extra if extra != '' else '')
powerline.append(' %s ' % branch, fg, bg)
def add_fossil_segment(powerline):
"""Wraps _add_fossil_segment in exception handling."""
# FIXME This function was added when introducing a testing framework,
# during which the 'powerline' object was passed into the
# `add_[segment]_segment` functions instead of being a global variable. At
# that time it was unclear whether the below exceptions could actually be
# thrown. It would be preferable to find out whether they ever will. If so,
# write a comment explaining when. Otherwise remove.
try:
_add_fossil_segment(powerline)
except OSError:
pass
except subprocess.CalledProcessError:
pass

View file

@ -1,39 +0,0 @@
import os
import subprocess
def get_hg_status():
has_modified_files = False
has_untracked_files = False
has_missing_files = False
p = subprocess.Popen(['hg', 'status'], stdout=subprocess.PIPE)
output = p.communicate()[0].decode("utf-8")
for line in output.split('\n'):
if line == '':
continue
elif line[0] == '?':
has_untracked_files = True
elif line[0] == '!':
has_missing_files = True
else:
has_modified_files = True
return has_modified_files, has_untracked_files, has_missing_files
def add_hg_segment(powerline):
branch = os.popen('hg branch 2> /dev/null').read().rstrip()
if len(branch) == 0:
return False
bg = Color.REPO_CLEAN_BG
fg = Color.REPO_CLEAN_FG
has_modified_files, has_untracked_files, has_missing_files = get_hg_status()
if has_modified_files or has_untracked_files or has_missing_files:
bg = Color.REPO_DIRTY_BG
fg = Color.REPO_DIRTY_FG
extra = ''
if has_untracked_files:
extra += '+'
if has_missing_files:
extra += '!'
branch += (' ' + extra if extra != '' else '')
return powerline.append(' %s ' % branch, fg, bg)

View file

@ -1,21 +0,0 @@
def add_hostname_segment(powerline):
if powerline.args.colorize_hostname:
from lib.color_compliment import stringToHashToColorAndOpposite
from lib.colortrans import rgb2short
from socket import gethostname
hostname = gethostname()
FG, BG = stringToHashToColorAndOpposite(hostname)
FG, BG = (rgb2short(*color) for color in [FG, BG])
host_prompt = ' %s ' % hostname.split('.')[0]
powerline.append(host_prompt, FG, BG)
else:
if powerline.args.shell == 'bash':
host_prompt = ' \\h '
elif powerline.args.shell == 'zsh':
host_prompt = ' %m '
else:
import socket
host_prompt = ' %s ' % socket.gethostname().split('.')[0]
powerline.append(host_prompt, Color.HOSTNAME_FG, Color.HOSTNAME_BG)

View file

@ -1,17 +0,0 @@
import os
import re
import subprocess
def add_jobs_segment(powerline):
pppid_proc = subprocess.Popen(['ps', '-p', str(os.getppid()), '-oppid='],
stdout=subprocess.PIPE)
pppid = pppid_proc.communicate()[0].decode("utf-8").strip()
output_proc = subprocess.Popen(['ps', '-a', '-o', 'ppid'],
stdout=subprocess.PIPE)
output = output_proc.communicate()[0].decode("utf-8")
num_jobs = len(re.findall(str(pppid), output)) - 1
if num_jobs > 0:
powerline.append(' %d ' % num_jobs, Color.JOBS_FG, Color.JOBS_BG)

View file

@ -1,11 +0,0 @@
import subprocess
def add_node_version_segment(powerline):
try:
p1 = subprocess.Popen(["node", "--version"], stdout=subprocess.PIPE)
version = p1.communicate()[0].decode("utf-8").rstrip()
version = "node " + version
powerline.append(version, 15, 18)
except OSError:
return

View file

@ -1,14 +0,0 @@
import subprocess
def add_php_version_segment(powerline):
try:
output = subprocess.check_output(['php', '-r', 'echo PHP_VERSION;'], stderr=subprocess.STDOUT)
if '-' in output:
version = ' %s ' % output.split('-')[0]
else:
version = ' %s ' % output
powerline.append(version, 15, 4)
except OSError:
return

View file

@ -1,7 +0,0 @@
import os
def add_read_only_segment(powerline):
cwd = powerline.cwd or os.getenv('PWD')
if not os.access(cwd, os.W_OK):
powerline.append(' %s ' % powerline.lock, Color.READONLY_FG, Color.READONLY_BG)

View file

@ -1,12 +0,0 @@
def add_root_segment(powerline):
root_indicators = {
'bash': ' \\$ ',
'zsh': ' %# ',
'bare': ' $ ',
}
bg = Color.CMD_PASSED_BG
fg = Color.CMD_PASSED_FG
if powerline.args.prev_error != 0:
fg = Color.CMD_FAILED_FG
bg = Color.CMD_FAILED_BG
powerline.append(root_indicators[powerline.args.shell], fg, bg)

View file

@ -1,15 +0,0 @@
import subprocess
def add_ruby_version_segment(powerline):
try:
p1 = subprocess.Popen(["ruby", "-v"], stdout=subprocess.PIPE)
p2 = subprocess.Popen(["sed", "s/ (.*//"], stdin=p1.stdout, stdout=subprocess.PIPE)
version = p2.communicate()[0].decode("utf-8").rstrip()
if os.environ.has_key("GEM_HOME"):
gem = os.environ["GEM_HOME"].split("@")
if len(gem) > 1:
version += " " + gem[1]
powerline.append(version, 15, 1)
except OSError:
return

View file

@ -1,15 +0,0 @@
def add_set_term_title_segment(powerline):
term = os.getenv('TERM')
if not (('xterm' in term) or ('rxvt' in term)):
return
if powerline.args.shell == 'bash':
set_title = '\\[\\e]0;\\u@\\h: \\w\\a\\]'
elif powerline.args.shell == 'zsh':
set_title = '\033]0;%n@%m: %~\007'
else:
import socket
set_title = '\033]0;%s@%s: %s\007' % (os.getenv('USER'), socket.gethostname().split('.')[0], powerline.cwd or os.getenv('PWD'))
powerline.append(set_title, None, None, '')

View file

@ -1,6 +0,0 @@
import os
def add_ssh_segment(powerline):
if os.getenv('SSH_CLIENT'):
powerline.append(' %s ' % powerline.network, Color.SSH_FG, Color.SSH_BG)

View file

@ -1,37 +0,0 @@
import subprocess
def _add_svn_segment(powerline):
is_svn = subprocess.Popen(['svn', 'status'],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
is_svn_output = is_svn.communicate()[1].decode("utf-8").strip()
if len(is_svn_output) != 0:
return
#"svn status | grep -c "^[ACDIMRX\\!\\~]"
p1 = subprocess.Popen(['svn', 'status'], stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
p2 = subprocess.Popen(['grep', '-c', '^[ACDIMR\\!\\~]'],
stdin=p1.stdout, stdout=subprocess.PIPE)
output = p2.communicate()[0].decode("utf-8").strip()
if len(output) > 0 and int(output) > 0:
changes = output.strip()
powerline.append(' %s ' % changes, Color.SVN_CHANGES_FG, Color.SVN_CHANGES_BG)
def add_svn_segment(powerline):
"""Wraps _add_svn_segment in exception handling."""
# FIXME This function was added when introducing a testing framework,
# during which the 'powerline' object was passed into the
# `add_[segment]_segment` functions instead of being a global variable. At
# that time it was unclear whether the below exceptions could actually be
# thrown. It would be preferable to find out whether they ever will. If so,
# write a comment explaining when. Otherwise remove.
try:
_add_svn_segment(powerline)
except OSError:
pass
except subprocess.CalledProcessError:
pass

View file

@ -1,10 +0,0 @@
def add_time_segment(powerline):
if powerline.args.shell == 'bash':
time = ' \\t '
elif powerline.args.shell == 'zsh':
time = ' %* '
else:
import time
time = ' %s ' % time.strftime('%H:%M:%S')
powerline.append(time, Color.HOSTNAME_FG, Color.HOSTNAME_BG)

View file

@ -1,16 +0,0 @@
import subprocess
import re
def add_uptime_segment(powerline):
try:
output = subprocess.check_output(['uptime'], stderr=subprocess.STDOUT)
raw_uptime = re.search('(?<=up).+(?=,\s+\d+\s+user)', output).group(0)
day_search = re.search('\d+(?=\s+day)', output)
days = '' if not day_search else '%sd ' % day_search.group(0)
hour_search = re.search('\d{1,2}(?=\:)', raw_uptime)
hours = '' if not hour_search else '%sh ' % hour_search.group(0)
minutes = re.search('(?<=\:)\d{1,2}|\d{1,2}(?=\s+min)', raw_uptime).group(0)
uptime = u' %s%s%sm \u2191 ' % (days, hours, minutes)
powerline.append(uptime, Color.CWD_FG, Color.PATH_BG)
except OSError:
return

View file

@ -1,16 +0,0 @@
def add_username_segment(powerline):
import os
if powerline.args.shell == 'bash':
user_prompt = ' \\u '
elif powerline.args.shell == 'zsh':
user_prompt = ' %n '
else:
user_prompt = ' %s ' % os.getenv('USER')
if os.getenv('USER') == 'root':
bgcolor = Color.USERNAME_ROOT_BG
else:
bgcolor = Color.USERNAME_BG
powerline.append(user_prompt, Color.USERNAME_FG, bgcolor)

View file

@ -1,11 +0,0 @@
import os
def add_virtual_env_segment(powerline):
env = os.getenv('VIRTUAL_ENV') or os.getenv('CONDA_ENV_PATH')
if env is None:
return
env_name = os.path.basename(env)
bg = Color.VIRTUAL_ENV_BG
fg = Color.VIRTUAL_ENV_FG
powerline.append(' %s ' % env_name, fg, bg)

5
setup.cfg Normal file
View file

@ -0,0 +1,5 @@
[bdist_wheel]
universal = 1
[metadata]
description-file = README.md

22
setup.py Executable file
View file

@ -0,0 +1,22 @@
#!/usr/bin/env python
from setuptools import setup, find_packages
setup(name="powerline-shell",
version="0.3.0",
description="A pretty prompt for your shell",
author="Buck Ryan",
url="https://github.com/banga/powerline-shell",
classifiers=[],
packages=[
"powerline_shell",
"powerline_shell.segments",
"powerline_shell.themes",
],
install_requires=[
"argparse",
],
entry_points="""
[console_scripts]
powerline-shell=powerline_shell:main
""",
)

View file

@ -3,7 +3,7 @@ import mock
import os
import tempfile
import shutil
import powerline_shell_base as p
import powerline_shell as p
class CwdTest(unittest.TestCase):
@ -15,14 +15,14 @@ class CwdTest(unittest.TestCase):
shutil.rmtree(self.dirname)
@mock.patch('os.getenv')
@mock.patch('powerline_shell_base.warn')
@mock.patch('powerline_shell.warn')
def test_normal(self, warn, getenv):
getenv.return_value = self.dirname
self.assertEqual(p.get_valid_cwd(), self.dirname)
self.assertEqual(warn.call_count, 0)
@mock.patch('os.getenv')
@mock.patch('powerline_shell_base.warn')
@mock.patch('powerline_shell.warn')
def test_nonexistent_warns(self, warn, getenv):
subdir = os.path.join(self.dirname, 'subdir')
getenv.return_value = subdir
@ -30,7 +30,7 @@ class CwdTest(unittest.TestCase):
self.assertEqual(warn.call_count, 1)
@mock.patch('os.getenv')
@mock.patch('powerline_shell_base.warn')
@mock.patch('powerline_shell.warn')
def test_falls_back_to_getcwd(self, warn, getenv):
getenv.return_value = None
os.chdir(self.dirname)
@ -38,7 +38,7 @@ class CwdTest(unittest.TestCase):
self.assertEqual(warn.call_count, 0)
@mock.patch('os.getenv')
@mock.patch('powerline_shell_base.warn')
@mock.patch('powerline_shell.warn')
def test_nonexistent_getcwd_warns(self, warn, getenv):
subdir = os.path.join(self.dirname, 'subdir')
getenv.return_value = None

25
test/repo_stats_test.py Normal file
View file

@ -0,0 +1,25 @@
import unittest
from powerline_shell.utils import RepoStats
class RepoStatsTest(unittest.TestCase):
def setUp(self):
self.repo_stats = RepoStats()
self.repo_stats.changed = 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.new, 0)
def test_n_or_empty__empty(self):
self.assertEqual(self.repo_stats.n_or_empty("changed"), 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["changed"], 1)

View file

@ -0,0 +1,63 @@
import unittest
import mock
import tempfile
import shutil
import sh
import powerline_shell.segments.fossil as fossil
from powerline_shell.utils import RepoStats
test_cases = {
"EXTRA new-file": RepoStats(new=1),
"EDITED modified-file": RepoStats(changed=1),
"CONFLICT conflicted-file": RepoStats(conflicted=1),
"ADDED added-file": RepoStats(staged=1),
}
class FossilTest(unittest.TestCase):
def setUp(self):
self.powerline = mock.MagicMock()
self.dirname = tempfile.mkdtemp()
sh.cd(self.dirname)
sh.fossil("init", "test.fossil")
sh.fossil("open", "test.fossil")
self.segment = fossil.Segment(self.powerline)
def tearDown(self):
shutil.rmtree(self.dirname)
def _add_and_commit(self, filename):
sh.touch(filename)
sh.fossil("add", filename)
sh.fossil("commit", "-m", "add file " + filename)
def _checkout_new_branch(self, branch):
sh.fossil("branch", "new", branch, "trunk")
@mock.patch("powerline_shell.segments.fossil.get_PATH")
def test_fossil_not_installed(self, get_PATH):
get_PATH.return_value = "" # so fossil can't be found
self.segment.start()
self.segment.add_to_powerline()
self.assertEqual(self.powerline.append.call_count, 0)
def test_non_fossil_directory(self):
sh.fossil("close", "--force")
self.segment.start()
self.segment.add_to_powerline()
self.assertEqual(self.powerline.append.call_count, 0)
def test_standard(self):
self._add_and_commit("foo")
self.segment.start()
self.segment.add_to_powerline()
self.assertEqual(self.powerline.append.call_args[0][0], " trunk ")
@mock.patch('powerline_shell.segments.fossil._get_fossil_status')
def test_all(self, check_output):
for stdout, result in test_cases.items():
stats = fossil.parse_fossil_stats([stdout])
self.assertEquals(result, stats)

View file

@ -3,19 +3,20 @@ import mock
import tempfile
import shutil
import sh
import segments.git as git
import powerline_shell.segments.git as git
class GitTest(unittest.TestCase):
def setUp(self):
self.powerline = mock.MagicMock()
git.Color = mock.MagicMock()
self.dirname = tempfile.mkdtemp()
sh.cd(self.dirname)
sh.git("init", ".")
self.segment = git.Segment(self.powerline)
def tearDown(self):
shutil.rmtree(self.dirname)
@ -24,36 +25,41 @@ class GitTest(unittest.TestCase):
sh.git("add", filename)
sh.git("commit", "-m", "add file " + filename)
def _new_branch(self, branch):
def _checkout_new_branch(self, branch):
sh.git("checkout", "-b", branch)
def _get_commit_hash(self):
return sh.git("rev-parse", "HEAD")
@mock.patch('segments.git.get_PATH')
@mock.patch('powerline_shell.segments.git.get_PATH')
def test_git_not_installed(self, get_PATH):
get_PATH.return_value = "" # so git can't be found
git.add_git_segment(self.powerline)
self.segment.start()
self.segment.add_to_powerline()
self.assertEqual(self.powerline.append.call_count, 0)
def test_non_git_directory(self):
shutil.rmtree(".git")
git.add_git_segment(self.powerline)
self.segment.start()
self.segment.add_to_powerline()
self.assertEqual(self.powerline.append.call_count, 0)
def test_big_bang(self):
git.add_git_segment(self.powerline)
self.segment.start()
self.segment.add_to_powerline()
self.assertEqual(self.powerline.append.call_args[0][0], ' Big Bang ')
def test_master_branch(self):
self._add_and_commit("foo")
git.add_git_segment(self.powerline)
self.segment.start()
self.segment.add_to_powerline()
self.assertEqual(self.powerline.append.call_args[0][0], ' master ')
def test_different_branch(self):
self._add_and_commit("foo")
self._new_branch("bar")
git.add_git_segment(self.powerline)
self._checkout_new_branch("bar")
self.segment.start()
self.segment.add_to_powerline()
self.assertEqual(self.powerline.append.call_args[0][0], ' bar ')
def test_detached(self):
@ -61,7 +67,8 @@ class GitTest(unittest.TestCase):
commit_hash = self._get_commit_hash()
self._add_and_commit("bar")
sh.git("checkout", "HEAD^")
git.add_git_segment(self.powerline)
self.segment.start()
self.segment.add_to_powerline()
# In detached mode, we output a unicode symbol and then the shortened
# commit hash.

View file

@ -0,0 +1,48 @@
import unittest
import mock
import tempfile
import shutil
import sh
import powerline_shell.segments.hg as hg
class HgTest(unittest.TestCase):
def setUp(self):
self.powerline = mock.MagicMock()
self.dirname = tempfile.mkdtemp()
sh.cd(self.dirname)
sh.hg("init", ".")
self.segment = hg.Segment(self.powerline)
def tearDown(self):
shutil.rmtree(self.dirname)
def _add_and_commit(self, filename):
sh.touch(filename)
sh.hg("add", filename)
sh.hg("commit", "-m", "add file " + filename)
def _checkout_new_branch(self, branch):
sh.hg("branch", branch)
@mock.patch("powerline_shell.segments.hg.get_PATH")
def test_hg_not_installed(self, get_PATH):
get_PATH.return_value = "" # so hg can"t be found
self.segment.start()
self.segment.add_to_powerline()
self.assertEqual(self.powerline.append.call_count, 0)
def test_non_hg_directory(self):
shutil.rmtree(".hg")
self.segment.start()
self.segment.add_to_powerline()
self.assertEqual(self.powerline.append.call_count, 0)
def test_standard(self):
self._add_and_commit("foo")
self.segment.start()
self.segment.add_to_powerline()
self.assertEqual(self.powerline.append.call_args[0][0], " default ")

View file

@ -0,0 +1,21 @@
import unittest
import mock
import powerline_shell.segments.hostname as hostname
from powerline_shell.themes.default import Color
from argparse import Namespace
class HostnameTest(unittest.TestCase):
def setUp(self):
self.powerline = mock.MagicMock()
self.powerline.theme = Color
self.segment = hostname.Segment(self.powerline)
def test_colorize(self):
self.powerline.segment_conf.return_value = True
self.segment.start()
self.segment.add_to_powerline()
args = self.powerline.append.call_args[0]
self.assertNotEqual(args[0], r" \h ")
self.assertNotEqual(args[1], Color.HOSTNAME_FG)
self.assertNotEqual(args[2], Color.HOSTNAME_BG)

View file

@ -1,6 +1,6 @@
import unittest
import mock
import segments.uptime as uptime
import powerline_shell.segments.uptime as uptime
test_cases = {
# linux test cases
@ -19,11 +19,12 @@ class UptimeTest(unittest.TestCase):
def setUp(self):
self.powerline = mock.MagicMock()
uptime.Color = mock.MagicMock()
self.segment = uptime.Segment(self.powerline)
@mock.patch('subprocess.check_output')
def test_all(self, check_output):
for stdout, result in test_cases.items():
check_output.return_value = stdout
uptime.add_uptime_segment(self.powerline)
self.segment.start()
self.segment.add_to_powerline()
self.assertEqual(self.powerline.append.call_args[0][0].split()[0], result)