2010-01-06 05:38:06 +00:00
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;;;
|
|
|
|
;;; ghc.el
|
|
|
|
;;;
|
|
|
|
|
|
|
|
;; Author: Kazu Yamamoto <Kazu@Mew.org>
|
|
|
|
;; Created: Sep 25, 2009
|
|
|
|
|
|
|
|
(require 'ghc-func)
|
|
|
|
(require 'ghc-comp)
|
2013-04-02 06:20:20 +00:00
|
|
|
(require 'ghc-info)
|
2010-01-06 05:38:06 +00:00
|
|
|
|
2015-08-17 19:36:21 +00:00
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;;;
|
|
|
|
;;; Customize Variables
|
|
|
|
;;;
|
|
|
|
|
|
|
|
(defcustom ghc-doc-browser-function #'browse-url
|
|
|
|
"Function used to browse documentation."
|
|
|
|
:type '(radio (function-item browse-url)
|
|
|
|
(function-item ghc-browse-url-safari))
|
|
|
|
:group 'ghc-mod)
|
|
|
|
|
2010-01-06 05:38:06 +00:00
|
|
|
;;; Code:
|
|
|
|
|
2010-03-10 07:51:42 +00:00
|
|
|
(defun ghc-browse-document (&optional haskell-org)
|
|
|
|
(interactive "P")
|
2013-04-02 06:20:20 +00:00
|
|
|
(let ((mod0 (ghc-extract-module))
|
2014-03-28 03:55:17 +00:00
|
|
|
(expr0 (ghc-things-at-point))
|
|
|
|
pkg-ver-path mod expr info)
|
|
|
|
(if (or mod0 (not expr0))
|
|
|
|
(setq mod (ghc-read-module-name mod0))
|
|
|
|
(setq expr (ghc-read-expression expr0))
|
|
|
|
(setq info (ghc-get-info expr0))
|
|
|
|
(setq mod (ghc-extact-module-from-info info)))
|
2014-11-29 03:11:06 +00:00
|
|
|
(setq pkg-ver-path (and mod (ghc-resolve-document-path mod)))
|
|
|
|
(if pkg-ver-path
|
2014-03-28 03:55:17 +00:00
|
|
|
(ghc-display-document pkg-ver-path mod haskell-org expr)
|
2015-08-14 07:19:43 +00:00
|
|
|
(message "No documentation found"))))
|
2010-01-06 05:38:06 +00:00
|
|
|
|
2014-03-28 03:55:17 +00:00
|
|
|
(ghc-defstruct pkg-ver-path pkg ver path)
|
|
|
|
|
|
|
|
(defun ghc-resolve-document-path (mod)
|
2010-01-06 05:38:06 +00:00
|
|
|
(with-temp-buffer
|
2014-06-16 01:21:15 +00:00
|
|
|
(ghc-call-process ghc-module-command nil t nil "doc" mod)
|
2010-01-06 05:38:06 +00:00
|
|
|
(goto-char (point-min))
|
2014-03-28 03:55:17 +00:00
|
|
|
(when (looking-at "^\\([^ ]+\\)-\\([0-9]*\\(\\.[0-9]+\\)*\\) \\(.*\\)$")
|
|
|
|
(ghc-make-pkg-ver-path
|
2010-10-28 07:24:45 +00:00
|
|
|
:pkg (match-string-no-properties 1)
|
2014-03-28 03:55:17 +00:00
|
|
|
:ver (match-string-no-properties 2)
|
|
|
|
:path (match-string-no-properties 4)))))
|
2010-01-06 05:38:06 +00:00
|
|
|
|
2010-06-14 03:03:14 +00:00
|
|
|
(defconst ghc-doc-local-format "file://%s/%s.html")
|
|
|
|
(defconst ghc-doc-hackage-format
|
2010-10-27 19:45:15 +00:00
|
|
|
"http://hackage.haskell.org/packages/archive/%s/%s/doc/html/%s.html")
|
2010-03-10 07:51:42 +00:00
|
|
|
|
2015-08-19 06:37:41 +00:00
|
|
|
(defun ghc-browse-url-safari (uri &rest _args)
|
2015-08-17 19:36:21 +00:00
|
|
|
"Open a URI in Safari using AppleScript. This preserves anchors."
|
|
|
|
(let ((script (format "
|
|
|
|
tell application \"Safari\"
|
|
|
|
open location \"%s\"
|
|
|
|
activate
|
|
|
|
end tell" uri)))
|
|
|
|
(do-applescript script)))
|
|
|
|
|
2014-03-28 03:55:17 +00:00
|
|
|
(defun ghc-display-document (pkg-ver-path mod haskell-org &optional symbol)
|
2015-08-17 19:36:21 +00:00
|
|
|
(let* ((pkg (ghc-pkg-ver-path-get-pkg pkg-ver-path))
|
|
|
|
(mod- (ghc-replace-character mod ?. ?-))
|
2014-03-28 03:55:17 +00:00
|
|
|
(ver (ghc-pkg-ver-path-get-ver pkg-ver-path))
|
|
|
|
(path (ghc-pkg-ver-path-get-path pkg-ver-path))
|
|
|
|
(pkg-with-ver (format "%s-%s" pkg ver))
|
|
|
|
(local (format ghc-doc-local-format path mod-))
|
|
|
|
(remote (format ghc-doc-hackage-format pkg ver mod-))
|
|
|
|
(file (format "%s/%s.html" path mod-))
|
|
|
|
(url0 (if (or haskell-org (not (file-exists-p file))) remote local))
|
|
|
|
(url (if symbol (ghc-add-anchor url0 symbol) url0)))
|
2015-08-17 19:36:21 +00:00
|
|
|
(funcall ghc-doc-browser-function url)))
|
2010-01-06 05:38:06 +00:00
|
|
|
|
2013-04-02 06:20:20 +00:00
|
|
|
(defun ghc-add-anchor (url symbol)
|
|
|
|
(let ((case-fold-search nil))
|
|
|
|
(if (string-match "^[A-Z]" symbol)
|
|
|
|
(concat url "#t:" symbol)
|
|
|
|
(if (string-match "^[a-z]" symbol)
|
|
|
|
(concat url "#v:" symbol)
|
|
|
|
(concat url "#v:" (ghc-url-encode symbol))))))
|
|
|
|
|
|
|
|
(defun ghc-url-encode (symbol)
|
|
|
|
(let ((len (length symbol))
|
|
|
|
(i 0)
|
|
|
|
acc)
|
|
|
|
(while (< i len)
|
2014-04-19 11:09:47 +00:00
|
|
|
(ghc-add acc (format "-%d-" (aref symbol i)))
|
2013-04-02 06:20:20 +00:00
|
|
|
(setq i (1+ i)))
|
|
|
|
(apply 'concat (nreverse acc))))
|
|
|
|
|
|
|
|
(defun ghc-extact-module-from-info (info)
|
2014-11-29 03:11:06 +00:00
|
|
|
(when (string-match "[`\u2018]\\([^'\u2019]+\\)['\u2019]" info)
|
2013-04-02 06:20:20 +00:00
|
|
|
(match-string 1 info)))
|
|
|
|
|
2010-01-06 05:38:06 +00:00
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
|
|
(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))
|
|
|
|
|
2010-05-04 07:35:40 +00:00
|
|
|
(defun ghc-read-module-name (def)
|
|
|
|
(read-from-minibuffer "Module name: " def ghc-input-map))
|
|
|
|
|
2013-05-20 08:04:18 +00:00
|
|
|
(defun ghc-read-expression (def)
|
2015-08-14 07:19:43 +00:00
|
|
|
(read-from-minibuffer "Identifier: " def ghc-input-map))
|
2013-05-20 08:04:18 +00:00
|
|
|
|
2010-05-04 07:35:40 +00:00
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
|
|
(defun ghc-extract-module ()
|
|
|
|
(interactive)
|
|
|
|
(save-excursion
|
|
|
|
(beginning-of-line)
|
|
|
|
(if (looking-at "^\\(import\\|module\\) +\\(qualified +\\)?\\([^ (\n]+\\)")
|
|
|
|
(match-string-no-properties 3))))
|
|
|
|
|
2010-10-27 18:55:31 +00:00
|
|
|
(provide 'ghc-doc)
|