Selaa lähdekoodia

Improve the logic of the search view.

Carsten Dominik 15 vuotta sitten
vanhempi
commit
9c32266c67
4 muutettua tiedostoa jossa 115 lisäystä ja 45 poistoa
  1. 5 0
      doc/ChangeLog
  2. 3 1
      doc/org.texi
  3. 9 0
      lisp/ChangeLog
  4. 98 44
      lisp/org-agenda.el

+ 5 - 0
doc/ChangeLog

@@ -1,3 +1,8 @@
+2010-01-05  Carsten Dominik  <carsten.dominik@gmail.com>
+
+	* org.texi (Search view): Point to the docstring of
+	`org-search-view' for more details.
+
 2009-12-14  Carsten Dominik  <carsten.dominik@gmail.com>
 
 	* org.texi (Agenda commands): Document that `>' prompts for a

+ 3 - 1
doc/org.texi

@@ -6958,7 +6958,9 @@ logic.  The search string @samp{+computer +wifi -ethernet -@{8\.11[bg]@}}
 will search for note entries that contain the keywords @code{computer}
 and @code{wifi}, but not the keyword @code{ethernet}, and which are also
 not matched by the regular expression @code{8\.11[bg]}, meaning to
-exclude both 8.11b and 8.11g.
+exclude both 8.11b and 8.11g.  The first @samp{+} is necessary to turn on
+word search, other @samp{+} characters are optional.  For more details, see
+the docstring of the command @code{org-search-view}.
 
 @vindex org-agenda-text-search-extra-files
 Note that in addition to the agenda files, this command will also search

+ 9 - 0
lisp/ChangeLog

@@ -1,3 +1,12 @@
+2010-01-05  Carsten Dominik  <carsten.dominik@gmail.com>
+
+	* org-agenda.el (org-agenda-search-view-always-boolean): New option.
+	(org-agenda-search-view-search-words-only): Obsolete variable, is
+	now an alias for `org-agenda-search-view-always-boolean'.
+	(org-agenda-search-view-force-full-words): New option.
+	(org-search-view): Improve docstring, and implement a better logic
+	for Boolean and phrase searches.
+
 2010-01-04  Carsten Dominik  <carsten.dominik@gmail.com>
 
 	* org-latex.el (org-export-as-latex): Do the first letbind in the

+ 98 - 44
lisp/org-agenda.el

@@ -891,14 +891,39 @@ current display in the agenda."
   :group 'org-agenda-daily/weekly
   :type 'plist)
 
-(defcustom org-agenda-search-view-search-words-only nil
-  "Non-nil means, the search string is interpreted as individual words
-The search then looks for each word separately in each entry and
-selects entries that have matches for all words.
-When nil, matching as loose words will only take place if the first
-word is preceded by + or -.  If that is not the case, the search
-string will just be matched as a substring in the entry, but with
-each space character allowing for any whitespace, including newlines."
+(defcustom org-agenda-search-view-always-boolean nil
+  "Non-nil means, the search string is interpreted as individual parts.
+
+The search string for search view can either be interpreted as a phrase,
+or as a list of snippets that define a boolean search for a number of
+strings.
+
+When this is non-nil, the string will be split on whitespace, and each
+snippet will be searched individually, and all must match in order to
+select an entry.  If a snippet is preceeded by \"-\", the snippet
+must *not* match.  \"+\" is syntactic sugar for positive selection.
+Each snipped may be found as a full word or a partial word, but see
+the variable `org-agenda-search-view-force-full-words'.
+
+When this is nil, search will look for the entire search phrase as one,
+with each space character matching any amount of whitespace, including
+line breaks.
+
+Even when this is nil, you can still switch to Boolean search dynamically
+by preceeding the first snippet with \"+\" or \"-\".  If the first snippet
+is a regexp marked with braces like \"{abc}\", this will also switch to
+boolean search."
+  :group 'org-agenda-search-view
+  :type 'boolean)
+
+(if (fboundp 'defvaralias)
+    (defvaralias 'org-agenda-search-view-search-words-only
+      'org-agenda-search-view-always-boolean))
+
+(defcustom org-agenda-search-view-force-full-words nil
+  "Non-nil me
+ans, search words must be matches as complete words.
+When nil, they may also match part of a word."
   :group 'org-agenda-search-view
   :type 'boolean)
 
@@ -3209,8 +3234,6 @@ that when \"+Ameli\" is searched as a work, it will also match \"Ameli's\"")
 ;;;###autoload
 (defun org-search-view (&optional todo-only string edit-at)
   "Show all entries that contain words or regular expressions.
-If the first character of the search string is an asterisks,
-search only the headlines.
 
 With optional prefix argument TODO-ONLY, only consider entries that are
 TODO entries.  The argument STRING can be used to pass a default search
@@ -3218,28 +3241,36 @@ string into this function.  If EDIT-AT is non-nil, it means that the
 user should get a chance to edit this string, with cursor at position
 EDIT-AT.
 
-The search string is broken into \"words\" by splitting at whitespace.
-Depending on the variable `org-agenda-search-view-search-words-only'
-and on whether the first character in the search string is \"+\" or \"-\",
-The string is then interpreted either as a substring with variable amounts
-of whitespace, or as a list or individual words that should be matched.
-
-The default is a substring match, where each space in the search string
-can expand to an arbitrary amount of whitespace, including newlines.
-
-If matching individual words, these words are then interpreted as a
-boolean expression with logical AND.  Words prefixed with a minus must
-not occur in the entry. Words without a prefix or prefixed with a plus
-must occur in the entry.  Matching is case-insensitive and the words
-are enclosed by word delimiters.
-
-Words enclosed by curly braces are interpreted as regular expressions
-that must or must not match in the entry.
-
-If the search string starts with an asterisk, search only in headlines.
-If (possibly after the leading star) the search string starts with an
-exclamation mark, this also means to look at TODO entries only, an effect
-that can also be achieved with a prefix argument.
+The search string can be viewed either as a phrase that should be found as
+is, or it can be broken into a number of snippets, each of which must match
+in a Boolean way to select an entry.  The default depends on the variable
+`org-agenda-search-view-always-boolean'.
+Even if this is turned off (the default) you can always switch to
+Boolean search dynamically by preceeding the first word with  \"+\" or \"-\".
+
+The default is a direct search of the whole phrase, where each space in
+the search string can expand to an arbitrary amount of whitespace,
+including newlines.
+
+If using a Boolean search, the search string is split on whitespace and
+each snipped is search separately, with logical AND to select an entry.
+Words prefixed with a minus must *not* occur in the entry.  Words without
+a prefix or prefixed with a plus must occur in the entry.  Matching is
+case-insensitive.  Words are enclosed by word delimiters (i.e. they must
+match whole words, not parts of a word) if
+`org-agenda-search-view-force-full-words' is set (default is nil).
+
+Boolean search snippets enclosed by curly braces are interpreted as
+regular expressions that must or (when preceeded with \"-\") must not
+match in the entry.
+
+- If the search string starts with an asterisk, search only in headlines.
+- If (possibly after the leading star) the search string starts with an
+  exclamation mark, this also means to look at TODO entries only, an effect
+  that can also be achieved with a prefix argument.
+- If (possibly after star and exclamation mark) the seatch string starts
+  with a colon, this will mean that the snippets of the boolean search
+  must match as full words. 
 
 This command searches the agenda files, and in addition the files listed
 in `org-agenda-text-search-extra-files'."
@@ -3254,17 +3285,21 @@ in `org-agenda-text-search-extra-files'."
 		      'org-complex-heading-regexp org-complex-heading-regexp
 		      'mouse-face 'highlight
 		      'help-echo (format "mouse-2 or RET jump to location")))
+	 (full-words org-agenda-search-view-force-full-words)
 	 regexp rtn rtnall files file pos
-	 marker category tags c neg re as-words
+	 marker category tags c neg re boolean
 	 ee txt beg end words regexps+ regexps- hdl-only buffer beg1 str)
     (unless (and (not edit-at)
 		 (stringp string)
 		 (string-match "\\S-" string))
-      (setq string (read-string "[+-]Word/{Regexp} ...: "
-				(cond
-				 ((integerp edit-at) (cons string edit-at))
-				 (edit-at string))
-				'org-agenda-search-history)))
+      (setq string (read-string
+		    (if org-agenda-search-view-always-boolean
+			"[+-]Word/{Regexp} ...: "
+		      "Phrase, or [+-]Word/{Regexp} ...: ")
+		    (cond
+		     ((integerp edit-at) (cons string edit-at))
+		     (edit-at string))
+		    'org-agenda-search-history)))
     (org-set-local 'org-todo-only todo-only)
     (setq org-agenda-redo-command
 	  (list 'org-search-view (if todo-only t nil) string
@@ -3278,21 +3313,40 @@ in `org-agenda-text-search-extra-files'."
     (when (equal (string-to-char words) ?!)
       (setq todo-only t
 	    words (substring words 1)))
-    (if (or org-agenda-search-view-search-words-only
-	    (member (string-to-char string) '(?- ?+)))
-	(setq as-words t))
+    (when (equal (string-to-char words) ?:)
+      (setq full-words t
+	    words (substring words 1)))
+    (if (or org-agenda-search-view-always-boolean
+	    (member (string-to-char string) '(?- ?+ ?\{)))
+	(setq boolean t))
     (setq words (org-split-string words))
-    (if as-words
+    (when boolean
+      (let (wds w)
+	(while (setq w (pop words))
+	  (if (or (equal (substring w 0 1) "\"")
+		  (and (> (length w) 1)
+		       (member (substring w 0 1) '("+" "-"))
+		       (equal (substring w 1 2) "\"")))
+	      (while (and words (not (equal (substring w -1) "\"")))
+		(setq w (concat w " " (pop words)))))
+	  (and (string-match "\\`\\([-+]?\\)\"" w)
+	       (setq w (replace-match "\\1" nil nil w)))
+	  (and (equal (substring w -1) "\"") (setq w (substring w 0 -1)))
+	  (push w wds))
+	(setq words (nreverse wds))))
+    (if boolean
 	(mapc (lambda (w)
 		(setq c (string-to-char w))
 		(if (equal c ?-)
 		    (setq neg t w (substring w 1))
 		  (if (equal c ?+)
 		      (setq neg nil w (substring w 1))
-		  (setq neg nil)))
+		    (setq neg nil)))
 		(if (string-match "\\`{.*}\\'" w)
 		    (setq re (substring w 1 -1))
-		  (setq re (concat "\\<" (regexp-quote (downcase w)) "\\>")))
+		  (if full-words
+		      (setq re (concat "\\<" (regexp-quote (downcase w)) "\\>"))
+		    (setq re (regexp-quote (downcase w)))))
 		(if neg (push re regexps-) (push re regexps+)))
 	      words)
       (push (mapconcat (lambda (w) (regexp-quote w)) words "\\s-+")