Path: blob/master/elisp/emacs-for-python/yasnippet/dropdown-list.el
990 views
;;; dropdown-list.el --- Drop-down menu interface1;;2;; Filename: dropdown-list.el3;; Description: Drop-down menu interface4;; Author: Jaeyoun Chung [[email protected]]5;; Maintainer:6;; Copyright (C) 2008 Jaeyoun Chung7;; Created: Sun Mar 16 11:20:45 2008 (Pacific Daylight Time)8;; Version:9;; Last-Updated: Sun Mar 16 12:19:49 2008 (Pacific Daylight Time)10;; By: dradams11;; Update #: 4312;; URL: http://www.emacswiki.org/cgi-bin/wiki/dropdown-list.el13;; Keywords: convenience menu14;; Compatibility: GNU Emacs 21.x, GNU Emacs 22.x15;;16;; Features that might be required by this library:17;;18;; `cl'.19;;20;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;21;;22;;; Commentary:23;;24;; According to Jaeyoun Chung, "overlay code stolen from company-mode.el."25;;26;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27;;28;;; Change log:29;;30;; 2008/03/16 dadams31;; Clean-up - e.g. use char-to-string for control chars removed by email posting.32;; Moved example usage code (define-key*, command-selector) inside the library.33;; Require cl.el at byte-compile time.34;; Added GPL statement.35;; 2008/01/06 Jaeyoun Chung36;; Posted to [email protected] at 9:10 p.m.37;;38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;39;;40;; This program is free software; you can redistribute it and/or41;; modify it under the terms of the GNU General Public License as42;; published by the Free Software Foundation; either version 3, or43;; (at your option) any later version.44;;45;; This program is distributed in the hope that it will be useful,46;; but WITHOUT ANY WARRANTY; without even the implied warranty of47;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU48;; General Public License for more details.49;;50;; You should have received a copy of the GNU General Public License51;; along with this program; see the file COPYING. If not, write to52;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth53;; Floor, Boston, MA 02110-1301, USA.54;;55;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;56;;57;;; Code:5859(eval-when-compile (require 'cl)) ;; decf, fourth, incf, loop, mapcar*6061;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6263(defface dropdown-list-face64'((t :inherit default :background "lightyellow" :foreground "black"))65"*Bla." :group 'dropdown-list)6667(defface dropdown-list-selection-face68'((t :inherit dropdown-list-face :background "purple"))69"*Bla." :group 'dropdown-list)7071(defvar dropdown-list-overlays nil)7273(defun dropdown-list-hide ()74(while dropdown-list-overlays75(delete-overlay (pop dropdown-list-overlays))))7677(defun dropdown-list-put-overlay (beg end &optional prop value prop2 value2)78(let ((ov (make-overlay beg end)))79(overlay-put ov 'window t)80(when prop81(overlay-put ov prop value)82(when prop2 (overlay-put ov prop2 value2)))83ov))8485(defun dropdown-list-line (start replacement &optional no-insert)86;; start might be in the middle of a tab, which means we need to hide the87;; tab and add spaces88(let ((end (+ start (length replacement)))89beg-point end-point90before-string after-string)91(goto-char (point-at-eol))92(if (< (current-column) start)93(progn (setq before-string (make-string (- start (current-column)) ? ))94(setq beg-point (point)))95(goto-char (point-at-bol)) ;; Emacs bug, move-to-column is wrong otherwise96(move-to-column start)97(setq beg-point (point))98(when (> (current-column) start)99(goto-char (1- (point)))100(setq beg-point (point))101(setq before-string (make-string (- start (current-column)) ? ))))102(move-to-column end)103(setq end-point (point))104(let ((end-offset (- (current-column) end)))105(when (> end-offset 0) (setq after-string (make-string end-offset ?b))))106(when no-insert107;; prevent inheriting of faces108(setq before-string (when before-string (propertize before-string 'face 'default)))109(setq after-string (when after-string (propertize after-string 'face 'default))))110(let ((string (concat before-string replacement after-string)))111(if no-insert112string113(push (dropdown-list-put-overlay beg-point end-point 'invisible t114'after-string string)115dropdown-list-overlays)))))116117(defun dropdown-list-start-column (display-width)118(let ((column (mod (current-column) (window-width)))119(width (window-width)))120(cond ((<= (+ column display-width) width) column)121((> column display-width) (- column display-width))122((>= width display-width) (- width display-width))123(t nil))))124125(defun dropdown-list-move-to-start-line (candidate-count)126(decf candidate-count)127(let ((above-line-count (save-excursion (- (vertical-motion (- candidate-count)))))128(below-line-count (save-excursion (vertical-motion candidate-count))))129(cond ((= below-line-count candidate-count)130t)131((= above-line-count candidate-count)132(vertical-motion (- candidate-count))133t)134((>= (+ below-line-count above-line-count) candidate-count)135(vertical-motion (- (- candidate-count below-line-count)))136t)137(t nil))))138139(defun dropdown-list-at-point (candidates &optional selidx)140(dropdown-list-hide)141(let* ((lengths (mapcar #'length candidates))142(max-length (apply #'max lengths))143(start (dropdown-list-start-column (+ max-length 3)))144(i -1)145(candidates (mapcar* (lambda (candidate length)146(let ((diff (- max-length length)))147(propertize148(concat (if (> diff 0)149(concat candidate (make-string diff ? ))150(substring candidate 0 max-length))151(format "%3d" (+ 2 i)))152'face (if (eql (incf i) selidx)153'dropdown-list-selection-face154'dropdown-list-face))))155candidates156lengths)))157(save-excursion158(and start159(dropdown-list-move-to-start-line (length candidates))160(loop initially (vertical-motion 0)161for candidate in candidates162do (dropdown-list-line (+ (current-column) start) candidate)163while (/= (vertical-motion 1) 0)164finally return t)))))165166(defun dropdown-list (candidates)167(let ((selection)168(temp-buffer))169(save-window-excursion170(unwind-protect171(let ((candidate-count (length candidates))172done key (selidx 0))173(while (not done)174(unless (dropdown-list-at-point candidates selidx)175(switch-to-buffer (setq temp-buffer (get-buffer-create "*selection*"))176'norecord)177(delete-other-windows)178(delete-region (point-min) (point-max))179(insert (make-string (length candidates) ?\n))180(goto-char (point-min))181(dropdown-list-at-point candidates selidx))182(setq key (read-key-sequence ""))183(cond ((and (stringp key)184(>= (aref key 0) ?1)185(<= (aref key 0) (+ ?0 (min 9 candidate-count))))186(setq selection (- (aref key 0) ?1)187done t))188((member key `(,(char-to-string ?\C-p) [up] "p"))189(setq selidx (mod (+ candidate-count (1- (or selidx 0)))190candidate-count)))191((member key `(,(char-to-string ?\C-n) [down] "n"))192(setq selidx (mod (1+ (or selidx -1)) candidate-count)))193((member key `(,(char-to-string ?\f))))194((member key `(,(char-to-string ?\r) [return]))195(setq selection selidx196done t))197(t (setq done t)))))198(dropdown-list-hide)199(and temp-buffer (kill-buffer temp-buffer)))200;; (when selection201;; (message "your selection => %d: %s" selection (nth selection candidates))202;; (sit-for 1))203selection)))204205(defun define-key* (keymap key command)206"Add COMMAND to the multiple-command binding of KEY in KEYMAP.207Use multiple times to bind different COMMANDs to the same KEY."208(define-key keymap key (combine-command command (lookup-key keymap key))))209210(defun combine-command (command defs)211"$$$$$ FIXME - no doc string"212(cond ((null defs) command)213((and (listp defs)214(eq 'lambda (car defs))215(= (length defs) 4)216(listp (fourth defs))217(eq 'command-selector (car (fourth defs))))218(unless (member `',command (cdr (fourth defs)))219(setcdr (fourth defs) (nconc (cdr (fourth defs)) `(',command))))220defs)221(t222`(lambda () (interactive) (command-selector ',defs ',command)))))223224(defvar command-selector-last-command nil "$$$$$ FIXME - no doc string")225226(defun command-selector (&rest candidates)227"$$$$$ FIXME - no doc string"228(if (and (eq last-command this-command) command-selector-last-command)229(call-interactively command-selector-last-command)230(let* ((candidate-strings231(mapcar (lambda (candidate)232(format "%s" (if (symbolp candidate)233candidate234(let ((s (format "%s" candidate)))235(if (>= (length s) 7)236(concat (substring s 0 7) "...")237s)))))238candidates))239(selection (dropdown-list candidate-strings)))240(when selection241(let ((cmd (nth selection candidates)))242(call-interactively cmd)243(setq command-selector-last-command cmd))))))244245;;;;;;;;;;;;;;;;;;;;246247(provide 'dropdown-list)248249;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;250;;; dropdown-list.el ends here251252