zsh-workers
 help / color / mirror / code / Atom feed
* "the backslashes" Re: Dynamic directory name function
       [not found]       ` <20150924093752.581dcee9@pwslap01u.europe.root.pri>
@ 2015-09-24 15:10         ` Bart Schaefer
  2015-09-24 15:18           ` Peter Stephenson
  2015-09-25 13:13         ` Peter Stephenson
  1 sibling, 1 reply; 6+ messages in thread
From: Bart Schaefer @ 2015-09-24 15:10 UTC (permalink / raw)
  To: zsh-workers

On Sep 24,  9:37am, Peter Stephenson wrote:
}
} There's also the backslashes,
} but more careful people than me probably don't mind those.

Thoughts on allowing modules to define the sort of keyword+builtin
pair that we used for typeset ?  Then zstyle could hook itself to
the parenthesized-list-parsing code and look like

     zstyle ZDN_wrapper_name zdn_top (
 	g   ~/git
 	ga  ~/alternate/git
 	gs  /scratch/$USER/git/:second2
 	:default: /:second1
    )

Of course that would be opening a potential can of syntactic worms.


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: "the backslashes" Re: Dynamic directory name function
  2015-09-24 15:10         ` "the backslashes" Re: Dynamic directory name function Bart Schaefer
@ 2015-09-24 15:18           ` Peter Stephenson
  2015-09-24 15:34             ` Bart Schaefer
  0 siblings, 1 reply; 6+ messages in thread
From: Peter Stephenson @ 2015-09-24 15:18 UTC (permalink / raw)
  To: zsh-workers

On Thu, 24 Sep 2015 08:10:19 -0700
Bart Schaefer <schaefer@brasslantern.com> wrote:
> On Sep 24,  9:37am, Peter Stephenson wrote:
> }
> } There's also the backslashes,
> } but more careful people than me probably don't mind those.
> 
> Thoughts on allowing modules to define the sort of keyword+builtin
> pair that we used for typeset ?  Then zstyle could hook itself to
> the parenthesized-list-parsing code and look like
> 
>      zstyle ZDN_wrapper_name zdn_top (
>  	g   ~/git
>  	ga  ~/alternate/git
>  	gs  /scratch/$USER/git/:second2
>  	:default: /:second1
>     )
> 
> Of course that would be opening a potential can of syntactic worms.

I've often wanted something like this, but it's really tricky to get
right.  In particular, I'd be unhappy about it if it's not following an
assignment --- parentheses are just too heavily overloaded to make this
can of worms more than a pig's breakfast (or do I mean dog's dinner?),
which isn't my cup of tea.

pws


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: "the backslashes" Re: Dynamic directory name function
  2015-09-24 15:18           ` Peter Stephenson
@ 2015-09-24 15:34             ` Bart Schaefer
  2015-09-24 16:10               ` Peter Stephenson
  0 siblings, 1 reply; 6+ messages in thread
From: Bart Schaefer @ 2015-09-24 15:34 UTC (permalink / raw)
  To: zsh-workers

On Sep 24,  4:18pm, Peter Stephenson wrote:
}
} On Thu, 24 Sep 2015 08:10:19 -0700
} Bart Schaefer <schaefer@brasslantern.com> wrote:
} > Thoughts on allowing modules to define the sort of keyword+builtin
} > pair that we used for typeset ?
} 
} I've often wanted something like this, but it's really tricky to get
} right.  In particular, I'd be unhappy about it if it's not following an
} assignment

So just throw in an "=" sign, you mean?

    zstyle ZDN_wrapper_name zdn_top=(
	g   ~/git
	ga  ~/alternate/git
	gs  /scratch/$USER/git/:second2
	:default: /:second1
    )

??


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: "the backslashes" Re: Dynamic directory name function
  2015-09-24 15:34             ` Bart Schaefer
@ 2015-09-24 16:10               ` Peter Stephenson
  2015-09-24 16:50                 ` Bart Schaefer
  0 siblings, 1 reply; 6+ messages in thread
From: Peter Stephenson @ 2015-09-24 16:10 UTC (permalink / raw)
  To: zsh-workers

On Thu, 24 Sep 2015 08:34:45 -0700
Bart Schaefer <schaefer@brasslantern.com> wrote:
> So just throw in an "=" sign, you mean?
> 
>     zstyle ZDN_wrapper_name zdn_top=(
> 	g   ~/git
> 	ga  ~/alternate/git
> 	gs  /scratch/$USER/git/:second2
> 	:default: /:second1
>     )

That can be made vaild syntax by a bit (?lot?) of trickery with reserved
words, but what does it mean, and what gets passed to the command?

pws


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: "the backslashes" Re: Dynamic directory name function
  2015-09-24 16:10               ` Peter Stephenson
@ 2015-09-24 16:50                 ` Bart Schaefer
  0 siblings, 0 replies; 6+ messages in thread
From: Bart Schaefer @ 2015-09-24 16:50 UTC (permalink / raw)
  To: zsh-workers

On Sep 24,  5:10pm, Peter Stephenson wrote:
}
} >     zstyle ZDN_wrapper_name zdn_top=(
} > 	g   ~/git
} > 	ga  ~/alternate/git
} > 	gs  /scratch/$USER/git/:second2
} > 	:default: /:second1
} >     )
} 
} That can be made vaild syntax by a bit (?lot?) of trickery with reserved
} words, but what does it mean, and what gets passed to the command?

It means exactly the same thing that writing it now without the equal
sign and parens means; it's merely syntactic sugar so you don't have
to use backslash-continuation to spread it across multiple lines.

Whether it gets exploded out into an ordinary argv before being passed
to bin_zstyle, or comes in as some other structure that bin_zstyle
itself unpacks, is an implementation detail for module, I'd expect.

No matter, was just a thought I threw out.


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Dynamic directory name function
       [not found]       ` <20150924093752.581dcee9@pwslap01u.europe.root.pri>
  2015-09-24 15:10         ` "the backslashes" Re: Dynamic directory name function Bart Schaefer
@ 2015-09-25 13:13         ` Peter Stephenson
  1 sibling, 0 replies; 6+ messages in thread
From: Peter Stephenson @ 2015-09-25 13:13 UTC (permalink / raw)
  To: Zsh Hackers' List

Here's what I'm going to commit.

diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo
index b966e78..330c6f5 100644
--- a/Doc/Zsh/contrib.yo
+++ b/Doc/Zsh/contrib.yo
@@ -12,6 +12,7 @@ such as shell functions, look for comments in the function source files.
 startmenu()
 menu(Utilities)
 menu(Recent Directories)
+menu(Other Directory Functions)
 menu(Version Control Information)
 menu(Prompt Themes)
 menu(ZLE Functions)
@@ -324,7 +325,7 @@ options tt(-Uz) are appropriate.
 )
 enditem()
 
-texinode(Recent Directories)(Version Control Information)(Utilities)(User Contributions)
+texinode(Recent Directories)(Other Directory Functions)(Utilities)(User Contributions)
 cindex(recent directories, maintaining list of)
 cindex(directories, maintaining list of recent)
 findex(cdr)
@@ -585,7 +586,189 @@ avoid side effects if the change to the directory is to be invisible at the
 command line.  See the contents of the function tt(chpwd_recent_dirs) for
 more details.
 
-texinode(Version Control Information)(Prompt Themes)(Recent Directories)(User Contributions)
+texinode(Other Directory Functions)(Version Control Information)(Recent Directories)(User Contributions)
+cindex(directories, named, dynamic, helper function)
+cindex(dynamic directory naming, helper function)
+cindex(named directories, dynamic, helper function)
+findex(zsh_directory_name_generic)
+sect(Abbreviated dynamic references to directories)
+
+The dynamic directory naming system is described in the subsection
+em(Dynamic named directories) of
+ifzman(the section em(Filename Expansion) in zmanref(expn))\
+ifnzman(noderef(Filename Expansion)).  In this, a reference to
+tt(~[)var(...)tt(]) is expanded by a function found by the hooks
+mechanism.
+
+The contributed function tt(zsh_directory_name_generic) provides a
+system allowing the user to refer to directories with only a limited
+amount of new code.  It supports all three of the standard interfaces
+for directory naming: converting from a name to a directory, converting
+in the reverse direction to find a short name, and completion of names.
+
+The main feature of this function is a path-like syntax,
+combining abbreviations at multiple levels separated by ":".
+As an example, ~[g:p:s] might specify:
+startitem()
+item(tt(g))(
+The top level directory for your git area.  This first component
+has to match, or the function will retrun indicating another
+directory name hook function should be tried.
+)
+item(tt(p))(
+The name of a project within your git area.
+)
+item(tt(s))(
+The source area within that project.
+)
+enditem()
+This allows you to collapse references to long hierarchies to a very
+compact form, particularly if the hierarchies are similar across different
+areas of the disk.
+
+Name components may be completed: if a description is shown at the top
+of the list of completions, it includes the path to which previous
+components expand, while the description for an individual completion
+shows the path segment it would add.  No additional configuration is
+needed for this as the completion system is aware of the dynamic
+directory name mechanism.
+
+subsect(Usage)
+
+To use the function, first define a wrapper function for your specific
+case.  We'll assume it's to be autoloaded.  This can have any name but
+we'll refer to it as zdn_mywrapper.  This wrapper function will define
+various variables and then call this function with the same arguments
+that the wrapper function gets.  This configuration is described below.
+
+Then arrange for the wrapper to be run as a zsh_directory_name hook:
+
+example(autoload -Uz add-zsh-hook zsh_diretory_name_generic zdn_mywrapper
+add-zsh-hook -U zsh_directory_name zdn_mywrapper)
+
+subsect(Configuration)
+
+The wrapper function should define a local associative array zdn_top.
+Alternatively, this can be set with a style called tt(mapping).  The
+context for the style is tt(:zdn:)var(wrapper-name) where
+var(wrapper-name) is the function calling zsh_directory_name_generic;
+for example:
+
+example(zstyle :zdn:zdn_mywrapper: mapping zdn_mywrapper_top)
+
+The keys in this associative array correspond to the first component of
+the name.  The values are matching directories.  They may have an
+optional suffix with a slash followed by a colon and the name of a
+variable in the same format to give the next component.  (The slash
+before the colon is to disambiguate the case where a colon is needed in
+the path for a drive.  There is otherwise no syntax for escaping this,
+so path components whose names start with a colon are not supported.)  A
+special component tt(:default:) specifies a variable in the form
+tt(/:)var(var) (the path section is ignored and so is usually empty)
+that will be used for the next component if no variable is given for the
+path.  Variables referred to within tt(zdn_top) have the same format as
+tt(zdn_top) itself, but contain relative paths.
+
+For example,
+
+example(local -A zdn_top=(
+  g   ~/git
+  ga  ~/alternate/git
+  gs  /scratch/$USER/git/:second2
+  :default: /:second1
+))
+
+This specifies the behaviour of a directory referred to as tt(~[g:...])
+or tt(~[ga:...]) or tt(~[gs:...]).  Later path components are optional;
+in that case tt(~[g]) expands to tt(~/git), and so on.  tt(gs) expands
+to tt(/scratch/$USER/git) and uses the associative array tt(second2) to
+match the second component; tt(g) and tt(ga) use the associative array
+tt(second1) to match the second component.
+
+When expanding a name to a directory, if the first component is not tt(g) or
+tt(ga) or tt(gs), it is not an error; the function simply returns 1 so that a
+later hook function can be tried.  However, matching the first component
+commits the function, so if a later component does not match, an error
+is printed (though this still does not stop later hooks from being
+executed).
+
+For components after the first, a relative path is expected, but note that
+multiple levels may still appear.  Here is an example of tt(second1):
+
+example(local -A second1=(
+  p   myproject
+  s   somproject
+  os  otherproject/subproject/:third
+))
+
+The path as found from tt(zdn_top) is extended with the matching
+directory, so tt(~[g:p]) becomes tt(~/git/myproject).  The slash between
+is added automatically (it's not possible to have a later component
+modify the name of a directory already matched).  Only tt(os) specifies
+a variable for a third component, and there's no tt(:default:), so it's
+an error to use a name like tt(~[g:p:x]) or tt(~[ga:s:y]) because
+there's nowhere to look up the tt(x) or tt(y).
+
+The associative arrays need to be visible within this function; the
+generic function therefore uses internal variable names beginning
+tt(_zdn_) in order to avoid clashes.  Note that the variable tt(reply)
+needs to be passed back to the shell, so should not be local in the
+calling function.
+
+The function does not test whether directories assembled by component
+actually exist; this allows the system to work across automounted
+file systems.  The error from the command trying to use a non-existent
+directory should be sufficient to indicate the problem.
+
+subsect(Complete example)
+
+Here is a full fictitious but usable autoloadable definition of the
+example function defined by the code above.  So tt(~[gs:p:s]) expands
+to tt(/scratch/$USER/git/myscratchproject/top/srcdir) (with tt($USER)
+also expanded).
+
+example(local -A zdn_top=(
+  g   ~/git
+  ga  ~/alternate/git
+  gs  /scratch/$USER/git/:second2
+  :default: /:second1
+)
+
+local -A second1=(
+  p   myproject
+  s   somproject
+  os  otherproject/subproject/:third
+)
+
+local -A second2=(
+  p   myscratchproject
+  s   somescratchproject
+)
+
+local -A third=(
+  s   top/srcdir
+  d   top/documentation
+)
+
+# autoload not needed if you did this at initialisation...
+autoload -Uz zsh_directory_name_generic
+zsh_directory_name_generic "$@)
+
+It is also possible to use global associative arrays, suitably named,
+and set the style for the context of your wrapper function to
+refer to this.  Then your set up code would contain the following:
+
+example(typeset -A zdn_mywrapper_top=(...)
+# ... and so on for other associative arrays ...
+zstyle ':zdn:zdn_mywrapper:' mapping zdn_mywrapper_top
+autoload -Uz add-zsh-hook zsh_directory_name_generic zdn_mywrapper
+add-zsh-hook -U zsh_directory_name zdn_mywrapper)
+
+and the function tt(zdn_mywrapper) would contain only the following:
+
+example(zsh_directory_name_generic "$@")
+
+texinode(Version Control Information)(Prompt Themes)(Other Directory Functions)(User Contributions)
 sect(Gathering information from version control systems)
 cindex(version control utility)
 
diff --git a/Doc/Zsh/manual.yo b/Doc/Zsh/manual.yo
index 86c72c0..119849e 100644
--- a/Doc/Zsh/manual.yo
+++ b/Doc/Zsh/manual.yo
@@ -164,6 +164,7 @@ User Contributions
 
 menu(Utilities)
 menu(Recent Directories)
+menu(Other Directory Functions)
 menu(Version Control Information)
 menu(Prompt Themes)
 menu(ZLE Functions)
diff --git a/Functions/Chpwd/zsh_directory_name_generic b/Functions/Chpwd/zsh_directory_name_generic
new file mode 100644
index 0000000..9430c95
--- /dev/null
+++ b/Functions/Chpwd/zsh_directory_name_generic
@@ -0,0 +1,151 @@
+## zsh_directory_name_generic
+#
+# This function is useful as a hook function for the zsh_directory_name
+# facility.
+#
+# See the zsh-contrib manual page for more.
+
+emulate -L zsh
+setopt extendedglob
+local -a match mbegin mend
+
+# The variable containing the top level mapping.
+local _zdn_topvar
+
+zmodload -i zsh/parameter
+zstyle -s ":zdn:${funcstack[2]}:" mapping _zdn_topvar || _zdn_topvar=zdn_top
+
+if (( ! ${(P)#_zdn_topvar} )); then
+  print -r -- "$0: $_zdn_topver is not set" >&2
+  return 1
+fi
+
+local _zdn_var=$_zdn_topvar
+local -A _zdn_assoc
+
+if [[ $1 = n ]]; then
+  # Turning a name into a directory.
+  local _zdn_name=$2
+  local -a _zdn_words
+  local _zdn_dir _zdn_cpt
+
+  _zdn_words=(${(s.:.)_zdn_name})
+  while (( ${#_zdn_words} )); do
+    if [[ -z ${_zdn_var} ]]; then
+      print -r -- "$0: too many components in directory name \`$_zdn_name'" >&2
+      return 1
+    fi
+
+    # Subscripting (P)_zdn_var directly seems not to work.
+    _zdn_assoc=(${(Pkv)_zdn_var})
+    _zdn_cpt=${_zdn_assoc[${_zdn_words[1]}]}
+    shift _zdn_words
+
+    if [[ -z $_zdn_cpt ]]; then
+      # If top level component, just try another expansion
+      if [[ $_zdn_var != $_zdn_top ]]; then
+	# Committed to this expansion, so report failure.
+	print -r -- "$0: no expansion for directory name \`$_zdn_name'" >&2
+      fi
+      return 1
+    fi
+    if [[ $_zdn_cpt = (#b)(*)/:([[:IDENT:]]##) ]]; then
+      _zdn_cpt=$match[1]
+      _zdn_var=$match[2]
+    else
+      # may be empty
+      _zdn_var=${${_zdn_assoc[:default:]}##*/:}
+    fi
+    _zdn_dir=${_zdn_dir:+$_zdn_dir/}$_zdn_cpt
+  done
+  if (( ${#_zdn_dir} )); then
+    typeset -ag reply
+    reply=($_zdn_dir)
+    return 0
+  fi
+elif [[ $1 = d ]]; then
+  # Turning a directory into a name.
+  local _zdn_dir=$2
+  local _zdn_rest=$_zdn_dir
+  local -a _zdn_cpts
+  local _zdn_pref _zdn_pref_raw _zdn_matched _zdn_cpt _zdn_name
+
+  while [[ -n $_zdn_var && -n $_zdn_rest ]]; do
+    _zdn_assoc=(${(Pkv)_zdn_var})
+    # Sorting in descending order will ensure prefixes
+    # come after longer strings with that perfix, so
+    # we match more specific directory names preferentially.
+    _zdn_cpts=(${(Ov)_zdn_assoc})
+    _zdn_cpt=''
+    for _zdn_pref_raw in $_zdn_cpts; do
+      _zdn_pref=${_zdn_pref_raw%/:*}
+      [[ -z $_zdn_pref ]] && continue
+      if [[ $_zdn_rest = $_zdn_pref(#b)(/|)(*) ]]; then
+	_zdn_cpt=${(k)_zdn_assoc[(r)$_zdn_pref_raw]}
+	# if we matched a /, too, add it...
+	_zdn_matched+=$_zdn_pref$match[1]
+	_zdn_rest=$match[2]
+	break
+      fi
+    done
+    if [[ -n $_zdn_cpt ]]; then
+      _zdn_name+=${_zdn_name:+${_zdh_name}:}$_zdn_cpt
+      if [[ ${_zdn_assoc[$_zdn_cpt]} = (#b)*/:([[:IDENT:]]##) ]]; then
+	_zdn_var=$match[1]
+      else
+	_zdn_var=${${_zdn_assoc[:default:]}##*/:}
+      fi
+    else
+      break
+    fi
+  done
+  if [[ -n $_zdn_name ]]; then
+    # matched something, so report that.
+    integer _zdn_len=${#_zdn_matched}
+    [[ $_zdn_matched[-1] = / ]] && (( _zdn_len-- ))
+    typeset -ag reply
+    reply=($_zdn_name $_zdn_len)
+    return 0
+  fi
+  # else let someone else have a go.
+elif [[ $1 = c ]]; then
+  # Completion
+
+  if [[ -n $SUFFIX ]]; then
+    _message "Can't complete in the middle of a dynamic directory name"
+  else
+    local -a _zdn_cpts
+    local _zdn_word _zdn_cpt _zdn_desc _zdn_sofar expl
+
+    while [[ -n ${_zdn_var} && ${PREFIX} = (#b)([^:]##):* ]]; do
+      _zdn_word=$match[1]
+      compset -P '[^:]##:'
+      _zdn_assoc=(${(Pkv)_zdn_var})
+      _zdn_cpt=${_zdn_assoc[$_zdn_word]}
+      # We only complete at the end so must match here
+      [[ -z $_zdn_cpt ]] && return 1
+      if [[ $_zdn_cpt = (#b)(*)/:([[:IDENT:]]##) ]]; then
+	_zdn_cpt=$match[1]
+	_zdn_var=$match[2]
+      else
+	_zdn_var=${${_zdn_assoc[:default:]}##*/:}
+      fi
+      _zdn_sofar+=${_zdn_sofar:+${_zdn_sofar}/}$_zdn_cpt
+    done
+    if [[ -n $_zdn_var ]]; then
+      _zdn_assoc=(${(Pkv)_zdn_var})
+      local -a _zdn_cpts
+      for _zdn_cpt _zdn_desc in ${(kv)_zdn_assoc}; do
+	[[ $_zdn_cpt = :* ]] && continue
+	_zdn_cpts+=(${_zdn_cpt}:${_zdn_desc%/:[[:IDENT:]]##})
+      done
+      _describe -t dirnames "directory name under ${_zdn_sofar%%/}" \
+	_zdn_cpts -S: -r ':]'
+      return
+    fi
+  fi
+fi
+
+# Failed
+return 1
+## end


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2015-09-25 13:13 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <20150922204251.05f3d291@ntlworld.com>
     [not found] ` <150922210756.ZM30253@torch.brasslantern.com>
     [not found]   ` <20150923094821.5c5d0b80@pwslap01u.europe.root.pri>
     [not found]     ` <150923231024.ZM32382@torch.brasslantern.com>
     [not found]       ` <20150924093752.581dcee9@pwslap01u.europe.root.pri>
2015-09-24 15:10         ` "the backslashes" Re: Dynamic directory name function Bart Schaefer
2015-09-24 15:18           ` Peter Stephenson
2015-09-24 15:34             ` Bart Schaefer
2015-09-24 16:10               ` Peter Stephenson
2015-09-24 16:50                 ` Bart Schaefer
2015-09-25 13:13         ` Peter Stephenson

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).