branch: scratch/hyperbole-lexbind commit 36e4724a604278d71ed5053bd2bf569247425d18 Author: Bob Weiner <r...@gnu.org> Commit: Bob Weiner <r...@gnu.org>
7.0.3a bug fixes; add link-to-ibut, link-to-gbut --- Changes | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ DEMO | 5 +-- HY-NEWS | 14 ++++----- hactypes.el | 58 ++++++++++++++++++++++++++++++++--- hbut.el | 16 +++++++++- hib-kbd.el | 16 ++-------- hibtypes.el | 56 +++++++++++++++++++++++++++++----- hpath.el | 22 ++++++-------- htz.el | 45 ++++++++++++++------------- hui-mouse.el | 4 +-- hui.el | 12 ++++++-- hypb.el | 17 +++++++---- hyperbole.el | 2 +- hyrolo.el | 53 +++++++++++++++----------------- kotl/kotl-mode.el | 3 +- kotl/kvspec.el | 59 +++++++++++++++--------------------- 16 files changed, 329 insertions(+), 144 deletions(-) diff --git a/Changes b/Changes index 7d4d658..3ee534f 100644 --- a/Changes +++ b/Changes @@ -1,3 +1,94 @@ +2019-06-17 Bob Weiner <r...@gnu.org> + +* hactypes.el (link-to-gbut): Added. + hui.el (hui:link-possible-types): Added link-to-gbut. + + +2019-06-16 Bob Weiner <r...@gnu.org> + +* hyrolo.el: Replaced buffer-substring-no-properties with match-string-no-properties where possible. + (hyrolo-entry-regexp): Changed to require whitespace following the entry prefix. + (hyrolo-entry-group-number): Added. + +2019-06-12 Bob Weiner <r...@gnu.org> + +* hib-kbd.el (kbd-key): Fixed preceding character test to not depend on the buffer's syntax table, + potentially missing kbd-key ibuts, notably ones using the universal argument. + +* hbut.el (ibut:at-type-p): Added to test if point is on a specific type of implicit button. + +* hib-kbd.el (kbd-key:normalize): Removed aggregation of C-u arguments into something like: C-u(16) + as this no longer works in current Emacs and causes an error. Now this just stays as "C-u C-u". + +2019-06-11 Bob Weiner <r...@gnu.org> + +* hypb.el (hypb:format-quote): Modified to return non-string args as is rather than ignoring them so + this can be used across any argument set. Used in hypb:error. + +2019-06-10 Bob Weiner <r...@gnu.org> + +* htz.el (htz:date-parse): Fixed bug, missing seconds in format 4 leading to invalid time strings saved + on MSWindows machines (and maybe more). Allow for this in style 5 which is how Hyperbole stores dates. + (htz:time-make-string): Chop off any spurious digits at the right. + +* hactypes.el (link-to-ibut): Added. + hui.el (hui:link-possible-types): Added ibut:at-p call for link-to-ibut. + +2019-06-07 Bob Weiner <r...@gnu.org> + +* hibtypes.el (debugger-source): Added support for pytype package errors (close to Python pdb ones) + +2019-06-06 Bob Weiner <r...@gnu.org> + +* hpath.el (hpath:find): Removed file-relative-name from error calls + because does not work when current-directory and filename are on different + drives under Windows at least through Emacs 26. + (hpath:find): Fixed that default-directory was not set to current + button's location, so path was expanded relative to another + directory, notably in hpath:validate call. + +2019-06-05 Bob Weiner <r...@gnu.org> + +* hpath.el (hpath:is-p): Tightened remote-path check to prevent matches that end with :line-num or :col-num. + +2019-06-04 Bob Weiner <r...@gnu.org> + +* hactypes.el (annot-bib): Changed to find referent from the end of the buffer rather than beginning, + to avoid multiple earlier annot-bib buttons +* DEMO (Hyperbole Source Buttons): Changed example button from annot-bib to kbd-key. + +* kotl/kvspec.el (kvspec:hide-levels): Changed behavior so view updates to default level clipping when + 'l' is excluded from the viewspec. + +2019-06-02 Bob Weiner <r...@gnu.org> + +* kotl/kvspec.el (kvspec:lines-to-show): Changed behavior so view updates to default cell clipping when + 'c' is excluded from the viewspec. + +* kotl/kotl-mode.el (kotl-mode:show-all): Limited (kvspec:update t) call to interactive usage only. + When kotl-mode:show-all is called in kvspec:update-view, it doesn't overwrite the to be set viewspec. + +* kotl/kvspec.el (kvspec:show-lines-per-cell) + (kvspec:show-lines-this-cell): Fixed viewspec 'c0' to expand visible cells properly. + +2019-06-01 Bob Weiner <r...@gnu.org> + +* hibtypes.el (ipython_stack_frame): Added to handle ipython stack traces and exceptions. + +2019-05-30 Bob Weiner <r...@gnu.org> + +* hyperbole.el (hkey-maybe-global-set-key): Fix missing no-add argument + to hkey-global-set-key call. + +2019-05-26 Bob Weiner <r...@gnu.org> + +* hui-mouse.el (hkey-alist): For Python files, use derived-mode-p and add + support for helm-pydoc buffers. + +============================================================================== +V7.0.3a changes ^^^^: +============================================================================== + 2019-05-11 Bob Weiner <r...@gnu.org> * man/hyperbole.texi (External Viewers): Commented out reference to mailcap use diff --git a/DEMO b/DEMO index 1d3b4a1..22b9d51 100644 --- a/DEMO +++ b/DEMO @@ -816,8 +816,9 @@ displayed. Test this technique with a {C-x C-f} (find-file) and then a {?}. ** Hyperbole Source Buttons -If you ask for help with the Assist Key or {C-u C-h A} from within the -[FSF 19] button, the first line of the help buffer will look like this: +If you ask for help with the Assist Key or {C-h A} from within this button, +{M-x dired-other-window RET ~ RET}, the first line of the help buffer will +look like this: @loc> "DEMO" diff --git a/HY-NEWS b/HY-NEWS index 61e138c..0429549 100644 --- a/HY-NEWS +++ b/HY-NEWS @@ -887,7 +887,7 @@ are new in 2016 and you should look through them all. Implicit-Button menu. - On the Hyperbole Customize/Change-Key-Bindings menubar menu, renamed - these entries and added Mark-Think-Key. Similar updates done to the + these entries and added Mark-Thing-Key. Similar updates done to the minibuffer menu. MOUSE AND SMART KEY SUPPORT @@ -906,7 +906,7 @@ are new in 2016 and you should look through them all. help mode so you can page through it with SPC and DEL keys and then quit from it with {q}. - - A click of the Action Mouse Key within an inactive minibuffer menu + - A click of the Action Mouse Key within an inactive minibuffer window displays the Hyperbole minibuffer menu, allowing you to invoke menu entries with the mouse. A click of the Assist Key in the same place displays the buffer, window and frame jump menu just as does a click @@ -1008,13 +1008,13 @@ are new in 2016 and you should look through them all. - Added missing {C-c C-i}/{C-c TAB} binding mentioned in the EXAMPLE.kotl file; sets cell attributes. - - {C=t} Transpose characters - Added error checks at the beginning of cells + - {C-t} Transpose characters - Added error checks at the beginning of cells and end of lines for times when there are not 2 chars to transpose. ROLO - Improved {M-s} interactive string searching for rolo match buffer strings. - Made {C-u M-s} to a regexp search for rolo match buffer strings. + Made {C-u M-s} do a regexp search for rolo match buffer strings. - Documented the {l} hyrolo-locate command in the Hyperbole manual. @@ -1211,8 +1211,8 @@ are new in 2016 and you should look through them all. INSTALLATION - - Installation is greatly simplified. A single emacs initialization line - of the form: + - Hyperbole initialization is greatly simplified. A single "~/.emacs" + line of the form: (require 'hyperbole (expand-file-name "hyperbole" "<HYPERBOLE-DIR>/") @@ -1266,7 +1266,7 @@ are new in 2016 and you should look through them all. now begin with 'K'. Some used to start with 'O'. To edit the example Koutline, use {C-h h k e}. - - {C-c C-a} - Show-all, expand all cells nihe current view. + - {C-c C-a} - Show-all, expand all cells in the current view. {C-c C-o} - Overview, show only first line of outline cells. {C-c C-t} - Top-level, hide all cells below level 1 and show only the first line of each level 1 cell. diff --git a/hactypes.el b/hactypes.el index bfe03fc..f2ba02f 100644 --- a/hactypes.el +++ b/hactypes.el @@ -29,8 +29,8 @@ (key-regexp (concat "^[*]*[ \t]*\\\[" (ebut:key-to-label key) "\\\]")) citation) (if (save-excursion - (goto-char (point-min)) - (setq citation (re-search-forward key-regexp nil t))) + (goto-char (point-max)) + (setq citation (re-search-backward key-regexp nil t))) (progn (hpath:display-buffer (current-buffer)) (goto-char citation) (beginning-of-line)) @@ -272,11 +272,12 @@ Use `link-to-file' instead for a permanent link." nil nil nil 'ebut))) (beep)) (ebut:label-to-key but-lbl))))) - (or (called-interactively-p 'interactive) - (setq key-file (hpath:validate (hpath:substitute-value key-file)))) + (unless (called-interactively-p 'interactive) + (setq key-file (hpath:validate (hpath:substitute-value key-file)))) (let ((but (ebut:get key (find-file-noselect key-file)))) (if but (hbut:act but) - (hypb:error "(link-to-ebut): No button `%s' in `%s'." (ebut:key-to-label key) + (hypb:error "(link-to-ebut): No button `%s' in `%s'." + (ebut:key-to-label key) key-file)))) (defact link-to-elisp-doc (symbol) @@ -351,6 +352,29 @@ the window." (hpath:find-line path line-num)) (move-to-column column-num))) +(defact link-to-gbut (key) + "Performs action given by an existing global button, specified by KEY." + (interactive + (let ((gbut-file (hpath:validate (hpath:substitute-value gbut:file))) + but-lbl) + (if (not (file-readable-p gbut-file)) + (hypb:error "(link-to-gbut): You cannot read `%s'." gbut-file) + (list (progn + (find-file-noselect gbut-file) + (while (string-equal "" (setq but-lbl + (hargs:read-match + "Global button to link to: " + (ebut:alist gbut-file) + nil nil nil 'ebut))) + (beep)) + (ebut:label-to-key but-lbl)))))) + (let ((gbut-file (hpath:validate (hpath:substitute-value gbut:file))) + (but (ebut:get key (find-file-noselect (expand-file-name gbut:file))))) + (if but (hbut:act but) + (hypb:error "(link-to-gbut): No button `%s' in `%s'." + (ebut:key-to-label key) + gbut-file)))) + (defact link-to-Info-index-item (index-item) "Displays an Info index INDEX-ITEM cross-reference. INDEX-ITEM must be a string of the form \"(filename)item-name\". During @@ -373,6 +397,30 @@ available. Filename may be given without the .info suffix." (id-info string) (hypb:error "(link-to-Info-node): Invalid Info node: `%s'" string))) +(defact link-to-ibut (key-file key point) + "Performs action given by an implicit button, specified by KEY-FILE, KEY and POINT. +When creating the button, point must be on the implicit button to which to link +and its buffer must have a file attached." + (interactive + (let ((ibut-key (ibut:at-p t))) + (if (and ibut-key buffer-file-name) + (list buffer-file-name ibut-key (point)) + (list nil nil nil)))) + (or (called-interactively-p 'interactive) + (setq key-file (hpath:validate (hpath:substitute-value key-file)))) + (let (but) + (if (and key-file + (save-excursion + (save-restriction + (find-file-noselect key-file) + (widen) + (goto-char point) + (setq but (ibut:at-p))))) + (hbut:act but) + (hypb:error "(link-to-ibut): No button `%s' in `%s'." + (ebut:key-to-label key) + key-file)))) + (defact link-to-kcell (file cell-ref) "Displays FILE with kcell given by CELL-REF at window top. See documentation for `kcell:ref-to-id' for valid cell-ref formats. diff --git a/hbut.el b/hbut.el index e4104f9..02bf4ad 100644 --- a/hbut.el +++ b/hbut.el @@ -314,7 +314,7 @@ With TWO-LINES-FLAG non-nil, constrains label search to two lines." (t (if as-label (ebut:key-to-label lbl-key) lbl-key)))))))) (defun ebut:label-regexp (lbl-key &optional no-delim) - "Unnormalizes LBL-KEY. Returns regular expr matching delimited but label. + "Unnormalizes LBL-KEY. Returns regular expr matching delimited button label. Optional NO-DELIM leaves off delimiters and leading and trailing space." (if lbl-key (let* ((pos 0) @@ -1062,6 +1062,20 @@ With optional KEY-ONLY, returns only the label key for button." (hattr:set 'hbut:current 'args (cdr args)))) 'hbut:current)))) +(defun ibut:at-type-p (ibut-type-symbol) + "Returns non-nil if point is on a button of type `ibut-type-symbol`. +The return value is a list of the type's action type symbol and +associated arguments from the button." + (if (and ibut-type-symbol (symbolp ibut-type-symbol)) + (let ((type-name (symbol-name ibut-type-symbol))) + (unless (string-match "::" type-name) + (setq ibut-type-symbol (intern-soft (concat "ibtypes::" type-name)))) + (if ibut-type-symbol + (let ((types (htype:category 'ibtypes)) + ;; Global var used in (hact) function, don't delete. + (hrule:action 'actype:identity)) + (funcall ibut-type-symbol)))))) + (defun ibut:is-p (object) "Returns non-nil if object denotes an implicit Hyperbole button." (if (symbolp object) diff --git a/hib-kbd.el b/hib-kbd.el index 339930a..b24a766 100644 --- a/hib-kbd.el +++ b/hib-kbd.el @@ -70,7 +70,7 @@ Any key sequence must be a string of one of the following: binding) ;; Match only when start delimiter is preceded by whitespace or ;; is the 1st buffer character, so do not match to things like ${variable}. - (when (= (char-syntax (or (char-before start) ?\t)) ?\ ) + (when (memq (char-before start) '(nil ?\ ?\t ?\n ?\j ?\f)) (when (and (stringp key-sequence) (not (eq key-sequence ""))) (setq key-sequence (kbd-key:normalize key-sequence) @@ -171,17 +171,7 @@ With optional prefix arg FULL, displays full documentation for command." (string-to-number (substring norm-key-seq (match-beginning 2) (match-end 2))) norm-key-seq (substring norm-key-seq (match-end 0)))) - (let (arg-val) - (while (string-match "\\`C-u" norm-key-seq) - (if (or (not (listp arg)) - (not (integerp (setq arg-val (car arg))))) - (setq arg '(1) - arg-val 1)) - (setq arg-val (* arg-val 4) - arg (cons arg-val nil) - norm-key-seq (substring norm-key-seq (match-end 0))))) - (if arg (setq norm-key-seq (concat (format "\025%s" arg) norm-key-seq))) - ;; + ;; Quote Control and Meta key names (setq norm-key-seq (hypb:replace-match-string "C-\\(.\\)" norm-key-seq @@ -208,7 +198,7 @@ With optional prefix arg FULL, displays full documentation for command." (and (stringp key-sequence) (string-match kbd-key:extended-command-prefix key-sequence))) (defun kbd-key:hyperbole-hycontrol-key-p (key-sequence) - "Returns t if normalized KEY-SEQUENCE is given when in a HyControl mode, else nil. + "Returns t if normalized, non-nil KEY-SEQUENCE is given when in a HyControl mode, else nil. Allows for multiple key sequences strung together." (and key-sequence (featurep 'hycontrol) diff --git a/hibtypes.el b/hibtypes.el index c018f3c..07965ab 100644 --- a/hibtypes.el +++ b/hibtypes.el @@ -628,11 +628,53 @@ Requires the Emacs builtin Tramp library for ftp file retrievals." (require 'klink) ;;; ======================================================================== -;;; Jumps to source line associated with grep or compilation error messages. -;;; Also supports ripgrep (rg command). +;;; Jumps to source line associated with ipython, ripgreb, grep or ;;; With credit to Michael Lipp and Mike Williams for the idea. ;;; ======================================================================== +(defib ipython-stack-frame () + "Jumps to line associated with an ipython stack frame line numbered msg. +ipython outputs each pathname once followed by all matching lines in that pathname. +Messages are recognized in any buffer (other than a helm completion +buffer)." + ;; Locate and parse ipython stack trace messages found in any buffer other than a + ;; helm completion buffer. + ;; + ;; Sample ipython stack trace command output: + ;; + ;; ~/Dropbox/py/inview/inview_pr.py in ap(name_filter, value_filter, print_func) + ;; 1389 apc(name_filter, value_filter, print_func, defined_only=True) + ;; 1390 print('\n**** Modules/Packages ****') + ;; -> 1391 apm(name_filter, value_filter, print_func, defined_only=True) + ;; 1392 + ;; 1393 def apa(name_filter=None, value_filter=None, print_func=pd1, defined_only=False): + (unless (eq major-mode 'helm-major-mode) + (save-excursion + (beginning-of-line) + (let ((line-num-regexp "\\( *\\|-+> \\)?\\([1-9][0-9]*\\) ") + line-num + file) + (when (looking-at line-num-regexp) + ;; ipython stack trace matches and context lines (-A<num> option) + (setq line-num (match-string-no-properties 2) + file nil) + (while (and (= (forward-line -1) 0) + (looking-at line-num-regexp))) + (unless (or (looking-at line-num-regexp) + (not (re-search-forward " in " nil (point-at-eol))) + (and (setq file (buffer-substring-no-properties (point-at-bol) (match-beginning 0))) + (string-empty-p (string-trim file)))) + (let* ((but-label (concat file ":" line-num)) + (source-loc (if (file-name-absolute-p file) + nil + (hbut:key-src t)))) + (if (stringp source-loc) + (setq file (expand-file-name file (file-name-directory source-loc)))) + (when (file-readable-p file) + (setq line-num (string-to-number line-num)) + (ibut:label-set but-label) + (hact 'link-to-file-line file line-num))))))))) + (defib ripgrep-msg () "Jumps to line associated with a ripgrep (rg) line numbered msg. Ripgrep outputs each pathname once followed by all matching lines in that pathname. @@ -733,13 +775,13 @@ This works with JavaScript and Python tracebacks, gdb, dbx, and xdb. Such lines (save-excursion (beginning-of-line) (cond - ;; Python pdb or traceback - ((looking-at ".+ File \"\\([^\"\n\r]+\\)\", line \\([0-9]+\\)") - (let* ((file (match-string-no-properties 1)) - (line-num (match-string-no-properties 2)) + ;; Python pdb or traceback, pytype error + ((looking-at "\\(^\\|.+ \\)File \"\\([^\"\n\r]+\\)\", line \\([0-9]+\\)") + (let* ((file (match-string-no-properties 2)) + (line-num (match-string-no-properties 3)) (but-label (concat file ":" line-num))) (setq line-num (string-to-number line-num)) - (ibut:label-set but-label (match-beginning 1) (match-end 1)) + (ibut:label-set but-label (match-beginning 2) (match-end 2)) (hact 'link-to-file-line file line-num))) ;; JavaScript traceback diff --git a/hpath.el b/hpath.el index f31c9ed..9b4f416 100644 --- a/hpath.el +++ b/hpath.el @@ -799,7 +799,7 @@ Returns non-nil iff file is displayed within a buffer (not with an external program)." (interactive "FFind file: ") (let ((case-fold-search t) - modifier loc dir anchor hash path) + modifier loc default-directory anchor hash path) (if (string-match hpath:prefix-regexp filename) (setq modifier (aref filename 0) filename (substring filename (match-end 0)))) @@ -810,19 +810,17 @@ program)." (substring filename 0 (match-end 1))) filename)) loc (hattr:get 'hbut:current 'loc) - dir (file-name-directory - ;; Loc may be a buffer without a file - (if (stringp loc) loc default-directory)) - filename (hpath:absolute-to path dir)) + default-directory (file-name-directory + ;; Loc may be a buffer without a file + (if (stringp loc) loc default-directory)) + filename (hpath:absolute-to path default-directory)) (let ((remote-filename (hpath:remote-p path))) (or modifier remote-filename - (file-exists-p path) - (error "(hpath:find): \"%s\" does not exist" - (file-relative-name filename))) + (file-exists-p filename) + (error "(hpath:find): \"%s\" does not exist" filename)) (or modifier remote-filename - (file-readable-p path) - (error "(hpath:find): \"%s\" is not readable" - (file-relative-name filename))) + (file-readable-p filename) + (error "(hpath:find): \"%s\" is not readable" filename)) ;; If filename is a remote file (not a directory, we have to copy it to ;; a temporary local file and then display that. (when (and remote-filename (not (file-directory-p remote-filename))) @@ -1042,7 +1040,7 @@ nonexistent local paths are allowed." (not (string-match "[\t\n\r\"`'|{}\\]" path)) (or (not (hpath:www-p path)) (string-match "\\`ftp[:.]" path)) - (let ((remote-path (string-match "@.+:\\|^/.+:\\|..+:/" path))) + (let ((remote-path (string-match "\\(@.+:\\|^/.+:\\|..+:/\\).*[^:0-9/]" path))) (if (cond (remote-path (cond ((eq type 'file) (not (string-equal "/" (substring path -1)))) diff --git a/htz.el b/htz.el index 3341d6b..6ff6713 100644 --- a/htz.el +++ b/htz.el @@ -61,8 +61,8 @@ Optional 2nd argument TIMEZONE specifies a timezone to be represented in." (defun htz:date-parse (date &optional parsed-current-date) "Parse DATE string and return a vector [year month day time timezone]. -19 is prepended to year if necessary. Timezone in DATE is optional, it -defaults to the value of `htz:local'. +If a two-digit year, the first two digits of the current year are prepended. +Timezone in DATE is optional, it defaults to the value of `htz:local'. Recognizes the following styles: (1) \"(1 30 1999)\" or \"(1 30 1999)\" `calendar-julian-date' requires `parsed-current-date' arg @@ -73,11 +73,8 @@ Recognizes the following styles: (6) \"Mar 29 14:00\" `ls -l date' requires `parsed-current-date' arg (7) \"Mar 7 1994\" `ls -l date' requires `parsed-current-date' arg" (let ((date (or date "")) - (year nil) - (month nil) - (day nil) - (time nil) - (zone nil)) ; This may be nil. + year month day time + zone) ; This may be nil. (if (listp date) (setq month (nth 0 date) day (nth 1 date) @@ -87,31 +84,34 @@ Recognizes the following styles: ;; Style (1) (setq year 3 month 1 day 2 time nil zone nil)) ((string-match - "\\([0-9][0-9][0-9][0-9]\\)\\([0-1][0-9]\\)\\([0-3][0-9]\\):?\\([0-2][0-9]:[0-5][0-9:]+\\)[ ]*\\'" date) - ;; Style (4) + ;; Allow for 3 digits in hour to handle prior error in + ;; generating hours fixed on 2019-06-10; 3rd digit + ;; removed in htz:time-make-string and htz:time-parse. + "\\([0-9][0-9][0-9][0-9]\\)\\([0-1][0-9]\\)\\([0-3][0-9]\\):?\\([0-9][0-9][0-9]?:[0-5][0-9:]+\\)[ ]*\\'" date) + ;; Style (5) (setq year 1 month 2 day 3 time 4 zone nil)) ((string-match "\\([0-9]+\\) \\([^ ,]+\\) \\([0-9]+\\) \\([0-9]+:[0-9:]+\\)[ ]*\\'" date) - ;; Styles: (1) and (2) without timezone + ;; Styles: (2) and (3) without timezone (setq year 3 month 2 day 1 time 4 zone nil)) ((string-match "\\([0-9]+\\) \\([^ ,]+\\) \\([0-9]+\\) \\([0-9]+:[0-9:]+\\)[ ]*\\([-+a-zA-Z0-9]+\\)" date) - ;; Styles: (1) and (2) with timezone and buggy timezone + ;; Styles: (2) and (3) with timezone and buggy timezone (setq year 3 month 2 day 1 time 4 zone 5)) ((string-match - "\\([^ ,]+\\) +\\([0-9]+\\) \\([0-9]+:[0-9:]+\\) \\([0-9]+\\)" date) - ;; Styles: (3) without timezone - (setq year 4 month 1 day 2 time 3 zone nil)) + "\\([^ ,]+\\) +\\([0-9]+\\) \\([0-9]+:[0-9:]+\\(:[0-9]+\\)?\\) \\([0-9]+\\)" date) + ;; Styles: (4) without timezone + (setq year 5 month 1 day 2 time 3 zone nil)) ((string-match - "\\([^ ,]+\\) +\\([0-9]+\\) \\([0-9]+:[0-9:]+\\) \\([-+a-zA-Z0-9]+\\) \\([0-9]+\\)" date) - ;; Styles: (3) with timezone - (setq year 5 month 1 day 2 time 3 zone 4)) + "\\([^ ,]+\\) +\\([0-9]+\\) \\([0-9]+:[0-9:]+\\(:[0-9]+\\)?\\) \\([-+a-zA-Z0-9]+\\) \\([0-9]+\\)" date) + ;; Styles: (4) with timezone + (setq year 6 month 1 day 2 time 3 zone 5)) ((string-match "^\\([^ ,]+\\) +\\([0-9]+\\) +\\([0-9]+:[0-9:]+\\)$" date) - ;; Style: (5) + ;; Style: (6) (setq year nil month 1 day 2 time 3 zone nil)) ((string-match "^\\([^ ,]+\\) +\\([0-9]+\\) +\\([0-9][0-9][0-9][0-9]\\)$" date) - ;; Style: (6) + ;; Style: (7) (setq year 3 month 1 day 2 time nil zone nil)) (t (error "(htz:date-parse): Invalid date format: `%s'" date))) (if year @@ -249,7 +249,10 @@ See `htz:date-parse' for a list of acceptable date formats." ;; Return [hour minute second] (vector (if hour - (substring time (match-beginning hour) (match-end hour)) "0") + ;; Remove possible 3rd digit in hour to handle prior error in + ;; generating hours fixed on 2019-06-10; 3rd digit + ;; removed in htz:time-make-string and here. + (format "%02.2s" (substring time (match-beginning hour) (match-end hour))) "0") (if minute (substring time (match-beginning minute) (match-end minute)) "0") (if second @@ -322,7 +325,7 @@ Optional argument TIMEZONE specifies a time zone." ;; Partly copied from Calendar program by Edward M. Reingold. (defun htz:time-make-string (hour minute second) "Make time string from HOUR, MINUTE, and SECOND." - (format "%02d:%02d:%02d" hour minute second)) + (format "%02.2d:%02.2d:%02.2d" hour minute second)) (defun htz:zone-to-hour (timezone) "Translate TIMEZONE (in zone name or integer) to integer hour." diff --git a/hui-mouse.el b/hui-mouse.el index 0878500..d3396b2 100644 --- a/hui-mouse.el +++ b/hui-mouse.el @@ -243,9 +243,9 @@ Its default value is #'smart-scroll-down." ;; ;; Python files - ensure this comes before Imenu for more advanced ;; definition lookups - ((and (or (and (eq major-mode 'python-mode) buffer-file-name) + ((and (or (and (derived-mode-p 'python-mode) buffer-file-name) (let ((case-fold-search)) - (string-match "\\`\\(Pydoc:\\|\\*?Python\\)" (buffer-name)))) + (string-match "\\`\\([ *]?Pydoc[: ]\\|\\*?Python\\)" (buffer-name)))) (setq hkey-value (smart-python-at-tag-p))) . ((smart-python hkey-value) . (smart-python hkey-value 'next-tag))) ;; diff --git a/hui.el b/hui.el index f0d1d48..1e8ebf0 100644 --- a/hui.el +++ b/hui.el @@ -66,7 +66,7 @@ (message "{%s} now runs `%s'" new-key-text cmd)))) (defun hui:ebut-create (&optional start end) - "Creates an explicit but starting from label between optional START and END. + "Creates an explicit Hyperbole button starting from label between optional START and END. Indicates by delimiting and adding any necessary instance number of the button label." (interactive (list (and (marker-position (hypb:mark-marker t)) @@ -862,7 +862,9 @@ possible types. Referent Context Possible Link Type Returned ---------------------------------------------------- +Global Button link-to-gbut Explicit Button link-to-ebut +Implicit Button link-to-ibut Info Index Item link-to-Info-index-item Info Node link-to-Info-node Mail Reader Message link-to-mail @@ -877,8 +879,12 @@ Buffer without File link-to-buffer-tmp" (let (val) (delq nil - (list (if (ebut:at-p) - (list 'link-to-ebut buffer-file-name (ebut:label-p))) + (list (cond ((eq (current-buffer) (get-file-buffer gbut:file)) + (list 'link-to-gbut buffer-file-name (ebut:label-p))) + ((ebut:at-p) + (list 'link-to-ebut buffer-file-name (ebut:label-p))) + ((ibut:at-p) + (list 'link-to-ibut buffer-file-name (ibut:label-p)))) (cond ((eq major-mode 'Info-mode) (if (and Info-current-node (member Info-current-node diff --git a/hypb.el b/hypb.el index 6abc17a..c8573a1 100644 --- a/hypb.el +++ b/hypb.el @@ -220,7 +220,10 @@ Global keymap is used unless optional KEYMAP is given." (defun hypb:error (&rest args) "Signals an error typically to be caught by `hyperbole'." - (let ((msg (if (< (length args) 2) (car args) (apply 'format args)))) + (let ((msg (if (< (length args) 2) + (car args) + (apply 'format (cons (car args) + (mapcar #'hypb:format-quote (cdr args))))))) (put 'error 'error-message msg) (error msg))) @@ -233,14 +236,16 @@ FILE is temporarily read into a buffer to determine the major mode if necessary. (unless (or existing-flag (null buf)) (kill-buffer buf))))) -(defun hypb:format-quote (string) - "Replace all single % with %% in STRING so a call to `format' or `message' ignores them." - (if (stringp string) +(defun hypb:format-quote (arg) + "Replace all single % with %% in any string ARG so that a call to `format' or `message' ignores them. +Return either the modified string or the original ARG." + (if (stringp arg) (replace-regexp-in-string "@@@" "%%" (replace-regexp-in-string - "%" "%%" (replace-regexp-in-string "%%" "@@@" string nil t) + "%" "%%" (replace-regexp-in-string "%%" "@@@" arg nil t) nil t) - nil t))) + nil t) + arg)) ;;;###autoload (defun hypb:functionp (obj) diff --git a/hyperbole.el b/hyperbole.el index 4aa427d..b4f3a81 100644 --- a/hyperbole.el +++ b/hyperbole.el @@ -300,7 +300,7 @@ With third argument NO-ADD non-nil, skip storage of prior KEY binding which prevents automatic removal of any local bindings to the same key." (or (global-key-binding key) (where-is-internal command) - (hkey-global-set-key key command))) + (hkey-global-set-key key command no-add))) (defun hkey-set-bindings (key-binding-list) "Sets keys bound by Hyperbole to those in KEY-BINDING-LIST. diff --git a/hyrolo.el b/hyrolo.el index d716a33..1c8719a 100644 --- a/hyrolo.el +++ b/hyrolo.el @@ -189,10 +189,8 @@ entry which begins with the parent string." parent (substring name 0 end) name (substring name (min (1+ end) (length name)))) (if (re-search-forward - (concat "\\(" hyrolo-entry-regexp "\\)[ \t]*" - (regexp-quote parent)) nil t) - (setq level (buffer-substring-no-properties (match-beginning 1) - (match-end 1))) + (concat hyrolo-entry-regexp (regexp-quote parent)) nil t) + (setq level (match-string-no-properties 1)) (error "(hyrolo-add): `%s' category not found in \"%s\"." parent file))) (narrow-to-region (point) @@ -210,10 +208,10 @@ entry which begins with the parent string." ;; entry by moving to an entry with the same (or nearest) first character ;; to that of `name'. (if (and (= level-len 1) - (equal hyrolo-entry-regexp "^\\*+")) + (equal hyrolo-entry-regexp "^\\(\\*+\\)\\([ \t]+\\)")) (progn (goto-char (point-min)) - (if (re-search-forward (concat "^\\*[ \t]*" - (char-to-string first-char)) + (if (re-search-forward (concat hyrolo-entry-regexp + (regexp-quote (char-to-string first-char))) nil t) (goto-char (match-beginning 0)) (goto-char (point-max)) @@ -235,17 +233,12 @@ entry which begins with the parent string." (setq again nil))))) (goto-char (point-min))) - (while (and again - (re-search-forward - (concat "\\(" hyrolo-entry-regexp "\\)\\([ \t]*\\)") - nil 'end)) - (setq entry-level (buffer-substring-no-properties (match-beginning 1) - (match-end 1))) + (while (and again (re-search-forward hyrolo-entry-regexp nil 'end)) + (setq entry-level (match-string-no-properties 1)) (if (/= (length entry-level) level-len) (hyrolo-to-entry-end t entry-level) (setq entry (buffer-substring-no-properties (point) (+ (point) len)) - entry-spc (buffer-substring-no-properties (match-beginning 2) - (match-end 2))) + entry-spc (match-string-no-properties hyrolo-entry-regexp-)) (cond ((string< entry name) (hyrolo-to-entry-end t entry-level)) ((string< name entry) @@ -534,7 +527,7 @@ Returns t if entry is killed, nil otherwise." (defun hyrolo-locate () "Interactively search for an entry beginning with a set of search characters." (interactive) - (hyrolo-isearch-for-regexp (concat hyrolo-entry-regexp "[ \t]*"))) + (hyrolo-isearch-for-regexp hyrolo-entry-regexp)) (defun hyrolo-mail-to () "Start composing mail addressed to the first e-mail address at or after point." @@ -1301,8 +1294,7 @@ Name is returned as `last, first-and-middle'." (skip-chars-forward " \t") (if (or (looking-at "[^ \t\n\r]+ ?, ?[^ \t\n\r]+") (looking-at "\\( ?[^ \t\n\r]+\\)+")) - (buffer-substring-no-properties (match-beginning 0) - (match-end 0)))))))) + (match-string-no-properties 0))))))) (defun hyrolo-narrowed-p () (or (/= (point-min) 1) (/= (1+ (buffer-size)) (point-max)))) @@ -1393,11 +1385,8 @@ Returns point where matching entry begins or nil if not found." (while (and (not level) (search-forward parent nil t)) (save-excursion (beginning-of-line) - (if (looking-at - (concat "\\(" hyrolo-entry-regexp "\\)[ \t]*" - (regexp-quote parent))) - (setq level (buffer-substring-no-properties (match-beginning 1) - (match-end 1)))))) + (if (looking-at (concat hyrolo-entry-regexp (regexp-quote parent))) + (setq level (match-string-no-properties 1))))) level)) ((equal name real-name)) ;; Try next file. (t ;; Found parent but not child @@ -1415,9 +1404,7 @@ Returns point where matching entry begins or nil if not found." (beginning-of-line) (setq found (if (looking-at - (concat "\\(" hyrolo-entry-regexp - "\\)[ \t]*" - (regexp-quote name))) + (concat hyrolo-entry-regexp (regexp-quote name))) (point)))))))) (or found (hyrolo-kill-buffer))) ;; conditionally kill (widen) @@ -1472,10 +1459,18 @@ Calls the functions given by `hyrolo-mode-hook'. "Buffer used to display set of last matching rolo entries.") (define-obsolete-variable-alias 'rolo-display-buffer 'hyrolo-display-buffer "06.00") -(defvar hyrolo-entry-regexp "^\\*+" +(defvar hyrolo-entry-group-number 1 + "Group number within `hyrolo-entry-regexp' whose length represents the level of any entry matched.") + +(defvar hyrolo-entry-trailing-space-group-number 2 + "Group number within `hyrolo-entry-regexp; containing trailing space.") + +(defvar hyrolo-entry-regexp "^\\(\\*+\\)\\([ \t]+\\)" "Regular expression to match the beginning of a rolo entry. -This pattern must match the beginning of the line. Entries may be nested -through the use of increasingly longer beginning patterns.") +This pattern must match the beginning of the line. Use +`hyrolo-entry-group-number' to compute the entry's level in the +hierarchy. Use `hyrolo-entry-trailing-space-group-number' to capture +the whitespace following the entry hierarchy level.") (define-obsolete-variable-alias 'rolo-entry-regexp 'hyrolo-entry-regexp "06.00") (defconst hyrolo-hdr-format diff --git a/kotl/kotl-mode.el b/kotl/kotl-mode.el index ffb4908..7deefb5 100644 --- a/kotl/kotl-mode.el +++ b/kotl/kotl-mode.el @@ -2554,7 +2554,8 @@ With optional prefix ARG, toggle display of blank lines between cells." (kview:set-attr kview 'lines-to-show 0) (outline-flag-region (point-min) (point-max) nil) (if arg (kvspec:toggle-blank-lines)) - (kvspec:update t)))) + (if (called-interactively-p 'interactive) + (kvspec:update t))))) ;;;###autoload (defun kotl-mode:top-cells (&optional arg) diff --git a/kotl/kvspec.el b/kotl/kvspec.el index 98dfc5f..a7e7ee0 100644 --- a/kotl/kvspec.el +++ b/kotl/kvspec.el @@ -114,15 +114,14 @@ display all levels of cells." (kview:set-attr kview 'levels-to-show levels-to-keep)) (defun kvspec:show-lines-per-cell (num) - "Show NUM lines per cell." + "Show NUM lines per visible cell; 0 means show all lines in each visible cell." (if (or (not (integerp num)) (< num 0)) (error "(kvspec:show-lines-per-cell): Invalid lines per cell, `%d'" num)) (kview:set-attr kview 'lines-to-show num) - (if (not (zerop num)) - ;; Now show NUM lines in cells. - (kview:map-tree (lambda (kview) - (kcell-view:expand (point)) - (kvspec:show-lines-this-cell num)) kview t t))) + ;; Now show NUM lines in cells. + (kview:map-tree (lambda (kview) + (kcell-view:expand (point)) + (kvspec:show-lines-this-cell num)) kview t t)) (defun kvspec:toggle-blank-lines () "Toggle blank lines between cells on or off." @@ -224,32 +223,25 @@ view specs." ;; "l" means use value of kview:default-levels-to-show. ;; "l0" means show all levels. (let (levels) - (if (not (string-match "l\\([0-9]+\\)?" kvspec:current)) - ;; Don't change the view if no view spec is given but note that - ;; all levels should be shown in the future. - (kview:set-attr kview 'levels-to-show 0) - (if (match-beginning 1) - (setq levels (string-to-number (match-string 1 kvspec:current))) - (setq levels kview:default-levels-to-show)) - (kvspec:levels-to-show levels)))) + (if (and (string-match "l\\([0-9]+\\)?" kvspec:current) + (match-beginning 1)) + (setq levels (string-to-number (match-string 1 kvspec:current))) + (setq levels kview:default-levels-to-show)) + (kvspec:levels-to-show levels))) (defun kvspec:lines-to-show () "Show a set number of lines per cell according to `kvspec:current'." - ;; "c" means use value of kview:default-lines-to-show. + ;; "c" or no "c" means use value of kview:default-lines-to-show. ;; "c0" means show all lines. - (cond ((not (string-match "c\\([0-9]+\\)?" kvspec:current)) - ;; Don't change the view if no view spec is given but note that all - ;; lines should be shown in the future. - (kview:set-attr kview 'lines-to-show 0)) - ((match-beginning 1) - (kvspec:show-lines-per-cell - (string-to-number (match-string 1 kvspec:current)))) - (t (kvspec:show-lines-per-cell kview:default-lines-to-show)))) + (if (and (string-match "c\\([0-9]+\\)?" kvspec:current) + (match-beginning 1)) + (kvspec:show-lines-per-cell + (string-to-number (match-string 1 kvspec:current))) + (kvspec:show-lines-per-cell kview:default-lines-to-show))) (defun kvspec:numbering () "Set the type of numbering (label) display according to `kvspec:current'." - (if (not (string-match "n\\([.*~0-2]\\)?" kvspec:current)) - nil + (when (string-match "n\\([.*~0-2]\\)?" kvspec:current) ;; "n" means use value of kview:default-label-type. ;; "n0" means display idstamps. ;; "n1" means display alpha labels. @@ -259,22 +251,21 @@ view specs." ;; "n~" means no labels. (let (spec type) (if (match-beginning 1) - (setq spec (string-to-char - (substring kvspec:current - (match-beginning 1) (match-end 1))) + (setq spec (string-to-char (match-string 1 kvspec:current)) type (cdr (assq spec kvspec:label-type-alist))) (setq type kview:default-label-type)) (kview:set-label-type kview type)))) (defun kvspec:show-lines-this-cell (num) "Assume the current cell is fully expanded and collapse to show NUM lines within it. -If NUM is greater than the number of lines available, the cell remains fully expanded." +If NUM is less than 1 or greater than the number of lines available, the cell remains fully expanded." ;; Use free variable label-sep-len bound in kview:map-* for speed. - (let ((start (goto-char (kcell-view:start (point) label-sep-len))) - (end (kcell-view:end-contents))) - ;; Hide all but num lines of the cell. - (and (> num 0) (search-forward "\n" end t num) - (outline-flag-region (1- (point)) end t)))) + (unless (< num 1) + (let ((start (goto-char (kcell-view:start (point) label-sep-len))) + (end (kcell-view:end-contents))) + ;; Hide all but num lines of the cell. + (and (search-forward "\n" end t num) + (outline-flag-region (1- (point)) end t))))) (defun kvspec:update-modeline () "Setup or update display of the current kview spec in the modeline."