diff --git a/README.md b/README.md index 822f41a..c0a5589 100644 --- a/README.md +++ b/README.md @@ -4,34 +4,41 @@ Marker is a command palette for the terminal. It lets you bookmark commands (or commands templates) and easily retreive them with the help of a real-time fuzzy matcher. -It's also shipped with many commands common usage(Thanks to [tldr](https://github.com/tldr-pages/tldr)). - +It's also shipped with many commands common usage (thanks to [tldr](https://github.com/tldr-pages/tldr)). + +After installation, all commands have to be stored in the folder `~/.local/share/marker/`, including the tldr commands. +All new commands, bookmarked with `Ctrl-k` will be stored in the file `/.local/share/marker/bookmarked_commands.txt`. + ## Features: - A UI selector that lets you easily select the desired command if more than one command is matched. - Fuzzy matching (through commands and their descriptions). -- Command template: You can bookmark commands with place-holders and place the cursor at those place-holders using a keyboard shortcut. +- Command template that allows to bookmark commands with place-holders and place the cursor at those place-holders using a keyboard shortcut. - Portability across supported shells: you can use bookmarked commands in both Bash and Zshell. ## Usage -- `Ctrl-space`: search for commands that match the current written string in the command-line. -- `Ctrl-k` (or `marker add`): Bookmark a command. -- `Ctrl-t`: place the cursor at the next placeholder, identified by '{{anything}}' -- `marker remove`: remove a bookmark +- `Ctrl-space`: Search for commands that match the current written string in the command-line +- `Ctrl-k` (or `marker add`): Bookmark a command +- `Ctrl-t`: Place the cursor at the next placeholder, identified by '{{anything}}' +- `Ctrl-g`: Copy to clipboard you just selected - it requires xsel installed -You can customize key binding using environment variables, respectively with ```MARKER_KEY_GET```, ```MARKER_KEY_MARK``` and ```MARKER_KEY_NEXT_PLACEHOLDER```. +You can customize key binding using environment variables, respectively with ```MARKER_KEY_GET```, ```MARKER_KEY_MARK```, ```MARKER_KEY_NEXT_PLACEHOLDER``` and ```MARKER_KEY_COPY```. ## Requirements -- python (2.7+ or 3.0+) -- Bash-4.3+ or Zshell. -- Linux Or OSX +- python 2.7+ or 3.0+ +- Bash 4.3+ or Zsh +- Linux or OSX +- xsel by Conrad Parker -#####Note: -In OSX, it seems like Bash 3.x is the default shell which is not supported. you have to [update your Bash to 4.3+](http://apple.stackexchange.com/a/24635) or [change your shell to zshell](http://stackoverflow.com/a/1822126/1117720) in order to use Marker. +##Note: +I have not tested this forked version on macOS. +The original version of the tool, however, mentioned that: in OSX, it seems like Bash 3.x is the default shell which is not supported. you have to [update your Bash to 4.3+](http://apple.stackexchange.com/a/24635) or [change your shell to zshell](http://stackoverflow.com/a/1822126/1117720) in order to use Marker. ## Installation - `mkdir ~/.marker && cd ~/.marker` or go wherever you want to install Marker -- `git clone https://github.com/pindexis/marker .` +- `git clone` the repository to the current working directory - `./install.py` +- `apt-get install xsel` to install xsel, in order to use the `Ctrl-g` feature +- `mv ./tldr ~/.local/share/marker/` to copy the commands within the tldr folder into the marker home folder where all the commands need to be added ## License [MIT](LICENSE) diff --git a/bin/marker.sh b/bin/marker.sh index 5c49a79..4f59240 100644 --- a/bin/marker.sh +++ b/bin/marker.sh @@ -6,12 +6,13 @@ alias marker="${MARKER_HOME}/bin/marker" marker_key_mark="${MARKER_KEY_MARK:-\C-k}" marker_key_get="${MARKER_KEY_GET:-\C-@}" marker_key_next_placeholder="${MARKER_KEY_NEXT_PLACEHOLDER:-\C-t}" +marker_key_copy="${MARKER_KEY_COPY:-\C-g}" function get_cursor_position(){ # based on a script from http://invisible-island.net/xterm/xterm.faq.html exec < /dev/tty oldstty=$(stty -g) - stty raw -echo min 0 + #stty raw -echo min 0 # on my system, the following line can be replaced by the line below it echo -en "\033[6n" > /dev/tty # tput u7 > /dev/tty # when TERM=xterm (and relatives) @@ -90,13 +91,13 @@ if [[ -n "$ZSH_VERSION" ]]; then } # move the cursor the next placeholder function _move_cursor_to_next_placeholder { - match=$(echo "$BUFFER" | perl -nle 'print $& if m{{{.+?}}}' | head -n 1) + match=$(echo "$BUFFER" | perl -nle 'print $& if m{\{\{.+?\}\}}' | head -n 1) if [[ ! -z "$match" ]]; then len=${#match} match=$(echo "$match" | sed 's/"/\\"/g') placeholder_offset=$(echo "$BUFFER" | python -c 'import sys;keyboard_input = raw_input if sys.version_info[0] == 2 else input; print(keyboard_input().index("'$match'"))') CURSOR="$placeholder_offset" - BUFFER="${BUFFER[1,$placeholder_offset]}${BUFFER[$placeholder_offset+1+$len,-1]}" + BUFFER="${BUFFER[1,$placeholder_offset]}${BUFFER[$placeholder_offset+2+$len,-1]}" fi } @@ -110,12 +111,13 @@ if [[ -n "$ZSH_VERSION" ]]; then zle -N _marker_mark_2 bindkey '\emm2' _marker_mark_2 bindkey -s "$marker_key_mark" '\emm1\emm2' + bindkey -s "$marker_key_copy" '^Uecho "^Y" | perl -pe 'chomp' | xsel -i -b \r' elif [[ -n "$BASH" ]]; then # move the cursor the next placeholder '%%' function _move_cursor_to_next_placeholder { - match=$(echo "$READLINE_LINE" | perl -nle 'print $& if m{{{.+?}}}' | head -n 1) + match=$(echo "$READLINE_LINE" | perl -nle 'print $& if m{\{\{.+?\}\}}' | head -n 1) if [[ ! -z "$match" ]]; then len=${#match} match=$(echo "$match" | sed 's/"/\\"/g') @@ -169,4 +171,5 @@ elif [[ -n "$BASH" ]]; then bind '"'"$marker_key_mark"'":"\emm1\n\emm2"' bind -x '"'"$marker_key_next_placeholder"'":"_move_cursor_to_next_placeholder"' + bind '"'"$marker_key_copy"'":"\C-uecho \C-y | perl -pe 'chomp' | xsel -i -b \r"' fi diff --git a/install.py b/install.py index 44cbe63..c690339 100755 --- a/install.py +++ b/install.py @@ -45,12 +45,16 @@ def show_post_installation_message(config_dir_rel): rcfile = '.bash_profile' else: rcfile = '.%src' % get_shell() - - print("\nPlease add the following line to your ~/%s:" % rcfile) + print("\nThe following line needed to be added to your ~/%s:" % rcfile) print('\n' + source_msg) print('\n') - print("\nPlease restart the terminal after doing that(or re-source your *.rc).") - + autoAdd = raw_input('Add automagically to your RC file? [Y/n]: ') + if autoAdd == '' or autoAdd.lower() == 'y': + with open(os.path.expanduser('~') + '/' + rcfile, 'a') as File: + File.write(source_msg) + print("\nPlease `source ~/%s`."%rcfile) + else: + print("\nAdd manually and `source ~/%s`."%rcfile) def verify_requirements(): if not get_shell() in SUPPORTED_SHELLS: @@ -85,7 +89,7 @@ def main(): install_dir = os.path.join(os.path.dirname(os.path.abspath(__file__))) mkdir(config_dir_abosulte_path) - + write_to_file( os.path.join(config_dir_abosulte_path, 'marker.sh'), generate_marker_sh(config_dir_abosulte_path, install_dir)) diff --git a/marker/command.py b/marker/command.py index 1ef2489..d05745a 100644 --- a/marker/command.py +++ b/marker/command.py @@ -25,7 +25,7 @@ def remove(commands, command): pass class Command(object): - '''A Command is composed of the shell command string and an optionnal alias''' + '''A Command is composed of the shell command string and an optional alias''' def __init__(self, cmd, alias): if not cmd: raise "empty command argument" diff --git a/marker/core.py b/marker/core.py index 5f89d75..a1d27ad 100644 --- a/marker/core.py +++ b/marker/core.py @@ -1,6 +1,7 @@ # App logic from __future__ import print_function import os +import fnmatch from . import keys from . import readchar from . import command @@ -14,23 +15,16 @@ else: keyboard_input = input -def get_os(): - if platform == 'Darwin': - return 'osx' - elif platform.startswith('linux'): - return 'linux' - else: - # throw is better - return 'unknown' - -def get_user_marks_path(): - return os.path.join(os.getenv('MARKER_DATA_HOME'), 'user_commands.txt') -def get_tldr_os_marks_path(): - return os.path.join(os.getenv('MARKER_HOME'), 'tldr', get_os()+'.txt') -def get_tldr_common_marks_path(): - return os.path.join(os.getenv('MARKER_HOME'), 'tldr', 'common.txt') - - +def get_user_commands_path(): + return os.path.join(os.getenv('MARKER_DATA_HOME'), 'bookmarked_commands.txt') +#def get_tldr_os_marks_path(): +# return os.path.join(os.getenv('MARKER_HOME'), 'tldr', get_os()+'.txt') +def get_all_commands_path(): + return [os.path.join(dirpath, f) + for dirpath, dirnames, files in os.walk(os.getenv('MARKER_DATA_HOME')) + for f in fnmatch.filter(files, '*.txt')] + #return os.path.join(os.getenv('MARKER_HOME'), 'tldr', 'common.txt') + def mark_command(cmd_string, alias): ''' Adding a new Mark ''' if cmd_string: @@ -50,16 +44,19 @@ def mark_command(cmd_string, alias): # ## isn't allowed since it's used as seperator print ("command can't contain ##(it's used as command alias seperator)") return - commands = command.load(get_user_marks_path()) + commands = command.load(get_user_commands_path()) command.add(commands, command.Command(cmd_string, alias)) - command.save(commands, get_user_marks_path()) + command.save(commands, (get_user_commands_path())) def get_selected_command_or_input(search): ''' Display an interactive UI interface where the user can type and select commands this function returns the selected command if there is matches or the written characters in the prompt line if no matches are present ''' - commands = command.load(get_user_marks_path()) + command.load(get_tldr_os_marks_path()) + command.load(get_tldr_common_marks_path()) - state = State(commands, search) + user_commands = command.load(get_user_commands_path()) + get_commands = [] + for files in get_all_commands_path(): + get_commands.extend (command.load(files)) + state = State(get_commands, search) # draw the screen (prompt + matchd marks) renderer.refresh(state) # wait for user input(returns selected mark) @@ -70,10 +67,10 @@ def get_selected_command_or_input(search): return state.input return output.cmd - def remove_command(search): ''' Remove a command interactively ''' - commands = command.load(get_user_marks_path()) + user_commands = command.load(get_user_commands_path()) + #commands = command.load(get_user_marks_path()) state = State(commands, search) renderer.refresh(state) selected_mark = read_line(state)