1#!/bin/zsh -fi
2# A zsh sticky-note ("post-it") application.  Load this file as a function:
3#    autoload -Uz sticky-note
4#
5# It may then be bound as a widget:
6#    zle -N sticky-note
7# And/or run as a command:
8#    sticky-note
9#    sticky-note -b
10#    sticky-note -l ...
11# The -b option is like "zed -b": it installs keymaps/bindings only.
12# Use the -l option to list previous sticky notes.  Most options of the
13# "fc -l" command are supported, for selecting which notes to display.
14# If "sticky-note -l" is run from inside a widget, the cursor is moved
15# to the top left of the terminal before display and returned to its
16# original position after display.  The -l option is implicitly added
17# when sticky-note is called from zle-line-init, to avoid inadvertently
18# trapping the user inside the note editor.
19#
20# Otherwise, invoke the line editor with the previous notes available
21# as an editor history.  Two quick taps on the return/enter key finish
22# the note, or you can use ^X^W as usual (ZZ in vicmd mode).
23
24# The application is configured by three zstyles, all using the context
25# ":sticky-note".  The first two styles are "notefile" and "maxnotes"
26# to name the file in which notes are stored and the maximum number of
27# notes to retain:
28#   zstyle :sticky-note notefile ~/.zsticky
29#   zstyle :sticky-note maxnotes 1000
30
31# The "theme" style may be set to control the appearance of the notes.
32# The style is an associative array; the current set of values (defaults
33# in parens) are:
34#   bg    => name or ANSI escape for background color (yellow)
35#   fg    => name or ANSI escape for foreground color (black)
36#   color => ANSI escape for color scheme ($theme[bg]$theme[fg])
37#   reset => ANSI escape to restore "normal" colors
38# Values given as names are looked up in the $bg and $fg arrays from the
39# "colors" function.  If a "color" field is set, the "bg" and "fg" fields
40# are not used.  Example:
41#   zstyle :sticky-note theme \
42#     bg red \
43#     fg $fg_bold[yellow]
44
45# For backwards compatibility with an earlier version, the notefile may
46# also be named by the STICKYFILE variable (defaults to $HOME/.zsticky).
47# The number of notes stored may be given by STICKYSIZE (1000).
48
49# I encourage all you creative people to contribute enhancements ...
50
51emulate -LR zsh
52setopt nobanghist extendedhistory histignoredups
53
54local STICKYFILE=${STICKYFILE:-$HOME/.zsticky}
55local STICKYSIZE=${STICKYSIZE:-1000}
56local sticky stickyfile stickysize
57
58zstyle -s :sticky-note notefile stickyfile || stickyfile=$STICKYFILE
59zstyle -s :sticky-note maxnotes stickysize || stickysize=$STICKYSIZE
60
61# Set up keybindings (adapted from "zed")
62if ! bindkey -M sticky >& /dev/null
63then
64  bindkey -N sticky main
65  bindkey -M sticky ^X^W accept-line
66  bindkey -M sticky ^M^M accept-line	# Two quick RETs ends note
67  bindkey -M sticky ^M self-insert-unmeta
68fi
69if ! bindkey -M sticky-vicmd >& /dev/null 
70then
71  bindkey -N sticky-vicmd vicmd
72  bindkey -M sticky-vicmd ZZ accept-line
73fi
74
75[[ "$1" == -b ]] && return 0
76
77# Look up color theme
78local -A theme
79(($+bg && $+fg)) || { autoload -Uz colors; colors }
80zstyle -m :sticky-note theme '*' || {
81    zstyle :sticky-note theme bg yellow fg black
82}
83zstyle -a :sticky-note theme theme
84(( ${+bg[$theme[bg]]} )) && theme[bg]=$bg[$theme[bg]]
85(( ${+fg[$theme[fg]]} )) && theme[fg]=$fg[$theme[fg]]
86(( ${+theme[color]} )) || theme[color]=$theme[bg]$theme[fg]
87(( ${+theme[reset]} )) || theme[reset]=$reset_color
88
89# If invoked as a widget, behave a bit like run-help
90if zle
91then
92  zmodload -i zsh/parameter
93  if [[ $* == -*l* || $functrace == *zle-line-init:* ]]
94  then
95    fc -ap $stickyfile $stickysize $stickysize
96    echoti sc
97    echoti home
98    print -nr "$theme[color]"
99    fc -l "${@:--1}" | while read -r sticky; do print -- "$sticky"; done
100    print -nr "$theme[reset]"
101    echoti rc
102  elif [[ $CONTEXT = (cont|select|vared) ]]
103  then
104    zle -M "No stickies during ${${(z)PREBUFFER}[1]:-$CONTEXT}, sorry"
105    zle .beep
106    zle -R
107  else
108    zle .push-line
109    BUFFER=sticky-note
110    zle .accept-line
111  fi
112  return 0
113fi
114
115# Invoked as a command, behave like zed, but write a history file
116fc -ap $stickyfile $stickysize $stickysize
117
118# With a -l option, list the existing sticky notes
119if [[ "$*" == -*l* ]]
120then
121  print -nr "$theme[color]"
122  # Use read/print loop to interpolate "\n" in history lines
123  fc -f "$@" | while read -r sticky; do print -- "$sticky"; done
124  print -nr "$theme[reset]"
125  return 0
126fi
127
128# Edit a new sticky note and add it to the stickyfile
129while vared -h -p "%{$theme[color]%}" -M sticky -m sticky-vicmd sticky
130do
131  {
132    [[ -n "$sticky" ]] && print -s -- "$sticky"
133  } always {
134    (( TRY_BLOCK_ERROR = 0 ))
135  } && break
136  echo -n -e '\a'
137done
138return 0
139