emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Invisible colons in Emacs Info.


From: Stefan Monnier
Subject: Re: Invisible colons in Emacs Info.
Date: Wed, 25 Jun 2003 17:12:44 -0400

So it seems that the problem was introduced by revision 1.333 where
we're trying to allow menu entry names with colons in them.
The problem is that we skip `:' that could be part of the name without
checking whether that is the last colon and is thus obviously not part
of the name.

Others will say the problem is simply that we allow `:', but assuming we
still want to allow colon despite the obvious ambiguity, I think the patch
below should help.

It uses regexp matching rather than combinations of skip-chars-forward
and stuff.  The reason for the change is because, by using regexp-matching,
we can take advantage of its backtracking to resolve the ambiguity.

Problem is that it still fails on:

   * name1:foo:node.           des:cription.

because it thinks the menu entry name is "name1:foo:node.           des"
and the node name is "cription".  I'm sure we can tune the disambiguation
by saying that menu entries can have `:' but cannot have `.' after the `:'
or some other such heuristic.  If anybody wants to spice up the
Info-menu-entry-name-re regexp accordingly...
We really need a better solution.



        Stefan


Index: info.el
===================================================================
RCS file: /cvsroot/emacs/emacs/lisp/info.el,v
retrieving revision 1.358
diff -u -b -r1.358 info.el
--- info.el     19 Jun 2003 20:53:06 -0000      1.358
+++ info.el     25 Jun 2003 20:56:33 -0000
@@ -902,7 +902,9 @@
                    nodename end)
                (re-search-backward "^\^_")
                (search-forward "Node: ")
-               (setq nodename (Info-following-node-name))
+               (setq nodename
+                     (and (looking-at (Info-following-node-name-re))
+                          (match-string 1)))
                (search-forward "\n\^_" nil 'move)
                (beginning-of-line)
                (setq end (point))
@@ -1201,8 +1203,8 @@
                  nodename)
     (setq filename (if (= (match-beginning 1) (match-end 1))
                       ""
-                    (substring nodename (match-beginning 2) (match-end 2)))
-         nodename (substring nodename (match-beginning 3) (match-end 3)))
+                    (match-string 2 nodename))
+         nodename (match-string 3 nodename))
     (let ((trim (string-match "\\s *\\'" filename)))
       (if trim (setq filename (substring filename 0 trim))))
     (let ((trim (string-match "\\s *\\'" nodename)))
@@ -1385,35 +1387,33 @@
 (defun Info-extract-pointer (name &optional errorname)
   "Extract the value of the node-pointer named NAME.
 If there is none, use ERRORNAME in the error message;
-if ERRORNAME is nil, just return nil.
-Bind this in case the user sets it to nil."
+if ERRORNAME is nil, just return nil."
+  ;; Bind this in case the user sets it to nil.
   (let ((case-fold-search t))
     (save-excursion
-      (save-restriction
        (goto-char (point-min))
        (let ((bound (point)))
          (forward-line 1)
-         (cond ((re-search-backward (concat name ":") bound t)
-                (goto-char (match-end 0))
-                (Info-following-node-name))
+       (cond ((re-search-backward
+               (concat name ":" (Info-following-node-name-re)) bound t)
+              (match-string 1))
                ((not (eq errorname t))
                 (error "Node has no %s"
-                       (capitalize (or errorname name))))))))))
+                     (capitalize (or errorname name)))))))))
 
-(defun Info-following-node-name (&optional allowedchars)
-  "Return the node name in the buffer following point.
+(defun Info-following-node-name-re (&optional allowedchars)
+  "Return a regexp matching a node name.
 ALLOWEDCHARS, if non-nil, goes within [...] to make a regexp
-saying which chars may appear in the node name."
-  (skip-chars-forward " \t")
-  (buffer-substring-no-properties
-   (point)
-   (progn
-     (while (looking-at (concat "[" (or allowedchars "^,\t\n") "]"))
-       (skip-chars-forward (concat (or allowedchars "^,\t\n") "("))
-       (if (looking-at "(")
-          (skip-chars-forward "^)")))
-     (skip-chars-backward " ")
-     (point))))
+saying which chars may appear in the node name.
+Submatch 1 is the complete node name.
+Submatch 2 if non-nil is the parenthesized file name part of the node name.
+Submatch 3 is the local part of the node name.
+End of submatch 0, 1, and 3 are the same, so you can safely concat."
+  (concat "[ \t]*"                     ;Skip leading space.
+         "\\(\\(([^)]+)\\)?"   ;Node name can start with a file name.
+         "\\([" (or allowedchars "^,\t\n") "]*" ;Any number of allowed chars.
+         "[" (or allowedchars "^,\t\n") " ]" ;The last char can't be a space.
+         "\\|\\)\\)"))                       ;Allow empty node names.
 
 (defun Info-next ()
   "Go to the next node of this node."
@@ -1472,9 +1472,7 @@
 
        (goto-char (point-min))
        (while (re-search-forward "\\*note[ \n\t]*\\([^:]*\\):" nil t)
-        (setq str (buffer-substring-no-properties
-                   (match-beginning 1)
-                   (1- (point))))
+        (setq str (match-string-no-properties 1))
         ;; See if this one should be the default.
         (and (null default)
              (<= (match-beginning 0) start-point)
@@ -1494,23 +1492,14 @@
         (if (eq default t) (setq default str))
         (if (eq alt-default t) (setq alt-default str))
         ;; Don't add this string if it's a duplicate.
-        ;; We use a loop instead of "(assoc str completions)" because
-        ;; we want to do a case-insensitive compare.
-        (let ((tail completions)
-              (tem (downcase str)))
-          (while (and tail
-                      (not (string-equal tem (downcase (car (car tail))))))
-            (setq tail (cdr tail)))
-          (or tail
-              (setq completions
-                    (cons (cons str nil)
-                          completions))))))
+        (or (assoc-string str completions t)
+            (push str completions))))
      ;; If no good default was found, try an alternate.
      (or default
         (setq default alt-default))
      ;; If only one cross-reference found, then make it default.
      (if (eq (length completions) 1)
-         (setq default (car (car completions))))
+         (setq default (car completions)))
      (if completions
         (let ((input (completing-read (if default
                                           (concat
@@ -1543,19 +1532,21 @@
       (setq i (+ i 1)))
     (Info-goto-node target)))
 
+(defconst Info-menu-entry-name-re "\\(?:[^:\n]\\|:[^,.;() \t\n]\\)*"
+  "Regexp that matches a menu entry name upto but not including the colon.
+Because of ambiguities, this should be concatenated with something like
+`:' and `Info-following-node-name-re'.")
+
 (defun Info-extract-menu-node-name (&optional multi-line)
   (skip-chars-forward " \t\n")
-  (let ((beg (point))
-       str)
-    (while (not (looking-at ":*[,.;() \t\n]"))
-      (skip-chars-forward "^:")
-      (forward-char 1))
-    (setq str
-         (if (looking-at ":")
-             (buffer-substring-no-properties beg (1- (point)))
-           (skip-chars-forward " \t\n")
-           (Info-following-node-name (if multi-line "^.,\t" "^.,\t\n"))))
-    (replace-regexp-in-string "[ \n]+" " " str)))
+  (let (str)
+    (when (looking-at (concat Info-menu-entry-name-re ":\\(:\\|"
+                             (Info-following-node-name-re
+                              (if multi-line "^.,\t" "^.,\t\n")) "\\)"))
+      (setq str (or (match-string 2)
+                   (buffer-substring (match-beginning 0)
+                                     (1- (match-beginning 1)))))
+      (replace-regexp-in-string "[ \n]+" " " str))))
 
 ;; No one calls this.
 ;;(defun Info-menu-item-sequence (list)
@@ -1567,7 +1558,8 @@
 (defvar Info-complete-next-re nil)
 (defvar Info-complete-cache nil)
 
-(defconst Info-node-spec-re "[^.,:(]*\\(([^)]*)[^.,:]*\\)?[,:.]"
+(defconst Info-node-spec-re
+  (concat (Info-following-node-name-re "^.,:") "[,:.]")
   "Regexp to match the text after a : until the terminating `.'.")
 
 (defun Info-complete-menu-item (string predicate action)
@@ -1594,7 +1586,7 @@
           (concat "\n\\* +" (regexp-quote string) ":") nil t)
        (let ((pattern (concat "\n\\* +\\("
                               (regexp-quote string)
-                              "[^\t\n]*?\\):" Info-node-spec-re))
+                              Info-menu-entry-name-re "\\):" 
Info-node-spec-re))
              completions)
          ;; Check the cache.
          (if (and (equal (nth 0 Info-complete-cache) Info-current-file)
@@ -2373,9 +2365,7 @@
          (save-excursion
            (goto-char (point-min))
            (while (re-search-forward "\\*note[ \n\t]*\\([^:]*\\):" nil t)
-             (setq str (buffer-substring
-                        (match-beginning 1)
-                        (1- (point))))
+             (setq str (match-string 1))
              (setq i 0)
              (while (setq i (string-match "[ \n\t]+" str i))
                (setq str (concat (substring str 0 i) " "
@@ -2798,7 +2788,7 @@
            (let* ((nbeg (match-beginning 2))
                   (nend (match-end 2))
                   (tbeg (match-beginning 1))
-                  (tag (buffer-substring tbeg (match-end 1))))
+                  (tag (match-string 1)))
              (if (string-equal tag "Node")
                  (put-text-property nbeg nend 'font-lock-face 
'info-header-node)
                (put-text-property nbeg nend 'font-lock-face 'info-header-xref)
@@ -2817,7 +2807,7 @@
                                    ((equal tag "Up") Info-up-link-keymap))))))
          (when Info-use-header-line
            (goto-char (point-min))
-           (let ((header-end (save-excursion (end-of-line) (point)))
+           (let ((header-end (line-end-position))
                  header)
              ;; If we find neither Next: nor Prev: link, show the entire
              ;; node header.  Otherwise, don't show the File: and Node:
@@ -2829,7 +2819,7 @@
                  (progn
                    (goto-char (match-beginning 1))
                    (setq header (buffer-substring (point) header-end)))
-               (if (re-search-forward "node:[ \t]*[^ \t]+[ \t]*" nil t)
+               (if (re-search-forward "node:[ \t]*[^ \t]+[ \t]*" header-end t)
                    (setq header
                          (concat "No next, prev or up links  --  "
                                  (buffer-substring (point) header-end)))
@@ -2933,9 +2923,9 @@
                   (< (- (point-max) (point)) Info-fontify-maximum-menu-size))
          (let ((n 0)
                cont)
-           (while (re-search-forward (concat "^\\* +\\([^:\t\n]*\\)\\(:"
-                                             Info-node-spec-re
-                                             "\\([ \t]*\\)\\)")
+           (while (re-search-forward
+                   (concat "^\\* +\\(" Info-menu-entry-name-re "\\)\\(:"
+                           Info-node-spec-re "\\([ \t]*\\)\\)")
                                      nil t)
              (setq n (1+ n))
              (if (and (<= n 9) (zerop (% n 3))) ; visual aids to help with 1-9 
keys
@@ -2952,11 +2942,11 @@
                      '(font-lock-face info-xref
                        mouse-face highlight))))
              (when (eq Info-hide-note-references t)
-               (put-text-property (match-beginning 2) (1- (match-end 4))
+               (put-text-property (match-beginning 2) (1- (match-end 6))
                                   'invisible t)
                ;; We need a stretchable space like :align-to but with
                ;; a minimum value.
-               (put-text-property (1- (match-end 4)) (match-end 4) 'display
+               (put-text-property (1- (match-end 6)) (match-end 6) 'display
                                   (if (>= 22 (- (match-end 1)
                                                 (match-beginning 0)))
                                       '(space :align-to 24)





reply via email to

[Prev in Thread] Current Thread [Next in Thread]