浏览代码

colorize: update plugin to support less options (#8392)

ProbstDJakob 4 年之前
父节点
当前提交
e21fbe7dff
共有 2 个文件被更改,包括 64 次插入37 次删除
  1. 7 11
      plugins/colorize/README.md
  2. 57 26
      plugins/colorize/colorize.plugin.zsh

+ 7 - 11
plugins/colorize/README.md

@@ -17,10 +17,10 @@ plugins=(... colorize)
 
 ### Requirements
 
-This plugin requires that either of the following tools be installed:
+This plugin requires that at least one of the following tools is installed:
 
-* Chroma: [https://github.com/alecthomas/chroma](https://github.com/alecthomas/chroma)
-* Pygments be installed: [pygments.org](https://pygments.org/)
+* [Chroma](https://github.com/alecthomas/chroma)
+* [Pygments](https://pygments.org/download/)
 
 ### Colorize tool
 
@@ -41,12 +41,8 @@ ZSH_COLORIZE_STYLE="colorful"
 ## Usage
 
 * `ccat <file> [files]`: colorize the contents of the file (or files, if more than one are provided).
-  If no arguments are passed it will colorize the standard input or stdin.
+  If no files are passed it will colorize the standard input.
 
-* `cless <file> [files]`: colorize the contents of the file (or files, if more than one are provided) and
-  open less. If no arguments are passed it will colorize the standard input or stdin.
-
-Note that `cless` will behave as less when provided more than one file: you have to navigate files with
-the commands `:n` for next and `:p` for previous. The downside is that less options are not supported.
-But you can circumvent this by either using the LESS environment variable, or by running `ccat file1 file2|less --opts`.
-In the latter form, the file contents will be concatenated and presented by less as a single file.
+* `cless [less-options] <file> [files]`: colorize the contents of the file (or files, if more than one are provided) and open less.
+  If no files are passed it will colorize the standard input.
+  The LESSOPEN and LESSCLOSE will be overwritten for this to work, but only in a local scope.

+ 57 - 26
plugins/colorize/colorize.plugin.zsh

@@ -1,8 +1,11 @@
-# easier alias to use the plugin
-alias ccat='colorize_via_pygmentize'
-alias cless='colorize_via_pygmentize_less'
+# Easier alias to use the plugin
+alias ccat="colorize_cat"
+alias cless="colorize_less"
 
-colorize_via_pygmentize() {
+# '$0:A' gets the absolute path of this file
+ZSH_COLORIZE_PLUGIN_PATH=$0:A
+
+colorize_check_requirements() {
     local available_tools=("chroma" "pygmentize")
 
     if [ -z "$ZSH_COLORIZE_TOOL" ]; then
@@ -23,6 +26,12 @@ colorize_via_pygmentize() {
         echo "Package '$ZSH_COLORIZE_TOOL' is not installed!" >&2
         return 1
     fi
+}
+
+colorize_cat() {
+    if ! colorize_check_requirements; then
+        return 1
+    fi
 
     # If the environment variable ZSH_COLORIZE_STYLE
     # is set, use that theme instead. Otherwise,
@@ -32,7 +41,7 @@ colorize_via_pygmentize() {
         ZSH_COLORIZE_STYLE="emacs"
     fi
 
-    # pygmentize stdin if no arguments passed
+    # Use stdin if no arguments have been passed.
     if [ $# -eq 0 ]; then
         if [[ "$ZSH_COLORIZE_TOOL" == "pygmentize" ]]; then
             pygmentize -O style="$ZSH_COLORIZE_STYLE" -g
@@ -42,12 +51,9 @@ colorize_via_pygmentize() {
         return $?
     fi
 
-    # guess lexer from file extension, or
-    # guess it from file contents if unsuccessful
-
+    # Guess lexer from file extension, or guess it from file contents if unsuccessful.
     local FNAME lexer
-    for FNAME in "$@"
-    do
+    for FNAME in "$@"; do
         if [[ "$ZSH_COLORIZE_TOOL" == "pygmentize" ]]; then
             lexer=$(pygmentize -N "$FNAME")
             if [[ $lexer != text ]]; then
@@ -61,22 +67,47 @@ colorize_via_pygmentize() {
     done
 }
 
-colorize_via_pygmentize_less() (
-    # this function is a subshell so tmp_files can be shared to cleanup function
-    declare -a tmp_files
+# The less option 'F - Forward forever; like "tail -f".' will not work in this implementation
+# caused by the lack of the ability to follow the file within pygmentize.
+colorize_less() {
+    if ! colorize_check_requirements; then
+        return 1
+    fi
 
-    cleanup () {
-        [[ ${#tmp_files} -gt 0 ]] && rm -f "${tmp_files[@]}"
-        exit
-    }
-    trap 'cleanup' EXIT HUP TERM INT
+    _cless() {
+        # LESS="-R $LESS" enables raw ANSI colors, while maintain already set options.
+        local LESS="-R $LESS"
 
-    while (( $# != 0 )); do     #TODO: filter out less opts
-        tmp_file="$(mktemp -t "tmp.colorize.XXXX.$(sed 's/\//./g' <<< "$1")")"
-        tmp_files+=("$tmp_file")
-        colorize_via_pygmentize "$1" > "$tmp_file"
-        shift 1
-    done
+        # This variable tells less to pipe every file through the specified command
+        # (see the man page of less INPUT PREPROCESSOR).
+        # 'zsh -ic "colorize_cat %s 2> /dev/null"' would not work for huge files like
+        # the ~/.zsh_history. For such files the tty of the preprocessor will be supended.
+        # Therefore we must source this file to make colorize_cat available in the
+        # preprocessor without the interactive mode.
+        # `2>/dev/null` will suppress the error for large files 'broken pipe' of the python
+        # script pygmentize, which will show up if less has not fully "loaded the file"
+        # (e.g. when not scrolled to the bottom) while already the next file will be displayed.
+        local LESSOPEN="| zsh -c 'source \"$ZSH_COLORIZE_PLUGIN_PATH\"; \
+        ZSH_COLORIZE_TOOL=$ZSH_COLORIZE_TOOL ZSH_COLORIZE_STYLE=$ZSH_COLORIZE_STYLE \
+        colorize_cat %s 2> /dev/null'"
 
-    less -f "${tmp_files[@]}"
-)
+        # LESSCLOSE will be set to prevent any errors by executing a user script
+        # which assumes that his LESSOPEN has been executed.
+        local LESSCLOSE=""
+
+        LESS="$LESS" LESSOPEN="$LESSOPEN" LESSCLOSE="$LESSCLOSE" less "$@"
+    }
+
+    if [ -t 0 ]; then
+        _cless "$@"
+    else
+        # The input is not associated with a terminal, therefore colorize_cat will
+        # colorize this input and pass it to less.
+        # Less has now to decide what to use. If any files have been provided, less
+        # will ignore the input by default, otherwise the colorized input will be used.
+        # If files have been supplied and the input has been redirected, this will
+        # lead to unnecessary overhead, but retains the ability to use the less options
+        # without checking for them inside this script.
+        colorize_cat | _cless "$@"
+    fi
+}