;;; -*- lexical-binding: t -*- ;;; w3m-haddock.el --- Make browsing haddocks with w3m-mode better. ;; Copyright (C) 2014 Chris Done ;; Author: Chris Done ;; This file is not part of GNU Emacs. ;; This file is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 3, or (at your option) ;; any later version. ;; This file is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs; see the file COPYING. If not, write to ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ;; Boston, MA 02110-1301, USA. (require 'cl-lib) (require 'haskell-mode) (require 'haskell-font-lock) (declare-function w3m-buffer-title "ext:w3m") (declare-function w3m-browse-url "ext:w3m") (defvar w3m-current-url) (add-hook 'w3m-display-hook 'w3m-haddock-display) ;;;###autoload (defface w3m-haddock-heading-face '((((class color)) :inherit highlight)) "Face for quarantines." :group 'haskell) (defcustom haskell-w3m-haddock-dirs '("~/.cabal/share/doc/") "The path to your cabal documentation dir. It should contain directories of package-name-x.x. You can rebind this if you're using hsenv by adding it to your .dir-locals.el in your project root. E.g. ((haskell-mode . ((haskell-w3m-haddock-dirs . (\"/home/chris/Projects/foobar/.hsenv/cabal/share/doc\"))))) " :group 'haskell :type 'list) (defvar w3m-haddock-entry-regex "^\\(\\(data\\|type\\) \\|[a-z].* :: \\)" "Regex to match entry headings.") (defun haskell-w3m-open-haddock () "Open a haddock page in w3m." (interactive) (let* ((entries (cl-remove-if (lambda (s) (string= s "")) (apply 'append (mapcar (lambda (dir) (split-string (shell-command-to-string (concat "ls -1 " dir)) "\n")) haskell-w3m-haddock-dirs)))) (package-dir (ido-completing-read "Package: " entries))) (cond ((member package-dir entries) (unless (cl-loop for dir in haskell-w3m-haddock-dirs when (w3m-haddock-find-index dir package-dir) do (progn (w3m-browse-url (w3m-haddock-find-index dir package-dir) t) (cl-return t))) (w3m-browse-url (concat "http://hackage.haskell.org/package/" package-dir) t))) (t (w3m-browse-url (concat "http://hackage.haskell.org/package/" package-dir) t))))) (defun w3m-haddock-find-index (dir package) (let ((html-index (concat dir "/" package "/html/index.html")) (index (concat dir "/" package "/index.html"))) (cond ((file-exists-p html-index) html-index) ((file-exists-p index) index)))) (defun w3m-haddock-page-p () "Haddock general page?" (save-excursion (goto-char (point-max)) (forward-line -2) (looking-at "[ ]*Produced by Haddock"))) (defun w3m-haddock-source-p () "Haddock source page?" (save-excursion (goto-char (point-min)) (or (looking-at "Location: https?://hackage.haskell.org/package/.*/docs/src/") (looking-at "Location: file://.*cabal/share/doc/.*/html/src/") (looking-at "Location: .*src/.*.html$")))) (defun w3m-haddock-p () "Any haddock page?" (or (w3m-haddock-page-p) (w3m-haddock-source-p))) (defun w3m-haddock-find-tag () "Find a tag by jumping to the \"All\" index and doing a search-forward." (interactive) (when (w3m-haddock-p) (let ((ident (haskell-ident-at-point))) (when ident (w3m-browse-url (replace-regexp-in-string "docs/.*" "docs/doc-index-All.html" w3m-current-url)) (search-forward ident))))) (defun w3m-haddock-display (_url) "To be run by w3m's display hook. This takes a normal w3m buffer containing hadddock documentation and reformats it to be more usable and look like a dedicated documentation page." (when (w3m-haddock-page-p) (save-excursion (goto-char (point-min)) (let ((inhibit-read-only t)) (delete-region (point) (line-end-position)) (w3m-haddock-next-heading) ;; Start formatting entries (while (looking-at w3m-haddock-entry-regex) (when (w3m-haddock-valid-heading) (w3m-haddock-format-heading)) (w3m-haddock-next-heading)))) (rename-buffer (concat "*haddock: " (w3m-buffer-title (current-buffer)) "*"))) (when (w3m-haddock-source-p) (font-lock-mode -1) (let ((n (line-number-at-pos))) (save-excursion (goto-char (point-min)) (forward-line 1) (let ((text (buffer-substring (point) (point-max))) (inhibit-read-only t)) (delete-region (point) (point-max)) (insert (haskell-fontify-as-mode text 'haskell-mode)))) (goto-char (point-min)) (forward-line (1- n))))) (defun w3m-haddock-format-heading () "Format a haddock entry." (let ((o (make-overlay (line-beginning-position) (1- (save-excursion (w3m-haddock-header-end)))))) (overlay-put o 'face 'w3m-haddock-heading-face)) (let ((end (save-excursion (w3m-haddock-next-heading) (when (w3m-haddock-valid-heading) (point))))) (when end (save-excursion (w3m-haddock-header-end) (indent-rigidly (point) end 4))))) (defun w3m-haddock-next-heading () "Go to the next heading, or end of the buffer." (forward-line 1) (or (search-forward-regexp w3m-haddock-entry-regex nil t 1) (goto-char (point-max))) (goto-char (line-beginning-position))) (defun w3m-haddock-valid-heading () "Is this a valid heading?" (not (get-text-property (point) 'face))) (defun w3m-haddock-header-end () "Go to the end of the header." (search-forward-regexp "\n[ \n]")) (provide 'w3m-haddock)