diff options
| author | David Kastrup | 2004-06-03 19:53:53 +0000 |
|---|---|---|
| committer | David Kastrup | 2004-06-03 19:53:53 +0000 |
| commit | d0923e437b00977c09b7ef1a54677ccf4d3e00ab (patch) | |
| tree | 99daa29cf40d57c0947876ea1485a85ac429518f | |
| parent | 67c41d86aa7ba1b3bf24d62bf87a3668bfac4f0e (diff) | |
| download | emacs-d0923e437b00977c09b7ef1a54677ccf4d3e00ab.tar.gz emacs-d0923e437b00977c09b7ef1a54677ccf4d3e00ab.zip | |
(woman-mapcan): More concise code.
(woman-topic-all-completions, woman-topic-all-completions-1)
(woman-topic-all-completions-merge): Replace by a simpler and
much faster implementation based on O(n log n) sort/merge instead
of the old O(n^2) behavior.
| -rw-r--r-- | lisp/woman.el | 104 |
1 files changed, 53 insertions, 51 deletions
diff --git a/lisp/woman.el b/lisp/woman.el index d69c631f27b..ba511bca1ae 100644 --- a/lisp/woman.el +++ b/lisp/woman.el | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | ;;; woman.el --- browse UN*X manual pages `wo (without) man' | 1 | ;;; woman.el --- browse UN*X manual pages `wo (without) man' |
| 2 | 2 | ||
| 3 | ;; Copyright (C) 2000, 2002 Free Software Foundation, Inc. | 3 | ;; Copyright (C) 2000, 2002, 2004 Free Software Foundation, Inc. |
| 4 | 4 | ||
| 5 | ;; Author: Francis J. Wright <F.J.Wright@qmul.ac.uk> | 5 | ;; Author: Francis J. Wright <F.J.Wright@qmul.ac.uk> |
| 6 | ;; Maintainer: Francis J. Wright <F.J.Wright@qmul.ac.uk> | 6 | ;; Maintainer: Francis J. Wright <F.J.Wright@qmul.ac.uk> |
| @@ -402,6 +402,7 @@ | |||
| 402 | ;; Alexander Hinds <ahinds@thegrid.net> | 402 | ;; Alexander Hinds <ahinds@thegrid.net> |
| 403 | ;; Stefan Hornburg <sth@hacon.de> | 403 | ;; Stefan Hornburg <sth@hacon.de> |
| 404 | ;; Theodore Jump <tjump@cais.com> | 404 | ;; Theodore Jump <tjump@cais.com> |
| 405 | ;; David Kastrup <dak@gnu.org> | ||
| 405 | ;; Paul Kinnucan <paulk@mathworks.com> | 406 | ;; Paul Kinnucan <paulk@mathworks.com> |
| 406 | ;; Jonas Linde <jonas@init.se> | 407 | ;; Jonas Linde <jonas@init.se> |
| 407 | ;; Andrew McRae <andrewm@optimation.co.nz> | 408 | ;; Andrew McRae <andrewm@optimation.co.nz> |
| @@ -438,7 +439,8 @@ | |||
| 438 | "Return concatenated list of FN applied to successive `car' elements of X. | 439 | "Return concatenated list of FN applied to successive `car' elements of X. |
| 439 | FN must return a list, cons or nil. Useful for splicing into a list." | 440 | FN must return a list, cons or nil. Useful for splicing into a list." |
| 440 | ;; Based on the Standard Lisp function MAPCAN but with args swapped! | 441 | ;; Based on the Standard Lisp function MAPCAN but with args swapped! |
| 441 | (and x (nconc (funcall fn (car x)) (woman-mapcan fn (cdr x))))) | 442 | ;; More concise implementation than the recursive one. -- dak |
| 443 | (apply #'nconc (mapcar fn x))) | ||
| 442 | 444 | ||
| 443 | (defun woman-parse-colon-path (paths) | 445 | (defun woman-parse-colon-path (paths) |
| 444 | "Explode search path string PATHS into a list of directory names. | 446 | "Explode search path string PATHS into a list of directory names. |
| @@ -1367,15 +1369,16 @@ The cdr of each alist element is the path-index / filename." | |||
| 1367 | ;; is re-processed by `woman-topic-all-completions-merge'. | 1369 | ;; is re-processed by `woman-topic-all-completions-merge'. |
| 1368 | (let (dir files (path-index 0)) ; indexing starts at zero | 1370 | (let (dir files (path-index 0)) ; indexing starts at zero |
| 1369 | (while path | 1371 | (while path |
| 1370 | (setq dir (car path) | 1372 | (setq dir (pop path)) |
| 1371 | path (cdr path)) | ||
| 1372 | (if (woman-not-member dir path) ; use each directory only once! | 1373 | (if (woman-not-member dir path) ; use each directory only once! |
| 1373 | (setq files | 1374 | (push (woman-topic-all-completions-1 dir path-index) |
| 1374 | (nconc files | 1375 | files)) |
| 1375 | (woman-topic-all-completions-1 dir path-index)))) | ||
| 1376 | (setq path-index (1+ path-index))) | 1376 | (setq path-index (1+ path-index))) |
| 1377 | ;; Uniquefy topics: | 1377 | ;; Uniquefy topics: |
| 1378 | (woman-topic-all-completions-merge files))) | 1378 | ;; Concate all lists with a single nconc call to |
| 1379 | ;; avoid retraversing the first lists repeatedly -- dak | ||
| 1380 | (woman-topic-all-completions-merge | ||
| 1381 | (apply #'nconc files)))) | ||
| 1379 | 1382 | ||
| 1380 | (defun woman-topic-all-completions-1 (dir path-index) | 1383 | (defun woman-topic-all-completions-1 (dir path-index) |
| 1381 | "Return an alist of the man topics in directory DIR with index PATH-INDEX. | 1384 | "Return an alist of the man topics in directory DIR with index PATH-INDEX. |
| @@ -1388,55 +1391,54 @@ of the first `woman-cache-level' elements from the following list: | |||
| 1388 | ;; unnecessary. So let us assume that `woman-file-regexp' will | 1391 | ;; unnecessary. So let us assume that `woman-file-regexp' will |
| 1389 | ;; filter out any directories, which probably should not be there | 1392 | ;; filter out any directories, which probably should not be there |
| 1390 | ;; anyway, i.e. it is a user error! | 1393 | ;; anyway, i.e. it is a user error! |
| 1391 | (mapcar | 1394 | ;; |
| 1392 | (lambda (file) | 1395 | ;; Don't sort files: we do that when merging, anyway. -- dak |
| 1393 | (cons | 1396 | (let (newlst (lst (directory-files dir nil woman-file-regexp t)) |
| 1394 | (file-name-sans-extension | 1397 | ;; Make an explicit regexp for stripping extension and |
| 1395 | (if (string-match woman-file-compression-regexp file) | 1398 | ;; compression extension: file-name-sans-extension is a |
| 1396 | (file-name-sans-extension file) | 1399 | ;; far too costly function. -- dak |
| 1397 | file)) | 1400 | (ext (format "\\(\\.[^.\\/]*\\)?\\(%s\\)?\\'" |
| 1398 | (if (> woman-cache-level 1) | 1401 | woman-file-compression-regexp))) |
| 1399 | (cons | 1402 | ;; Use a loop instead of mapcar in order to avoid the speed |
| 1400 | path-index | 1403 | ;; penalty of binding function arguments. -- dak |
| 1401 | (if (> woman-cache-level 2) | 1404 | (dolist (file lst newlst) |
| 1402 | (cons file nil)))))) | 1405 | (push |
| 1403 | (directory-files dir nil woman-file-regexp))) | 1406 | (cons |
| 1407 | (if (string-match ext file) | ||
| 1408 | (substring file 0 (match-beginning 0)) | ||
| 1409 | file) | ||
| 1410 | (and (> woman-cache-level 1) | ||
| 1411 | (cons | ||
| 1412 | path-index | ||
| 1413 | (and (> woman-cache-level 2) | ||
| 1414 | (list file))))) | ||
| 1415 | newlst)))) | ||
| 1404 | 1416 | ||
| 1405 | (defun woman-topic-all-completions-merge (alist) | 1417 | (defun woman-topic-all-completions-merge (alist) |
| 1406 | "Merge the alist ALIST so that the keys are unique. | 1418 | "Merge the alist ALIST so that the keys are unique. |
| 1407 | Also make each path-info component into a list. | 1419 | Also make each path-info component into a list. |
| 1408 | \(Note that this function changes the value of ALIST.)" | 1420 | \(Note that this function changes the value of ALIST.)" |
| 1409 | ;; Intended to be fast by avoiding recursion and list copying. | 1421 | ;; Replaces unreadably "optimized" O(n^2) implementation. |
| 1410 | (if (> woman-cache-level 1) | 1422 | ;; Instead we use sorting to merge stuff efficiently. -- dak |
| 1411 | (let ((newalist alist)) | 1423 | (let (elt newalist) |
| 1412 | (while newalist | 1424 | ;; Sort list into reverse order |
| 1413 | (let ((tail newalist) (topic (car (car newalist)))) | 1425 | (setq alist (sort alist (lambda(x y) (string< (car y) (car x))))) |
| 1414 | ;; Make the path-info into a list: | 1426 | ;; merge duplicate keys. |
| 1415 | (setcdr (car newalist) (list (cdr (car newalist)))) | 1427 | (if (> woman-cache-level 1) |
| 1416 | (while tail | 1428 | (while alist |
| 1417 | (while (and tail (not (string= topic (car (car (cdr tail)))))) | 1429 | (setq elt (pop alist)) |
| 1418 | (setq tail (cdr tail))) | 1430 | (if (equal (car elt) (caar newalist)) |
| 1419 | (if tail ; merge path-info into (car newalist) | 1431 | (unless (member (cdr elt) (cdar newalist)) |
| 1420 | (let ((path-info (cdr (car (cdr tail))))) | 1432 | (setcdr (car newalist) (cons (cdr elt) |
| 1421 | (if (member path-info (cdr (car newalist))) | 1433 | (cdar newalist)))) |
| 1422 | () | 1434 | (setcdr elt (list (cdr elt))) |
| 1423 | ;; Make the path-info into a list: | 1435 | (push elt newalist))) |
| 1424 | (nconc (car newalist) (list path-info))) | ||
| 1425 | (setcdr tail (cdr (cdr tail)))) | ||
| 1426 | )) | ||
| 1427 | (setq newalist (cdr newalist)))) | ||
| 1428 | alist) | ||
| 1429 | ;; woman-cache-level = 1 => elements are single-element lists ... | 1436 | ;; woman-cache-level = 1 => elements are single-element lists ... |
| 1430 | (while (and alist (member (car alist) (cdr alist))) | 1437 | (while alist |
| 1431 | (setq alist (cdr alist))) | 1438 | (setq elt (pop alist)) |
| 1432 | (if alist | 1439 | (unless (equal (car elt) (caar newalist)) |
| 1433 | (let ((newalist alist) cdr_alist) | 1440 | (push elt newalist)))) |
| 1434 | (while (setq cdr_alist (cdr alist)) | 1441 | newalist)) |
| 1435 | (if (not (member (car cdr_alist) (cdr cdr_alist))) | ||
| 1436 | (setq alist cdr_alist) | ||
| 1437 | (setcdr alist (cdr cdr_alist))) | ||
| 1438 | ) | ||
| 1439 | newalist)))) | ||
| 1440 | 1442 | ||
| 1441 | (defun woman-file-name-all-completions (topic) | 1443 | (defun woman-file-name-all-completions (topic) |
| 1442 | "Return an alist of the files in all man directories that match TOPIC." | 1444 | "Return an alist of the files in all man directories that match TOPIC." |