/vendor/haskell-mode/haskell-cabal.el
Emacs Lisp | 183 lines | 87 code | 28 blank | 68 comment | 1 complexity | a4ed4316e6c8346d104682c32ac81437 MD5 | raw file
Possible License(s): GPL-2.0
1;;; haskell-cabal.el --- Support for Cabal packages 2 3;; Copyright (C) 2007, 2008 Stefan Monnier 4 5;; Author: Stefan Monnier <monnier@iro.umontreal.ca> 6 7;; This file is free software; you can redistribute it and/or modify 8;; it under the terms of the GNU General Public License as published by 9;; the Free Software Foundation; either version 3, or (at your option) 10;; any later version. 11 12;; This file is distributed in the hope that it will be useful, 13;; but WITHOUT ANY WARRANTY; without even the implied warranty of 14;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15;; GNU General Public License for more details. 16 17;; You should have received a copy of the GNU General Public License 18;; along with GNU Emacs; see the file COPYING. If not, write to 19;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 20;; Boston, MA 02110-1301, USA. 21 22;;; Commentary: 23 24;; Todo: 25 26;; - distinguish continued lines from indented lines. 27;; - indent-line-function. 28;; - outline-minor-mode. 29 30;;; Code: 31 32;; (defun haskell-cabal-extract-fields-from-doc () 33;; (require 'xml) 34;; (require 'cl) 35;; (let ((section (completing-read 36;; "Section: " 37;; '("general-fields" "library" "executable" "buildinfo")))) 38;; (goto-char (point-min)) 39;; (search-forward (concat "<sect3 id=\"" section "\">"))) 40;; (let* ((xml (xml-parse-region 41;; (progn (search-forward "<variablelist>") (match-beginning 0)) 42;; (progn (search-forward "</variablelist>") (point)))) 43;; (varlist (remove-if-not 'consp (cddar xml))) 44;; (syms (mapcar (lambda (entry) (caddr (assq 'literal (assq 'term entry)))) 45;; varlist)) 46;; (fields (mapcar (lambda (sym) (substring-no-properties sym 0 -1)) syms))) 47;; fields)) 48 49(eval-when-compile (require 'cl)) 50 51(defconst haskell-cabal-general-fields 52 ;; Extracted with (haskell-cabal-extract-fields-from-doc "general-fields") 53 '("name" "version" "cabal-version" "license" "license-file" "copyright" 54 "author" "maintainer" "stability" "homepage" "package-url" "synopsis" 55 "description" "category" "tested-with" "build-depends" "data-files" 56 "extra-source-files" "extra-tmp-files")) 57 58(defconst haskell-cabal-library-fields 59 ;; Extracted with (haskell-cabal-extract-fields-from-doc "library") 60 '("exposed-modules")) 61 62(defconst haskell-cabal-executable-fields 63 ;; Extracted with (haskell-cabal-extract-fields-from-doc "executable") 64 '("executable" "main-is")) 65 66(defconst haskell-cabal-buildinfo-fields 67 ;; Extracted with (haskell-cabal-extract-fields-from-doc "buildinfo") 68 '("buildable" "other-modules" "hs-source-dirs" "extensions" "ghc-options" 69 "ghc-prof-options" "hugs-options" "nhc-options" "includes" 70 "install-includes" "include-dirs" "c-sources" "extra-libraries" 71 "extra-lib-dirs" "cc-options" "ld-options" "frameworks")) 72 73(defvar haskell-cabal-mode-syntax-table 74 (let ((st (make-syntax-table))) 75 ;; The comment syntax can't be described simply in syntax-table. 76 ;; We could use font-lock-syntactic-keywords, but is it worth it? 77 ;; (modify-syntax-entry ?- ". 12" st) 78 (modify-syntax-entry ?\n ">" st) 79 st)) 80 81(defvar haskell-cabal-font-lock-keywords 82 ;; The comment syntax can't be described simply in syntax-table. 83 ;; We could use font-lock-syntactic-keywords, but is it worth it? 84 '(("^[ \t]*--.*" . font-lock-comment-face) 85 ("^ *\\([^ \t:]+\\):" (1 font-lock-keyword-face)) 86 ("^\\(Library\\)[ \t]*\\({\\|$\\)" (1 font-lock-keyword-face)) 87 ("^\\(Executable\\)[ \t]+\\([^\n \t]*\\)" 88 (1 font-lock-keyword-face) (2 font-lock-function-name-face)) 89 ("^\\(Flag\\)[ \t]+\\([^\n \t]*\\)" 90 (1 font-lock-keyword-face) (2 font-lock-constant-face)) 91 ("^ *\\(if\\)[ \t]+.*\\({\\|$\\)" (1 font-lock-keyword-face)) 92 ("^ *\\(}[ \t]*\\)?\\(else\\)[ \t]*\\({\\|$\\)" 93 (2 font-lock-keyword-face)))) 94 95(defvar haskell-cabal-buffers nil 96 "List of Cabal buffers.") 97 98;; (defsubst* inferior-haskell-string-prefix-p (str1 str2) 99;; "Return non-nil if STR1 is a prefix of STR2" 100;; (eq t (compare-strings str2 nil (length str1) str1 nil nil))) 101 102(defun haskell-cabal-find-file () 103 "Return a buffer visiting the cabal file of the current directory, or nil." 104 (catch 'found 105 ;; ;; First look for it in haskell-cabal-buffers. 106 ;; (dolist (buf haskell-cabal-buffers) 107 ;; (if (inferior-haskell-string-prefix-p 108 ;; (with-current-buffer buf default-directory) default-directory) 109 ;; (throw 'found buf))) 110 ;; Then look up the directory hierarchy. 111 (let ((user (nth 2 (file-attributes default-directory))) 112 ;; Abbreviate, so as to stop when we cross ~/. 113 (root (abbreviate-file-name default-directory)) 114 files) 115 (while (and root (equal user (nth 2 (file-attributes root)))) 116 (if (setq files (directory-files root 'full "\\.cabal\\'")) 117 ;; Avoid the .cabal directory. 118 (dolist (file files (throw 'found nil)) 119 (unless (file-directory-p file) 120 (throw 'found (find-file-noselect file)))) 121 (if (equal root 122 (setq root (file-name-directory 123 (directory-file-name root)))) 124 (setq root nil)))) 125 nil))) 126 127(autoload 'derived-mode-p "derived") ; Emacs 21 128 129(defun haskell-cabal-buffers-clean (&optional buffer) 130 (let ((bufs ())) 131 (dolist (buf haskell-cabal-buffers) 132 (if (and (buffer-live-p buf) (not (eq buf buffer)) 133 (with-current-buffer buf (derived-mode-p 'haskell-cabal-mode))) 134 (push buf bufs))) 135 (setq haskell-cabal-buffers bufs))) 136 137(defun haskell-cabal-unregister-buffer () 138 (haskell-cabal-buffers-clean (current-buffer))) 139 140;;;###autoload 141(add-to-list 'auto-mode-alist '("\\.cabal\\'" . haskell-cabal-mode)) 142 143;;;###autoload 144(define-derived-mode haskell-cabal-mode fundamental-mode "Haskell-Cabal" 145 "Major mode for Cabal package description files." 146 (set (make-local-variable 'font-lock-defaults) 147 '(haskell-cabal-font-lock-keywords t t nil nil)) 148 (add-to-list 'haskell-cabal-buffers (current-buffer)) 149 (add-hook 'change-major-mode-hook 'haskell-cabal-unregister-buffer nil 'local) 150 (add-hook 'kill-buffer-hook 'haskell-cabal-unregister-buffer nil 'local) 151 (set (make-local-variable 'comment-start) "-- ") 152 (set (make-local-variable 'comment-start-skip) "\\(^[ \t]*\\)--[ \t]*") 153 (set (make-local-variable 'comment-end) "") 154 (set (make-local-variable 'comment-end-skip) "[ ]*\\(\\s>\\|\n\\)") 155) 156 157(defun haskell-cabal-get-setting (name) 158 (save-excursion 159 (let ((case-fold-search t)) 160 (goto-char (point-min)) 161 (when (re-search-forward 162 (concat "^" (regexp-quote name) 163 ":[ \t]*\\(.*\\(\n[ \t]+[ \t\n].*\\)*\\)") 164 nil t) 165 (let ((val (match-string 1)) 166 (start 1)) 167 (when (match-end 2) ;Multiple lines. 168 ;; The documentation is not very precise about what to do about 169 ;; the \n and the indentation: are they part of the value or 170 ;; the encoding? I take the point of view that \n is part of 171 ;; the value (so that values can span multiple lines as well), 172 ;; and that only the first char in the indentation is part of 173 ;; the encoding, the rest is part of the value (otherwise, lines 174 ;; in the value cannot start with spaces or tabs). 175 (while (string-match "^[ \t]\\(?:\\.$\\)?" val start) 176 (setq start (1+ (match-beginning 0))) 177 (setq val (replace-match "" t t val)))) 178 val))))) 179 180(provide 'haskell-cabal) 181 182;; arch-tag: d455f920-5e4d-42b6-a2c7-4a7e84a05c29 183;;; haskell-cabal.el ends here