/vendor/haskell-mode/haskell-cabal.el

http://github.com/rejeep/emacs · Emacs Lisp · 183 lines · 87 code · 28 blank · 68 comment · 1 complexity · a4ed4316e6c8346d104682c32ac81437 MD5 · raw file

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