Merge branch 'master' of https://github.com/0rax/cheat into 0rax-master

* 'master' of https://github.com/0rax/cheat:
  change block generator not to held block is they are empty + minor pep8 refacto (pylint:8.85/10)
  remove debug
  indent block fail :x, no more triple output
  Some minor modification (open inside the generator for better readability) + one bug fixe (the last block wasnt yiel by the generator)
  Following #128 talk about how output should be made for search. Just changed the way on how the search function output the result, no more ala grep output. Cheatsheets are now parsed [by CheatSheets.__parse_cheat_command_block(self, cheat_fp)] into block (separated by newline), i have seen that all block in cheatsheets are delimited by a blank line, so instead of parsing from first consecutive # to last consecutive command, an output that is not used by all sheets (reference to "7z" cheatfile). And so the block are parsed by begin of the document to blanck line to end of the document. Finally the output is made by indenting the block content by 4 spaces + the title of the sheet on the top. This is a way to handle subcommands in my mind (search "git commit" and you now have all what you want)
  Added search function into cheat, used a grep like output, if needed it could be changed, discussion is open inside #128 issue
  fix edit autocomplete (just dont show description of -e/--edit option and i dont know why
  Add hint to use sudo when creation fail of sheet.
  In response to Issue #108: Added option to copy sheet while not editable.
  Added autocompletion for fish shell

Conflicts:
	setup.py
This commit is contained in:
Chris Lane 2013-11-11 17:42:48 -05:00
commit 6cae837a2f
3 changed files with 126 additions and 34 deletions

12
autocompletion/cheat.fish Normal file
View File

@ -0,0 +1,12 @@
#completion for cheat
complete -c cheat -s h -l help -f -x --description "Display help and exit"
complete -c cheat -l edit -f -x --description "Edit <cheatsheet>"
complete -c cheat -s e -f -x --description "Edit <cheatsheet>"
complete -c cheat -s l -l list -f -x --description "List all available cheatsheets"
complete -c cheat -s d -l cheat-directories -f -x --description "List all current cheat dirs"
complete -c cheat --authoritative -f
for cheatsheet in (cheat -l | cut -d' ' -f1)
complete -c cheat -a "$cheatsheet"
complete -c cheat -o e -a "$cheatsheet"
complete -c cheat -o '-edit' -a "$cheatsheet"
end

140
cheat
View File

@ -3,7 +3,7 @@
cheat.py -- cheat allows you to create and view interactive cheatsheets on the
command-line. It was designed to help remind *nix system
administrators of options for commands that they use frequently,
but not frequently enough to remember.
but not frequently enough to remember.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -25,9 +25,9 @@ import argparse
import subprocess
from textwrap import dedent
DEFAULT_CHEAT_DIR = os.environ.get('DEFAULT_CHEAT_DIR') or \
os.path.join(os.path.expanduser('~'), '.cheat')
USE_PYGMENTS = False
DEFAULT_CHEAT_DIR = (os.environ.get('DEFAULT_CHEAT_DIR') or
os.path.join(os.path.expanduser('~'), '.cheat'))
USE_PYGMENTS = False
# NOTE remove this check if it is confirmed to work on windows
if os.name == 'posix' and 'CHEATCOLORS' in os.environ:
@ -40,8 +40,9 @@ if os.name == 'posix' and 'CHEATCOLORS' in os.environ:
except ImportError:
pass
def pretty_print(filename):
"Applies syntax highlighting to a cheatsheet and writes it to stdout"
"""Applies syntax highlighting to a cheatsheet and writes it to stdout"""
try:
if os.path.splitext(filename)[1]:
lexer = get_lexer_for_filename(filename)
@ -58,8 +59,9 @@ def pretty_print(filename):
fmt = TerminalFormatter()
highlight(code, lexer, fmt, sys.stdout)
class CheatSheets(object):
"""Cheatsheets database class."""
dirs = None
sheets = None
@ -67,7 +69,8 @@ class CheatSheets(object):
self.dirs = self.__cheat_directories()
# verify that we have at least one cheat directory
if not self.dirs:
error_msg = 'The {default} dir does not exist or the CHEATPATH var is not set.'
error_msg = ('The {default} dir does not exist'
' or the CHEATPATH var is not set.')
print >> sys.stderr, error_msg.format(default=DEFAULT_CHEAT_DIR)
exit(1)
self.sheets = self.__cheat_files()
@ -91,7 +94,9 @@ class CheatSheets(object):
return default
def __cheat_files(self):
"""Assembles a dictionary of cheatsheets found in the above directories."""
"""
Assembles a dictionary of cheatsheets found in the above directories.
"""
cheats = {}
for cheat_dir in reversed(self.dirs):
cheats.update(dict([(cheat, cheat_dir)
@ -103,40 +108,70 @@ class CheatSheets(object):
def edit(self, cheat):
"""Creates or edits a cheatsheet"""
# Assert that the EDITOR environment variable is set and that at least 3
# arguments have been given
# Assert that the EDITOR environment variable is set and that at least
# 3 arguments have been given
if 'EDITOR' not in os.environ:
print >> sys.stderr, ('In order to create/edit a cheatsheet you '
'must set your EDITOR environment variable to your favorite '
'editor\'s path.')
'must set your EDITOR environment variable '
'to your favorite editor\'s path.')
exit(1)
elif os.environ['EDITOR'] == "":
print >> sys.stderr, ('Your EDITOR environment variable is set '
'to nothing, in order to create/edit a cheatsheet your must '
'set it to a valid editor\'s path.')
'to nothing, in order to create/edit a '
'cheatsheet your must set it to a valid '
'editor\'s path.')
exit(1)
else:
editor = os.environ['EDITOR'].split()
# if the cheatsheet already exists, open it for editing
try:
if cheat in sheets.sheets:
subprocess.call(editor + [os.path.join(self.sheets[cheat], cheat)])
if cheat in self.sheets:
sheet_path = os.path.join(self.sheets[cheat], cheat)
if os.access(sheet_path, os.W_OK):
subprocess.call(editor + [sheet_path])
else:
print >> sys.stderr, ("Sheet '%s' [%s] is not editable."
% (cheat, sheet_path))
print ('Do you want to '
'copy it to your user cheatsheets directory [%s] '
'before editing ?\nKeep in mind that your sheet '
'will always be used before system-wide one.'
% DEFAULT_CHEAT_DIR)
awn = raw_input('[y/n] ')
if awn != 'y':
print ('Ok, if you want to edit system-wide sheet, '
'please try `cheat -e <cheatsheet>` '
'again with sudo.')
exit(1)
import shutil
new_sheet = os.path.join(DEFAULT_CHEAT_DIR, cheat)
shutil.copy(sheet_path, new_sheet)
subprocess.call(editor + [new_sheet])
# otherwise, create it
else:
import cheatsheets as cs
# Attempt to write the new cheatsheet to the user's ~/.cheat dir if it
# exists. If it does not exist, attempt to create it.
# Attempt to write the new cheatsheet to the user's ~/.cheat
# dir if it exists. If it does not exist, attempt to create it.
if os.access(DEFAULT_CHEAT_DIR, os.W_OK) or os.makedirs(DEFAULT_CHEAT_DIR):
subprocess.call(editor + [os.path.join(DEFAULT_CHEAT_DIR, cheat)])
subprocess.call(editor
+ [os.path.join(DEFAULT_CHEAT_DIR, cheat)])
# If the directory cannot be created, write to the python package
# directory, though that will likely require the use of sudo
# If the directory cannot be created, write to the python
# package directory, though that will likely require the use
# of sudo
else:
subprocess.call(editor + [os.path.join(cs.cheat_dir, cheat)])
except OSError, e:
if os.access(sheet_path, os.W_OK):
subprocess.call(editor
+ [os.path.join(cs.cheat_dir, cheat)])
else:
error_msg = ("Couldn't create '%s' cheatsheet.\n"
"Please retry usig sudo." % cheat)
print >> sys.stderr, error_msg
exit(1)
except OSError, errno:
print >> sys.stderr, ("Could not launch `%s` as your editor : %s"
% (editor[0], e.strerror))
% (editor[0], errno.strerror))
exit(1)
def list(self):
@ -145,6 +180,35 @@ class CheatSheets(object):
return ('\n'.join(sorted(['%s [%s]' % (key.ljust(max_command), value)
for key, value in self.sheets.items()])))
def __parse_cheat_command_block(self, cheat):
"""Parse text blocks inside specified sheet file"""
block = ""
path = os.path.join(self.sheets[cheat], cheat)
with open(path) as cheat_fp:
for line in cheat_fp.readlines():
if line == '\n':
if block:
yield block
block = ""
else:
block += line
if block:
yield block
def search(self, term):
"""Search for a term in sheetcheats"""
for cheat in self.sheets.keys():
output = ''
for block in self.__parse_cheat_command_block(cheat):
if term in block:
if not output:
output = cheat + ":\n"
output += ''.join([" " + line + '\n' for line
in block.split('\n')])
if output:
print output,
# Custom action for argparse
class ListDirectories(argparse.Action):
"""List cheat directories and exit"""
@ -152,20 +216,30 @@ class ListDirectories(argparse.Action):
print("\n".join(sheets.dirs))
parser.exit()
class ListCheatsheets(argparse.Action):
"""List cheatsheets and exit"""
def __call__(self, parser, namespace, values, option_string=None):
print sheets.list()
parser.exit()
class EditSheet(argparse.Action):
"""If the user wants to edit a cheatsheet"""
def __call__(self, parser, namespace, values, option_string=None):
sheets.edit(values[0])
parser.exit()
def main():
class SearchSheet(argparse.Action):
"""If the user wants to search a term inside all cheatsheets"""
def __call__(self, parser, namespace, values, option_string=None):
sheets.search(values[0])
parser.exit()
def main():
"""Main execution function"""
global sheets
sheets = CheatSheets()
@ -177,13 +251,13 @@ def main():
epi = dedent('''
Examples:
To look up 'tar':
cheat tar
To create or edit the cheatsheet for 'foo':
cheat -e foo
To list the directories on the CHEATPATH
cheat -d
@ -193,7 +267,8 @@ def main():
parser = argparse.ArgumentParser(prog='cheat',
description=desc, epilog=epi,
formatter_class=argparse.RawDescriptionHelpFormatter)
formatter_class=argparse.
RawDescriptionHelpFormatter)
parser_group = parser.add_mutually_exclusive_group()
parser_group.add_argument('sheet', metavar='cheatsheet',
action='store', type=str, nargs='?',
@ -201,6 +276,9 @@ def main():
parser_group.add_argument('-e', '--edit', metavar='cheatsheet',
action=EditSheet, type=str, nargs=1,
help='Edit <cheatsheet>')
parser_group.add_argument('-s', '--search', metavar='term',
action=SearchSheet, type=str, nargs=1,
help='Search <term> inside all cheatsheets')
parser_group.add_argument('-l', '--list',
action=ListCheatsheets, nargs=0,
help='List all available cheatsheets')
@ -219,8 +297,8 @@ def main():
pretty_print(filename)
else:
with open(filename) as istream:
for l in istream:
sys.stdout.write(l)
for line in istream:
sys.stdout.write(line)
# if it does not, say so
else:

View File

@ -17,6 +17,8 @@ setup(name='cheat',
package_data={'cheatsheets': [f for f in os.listdir('cheatsheets')
if '.' not in f]},
scripts=['cheat'],
data_files=[('/usr/share/zsh/site-functions', ['autocompletion/_cheat.zsh']),
('/etc/bash_completion.d' , ['autocompletion/cheat.bash'])]
)
data_files=[('/usr/share/zsh/site-functions', ['autocompletion/cheat.zsh']),
('/etc/bash_completion.d' , ['autocompletion/cheat.bash']),
('/usr/share/fish/completions' , ['autocompletion/cheat.fish'])
]
)