initial import (v0.1)

This commit is contained in:
Kazu Yamamoto
2010-01-06 14:38:06 +09:00
commit 35f60507c6
8 changed files with 618 additions and 0 deletions

16
elisp/Makefile Normal file
View File

@@ -0,0 +1,16 @@
SRCS = ghc.el ghc-func.el ghc-doc.el ghc-comp.el
TEMPFILE = temp.el
all: $(TEMPFILE) ghc.el
emacs -batch -q -no-site-file -l ./$(TEMPFILE) -f ghc-compile
$(TEMPFILE):
@echo '(setq load-path (cons "." load-path))' >> $(TEMPFILE)
@echo '(defun ghc-compile () (mapcar (lambda (x) (byte-compile-file x)) (list ' >> $(TEMPFILE)
@echo $(SRCS)| sed -e 's/\(ghc[^ ]*\.el\)/"\1"/g' >> $(TEMPFILE)
@echo ')))' >> $(TEMPFILE)
clean:
rm -f *.elc $(TEMPFILE)

183
elisp/ghc-comp.el Normal file
View File

@@ -0,0 +1,183 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; ghc-comp.el
;;;
;; Author: Kazu Yamamoto <Kazu@Mew.org>
;; Created: Sep 25, 2009
;;; Code:
(require 'ghc-func)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Customize Variables
;;;
(defvar ghc-module-command "ghc-mod")
(defvar ghc-idle-timer-interval 30)
;; must be sorted
(defvar ghc-reserved-keyword-for-bol '("class" "data" "default" "import" "infix" "infixl" "infixr" "instance" "main" "module" "newtype" "type"))
;; must be sorted
(defvar ghc-reserved-keyword '("case" "deriving" "do" "else" "if" "in" "let" "module" "of" "then" "where"))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Initializer
;;;
(defvar ghc-module-names nil) ;; completion for "import"
(defvar ghc-merged-keyword nil) ;; completion for type/func/...
(defvar ghc-keyword-prefix "ghc-keyword-")
(defvar ghc-keyword-Prelude nil)
(defvar ghc-loaded-module nil)
(defun ghc-comp-init ()
(setq ghc-module-names (ghc-load-keyword "list"))
(setq ghc-keyword-Prelude (ghc-load-keyword "browse" "Prelude"))
(setq ghc-loaded-module '("Prelude"))
(ghc-merge-keywords)
(run-with-idle-timer ghc-idle-timer-interval 'repeat 'ghc-idle-timer))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Executing command
;;;
(defun ghc-load-keyword (&rest args)
(when (ghc-which ghc-module-command)
(ghc-read-lisp
(lambda ()
(let ((msg (mapconcat 'identity (cons ghc-module-command args) " ")))
(message "Executing \"%s\"..." msg)
(apply 'call-process-shell-command
ghc-module-command nil t nil (cons "-l" args))
(message "Executing \"%s\"...done" msg))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Completion
;;;
(defvar ghc-completion-buffer-name "*Completions*")
(defun ghc-complete ()
(interactive)
(if (ghc-should-scroll)
(ghc-scroll-completion-buffer)
(ghc-try-complete)))
(defun ghc-should-scroll ()
(let ((window (ghc-completion-window)))
(and (eq last-command this-command)
window (window-live-p window) (window-buffer window)
(buffer-name (window-buffer window)))))
(defun ghc-scroll-completion-buffer ()
(let ((window (ghc-completion-window)))
(with-current-buffer (window-buffer window)
(if (pos-visible-in-window-p (point-max) window)
(set-window-start window (point-min))
(save-selected-window
(select-window window)
(scroll-up))))))
(defun ghc-completion-window ()
(get-buffer-window ghc-completion-buffer-name 0))
(defun ghc-try-complete ()
(let* ((end (point))
(symbols (ghc-select-completion-symbol))
(beg (ghc-completion-start-point))
(pattern (buffer-substring-no-properties beg end))
(completion (try-completion pattern symbols)))
(cond
((eq completion t) ;; completed
) ;; do nothing
((null completion) ;; no completions
(ding))
((not (string= pattern completion)) ;; ???
(delete-region beg end)
(insert completion)
(delete-other-windows))
(t ;; multiple completions
(let* ((list0 (all-completions pattern symbols))
(list (sort list0 'string<)))
(if (> (length list) 1)
(with-output-to-temp-buffer ghc-completion-buffer-name
(display-completion-list list pattern))
(delete-other-windows)))))))
(defun ghc-select-completion-symbol ()
(cond
((or (minibufferp)
(save-excursion
(beginning-of-line)
(looking-at "import ")))
ghc-module-names)
((or (bolp)
(let ((end (point)))
(save-excursion
(beginning-of-line)
(not (search-forward " " end t)))))
ghc-reserved-keyword-for-bol)
(t ghc-merged-keyword)))
(defun ghc-completion-start-point ()
(save-excursion
(let ((beg (save-excursion (beginning-of-line) (point))))
(if (search-backward " " beg t)
(1+ (point))
beg))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Background Idle Timer
;;;
(defun ghc-idle-timer ()
(let ((mods (ghc-gather-import-modules))
keywords)
(dolist (mod mods)
(when (and (member mod ghc-module-names)
(not (member mod ghc-loaded-module)))
(setq keywords (ghc-load-keyword "browse" mod))
(when (or (consp keywords) (null keywords))
(set (intern (concat ghc-keyword-prefix mod)) keywords)
(setq ghc-loaded-module (cons mod ghc-loaded-module)))))
(ghc-merge-keywords)))
(defun ghc-gather-import-modules ()
(let ((bufs (mapcar 'buffer-name (buffer-list)))
ret)
(save-excursion
(dolist (buf bufs)
(when (string-match "\\.hs$" buf)
(set-buffer buf)
(setq ret (cons (ghc-gather-import-modules-buffer) ret)))))
(ghc-uniq-lol ret)))
(defun ghc-gather-import-modules-buffer ()
(let (ret)
(save-excursion
(goto-char (point-min))
(while (re-search-forward "^import *\\([^\n ]+\\)" nil t)
(setq ret (cons (match-string-no-properties 1) ret))
(forward-line)))
ret))
(defun ghc-merge-keywords ()
(let* ((modkeys (mapcar 'ghc-module-keyword ghc-loaded-module))
(keywords (cons ghc-reserved-keyword modkeys))
(uniq-sorted (sort (ghc-uniq-lol keywords) 'string<)))
(setq ghc-merged-keyword uniq-sorted)))
(defun ghc-module-keyword (mod)
(symbol-value (intern (concat ghc-keyword-prefix mod))))
(provide 'ghc-comp)

64
elisp/ghc-doc.el Normal file
View File

@@ -0,0 +1,64 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; ghc.el
;;;
;; Author: Kazu Yamamoto <Kazu@Mew.org>
;; Created: Sep 25, 2009
(require 'ghc-func)
(require 'ghc-comp)
;;; Code:
(defun ghc-browse-document ()
(interactive)
(let* ((mod0 (ghc-extract-module))
(mod (ghc-read-module-name mod0))
(pkg (ghc-resolve-package-name mod)))
(ghc-display-document pkg mod)))
(defun ghc-extract-module ()
(interactive)
(save-excursion
(beginning-of-line)
(when (looking-at "^import +\\([^ \n]+\\)")
(match-string-no-properties 1))))
(defun ghc-resolve-package-name (mod)
(with-temp-buffer
(call-process "ghc-pkg" nil t nil "find-module" "--simple-output" mod)
(goto-char (point-min))
(when (looking-at "^\\([^-]+\\)-")
(match-string-no-properties 1))))
(defun ghc-resolve-document-path (pkg)
(with-temp-buffer
(call-process "ghc-pkg" nil t nil "field" pkg "haddock-html")
(goto-char (point-min))
(when (looking-at "^haddock-html: \\([^ \n]+\\)$")
(match-string-no-properties 1))))
(defun ghc-display-document (pkg mod)
(when (and pkg mod)
(let* ((mod- (ghc-replace-character mod ?. ?-))
(path (ghc-resolve-document-path pkg))
(url (format "file://%s/%s.html" path mod-)))
(browse-url url))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defvar ghc-input-map nil)
(unless ghc-input-map
(setq ghc-input-map
(if (boundp 'minibuffer-local-map)
(copy-keymap minibuffer-local-map)
(make-sparse-keymap)))
(define-key ghc-input-map "\t" 'ghc-complete))
(defun ghc-read-module-name (def)
(read-from-minibuffer "Module name: " def ghc-input-map))
(provide 'ghc-doc)

41
elisp/ghc-func.el Normal file
View File

@@ -0,0 +1,41 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; ghc-func.el
;;;
;; Author: Kazu Yamamoto <Kazu@Mew.org>
;; Created: Sep 25, 2009
;;; Code:
(defun ghc-replace-character (string from to)
"Replace characters equal to FROM to TO in STRING."
(dotimes (cnt (length string) string)
(if (char-equal (aref string cnt) from)
(aset string cnt to))))
(defun ghc-which (cmd)
(catch 'loop
(dolist (dir exec-path)
(let ((path (expand-file-name cmd dir)))
(if (file-exists-p path)
(throw 'loop path))))))
(defun ghc-uniq-lol (lol)
(let ((hash (make-hash-table :test 'equal))
ret)
(dolist (lst lol)
(dolist (key lst)
(puthash key key hash)))
(maphash (lambda (key val) (setq ret (cons key ret))) hash)
ret))
(defun ghc-read-lisp (func)
(with-temp-buffer
(funcall func)
(goto-char (point-min))
(condition-case nil
(read (current-buffer))
(error ()))))
(provide 'ghc-func)

46
elisp/ghc.el Normal file
View File

@@ -0,0 +1,46 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; ghc.el
;;;
;; Author: Kazu Yamamoto <Kazu@Mew.org>
;; Created: Sep 25, 2009
;; Revised:
;; Put the following code to your "~/.emacs".
;;
;; (autoload 'ghc-init "ghc" nil t)
;; (add-hook 'haskell-mode-hook (lambda () (ghc-init)))
;;; Code:
(defvar ghc-version "0.1")
;; (require 'haskell-mode)
(require 'ghc-comp)
(require 'ghc-doc)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Customize Variables
;;;
(defvar ghc-completion-key "\e\t")
(defvar ghc-document-key "\e\C-d")
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Initializer
;;;
(defvar ghc-initialized nil)
(defun ghc-init ()
(unless ghc-initialized
(define-key haskell-mode-map ghc-completion-key 'ghc-complete)
(define-key haskell-mode-map ghc-document-key 'ghc-browse-document)
(ghc-comp-init)
(setq ghc-initialized t)))