Make completions for commands work. Infrastructure for completions of
arbitrary args.
This commit is contained in:
		
							parent
							
								
									75ca225dc5
								
							
						
					
					
						commit
						09446473c8
					
				| 
						 | 
				
			
			@ -42,6 +42,8 @@
 | 
			
		|||
(define result-window #f)
 | 
			
		||||
(define result-frame-window #f)
 | 
			
		||||
 | 
			
		||||
(define executable-completions #f)
 | 
			
		||||
 | 
			
		||||
(define key-control-x 24)
 | 
			
		||||
(define key-o 111)
 | 
			
		||||
(define key-tab 9)
 | 
			
		||||
| 
						 | 
				
			
			@ -275,38 +277,70 @@
 | 
			
		|||
(define (run)
 | 
			
		||||
 | 
			
		||||
  (init-windows!)
 | 
			
		||||
  (init-executables-completion-set!)
 | 
			
		||||
 | 
			
		||||
  '(set-interrupt-handler interrupt/keyboard 
 | 
			
		||||
			  (lambda a 
 | 
			
		||||
			    (set! active-keyboard-interrupt a)))
 | 
			
		||||
      
 | 
			
		||||
  ;;Loop
 | 
			
		||||
  (paint)
 | 
			
		||||
  (let loop ((ch (wait-for-input)) (c-x-pressed? #f))
 | 
			
		||||
  (let loop ((ch (wait-for-input)) (c-x-pressed? #f)
 | 
			
		||||
	     (completion-select-list #f))
 | 
			
		||||
 | 
			
		||||
    (cond
 | 
			
		||||
 | 
			
		||||
     ;; Ctrl-x -> wait for next input
 | 
			
		||||
     ((= ch key-control-x)
 | 
			
		||||
      (loop (wait-for-input) #t))
 | 
			
		||||
      (loop (wait-for-input) #t completion-select-list))
 | 
			
		||||
 | 
			
		||||
     ;; user hit tab twice and pressed some other key to navigate the
 | 
			
		||||
     ;; completion-select-list
 | 
			
		||||
     ((and (focus-on-result-buffer?) completion-select-list)
 | 
			
		||||
      (if (= ch 10)
 | 
			
		||||
	  (begin
 | 
			
		||||
	    (focus-command-buffer!)
 | 
			
		||||
	    (complete-in-command-buffer 
 | 
			
		||||
	     (last (buffer-text command-buffer))
 | 
			
		||||
	     (select-list-selected-entry completion-select-list))
 | 
			
		||||
	    (loop (wait-for-input) c-x-pressed? #f))
 | 
			
		||||
	  (let ((select-list
 | 
			
		||||
		 (select-list-handle-key-press 
 | 
			
		||||
		  completion-select-list
 | 
			
		||||
		  (make-key-pressed-message (active-command) (current-result)
 | 
			
		||||
					    result-buffer ch #f))))
 | 
			
		||||
	    (paint-completion-select-list 
 | 
			
		||||
	     select-list (last (buffer-text command-buffer)))
 | 
			
		||||
	    (loop (wait-for-input) c-x-pressed? select-list))))
 | 
			
		||||
 | 
			
		||||
     ;; tab pressed twice, select completion using select-list
 | 
			
		||||
     ((and (focus-on-command-buffer?)
 | 
			
		||||
	   completion-select-list
 | 
			
		||||
	   (= ch key-tab))
 | 
			
		||||
      (focus-result-buffer!)
 | 
			
		||||
      (loop (wait-for-input) #f completion-select-list))
 | 
			
		||||
 | 
			
		||||
     ;; tab is pressed in the first place, offer completions
 | 
			
		||||
     ((and (focus-on-command-buffer?)
 | 
			
		||||
	   (= ch key-tab))
 | 
			
		||||
      (offer-completions (last (buffer-text command-buffer)))
 | 
			
		||||
      (loop (wait-for-input) #f))
 | 
			
		||||
      (let ((maybe-select-list
 | 
			
		||||
	     (offer-completions (last (buffer-text command-buffer)))))
 | 
			
		||||
	(loop (wait-for-input) #f maybe-select-list)))
 | 
			
		||||
 | 
			
		||||
     ;; F7 toggle scheme-mode / command-mode (FIXME: find a better key)
 | 
			
		||||
     ((= ch key-f7)
 | 
			
		||||
      (toggle-command/scheme-mode)
 | 
			
		||||
      (loop (wait-for-input) #f))
 | 
			
		||||
      (loop (wait-for-input) #f #f))
 | 
			
		||||
 | 
			
		||||
     ((= ch key-f8)
 | 
			
		||||
      (show-shell-screen)
 | 
			
		||||
      (paint)
 | 
			
		||||
      (loop (wait-for-input) #f))
 | 
			
		||||
      (loop (wait-for-input) #f #f))
 | 
			
		||||
 | 
			
		||||
     ;; C-x o --- toggle buffer focus
 | 
			
		||||
     ((and c-x-pressed? (= ch key-o))
 | 
			
		||||
      (toggle-buffer-focus)
 | 
			
		||||
      (loop (wait-for-input) #f))
 | 
			
		||||
      (loop (wait-for-input) #f #f))
 | 
			
		||||
 | 
			
		||||
     ;; C-x p --- insert selection
 | 
			
		||||
     ((and c-x-pressed? 
 | 
			
		||||
| 
						 | 
				
			
			@ -317,7 +351,7 @@
 | 
			
		|||
       (post-message 
 | 
			
		||||
	(history-entry-plugin (entry-data (current-history-item)))
 | 
			
		||||
	(make-selection-message (active-command) (current-result))))
 | 
			
		||||
      (loop (wait-for-input) #f))
 | 
			
		||||
      (loop (wait-for-input) #f #f))
 | 
			
		||||
 | 
			
		||||
     ((and c-x-pressed? (focus-on-result-buffer?))
 | 
			
		||||
      (let ((key-message
 | 
			
		||||
| 
						 | 
				
			
			@ -329,7 +363,7 @@
 | 
			
		|||
	 (post-message
 | 
			
		||||
	  (history-entry-plugin (entry-data (current-history-item)))
 | 
			
		||||
	  key-message))
 | 
			
		||||
	(loop (wait-for-input) #f)))
 | 
			
		||||
	(loop (wait-for-input) #f #f)))
 | 
			
		||||
 | 
			
		||||
     ;; C-x r --- redo
 | 
			
		||||
     ((and c-x-pressed? (focus-on-command-buffer?)
 | 
			
		||||
| 
						 | 
				
			
			@ -341,7 +375,7 @@
 | 
			
		|||
     
 | 
			
		||||
     ((= ch key-f2)
 | 
			
		||||
      (paint)
 | 
			
		||||
      (loop (wait-for-input) c-x-pressed?))
 | 
			
		||||
      (loop (wait-for-input) c-x-pressed? #f))
 | 
			
		||||
 | 
			
		||||
     ;; forward in result history
 | 
			
		||||
     ((= ch key-npage)
 | 
			
		||||
| 
						 | 
				
			
			@ -350,7 +384,7 @@
 | 
			
		|||
	(paint-active-command-window)
 | 
			
		||||
	(paint-result-window (entry-data (current-history-item))))
 | 
			
		||||
      (refresh-result-window)
 | 
			
		||||
      (loop (wait-for-input) c-x-pressed?))
 | 
			
		||||
      (loop (wait-for-input) c-x-pressed? #f))
 | 
			
		||||
     
 | 
			
		||||
     ;; back in result history
 | 
			
		||||
     ((= ch key-ppage)
 | 
			
		||||
| 
						 | 
				
			
			@ -359,11 +393,11 @@
 | 
			
		|||
	(paint-active-command-window)
 | 
			
		||||
	(paint-result-window (entry-data (current-history-item))))
 | 
			
		||||
      (refresh-result-window)
 | 
			
		||||
      (loop (wait-for-input) c-x-pressed?))
 | 
			
		||||
      (loop (wait-for-input) c-x-pressed? #f))
 | 
			
		||||
 | 
			
		||||
     ((and (focus-on-command-buffer?) (= ch 10))
 | 
			
		||||
      (handle-return-key)
 | 
			
		||||
      (loop (wait-for-input) c-x-pressed?))
 | 
			
		||||
      (loop (wait-for-input) c-x-pressed? #f))
 | 
			
		||||
 | 
			
		||||
     (else 
 | 
			
		||||
      (cond
 | 
			
		||||
| 
						 | 
				
			
			@ -379,7 +413,7 @@
 | 
			
		|||
	  (paint-result-window (entry-data (current-history-item)))
 | 
			
		||||
	  (move-cursor command-buffer result-buffer)
 | 
			
		||||
	  (refresh-result-window))
 | 
			
		||||
	(loop (wait-for-input) #f))
 | 
			
		||||
	(loop (wait-for-input) #f #f))
 | 
			
		||||
       (else
 | 
			
		||||
	(input command-buffer ch)
 | 
			
		||||
	(werase (app-window-curses-win command-window))
 | 
			
		||||
| 
						 | 
				
			
			@ -387,7 +421,7 @@
 | 
			
		|||
			      command-buffer)
 | 
			
		||||
	(move-cursor command-buffer result-buffer)
 | 
			
		||||
	(refresh-command-window)
 | 
			
		||||
	(loop (wait-for-input) c-x-pressed?)))))))
 | 
			
		||||
	(loop (wait-for-input) c-x-pressed? #f)))))))
 | 
			
		||||
 | 
			
		||||
(define (window-init-curses-win! window)
 | 
			
		||||
  (set-app-window-curses-win!
 | 
			
		||||
| 
						 | 
				
			
			@ -446,6 +480,19 @@
 | 
			
		|||
	      (map app-window-curses-win all-windows))
 | 
			
		||||
    (clear)))
 | 
			
		||||
 | 
			
		||||
(define (get-path-list)
 | 
			
		||||
  (cond
 | 
			
		||||
   ((getenv "PATH")
 | 
			
		||||
    => (lambda (str)
 | 
			
		||||
	 (string-tokenize 
 | 
			
		||||
	  str (char-set-difference char-set:full (char-set #\:)))))
 | 
			
		||||
   (else
 | 
			
		||||
    '("/usr/bin" "/bin" "/usr/sbin" "/sbin"))))
 | 
			
		||||
 | 
			
		||||
(define (init-executables-completion-set!)
 | 
			
		||||
  (set! executable-completions 
 | 
			
		||||
	(make-completion-set-for-executables (get-path-list))))
 | 
			
		||||
 | 
			
		||||
(define (paint-bar-1)
 | 
			
		||||
  (mvwaddstr (app-window-curses-win bar-1) 0 1 "SCSH-NUIT")
 | 
			
		||||
  (wrefresh (app-window-curses-win bar-1)))
 | 
			
		||||
| 
						 | 
				
			
			@ -741,21 +788,136 @@
 | 
			
		|||
	(loop (cdr lst)
 | 
			
		||||
	      (string-append str " " (car lst))))))
 | 
			
		||||
 | 
			
		||||
(define (completions->select-list completions)
 | 
			
		||||
(define (completions->select-list completions num-lines)
 | 
			
		||||
  (debug-message "possible completions " completions)
 | 
			
		||||
  (make-select-list
 | 
			
		||||
   (map (lambda (s) (make-unmarked-element s #f s))
 | 
			
		||||
	completions)
 | 
			
		||||
   (result-buffer-num-lines result-buffer)))
 | 
			
		||||
   num-lines))
 | 
			
		||||
 | 
			
		||||
(define (command-contains-path? command)
 | 
			
		||||
  (or (string-contains command "/")
 | 
			
		||||
      (string-contains command "~")
 | 
			
		||||
      (string-contains command "..")))
 | 
			
		||||
 | 
			
		||||
(define (executables-in-dir dir)
 | 
			
		||||
  (with-cwd dir
 | 
			
		||||
    (filter-map 
 | 
			
		||||
     (lambda (file)
 | 
			
		||||
       (and (or (file-executable? file) (file-directory? file))
 | 
			
		||||
	    (absolute-file-name file dir)))
 | 
			
		||||
     (directory-files))))
 | 
			
		||||
 | 
			
		||||
(define (complete-path path)
 | 
			
		||||
  (let ((dir (file-name-directory path)))
 | 
			
		||||
    (map (lambda (p) 
 | 
			
		||||
	   (if (string-prefix? "/" p)
 | 
			
		||||
	       p
 | 
			
		||||
	       (string-append dir p)))
 | 
			
		||||
	 (glob (string-append path "*")))))
 | 
			
		||||
 | 
			
		||||
(define (complete-executable/path command)
 | 
			
		||||
  (if (and (file-exists? command) (file-directory? command))
 | 
			
		||||
      (executables-in-dir command)
 | 
			
		||||
      (complete-path command)))
 | 
			
		||||
 | 
			
		||||
(define (command-mode-complete command)
 | 
			
		||||
  (cond
 | 
			
		||||
   ((command-contains-path? command)
 | 
			
		||||
    (let ((new
 | 
			
		||||
	   (complete-executable/path (expand-file-name command (cwd)))))
 | 
			
		||||
      (debug-message "command-mode-complete " command)
 | 
			
		||||
      new))
 | 
			
		||||
   (else
 | 
			
		||||
    (append
 | 
			
		||||
     (completions-for (command-completions) command)
 | 
			
		||||
     (completions-for-executables executable-completions command)))))
 | 
			
		||||
	
 | 
			
		||||
(define (complete-in-command-buffer command completion)
 | 
			
		||||
  (let ((rest (substring completion 
 | 
			
		||||
			 (string-length command)
 | 
			
		||||
			 (string-length completion))))
 | 
			
		||||
    (debug-message "complete-in-command-buffer "
 | 
			
		||||
		   "'" command "'; '" completion "'; "
 | 
			
		||||
		   "'" rest "'")
 | 
			
		||||
    (for-each (lambda (c)
 | 
			
		||||
		(input command-buffer (char->ascii c)))
 | 
			
		||||
	      (string->list rest))
 | 
			
		||||
    (wclrtoeol (app-window-curses-win command-window))
 | 
			
		||||
    (print-command-buffer (app-window-curses-win command-window)
 | 
			
		||||
			  command-buffer)
 | 
			
		||||
    (move-cursor command-buffer result-buffer)
 | 
			
		||||
    (refresh-command-window)))
 | 
			
		||||
 | 
			
		||||
(define (paint-completion-select-list select-list command)
 | 
			
		||||
  (let ((win (app-window-curses-win result-window)))
 | 
			
		||||
    (wclear win)
 | 
			
		||||
    (wattron win (A-BOLD))
 | 
			
		||||
    (mvwaddstr win 0 0 
 | 
			
		||||
	       (string-append "Possible completions for " command))
 | 
			
		||||
    (wattrset win (A-NORMAL))
 | 
			
		||||
    (paint-result-buffer (paint-selection-list-at select-list 0 2))
 | 
			
		||||
    (refresh-result-window)))
 | 
			
		||||
 | 
			
		||||
(define (offer-completions command)
 | 
			
		||||
  (debug-message "offer-completions " command)
 | 
			
		||||
  (let* ((tokens/cursor-list (tokenize-command command))
 | 
			
		||||
	 (command (caar tokens/cursor-list)))
 | 
			
		||||
    (call-with-values
 | 
			
		||||
	(lambda ()
 | 
			
		||||
	  (find-token-with-cursor tokens/cursor-list))
 | 
			
		||||
      (lambda (prefix arg-pos)
 | 
			
		||||
	;; hook in completer functions here
 | 
			
		||||
	(let ((completions (command-mode-complete command)))
 | 
			
		||||
	  (if (= (length completions) 1)
 | 
			
		||||
	      (begin
 | 
			
		||||
		(complete-in-command-buffer command (car completions))
 | 
			
		||||
		#f)
 | 
			
		||||
	      (let ((select-list 
 | 
			
		||||
		     (completions->select-list 
 | 
			
		||||
	  (completions-for (command-completions) command))))
 | 
			
		||||
    (wclear (app-window-curses-win result-window))
 | 
			
		||||
    (paint-result-buffer (paint-selection-list select-list))
 | 
			
		||||
    (refresh-result-window)))
 | 
			
		||||
		      completions (- (result-buffer-num-lines result-buffer) 3))))
 | 
			
		||||
		(paint-completion-select-list select-list command)
 | 
			
		||||
		select-list)))))))
 | 
			
		||||
 | 
			
		||||
(define (find-token-with-cursor tokens/cursor-list)
 | 
			
		||||
  (let lp ((lst tokens/cursor-list) (i 0))
 | 
			
		||||
    (cond
 | 
			
		||||
     ((null? lst)
 | 
			
		||||
      (values #f i))
 | 
			
		||||
     ((cdar lst)
 | 
			
		||||
      (values (caar lst) i))
 | 
			
		||||
     (else
 | 
			
		||||
      (lp (cdr lst) (+ i 1))))))
 | 
			
		||||
 | 
			
		||||
(define (command-token-delimiter? c)
 | 
			
		||||
  (char-set-contains? char-set:whitespace c))
 | 
			
		||||
 | 
			
		||||
(define (skip-delimters delimiter? chars)
 | 
			
		||||
  (let lp ((chars chars) (i 0))
 | 
			
		||||
    (cond
 | 
			
		||||
     ((null? chars) (values '() i))
 | 
			
		||||
     ((delimiter? (car chars))
 | 
			
		||||
      (lp (cdr chars) (+ i 1)))
 | 
			
		||||
     (else (values chars i)))))
 | 
			
		||||
 | 
			
		||||
(define (tokenize-command command)
 | 
			
		||||
  (let ((cursor-pos (- (buffer-pos-col command-buffer) 2))) ;; don't ask
 | 
			
		||||
    (let lp ((chars (string->list command))
 | 
			
		||||
	     (token "")
 | 
			
		||||
	     (tokens '())
 | 
			
		||||
	     (i 0))
 | 
			
		||||
      (cond 
 | 
			
		||||
       ((null? chars)
 | 
			
		||||
	(reverse (cons (cons token (= i cursor-pos)) tokens)))
 | 
			
		||||
       ((command-token-delimiter? (car chars))
 | 
			
		||||
	(call-with-values
 | 
			
		||||
	    (lambda ()
 | 
			
		||||
	      (skip-delimters command-token-delimiter? chars))
 | 
			
		||||
	  (lambda (rest skipped)
 | 
			
		||||
	    (lp rest "" (cons (cons token (= i cursor-pos)) tokens)
 | 
			
		||||
		(+ i skipped)))))
 | 
			
		||||
       (else
 | 
			
		||||
	(lp (cdr chars) (string-append token (string (car chars)))
 | 
			
		||||
	    tokens (+ i 1)))))))
 | 
			
		||||
 | 
			
		||||
(define-record-type standard-result-obj standard-result-obj
 | 
			
		||||
  (make-standard-result-obj cursor-pos-y
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue