zsh-workers
 help / color / mirror / code / Atom feed
From: Bart Schaefer <schaefer@brasslantern.com>
To: Zsh Hackers List <zsh-workers@zsh.org>
Cc: vapnik spaknik <vapniks@yahoo.com>
Subject: Re: Suggested improvement for sticky-note
Date: Sat, 18 Nov 2023 21:25:19 -0800	[thread overview]
Message-ID: <CAH+w=7bbeg=XEQyNiaC_UwW=z1a8F0ERm-Mtzo_npbAhUKgnfg@mail.gmail.com> (raw)
In-Reply-To: <CAH+w=7ZRvCU1Uog_38D=CH7xTGYy1Lnuq4ORR0Oz77psZZAmag@mail.gmail.com>

[-- Attachment #1: Type: text/plain, Size: 899 bytes --]

On Sun, Nov 12, 2023 at 1:10 PM Bart Schaefer <schaefer@brasslantern.com> wrote:
>
> I'm sure there are undiscovered bugs with this, so mess around if interested.
>
> > That also caused me to notice that interrupting
> > sticky-note with a keyboard interrupt (^C) can cause old notes to
> > disappear, so that should be fixed.

I solved this.  It turns out that "fc -ap" removes events from the
history file if the enclosing function exits on interrupt.  This is
probably a bug of some sort, but easily worked around by addition of
an "always" block that clears the interrupt state.

> Also yet to be fixed is expiring
> lines from the display file when the corresponding lines expire from
> the history.

Fixed this too.

The attached revision also adds a "history-opts" style for changing
the localoptions used e.g. when navigating past notes and rewriting
the stickyfile.

[-- Attachment #2: sticky-note --]
[-- Type: application/octet-stream, Size: 11851 bytes --]

#!/bin/zsh -fi
# A zsh sticky-note ("post-it") application.  Load this file as a function:
#    autoload -Uz sticky-note
#
# It may then be bound as a widget:
#    zle -N sticky-note
# And/or run as a command:
#    sticky-note
#    sticky-note -b
#    sticky-note -l ...
# The -b option is like "zed -b": it installs keymaps/bindings only.
# Use the -l option to list previous sticky notes.  Most options of the
# "fc -l" command are supported, for selecting which notes to display.
# If "sticky-note -l" is run from inside a widget, the cursor is moved
# to the top left of the terminal before display and returned to its
# original position after display.  The -l option is implicitly added
# when sticky-note is called from zle-line-init, to avoid inadvertently
# trapping the user inside the note editor.
#
# Otherwise, invoke the line editor with the previous notes available
# as an editor history.  Two quick taps on the return/enter key finish
# the note, or you can use ^X^W as usual (ZZ in vicmd mode).

# The application is configured by several zstyles, all using the context
# ":sticky-note".  The complete list of styles is and their types is:
#   notefile       string (filename)
#   maxnotes       number
#   history-opts   array
#   vared-options  array
#   theme          associative array
#   display        associative array
#   list-display   boolean (string true|yes|on|1 or not set for false)

# The first two styles are "notefile" and "maxnotes" to name the file in
# which notes are stored and the maximum number of notes to retain:
#   zstyle :sticky-note notefile ~/.zsticky
#   zstyle :sticky-note maxnotes 1000

# For backwards compatibility with an earlier version, the notefile may
# also be named by the STICKYFILE variable (defaults to $HOME/.zsticky).
# The number of notes stored may be given by STICKYSIZE (1000).

# The "history-opts" style gives a list of setopt names, passed to
# "setopt localoptions ...".  Note that this means you must use the "no"
# prefix to disable an option.  The extendedhistory option is always used
# regardless of the setting of this style, to record note timestamps.
# Otherwise, the default is equivalent to
#   zstyle :sticky-note history-opts \
#     noappendhistory nobanghist histignoredups
# Values that do not contain the substring "hist" are ignored, along with:
#   histlexwords histnofunctions histnostore incappendhistory sharehistory
# Useful values include:
#   histexpiredupsfirst histfindnodups histignorealldups histsavenodups
# Other setopts not related to history are reset via "emulate -R zsh".

# The "vared-options" style lists options passed to vared when a note
# is edited. The vared options -A, -a, -c, -M, and -m are ignored.  The
# useful options are -i, -f, -e, -p, -r, and in unusual cases -t.  The
# options -p and -r should use the same syntax as the "prompt" value of
# the "theme" style, described below, and the -r option should.  As a
# special case, to make the note history unavailable when editing,
# include +h in the vared-options style.  Example:
#   zstyle :sticky-note vared-options +h -e -r %T

# The "theme" style may be set to control the appearance of the notes.
# The style is an associative array; the current set of values (defaults
# in parens) are:
#   bg     => name or ANSI escape for background color (yellow)
#   fg     => name or ANSI escape for foreground color (black)
#   color  => ANSI escape for color scheme ($theme[bg]$theme[fg])
#   reset  => ANSI escape to restore "normal" colors
#   prompt => Passed to vared.  May refer to %{${theme[bg]}%} et al.
# Values given as names are looked up in the $bg and $fg arrays from the
# "colors" function.  If a "color" field is set, the "bg" and "fg" fields
# are not used unless referenced in "prompt".  The prompt value should
# be single-quoted and must use appropriate %{...%} wrappers around
# zero-width outputs such as color changes.  Example:
#   zstyle :sticky-note theme \
#     bg red \
#     fg $fg_bold[yellow] \
#     prompt '%{$theme[bg]$fg_bold[white]%}POST-IT:%{$theme[reset]%}'
# NOTE:  You must define either color or both fg and bg, but the values
# $theme[color] and $theme[reset] are always generated if omitted.

# The "display" style is an associative array mapping custom display
# attribute names to the ANSI codes to enable them.  The style must use
# "%s" at the position where the note should appear, and must end with
# ANSI codes to discontinue the style.  An empty value turns off the
# display formatting.  For example:
#   zstyle :sticky-note display \
#     none "" \
#     blink "$(echoti blink)%s$(echoti sgr0)" \
#     reverse $'\e[07m%s\e[00m'
# If you use this style, a file named $STICKYFILE.display is created
# to preserve the display attributes of the notes in $STICKYFILE.
# NOTE: Changing the display zstyle does not change the display of
# previously created notes.  There is no default display style.

# To set the display for a note, type ctrl-x question-mark (^X?) to
# run the widget "_sticky-display".  When a "display" style is set, this
# replaces the _complete_help binding from the default keymap.  The
# keymap named "sticky" may be modified to customize this, after running
# "sticky-note -b" to initialize.

# By default the display style is only applied when "posting" notes to
# the top of the screen via the ZLE widget, but can be applied to the
# output of "sticky-note -l" by setting the "list-display" style:
#   zstyle :sticky-note list-display true

# I encourage all you creative people to contribute enhancements ...

emulate -LR zsh

typeset -gA .zsticky.display

# Set up keybindings (adapted from "zed")
if ! bindkey -M sticky >& /dev/null
then
  bindkey -N sticky main
  bindkey -M sticky ^X^W accept-line
  bindkey -M sticky ^M^M accept-line	# Two quick RETs ends note
  bindkey -M sticky ^M self-insert-unmeta
fi
if ! bindkey -M sticky-vicmd >& /dev/null 
then
  bindkey -N sticky-vicmd vicmd
  bindkey -M sticky-vicmd ZZ accept-line
fi
if ! functions _sticky-display >& /dev/null &&
     zstyle -m :sticky-note display '*'
then
  function _sticky-display {
    if [[ -z $compstate[vared] ]]
    then
      local save_buffer=$BUFFER save_cursor=$CURSOR
      BUFFER=
      zle -U $'\t'
      zle recursive-edit -K sticky-display
      .zsticky.display[last]=$BUFFER
      PREDISPLAY="[ $BUFFER ] "
      BUFFER=$save_buffer CURSOR=$save_cursor
      zle reset-prompt
    else
      zstyle -a :sticky-note display sticky_displays
      compadd -x "Press TAB to choose display mode, ENTER to set:" \
	      -V nosort ${.zsticky.display[last]} \
	      ${${(ok)sticky_displays}:#${.zsticky.display[last]}}
      compstate[insert]=menu
    fi
  }
  zle -N _sticky-display
  bindkey -M sticky '^X?' _sticky-display
  zle -C sticky-display-choices menu-complete _sticky-display
  bindkey -N sticky-display
  bindkey -M sticky-display $'\t' sticky-display-choices
  bindkey -M sticky-display ^M accept-line
fi

[[ "$1" == -b ]] && return 0

setopt noflowcontrol nobanghist extendedhistory histignoredups
setopt noappendhistory nosharehistory noincappendhistory
unsetopt histlexwords histnofunctions histnostore
zmodload -i zsh/datetime

local STICKYFILE=${STICKYFILE:-$HOME/.zsticky}
local STICKYSIZE=${STICKYSIZE:-1000}
local PREDISPLAY sticky stickyfile stickysize
local -A sticky_displays vared_options

zstyle -s :sticky-note notefile stickyfile || stickyfile=$STICKYFILE
zstyle -s :sticky-note maxnotes stickysize || stickysize=$STICKYSIZE

# Populate custom history setopts
() {
  local -a h0
  if zstyle -a :sticky-note history-opts h0
  then
    h0=( ${(M)h0:#*hist*} )
    h0=( ${h0:#*(extended|lexwords|nofunctions|nostore|incappend|share)*} )
    setopt $h0
  fi
}

# Populate options to vared
() {
  local -a v0
  if zstyle -a :sticky-note vared-options v0
  then
    v0[${v0[(i)-a]}]=()
    v0[${v0[(i)-A]}]=()
    v0[${v0[(i)-c]}]=()
    if (( ${v0[(I)+h]} ))
    then
      v0[${v0[(i)-h]}]=()
      v0[${v0[(i)+h]}]=()
    else
      v0+=(-h '')
    fi
    if (( ${v0[(I)-g]} ))
    then
      v0[${v0[(i)-g]}]=(-g '')
    fi
    if (( ${v0[(I)-e]} ))
    then
      v0[${v0[(i)-e]}]=(-e '')
    fi
  vared_options=( "$v0[@]" )
  else
    vared_options=(-h '')
  fi
}
: ${vared_options[-i]:=undefined-key}
: ${vared_options[-f]:=undefined-key}
: ${vared_options[-M]::=sticky}
: ${vared_options[-m]::=sticky-vicmd}

# Look up color theme
local -A theme
(($+bg && $+fg)) || { autoload -Uz colors; colors }
zstyle -m :sticky-note theme '*' || {
    zstyle :sticky-note theme bg yellow fg black
}
zstyle -a :sticky-note theme theme
(( ${+bg[$theme[bg]]} )) && theme[bg]=$bg[$theme[bg]]
(( ${+fg[$theme[fg]]} )) && theme[fg]=$fg[$theme[fg]]
(( ${+theme[color]} )) || theme[color]=$theme[bg]$theme[fg]
(( ${+theme[reset]} )) || theme[reset]=$reset_color
(( ${+theme[prompt]} )) || theme[prompt]=$vared_options[-p]

theme[prompt]="${(e)theme[prompt]}%{${theme[color]}%}"
vared_options[-p]=$theme[prompt]

# Load per-note display settings
if [[ -z ${.zsticky.display} && -r $stickyfile.display ]]
then
  source $stickyfile.display
  # Clean up notes expired from the STICKYFILE.
  () {
    local -a display_keys=(last) d0
    while IFS=': ' read -A d0
    do
      display_keys+=( $d0[2] )
    done < $stickyfile
    d0=( ${(k).zsticky.display} )
    set -- ${d0:|display_keys}
    while [[ -n $1 ]]
    do
      unset ".zsticky.display[$1]"
      shift
    done
    typeset -p 1 .zsticky.display >| $stickyfile.display
  }
fi

# If invoked as a widget, behave a bit like run-help
if zle
then
  zmodload -i zsh/parameter
  if [[ $* == -*l* || $functrace == *zle-line-init:* ]]
  then
    local num stamp ceol=${ echoti el }
    fc -ap $stickyfile $stickysize $stickysize
    echoti cup $LINES 1
    zle reset-prompt
    echoti sc
    echoti home
    print -nr "$theme[color]"
    fc -t %s -l "${@:--1}" |
      while read -r num stamp sticky
      do
	if [[ -n ${.zsticky.display[$stamp]} ]]
	then
	  printf -v sticky "${.zsticky.display[$stamp]}$theme[color]" $sticky
	fi
	printf %s\\n "$num  "${sticky//$'\\n'/$ceol$'\n'}$ceol
      done
    print -nr "$theme[reset]"
    echoti rc
  elif [[ $CONTEXT = (cont|select|vared) ]]
  then
    zle -M "No stickies during ${${(z)PREBUFFER}[1]:-$CONTEXT}, sorry"
    zle .beep
    zle -R
  else
    zle .push-line
    BUFFER=sticky-note
    zle .accept-line
  fi
  return 0
fi

# Invoked as a command, behave like zed, but write a history file
fc -ap $stickyfile $stickysize $stickysize

# With a -l option, list the existing sticky notes
if [[ "$*" == -*l* ]]
then
  local num stamp display
  print -nr "$theme[color]"
  # Use read/print loop to interpolate "\n" in history lines
  fc -t %s "$@" |
    while read -r num stamp sticky
    do
      if zstyle -t :sticky-note list-display &&
	  [[ -n ${.zsticky.display[$stamp]} ]]
      then
	printf -v sticky "${.zsticky.display[$stamp]}$theme[color]" $sticky
      fi
      print -- "${| strftime -s REPLY -n '%x %H:%M' $stamp }  $sticky"
    done
  print -nr "$theme[reset]"
  return 0
fi

# Edit a new sticky note and add it to the stickyfile
while {
    vared ${(kv)vared_options} sticky
  } always {
    # Assure we reach "return 0" to complete fc -ap
    TRY_BLOCK_INTERRUPT=0
} do
  {
    if [[ -n "$sticky" ]]
    then
      print -s -- "$sticky"
      fc -W && SAVEHIST=0
      # File is updated but internal "fc -l" is not yet.  Get the timestamp.
      stamp=${${${| local line;
		  while read line; do REPLY=$line; done <$stickyfile }#: }%%:*}
      if [[ -n $stamp && -n ${.zsticky.display[last]} && -n $sticky_displays ]]
      then
	.zsticky.display[$stamp]=${sticky_displays[${.zsticky.display[last]}]}
        typeset -p 1 .zsticky.display >| $stickyfile.display
      fi
    fi
  } always {
    unset sticky_displays
    (( TRY_BLOCK_ERROR = 0 ))
  } && break
  echo -n -e '\a'
done
return 0

      reply	other threads:[~2023-11-19  5:25 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <1185563186.165566.1619896723304.ref@mail.yahoo.com>
2021-05-01 19:18 ` vapnik spaknik
2021-05-02 23:57   ` Bart Schaefer
2021-05-04  2:06     ` vapnik spaknik
2021-05-09 20:50       ` Bart Schaefer
2021-05-11 10:18         ` Mikael Magnusson
2021-05-14 23:40           ` Termcap and boldface (was sticky-note) Bart Schaefer
2021-05-11 12:37         ` Suggested improvement for sticky-note vapnik spaknik
2023-11-12 21:10         ` Bart Schaefer
2023-11-19  5:25           ` Bart Schaefer [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CAH+w=7bbeg=XEQyNiaC_UwW=z1a8F0ERm-Mtzo_npbAhUKgnfg@mail.gmail.com' \
    --to=schaefer@brasslantern.com \
    --cc=vapniks@yahoo.com \
    --cc=zsh-workers@zsh.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).