FUEL: big refactoring of fuel-xref-mode and the fuel.xref vocab

The big difference is that links to words are now grouped by vocab and
sorted alphabetically which I think makes *fuel xref* much more usable
when you have lots of words in the list
db4
Björn Lindqvist 2014-11-06 01:20:19 +01:00 committed by John Benediktsson
parent 82c71577d2
commit 2b1e080ccd
3 changed files with 110 additions and 99 deletions

View File

@ -4,3 +4,11 @@ IN: fuel.xref.tests
{ t } [
"fuel" apropos-xref empty? not
] unit-test
{ t } [
"fuel" vocab-xref length 2 =
] unit-test
{ { } } [
"i-dont-exist!" callees-xref
] unit-test

View File

@ -2,9 +2,9 @@
! See http://factorcode.org/license.txt for BSD license.
USING: accessors arrays assocs definitions help.topics io.pathnames
kernel math math.order memoize namespaces sequences sets sorting
tools.completion tools.crossref vocabs vocabs.parser vocabs.hierarchy
words ;
kernel math math.order math.statistics memoize namespaces sequences sets
sorting tools.completion tools.crossref vocabs vocabs.parser
vocabs.hierarchy words ;
IN: fuel.xref
@ -12,7 +12,7 @@ IN: fuel.xref
: normalize-loc ( seq -- path line )
[ dup length 0 > [ first absolute-path ] [ drop f ] if ]
[ dup length 1 > [ second ] [ drop 1 ] if ] bi ;
[ dup length 1 > [ second ] when ] bi ;
: get-loc ( object -- loc ) normalize-loc 2array ;
@ -22,12 +22,15 @@ IN: fuel.xref
: vocab>xref ( vocab -- xref )
dup dup >vocab-link where normalize-loc 4array ;
: sort-xrefs ( seq -- seq' )
[ first ] sort-with ;
: format-xrefs ( seq -- seq' )
[ word? ] filter [ word>xref ] map ;
: group-xrefs ( xrefs -- xrefs' )
natural-sort [ second 1array ] collect-by
! Put the path to the vocab in the key
[ [ [ third ] map-find drop suffix ] keep ] assoc-map
>alist natural-sort ;
: filter-prefix ( seq prefix -- seq )
[ drop-prefix nip length 0 = ] curry filter members ;
@ -44,13 +47,15 @@ MEMO: (vocab-words) ( name -- seq )
PRIVATE>
: callers-xref ( word -- seq ) usage format-xrefs sort-xrefs ;
: callers-xref ( word -- seq ) usage format-xrefs group-xrefs ;
: callees-xref ( word -- seq ) uses format-xrefs sort-xrefs ;
: callees-xref ( word -- seq ) uses format-xrefs group-xrefs ;
: apropos-xref ( str -- seq ) words-matching keys format-xrefs ;
: apropos-xref ( str -- seq ) words-matching keys format-xrefs group-xrefs ;
: vocab-xref ( vocab -- seq ) words format-xrefs ;
: vocab-xref ( vocab -- seq )
dup ".private" append [ words ] bi@ append
format-xrefs group-xrefs ;
: word-location ( word -- loc ) where get-loc ;

View File

@ -51,12 +51,6 @@ cursor at the first ocurrence of the used word."
:group 'fuel-faces
:group 'fuel)
(defface fuel-xref-vocab-face '((t))
"Vocabulary names in cross-reference buffers."
:group 'fuel-xref
:group 'fuel-faces
:group 'fuel)
(defvar-local fuel-xref--word nil)
@ -71,7 +65,7 @@ cursor at the first ocurrence of the used word."
(let ((file (button-get button 'file))
(line (button-get button 'line)))
(when (not file)
(error "No file for this ref"))
(error "No file for this ref (it's probably a primitive)"))
(when (not (file-readable-p file))
(error "File '%s' is not readable" file))
(let ((word fuel-xref--word))
@ -88,6 +82,11 @@ cursor at the first ocurrence of the used word."
;;; The xref buffer:
(defun fuel-xref--eval<x--y> (arg word context)
"A helper for the very common task of calling an ( x -- y ) factor word."
(let ((cmd (list :fuel* (list (list arg word)) context)))
(fuel-eval--retort-result (fuel-eval--send/wait cmd))))
(defun fuel-xref--buffer ()
(or (get-buffer "*fuel xref*")
(with-current-buffer (get-buffer-create "*fuel xref*")
@ -95,103 +94,104 @@ cursor at the first ocurrence of the used word."
(fuel-popup-mode)
(current-buffer))))
(defvar fuel-xref--help-string
"(Press RET or click to follow crossrefs, or h for help on word at point)")
(defun fuel-xref--pluralize-count (count item)
(let ((fmt (if (= count 1) "%d %s" "%d %ss")))
(format fmt count item)))
(defun fuel-xref--title (word cc count thing)
(put-text-property 0 (length word) 'font-lock-face 'bold word)
(cond ((zerop count) (format "No known %s %s %s" thing cc word))
((= 1 count) (format "1 %s %s %s:" thing cc word))
(t (format "%s %ss %s %s:" count thing cc word))))
(defun fuel-xref--insert-link (title file line-num)
(insert-text-button title
:type 'fuel-xref--button-type
'help-echo (format "File: %s (%s)" file line-num)
'file file
'line line-num))
(defun fuel-xref--insert-ref (ref &optional no-vocab)
(when (and (stringp (cl-first ref))
(stringp (cl-third ref))
(numberp (cl-fourth ref)))
(insert " ")
(insert-text-button (cl-first ref)
:type 'fuel-xref--button-type
'help-echo (format "File: %s (%s)"
(cl-third ref)
(cl-fourth ref))
'file (cl-third ref)
'line (cl-fourth ref))
(when (and (not no-vocab) (stringp (cl-second ref)))
(insert (format " (in %s)" (cl-second ref))))
(newline)
t))
(defun fuel-xref--insert-word (word vocab file line-num)
(insert " ")
(fuel-xref--insert-link word file line-num)
(insert (if line-num (format " line %s" line-num)
" primitive"))
(newline))
(defun fuel-xref--fill-buffer (word cc refs &optional no-vocab app thing)
(defun fuel-xref--insert-vocab-words (vocab-def xrefs)
(destructuring-bind (vocab file) vocab-def
(insert "in ")
(fuel-xref--insert-link (or vocab "unknown vocabs") file 1)
(let ((count-str (fuel-xref--pluralize-count (length xrefs) "word")))
(insert (format " %s:\n" count-str))))
(dolist (xref xrefs)
(apply 'fuel-xref--insert-word xref))
(newline))
(defun fuel-xref--display-word-groups (search-str cc xref-groups)
"Should be called in a with-current-buffer context"
(let ((inhibit-read-only t)
(count 0))
(with-current-buffer (fuel-xref--buffer)
(let ((start (if app (goto-char (point-max))
(erase-buffer)
(point-min))))
(dolist (ref refs)
(when (fuel-xref--insert-ref ref no-vocab) (setq count (1+ count))))
(newline)
(goto-char start)
(save-excursion
(insert (fuel-xref--title word cc count (or thing "word")) "\n\n"))
count))))
(title-str (format "Words %s %s:\n\n" cc search-str)))
(erase-buffer)
(insert (propertize title-str 'font-lock-face 'bold))
(dolist (group xref-groups)
(apply 'fuel-xref--insert-vocab-words group)))
(goto-char (point-min))
(message "")
(fuel-popup--display (current-buffer)))
(defun fuel-xref--fill-and-display (word cc refs &optional no-vocab thing)
(let ((count (fuel-xref--fill-buffer word cc refs no-vocab nil (or thing "word"))))
(if (zerop count)
(error (fuel-xref--title word cc 0 (or thing "word")))
(message "")
(fuel-popup--display (fuel-xref--buffer)))))
(defun fuel-xref--display-vocabs (search-str cc xrefs)
"Should be called in a with-current-buffer context"
(put-text-property 0 (length search-str) 'font-lock-face 'bold search-str)
(let* ((inhibit-read-only t)
(xrefs (remove-if (lambda (el) (not (nth 2 el))) xrefs))
(count-str (fuel-xref--pluralize-count (length xrefs) "vocab"))
(title-str (format "%s %s %s:\n\n" count-str cc search-str)))
(erase-buffer)
(insert title-str)
(loop for (vocab _ file line-num) in xrefs do
(insert " ")
(fuel-xref--insert-link vocab file line-num)
(newline)))
(goto-char (point-min))
(message "")
(fuel-popup--display (current-buffer)))
(defun fuel-xref--callers (word)
(let ((cmd `(:fuel* (((:quote ,word) fuel-callers-xref)))))
(fuel-eval--retort-result (fuel-eval--send/wait cmd))))
(fuel-xref--eval<x--y> (list :quote word) 'fuel-callers-xref ""))
(defun fuel-xref--show-callers (word)
(let ((refs (fuel-xref--callers word)))
(with-current-buffer (fuel-xref--buffer) (setq fuel-xref--word word))
(fuel-xref--fill-and-display word "using" refs)))
(let ((res (fuel-xref--callers word)))
(with-current-buffer (fuel-xref--buffer)
(setq fuel-xref--word word)
(fuel-xref--display-word-groups word "calling" res))))
(defun fuel-xref--word-callers-files (word)
(mapcar 'cl-third (fuel-xref--callers word)))
(mapcar 'cadar (fuel-xref--callers word)))
(defun fuel-xref--show-callees (word)
(let* ((cmd `(:fuel* (((:quote ,word) fuel-callees-xref))))
(res (fuel-eval--retort-result (fuel-eval--send/wait cmd))))
(with-current-buffer (fuel-xref--buffer) (setq fuel-xref--word nil))
(fuel-xref--fill-and-display word "used by" res)))
(let ((res (fuel-xref--eval<x--y> (list :quote word) 'fuel-callees-xref "")))
(with-current-buffer (fuel-xref--buffer)
(setq fuel-xref--word nil)
(fuel-xref--display-word-groups word "used by" res))))
(defun fuel-xref--apropos (str)
(let* ((cmd `(:fuel* ((,str fuel-apropos-xref))))
(res (fuel-eval--retort-result (fuel-eval--send/wait cmd))))
(with-current-buffer (fuel-xref--buffer) (setq fuel-xref--word nil))
(fuel-xref--fill-and-display str "containing" res)))
(let ((res (fuel-xref--eval<x--y> str 'fuel-apropos-xref "")))
(with-current-buffer (fuel-xref--buffer)
(setq fuel-xref--word nil)
(fuel-xref--display-word-groups str "containing" res))))
(defun fuel-xref--show-vocab (vocab &optional app)
(let* ((cmd `(:fuel* ((,vocab fuel-vocab-xref)) ,vocab))
(res (fuel-eval--retort-result (fuel-eval--send/wait cmd))))
(with-current-buffer (fuel-xref--buffer) (setq fuel-xref--word nil))
(fuel-xref--fill-buffer vocab "in vocabulary" res t app)))
(defun fuel-xref--show-vocab-words (vocab &optional private)
(fuel-xref--show-vocab vocab)
(when private
(fuel-xref--show-vocab (format "%s.private" (substring-no-properties vocab))
t))
(fuel-popup--display (fuel-xref--buffer))
(goto-char (point-min)))
(defun fuel-xref--show-vocab-words (vocab)
(let ((res (fuel-xref--eval<x--y> vocab 'fuel-vocab-xref vocab)))
(with-current-buffer (fuel-xref--buffer)
(setq fuel-xref--word nil)
(fuel-xref--display-word-groups vocab "in vocabulary" res))))
(defun fuel-xref--show-vocab-usage (vocab)
(let* ((cmd `(:fuel* ((,vocab fuel-vocab-usage-xref))))
(res (fuel-eval--retort-result (fuel-eval--send/wait cmd))))
(with-current-buffer (fuel-xref--buffer) (setq fuel-xref--word nil))
(fuel-xref--fill-and-display vocab "using" res t "vocab")))
(let ((res (fuel-xref--eval<x--y> vocab 'fuel-vocab-usage-xref "")))
(with-current-buffer (fuel-xref--buffer)
(setq fuel-xref--word nil)
(fuel-xref--display-vocabs vocab "using" res))))
(defun fuel-xref--show-vocab-uses (vocab)
(let* ((cmd `(:fuel* ((,vocab fuel-vocab-uses-xref))))
(res (fuel-eval--retort-result (fuel-eval--send/wait cmd))))
(with-current-buffer (fuel-xref--buffer) (setq fuel-xref--word nil))
(fuel-xref--fill-and-display vocab "used by" res t "vocab")))
(let ((res (fuel-xref--eval<x--y> vocab 'fuel-vocab-uses-xref "")))
(with-current-buffer (fuel-xref--buffer)
(setq fuel-xref--word nil)
(fuel-xref--display-vocabs vocab "used by" res))))
;;; User commands:
@ -258,11 +258,10 @@ With prefix argument, force reload of vocabulary list."
"Show a list of words in current file.
With prefix argument, ask for the vocab."
(interactive "P")
(let ((vocab (or (and (not arg) (factor-current-vocab))
(let ((vocab (or (and (not arg) (factor-find-in))
(fuel-completion--read-vocab nil))))
(when vocab
(fuel-xref--show-vocab-words vocab
(factor-file-has-private)))))
(fuel-xref--show-vocab-words vocab))))
@ -284,7 +283,6 @@ With prefix argument, ask for the vocab."
(set-keymap-parent fuel-xref-mode-map button-buffer-map)
(define-key fuel-xref-mode-map "h" 'fuel-xref-show-help)
(font-lock-add-keywords nil '(("(in \\(.+\\))" 1 'fuel-xref-vocab-face)))
(setq buffer-read-only t))