branch: externals/hyperbole commit 55aad42c53c141f783f7d78a7c44294946af0498 Author: Bob Weiner <r...@gnu.org> Commit: Bob Weiner <r...@gnu.org>
Fix pathname button issues including with PATH values Add support for 'cd' and 'pushd' to hpath:prepend-shell-directory. Make 'hpath:substitute-dir' allow return value to be a directory. --- ChangeLog | 18 +++++++++++++ HY-NEWS | 22 ++++++++++------ Makefile | 4 +-- hargs.el | 76 +++++++++++++++++++++++++++++++---------------------- hibtypes.el | 12 +++++---- hpath.el | 73 +++++++++++++++++++++++++++----------------------- hyrolo.el | 1 + test/hpath-tests.el | 4 +-- 8 files changed, 129 insertions(+), 81 deletions(-) diff --git a/ChangeLog b/ChangeLog index 351c447063..8ad577ec97 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,23 @@ 2022-01-30 Bob Weiner <r...@gnu.org> +* hpath.el (hpath:at-p): Fix start and end delimiters to hargs:delimited so match + to PATH variable values when not surrounded by double quotes. + hargs.el (hargs:delimited): Fix not matching start and end patterns at the + beginning and end of lines. Also, simplify when start end end delims differ, + eliminating the need to check whether point is between strings rather than + inside one. + (hpath:delimited-possible-path): Fix hargs:delimited start delimiter + to use + instead of * to match to unquoted pathnames. Also, allow {} in paths + so can match to var names in path but not if braces are the first and last + chars of path. + +* hpath.el (hpath:prepend-shell-directory): Rename from 'hpath:prepend-ls-directory'. + Add support for 'cd' and 'pushd' commands. + hibtypes.el (ripgrep-msg, grep-msg): When in shell mode, expand path with above + function. + (hpath:substitute-dir): Fix to make 'locate-file' call handle dirs, + so final expansion can be a directory, e.g. on Macs App bundles are directories. + * hact.el (actype:act): Allow for builtin subr objects like 'cons'; This fixes Action Buttons that start with <progn ...>, for example. diff --git a/HY-NEWS b/HY-NEWS index f871ee79dc..a5bd5522cf 100644 --- a/HY-NEWS +++ b/HY-NEWS @@ -63,7 +63,7 @@ non-programmers to create their own implicit action button link types that execute key series, display URLs, display the contents of pathnames or invoke functions. See "(hyperbole)Action Button - Link Types" or the "DEMO#Defining New Action Button Types" + Link Types" or the "DEMO2#Defining New Action Button Types" section. - Easy Implicit Link Button Type Creation: `defil' is a new @@ -246,6 +246,13 @@ without an attached file, similar to what already existed for mail message buffers. + - 'ls' and 'grep' Directory Changes: 'ls' listings are now recognized + properly, prepending the preceding directory to each entry for viewing. + + Similarly, if a 'cd' or 'pushd' command is issued prior to a 'grep -n' + or 'ripgrep' command, Hyperbole will prepend that directory to the grep + output before jumping to the resultant path. + - Pathname Implicit Buttons: Much improved pathname handling including multiple variables per path, embedded . or .. within paths, better recognition of semicolon separated pathnames in Windows PATH variable. @@ -254,9 +261,6 @@ case of a value with both upper and lower case characters is never changed, for example a value of ${HOME}. - Recursive 'ls' listings are now recognized properly, prepending the - preceding directory to each entry for viewing. - Generalized Anchored Pathnames: "pathname#anchor" now works in programming modes as well as text and outlining modes where anchors are prefixed with the comment character in each mode or a # symbol. @@ -373,7 +377,7 @@ - Fast Window Links: The hkey-window-link command bound to {M-o w} rapidly creates a link button at point in the selected window, linking to point in the window chosen when prompted. - See "DEMO#Displaying File and Buffer Items and Moving Buffers". + See "DEMO2#Displaying File and Buffer Items and Moving Buffers". - Throw A Region Elsewhere Within the Same Buffer: {M-o t <window-id>} when used with a selected region can now throw to the source buffer @@ -491,7 +495,7 @@ * V7.1.1 =========================================================================== - DEMO + DEMO2 - Global Buttons: Added a new example of a labeled global implicit button displaying a todo file maintained in Koutliner format. See @@ -752,7 +756,7 @@ DOCUMENTATION - - DEMO: New sections on Button Files and Global Buttons. + - DEMO2: New sections on Button Files and Global Buttons. - Action Types: link-to-gbut, link-to-ibut - Added. @@ -764,7 +768,7 @@ ilink (link to implicit button), glink (link to global button), and elink (link to explicit button). - - DEMO (Action Buttons): Added description and examples. + - DEMO2 (Action Buttons): Added description and examples. - Path Variables: Better documented how these are handled. @@ -774,6 +778,8 @@ - Glossary: Updated Implicit Button and Global Button entries with changes. + - Installation: All new manual section on multiple ways to install Hyperbole. + KOUTLINER diff --git a/Makefile b/Makefile index 993c0d5102..29def5a18d 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ # Author: Bob Weiner # # Orig-Date: 15-Jun-94 at 03:42:38 -# Last-Mod: 24-Jan-22 at 00:45:29 by Bob Weiner +# Last-Mod: 30-Jan-22 at 13:03:37 by Bob Weiner # # Copyright (C) 1994-2021 Free Software Foundation, Inc. # See the file HY-COPY for license information. @@ -400,7 +400,7 @@ $(pkg_dir)/hyperbole-$(HYPB_VERSION).tar: $(HYPERBOLE_FILES) make version cd $(pkg_dir) && $(RM) -fr $(pkg_hyperbole) $(pkg_hyperbole)-$(HYPB_VERSION) cd .. && COPYFILE_DISABLE=1 $(TAR) -clf $(pkg_dir)/h.tar hyperbole - cd $(pkg_dir) && COPYFILE_DISABLE=1 $(TAR) xf h.tar && cd $(pkg_hyperbole) && $(MAKE) packageclean + cd $(pkg_dir) && COPYFILE_DISABLE=1 $(TAR) xvf h.tar && cd $(pkg_hyperbole) && $(MAKE) packageclean cd $(pkg_hyperbole) && make autoloads && chmod 755 topwin.py && \ cd $(pkg_dir) && $(RM) h.tar; \ mv $(pkg_hyperbole) $(pkg_hyperbole)-$(HYPB_VERSION) && \ diff --git a/hargs.el b/hargs.el index 51e5b66267..550888bb23 100644 --- a/hargs.el +++ b/hargs.el @@ -3,7 +3,7 @@ ;; Author: Bob Weiner ;; ;; Orig-Date: 31-Oct-91 at 23:17:35 -;; Last-Mod: 24-Jan-22 at 00:17:53 by Bob Weiner +;; Last-Mod: 30-Jan-22 at 22:15:38 by Bob Weiner ;; ;; Copyright (C) 1991-2021 Free Software Foundation, Inc. ;; See the "HY-COPY" file for license information. @@ -93,6 +93,8 @@ interactive form or takes no arguments." (hargs:iform-read interactive-form modifying)))))) (defun hargs:buffer-substring (start end) + "Return the buffer substring sans any properties between START and END positions. +Convert NUL characters to colons for use with grep lines." (let ((string (buffer-substring-no-properties start end))) ;; This may trigger on a colored grep-like output line which has ;; an embedded null character with a display text property that @@ -110,45 +112,57 @@ treated as a regular expression. END-REGEXP-FLAG is similar. With optional LIST-POSITIONS-FLAG, return list of (string-matched start-pos end-pos). With optional EXCLUDE-REGEXP, any matched string is ignored if it matches this regexp." (let* ((opoint (point)) + (line-begin (line-beginning-position)) + ;; This initial limit if the forward search limit for start delimiters (limit (if start-regexp-flag opoint (+ opoint (1- (length start-delim))))) - (start-search-func (if start-regexp-flag 're-search-forward - 'search-forward)) - (end-search-func (if end-regexp-flag 're-search-forward - 'search-forward)) + (forward-search-func (if start-regexp-flag 're-search-forward + 'search-forward)) + (reverse-search-func (if end-regexp-flag 're-search-backward + 'search-backward)) (count 0) first start end) - (save-excursion - (beginning-of-line) - (while (and (setq start (funcall start-search-func start-delim limit t)) - (setq count (1+ count)) - (< (point) opoint) - ;; This is not to find the real end delimiter but to find - ;; end delimiters that precede the current argument and are - ;; therefore false matches, hence the search is limited to - ;; prior to the original point. - (funcall end-search-func end-delim opoint t) - (setq count (1+ count))) - (setq first (or first start) - start nil)) - (when (and (not start) (> count 0) (zerop (% count 2))) - ;; Since strings can span lines but this function matches only - ;; strings that start on the current line, when start-delim and - ;; end-delim are the same and there are an even number of - ;; delimiters in the search range, causing the end-delim - ;; search to match to what should probably be the start-delim, - ;; assume point is within a string and not between two other strings. - ;; -- RSW, 02-05-2019 - (setq start (if (string-equal start-delim end-delim) - (point) - first))) - (when start + (if (string-equal start-delim end-delim) + (save-excursion + (beginning-of-line) + (while (and (setq start (funcall forward-search-func start-delim limit t)) + (setq count (1+ count)) + (< (point) opoint) + ;; This is not to find the real end delimiter but to find + ;; end delimiters that precede the current argument and are + ;; therefore false matches, hence the search is limited to + ;; prior to the original point. + (funcall forward-search-func end-delim opoint t) + (setq count (1+ count))) + (setq first (or first start) + start nil)) + (when (and (not start) (> count 0) (zerop (% count 2))) + ;; Since strings can span lines but this function matches only + ;; strings that start on the current line, when start-delim and + ;; end-delim are the same and there are an even number of + ;; delimiters in the search range, causing the end-delim + ;; search to match to what should probably be the start-delim, + ;; assume point is within a string and not between two other strings. + ;; -- RSW, 02-05-2019 + (setq start (if (string-equal start-delim end-delim) + (point) + first)))) + ;; + ;; Start and end delims are different, so don't have to worry + ;; about whether in or outside two of the same delimiters and + ;; can match much more simply. + (save-excursion + (setq start (when (funcall reverse-search-func start-delim line-begin t) + (match-end 0))))) + + (when start + (save-excursion (forward-line 2) (setq limit (point)) (goto-char opoint) - (and (funcall end-search-func end-delim limit t) + (and (funcall forward-search-func end-delim limit t) (setq end (match-beginning 0)) ;; Ignore any preceding backslash, e.g. when a double-quoted ;; string is embedded within a doc string, except when diff --git a/hibtypes.el b/hibtypes.el index acfa7ff531..7c005497b1 100644 --- a/hibtypes.el +++ b/hibtypes.el @@ -3,7 +3,7 @@ ;; Author: Bob Weiner ;; ;; Orig-Date: 19-Sep-91 at 20:45:31 -;; Last-Mod: 30-Jan-22 at 03:12:41 by Bob Weiner +;; Last-Mod: 30-Jan-22 at 16:37:47 by Bob Weiner ;; ;; Copyright (C) 1991-2021 Free Software Foundation, Inc. ;; See the "HY-COPY" file for license information. @@ -897,8 +897,9 @@ buffer)." ;; resolve any variables in the path before checking if absolute. (source-loc (unless (file-name-absolute-p (hpath:expand file)) (hbut:key-src t)))) - (when (stringp source-loc) - (setq file (expand-file-name file (file-name-directory source-loc)))) + (if (stringp source-loc) + (setq file (expand-file-name file (file-name-directory source-loc))) + (setq file (or (hpath:prepend-shell-directory file) file))) (when (file-readable-p file) (setq line-num (string-to-number line-num)) (ibut:label-set but-label) @@ -953,8 +954,9 @@ in grep and shell buffers." ;; resolve any variables in the path before checking if absolute. (source-loc (unless (file-name-absolute-p (hpath:expand file)) (hbut:key-src t)))) - (when (stringp source-loc) - (setq file (expand-file-name file (file-name-directory source-loc)))) + (if (stringp source-loc) + (setq file (expand-file-name file (file-name-directory source-loc))) + (setq file (or (hpath:prepend-shell-directory file) file))) (setq line-num (string-to-number line-num)) (ibut:label-set but-label) (hact 'link-to-file-line file line-num)))))) diff --git a/hpath.el b/hpath.el index 88f4315c7d..641beaf0f0 100644 --- a/hpath.el +++ b/hpath.el @@ -3,7 +3,7 @@ ;; Author: Bob Weiner ;; ;; Orig-Date: 1-Nov-91 at 00:44:23 -;; Last-Mod: 24-Jan-22 at 00:18:47 by Bob Weiner +;; Last-Mod: 30-Jan-22 at 23:08:42 by Bob Weiner ;; ;; Copyright (C) 1991-2021 Free Software Foundation, Inc. ;; See the "HY-COPY" file for license information. @@ -868,19 +868,19 @@ paths are checked for existence. With optional NON-EXIST, nonexistent local paths are allowed. Absolute pathnames must begin with a `/' or `~'." (let ((path (hpath:delimited-possible-path non-exist)) subpath) - (when (and path (not non-exist) (string-match hpath:prefix-regexp path)) + (when (and path (not non-exist) (string-match-p hpath:prefix-regexp path)) (setq non-exist t)) (cond ((and path (file-readable-p path)) path) - ((and path (string-match hpath:path-variable-value-regexp path) + ((and path (string-match-p hpath:path-variable-value-regexp path) ;; Don't allow more than one set of grouping chars - (not (string-match "\)\\s-*\(\\|\\]\\s-*\\[\\|\}\\s-*\{" path))) + (not (string-match-p "\)\\s-*\(\\|\\]\\s-*\\[\\|\}\\s-*\{" path))) ;; With point inside a path variable, return the path that point is on or to the right of. - (setq subpath (or (and (setq subpath (hargs:delimited "[:\"\']" "[:\"\']" t t nil "[\t\n\r\f]\\|[;:] \\| [;:]")) - (not (string-match "[:;\t\n\r\f]" subpath)) + (setq subpath (or (and (setq subpath (hargs:delimited "^\\s-*\\|[:\"\']" "[:\"\']\\|\\s-*$" t t nil "[\t\n\r\f]\\|[;:] \\| [;:]")) + (not (string-match-p "[:;\t\n\r\f]" subpath)) subpath) - (and (setq subpath (hargs:delimited "[;\"\']" "[;\"\']" t t nil "[\t\n\r\f]\\|[;:] \\| [;:]")) - (not (string-match "[;\t\n\r\f]\\|:[^:]*:" subpath)) + (and (setq subpath (hargs:delimited "^\\s-*\\|[;\"\']" "[;\"\']\\|\\s-*$" t t nil "[\t\n\r\f]\\|[;:] \\| [;:]")) + (not (string-match-p "[;\t\n\r\f]\\|:[^:]*:" subpath)) subpath))) (if subpath ;; Could be a shell command from a semicolon separated @@ -1030,19 +1030,19 @@ end-pos) or nil." ;; . or .., don't treat it as a pathname. Only look for ;; whitespace delimited filenames if non-exist is nil. (unless non-exist - (let* ((triplet (hargs:delimited "^\\|\\(\\s-\\|[\]\[(){}<>\;&,@]\\)*" - "\\([\]\[(){}<>\;&,@]\\|:*\\s-\\)+\\|$" + (let* ((triplet (hargs:delimited "^\\|\\(\\s-\\|[\]\[()<>\;&,@]\\)+" + "\\([\]\[()<>\;&,@]\\|:*\\s-\\)+\\|$" t t t)) (p (car triplet)) (punc (char-syntax ?.))) ;; May have matched to a string with an embedded double - ;; quote; if so, don't consider it a path. Also ignore - ;; whitespace delimited root dirs, e.g. " / ". - (when (and (stringp p) (not (string-match "\"\\|\\`[/\\]+\\'" p)) + ;; quote or surrounded by braces; if so, don't consider it a path. + ;; Also ignore whitespace delimited root dirs, e.g. " / ". + (when (and (stringp p) (not (string-match-p "\\`{.*}\\'\\|\"\\|\\`[/\\]+\\'" p)) (delq nil (mapcar (lambda (c) (/= punc (char-syntax c))) p))) - ;; Prepend proper directory to ls *, recursive ls or dir file listings - ;; when needed. - (setq p (or (hpath:prepend-ls-directory) p)) + ;; Prepend proper directory from cd, ls *, recursive ls or dir file + ;; listings when needed. + (setq p (or (hpath:prepend-shell-directory p) p)) (setcar triplet p) (if include-positions triplet @@ -1121,23 +1121,24 @@ Return any absolute or invalid PATH unchanged." substituted-path) (t (expand-file-name substituted-path)))))) -(defun hpath:prepend-ls-directory () +(defun hpath:prepend-shell-directory (&optional filename) "When in a shell buffer and on a filename result of an 'ls *' or recursive 'ls -R' or 'dir' command, prepend the subdir to the filename when needed and return it, else return nil." (when (derived-mode-p #'shell-mode) - (let ((filename (thing-at-point 'filename t)) - (prior-prompt-pos (save-excursion (comint-previous-prompt 1) (1- (point)))) + (let ((prior-prompt-pos (save-excursion (comint-previous-prompt 1) (1- (point)))) dir) + (unless (stringp filename) + (setq filename (thing-at-point 'filename t))) (save-excursion (when (and filename (if (memq system-type '(windows-nt cygwin ms-dos)) ;; Windows Cmd or PowerShell dir cmds - (and (re-search-backward "^\\s-*\\(Directory: \\|Directory of \\)\\(.+\\)$" prior-prompt-pos t) + (and (re-search-backward "^\\s-*\\(cd \\|pushd \\|Directory: \\|Directory of \\)\\(.+\\)$" prior-prompt-pos t) (setq dir (match-string-no-properties 2))) ;; POSIX (or (and (re-search-backward "^$\\|\\`\\|^\\(.+\\):$" prior-prompt-pos t) (setq dir (match-string-no-properties 1))) - (and (re-search-backward "\\(^\\| \\)ls.* [\'\"]?\\([^\'\"\n\r]+[^\'\" \n\r]\\)[\'\"]?$" prior-prompt-pos t) - (setq dir (match-string-no-properties 2))))) + (and (re-search-backward "\\(^\\| \\)\\(cd\\|pushd\\|ls\\)\\(\\s-+-[-a-zA-Z0-9]*\\)*\\s-+[\'\"]?\\([^&!;,\'\"\t\n\r\f]+[^&!;,\'\" \t\n\r\f]\\)[\'\"]?" prior-prompt-pos t) + (setq dir (match-string-no-properties 4))))) (and dir (not (string-empty-p dir)))) (unless (file-name-absolute-p filename) ;; If dir ends with a glob expression, then the dir is @@ -1148,7 +1149,7 @@ Return any absolute or invalid PATH unchanged." (when (file-directory-p dir) (setq dir (file-name-as-directory dir))) (when (and dir (not (string-empty-p dir)) (file-exists-p dir)) - (expand-file-name (concat (file-name-as-directory dir) filename)))))))))) + (expand-file-name filename dir))))))))) (defvar hpath:compressed-suffix-regexp (concat (regexp-opt '(".gz" ".Z" ".zip" ".bz2" ".xz" ".zst")) "\\'") "Regexp of compressed file name suffixes.") @@ -1670,7 +1671,7 @@ in-buffer path will not match." ;; Create a regexp from path by regexp-quoting it and then matching spaces ;; to any whitespace. (when (stringp path) - (let ((path-regexp (replace-regexp-in-string "[ \t\n\r]+" "[ \t\n\r]" (regexp-quote path) t t)) + (let ((path-regexp (replace-regexp-in-string "[ \t\n\r\f]+" "[ \t\n\r\f]" (regexp-quote path) t t)) (opoint (point)) found search-end-point @@ -2210,12 +2211,13 @@ function to call with FILENAME as its single argument." ;; var-name) nil) ((let ((case-fold-search t)) - (or (stringp (setq val (cond ((and (boundp sym) sym) - (symbol-value sym)) - ((and (string-match "path" var-name) - (seq-find (lambda (c) (memq c '(?: ?\;))) (or (getenv var-name) ""))) - nil) - (t (getenv var-name))))) + (or (stringp (setq val (cond ((and sym (boundp sym)) + (symbol-value sym)) + ((and (string-match-p "path" var-name) + (seq-find (lambda (c) (memq c '(?: ?\;))) + (or (getenv var-name) ""))) + nil) + (t (getenv var-name))))) (setq val nil)))) ((listp val) (setq val nil)) @@ -2225,7 +2227,7 @@ function to call with FILENAME as its single argument." val)) (defun hpath:substitute-dir (path-prefix var-name rest-of-path trailing-dir-sep-flag &optional return-path-flag) - "Return PATH-PREFIX, dir for VAR-NAME, TRAILING-DIR-SEP-FLAG and REST-OF-PATH when optional RETURN-PATH-FLAG is non-nil. + "Return the concatenation of PATH-PREFIX, dir for VAR-NAME, TRAILING-DIR-SEP-FLAG and REST-OF-PATH when optional RETURN-PATH-FLAG is non-nil. Otherwise, return just the dir for VAR-NAME. Trigger an error when no match. With RETURN-PATH-FLAG non-nil, return path expanded and with first variable value substituted. @@ -2254,7 +2256,9 @@ local pathname." (split-string (getenv var-name) "[:;]")) (t (getenv var-name))))))) ((listp val) - (unless (and (setq path (locate-file rest-of-path val (cons "" hpath:suffixes))) + (unless (and (setq path (locate-file rest-of-path val (cons "" hpath:suffixes) + (lambda (f) (when (file-readable-p f) + 'dir-ok)))) return-path-flag) (let* ((suffix-added (car (delq nil (mapcar (lambda (suffix) (when (string-suffix-p suffix path) @@ -2278,7 +2282,10 @@ local pathname." (cond ((and return-path-flag path) (concat path-prefix path)) ((and return-path-flag rest-of-path) - (concat path-prefix val trailing-dir-sep-flag rest-of-path)) + (if (stringp val) + (concat path-prefix val trailing-dir-sep-flag rest-of-path) + (error "(hpath:substitute-dir): Can't find match for \"%s\"" + (concat "$\{" var-name "\}/" rest-of-path)))) (t val)))) (defun hpath:substitute-match-value (regexp str new &optional literal fixedcase) diff --git a/hyrolo.el b/hyrolo.el index 272a994076..98d81ce3bc 100644 --- a/hyrolo.el +++ b/hyrolo.el @@ -28,6 +28,7 @@ (require 'custom) ;; For defface. (require 'hversion) (require 'hmail) +(require 'sort) (require 'xml) ;; Quiet byte compiler warnings for these free variables. diff --git a/test/hpath-tests.el b/test/hpath-tests.el index 6714def427..d1d9f7656d 100644 --- a/test/hpath-tests.el +++ b/test/hpath-tests.el @@ -3,7 +3,7 @@ ;; Author: Mats Lidell <ma...@gnu.org> ;; ;; Orig-Date: 28-Feb-21 at 23:26:00 -;; Last-Mod: 24-Jan-22 at 00:39:38 by Bob Weiner +;; Last-Mod: 30-Jan-22 at 16:36:14 by Bob Weiner ;; ;; Copyright (C) 2021 Free Software Foundation, Inc. ;; See the "HY-COPY" file for license information. @@ -192,7 +192,7 @@ (switch-to-buffer buffer) (shell-mode)) -(ert-deftest hpath:prepend-ls-directory-test () +(ert-deftest hpath:prepend-shell-directory-test () "Find file in ls -R listing." (let ((shell-buffer "*hypb-test-shell-buffer*")) (unwind-protect