/vendor/haskell-mode/haskell-hugs.el

http://github.com/rejeep/emacs · Emacs Lisp · 316 lines · 199 code · 37 blank · 80 comment · 12 complexity · bcbd008d785cafa2e12bcf5e3788da19 MD5 · raw file

  1. ;;; haskell-hugs.el --- simplistic interaction mode with a
  2. ;; Copyright 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
  3. ;; Copyright 1998, 1999 Guy Lapalme
  4. ;; Hugs interpreter for Haskell developped by
  5. ;; The University of Nottingham and Yale University, 1994-1997.
  6. ;; Web: http://www.haskell.org/hugs.
  7. ;; In standard Emacs terminology, this would be called
  8. ;; inferior-hugs-mode
  9. ;; Keywords: Hugs inferior mode, Hugs interaction mode
  10. ;; URL: http://cvs.haskell.org/cgi-bin/cvsweb.cgi/fptools/CONTRIB/haskell-modes/emacs/haskell-hugs.el?rev=HEAD
  11. ;; This file is not part of GNU Emacs.
  12. ;; This file is free software; you can redistribute it and/or modify
  13. ;; it under the terms of the GNU General Public License as published by
  14. ;; the Free Software Foundation; either version 3, or (at your option)
  15. ;; any later version.
  16. ;; This file is distributed in the hope that it will be useful,
  17. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. ;; GNU General Public License for more details.
  20. ;; You should have received a copy of the GNU General Public License
  21. ;; along with GNU Emacs; see the file COPYING. If not, write to the
  22. ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  23. ;; Boston, MA 02111-1307, USA.
  24. ;;; Commentary:
  25. ;; Purpose:
  26. ;;
  27. ;; To send a Haskell buffer to another buffer running a Hugs interpreter
  28. ;; The functions are adapted from the Hugs Mode developed by
  29. ;; Chris Van Humbeeck <chris.vanhumbeeck@cs.kuleuven.ac.be>
  30. ;; which used to be available at:
  31. ;; http://www-i2.informatik.rwth-aachen.de/Forschung/FP/Haskell/hugs-mode.el
  32. ;;
  33. ;; Installation:
  34. ;;
  35. ;; To use with the Haskell mode of
  36. ;; Moss&Thorn <http://www.haskell.org/haskell-mode>
  37. ;; add this to .emacs:
  38. ;;
  39. ;; (add-hook 'haskell-mode-hook 'turn-on-haskell-hugs)
  40. ;;
  41. ;; Customisation:
  42. ;; The name of the hugs interpreter is in variable
  43. ;; haskell-hugs-program-name
  44. ;; Arguments can be sent to the Hugs interpreter when it is called
  45. ;; by setting the value of the variable
  46. ;; haskell-hugs-program-args
  47. ;; which by default contains '("+.") so that the progress of the
  48. ;; interpreter is visible without any "^H" in the *hugs* Emacs buffer.
  49. ;;
  50. ;; This value can be interactively by calling C-cC-s with an
  51. ;; argument.
  52. ;;
  53. ;; If the command does not seem to respond, see the
  54. ;; content of the `comint-prompt-regexp' variable
  55. ;; to check that it waits for the appropriate Hugs prompt
  56. ;; the current value is appropriate for Hugs 1.3 and 1.4
  57. ;;
  58. ;;
  59. ;; `haskell-hugs-hook' is invoked in the *hugs* once it is started.
  60. ;;
  61. ;;; All functions/variables start with
  62. ;;; `(turn-(on/off)-)haskell-hugs' or `haskell-hugs-'.
  63. (defgroup haskell-hugs nil
  64. "Major mode for interacting with an inferior Hugs session."
  65. :group 'haskell
  66. :prefix "haskell-hugs-")
  67. (defun turn-on-haskell-hugs ()
  68. "Turn on Haskell interaction mode with a Hugs interpreter running in an
  69. another Emacs buffer named *hugs*.
  70. Maps the followind commands in the haskell keymap.
  71. \\[haskell-hugs-load-file]
  72. to save the current buffer and load it by sending the :load command
  73. to Hugs.
  74. \\[haskell-hugs-reload-file]
  75. to send the :reload command to Hugs without saving the buffer.
  76. \\[haskell-hugs-show-hugs-buffer]
  77. to show the Hugs buffer and go to it."
  78. (local-set-key "\C-c\C-s" 'haskell-hugs-start-process)
  79. (local-set-key "\C-c\C-l" 'haskell-hugs-load-file)
  80. (local-set-key "\C-c\C-r" 'haskell-hugs-reload-file)
  81. (local-set-key "\C-c\C-b" 'haskell-hugs-show-hugs-buffer))
  82. (defun turn-off-haskell-hugs ()
  83. "Turn off Haskell interaction mode with a Hugs interpreter within a buffer."
  84. (local-unset-key "\C-c\C-s")
  85. (local-unset-key "\C-c\C-l")
  86. (local-unset-key "\C-c\C-r")
  87. (local-unset-key "\C-c\C-b"))
  88. (define-derived-mode haskell-hugs-mode comint-mode "Haskell Hugs"
  89. ;; called by haskell-hugs-start-process,
  90. ;; itself called by haskell-hugs-load-file
  91. ;; only when the file is loaded the first time
  92. "Major mode for interacting with an inferior Hugs session.
  93. The commands available from within a Haskell script are:
  94. \\<haskell-mode-map>\\[haskell-hugs-load-file]
  95. to save the current buffer and load it by sending the :load command
  96. to Hugs.
  97. \\[haskell-hugs-reload-file]
  98. to send the :reload command to Hugs without saving the buffer.
  99. \\[haskell-hugs-show-hugs-buffer]
  100. to show the Hugs buffer and go to it.
  101. \\<haskell-hugs-mode-map>
  102. Commands:
  103. Return at end of buffer sends line as input.
  104. Return not at end copies rest of line to end and sends it.
  105. \\[comint-kill-input] and \\[backward-kill-word] are kill commands,
  106. imitating normal Unix input editing.
  107. \\[comint-interrupt-subjob] interrupts the comint or its current
  108. subjob if any.
  109. \\[comint-stop-subjob] stops, likewise.
  110. \\[comint-quit-subjob] sends quit signal."
  111. )
  112. ;; Hugs-interface
  113. (require 'comint)
  114. (require 'shell)
  115. (defvar haskell-hugs-process nil
  116. "The active Hugs subprocess corresponding to current buffer.")
  117. (defvar haskell-hugs-process-buffer nil
  118. "*Buffer used for communication with Hugs subprocess for current buffer.")
  119. (defcustom haskell-hugs-program-name "hugs"
  120. "*The name of the command to start the Hugs interpreter."
  121. :type 'string
  122. :group 'haskell-hugs)
  123. (defcustom haskell-hugs-program-args '("+.")
  124. "*A list of string args to send to the hugs process."
  125. :type '(repeat string)
  126. :group 'haskell-hugs)
  127. (defvar haskell-hugs-load-end nil
  128. "Position of the end of the last load command.")
  129. (defvar haskell-hugs-send-end nil
  130. "Position of the end of the last send command.")
  131. (defalias 'run-hugs 'haskell-hugs-start-process)
  132. (defun haskell-hugs-start-process (arg)
  133. "Start a Hugs process and invokes `haskell-hugs-hook' if not nil.
  134. Prompts for a list of args if called with an argument."
  135. (interactive "P")
  136. (message "Starting `hugs-process' %s" haskell-hugs-program-name)
  137. (if arg
  138. (setq haskell-hugs-program-args
  139. (read-minibuffer "List of args for Hugs:"
  140. (prin1-to-string haskell-hugs-program-args))))
  141. (setq haskell-hugs-process-buffer
  142. (apply 'make-comint
  143. "hugs" haskell-hugs-program-name nil
  144. haskell-hugs-program-args))
  145. (setq haskell-hugs-process
  146. (get-buffer-process haskell-hugs-process-buffer))
  147. ;; Select Hugs buffer temporarily
  148. (set-buffer haskell-hugs-process-buffer)
  149. (haskell-hugs-mode)
  150. (make-local-variable 'shell-cd-regexp)
  151. (make-local-variable 'shell-dirtrackp)
  152. (setq shell-cd-regexp ":cd")
  153. (setq shell-dirtrackp t)
  154. (add-hook 'comint-input-filter-functions 'shell-directory-tracker nil 'local)
  155. ; ? or module name in Hugs 1.4
  156. (setq comint-prompt-regexp "^\? \\|^[[:upper:]][_[:alnum:]\.]*> ")
  157. ;; comint's history syntax conflicts with Hugs syntax, eg. !!
  158. (setq comint-input-autoexpand nil)
  159. (run-hooks 'haskell-hugs-hook)
  160. (message "")
  161. )
  162. (defun haskell-hugs-wait-for-output ()
  163. "Wait until output arrives and go to the last input."
  164. (while (progn
  165. (goto-char comint-last-input-end)
  166. (and
  167. (not (re-search-forward comint-prompt-regexp nil t))
  168. (accept-process-output haskell-hugs-process)))))
  169. (defun haskell-hugs-send (&rest string)
  170. "Send `haskell-hugs-process' the arguments (one or more strings).
  171. A newline is sent after the strings and they are inserted into the
  172. current buffer after the last output."
  173. ;; Wait until output arrives and go to the last input.
  174. (haskell-hugs-wait-for-output)
  175. ;; Position for this input.
  176. (goto-char (point-max))
  177. (apply 'insert string)
  178. (comint-send-input)
  179. (setq haskell-hugs-send-end (marker-position comint-last-input-end)))
  180. (defun haskell-hugs-go (load-command cd)
  181. "Save the current buffer and load its file into the Hugs process.
  182. The first argument LOAD-COMMAND specifies how the file should be
  183. loaded: as a new file (\":load \") or as a reload (\":reload \").
  184. If the second argument CD is non-nil, change the Haskell-Hugs process to the
  185. current buffer's directory before loading the file.
  186. If the variable `haskell-hugs-command' is set then its value will be sent to
  187. the Hugs process after the load command. This can be used for a
  188. top-level expression to evaluate."
  189. (hack-local-variables) ;; In case they've changed
  190. (save-buffer)
  191. (let ((file (if (string-equal load-command ":load ")
  192. (concat "\"" buffer-file-name "\"")
  193. ""))
  194. (dir (expand-file-name default-directory))
  195. (cmd (and (boundp 'haskell-hugs-command)
  196. haskell-hugs-command
  197. (if (stringp haskell-hugs-command)
  198. haskell-hugs-command
  199. (symbol-name haskell-hugs-command)))))
  200. (if (and haskell-hugs-process-buffer
  201. (eq (process-status haskell-hugs-process) 'run))
  202. ;; Ensure the Hugs buffer is selected.
  203. (set-buffer haskell-hugs-process-buffer)
  204. ;; Start Haskell-Hugs process.
  205. (haskell-hugs-start-process nil))
  206. (if cd (haskell-hugs-send (concat ":cd " dir)))
  207. ;; Wait until output arrives and go to the last input.
  208. (haskell-hugs-wait-for-output)
  209. (haskell-hugs-send load-command file)
  210. ;; Error message search starts from last load command.
  211. (setq haskell-hugs-load-end (marker-position comint-last-input-end))
  212. (if cmd (haskell-hugs-send cmd))
  213. ;; Wait until output arrives and go to the last input.
  214. (haskell-hugs-wait-for-output)))
  215. (defun haskell-hugs-load-file (cd)
  216. "Save a hugs buffer file and load its file.
  217. If CD (prefix argument if interactive) is non-nil, change the Hugs
  218. process to the current buffer's directory before loading the file.
  219. If there is an error, set the cursor at the error line otherwise show
  220. the Hugs buffer."
  221. (interactive "P")
  222. (haskell-hugs-gen-load-file ":load " cd)
  223. )
  224. (defun haskell-hugs-reload-file (cd)
  225. "Save a hugs buffer file and load its file.
  226. If CD (prefix argument if interactive) is non-nil, change the Hugs
  227. process to the current buffer's directory before loading the file.
  228. If there is an error, set the cursor at the error line otherwise show
  229. the Hugs buffer."
  230. (interactive "P")
  231. (haskell-hugs-gen-load-file ":reload " cd)
  232. )
  233. (defun haskell-hugs-gen-load-file (cmd cd)
  234. "Save a hugs buffer file and load its file or reload depending on CMD.
  235. If CD is non-nil, change the process to the current buffer's directory
  236. before loading the file. If there is an error, set the cursor at the
  237. error line otherwise show the Hugs buffer."
  238. (save-excursion (haskell-hugs-go cmd cd))
  239. ;; Ensure the Hugs buffer is selected.
  240. (set-buffer haskell-hugs-process-buffer)
  241. ;; Error message search starts from last load command.
  242. (goto-char haskell-hugs-load-end)
  243. (if (re-search-forward
  244. "^ERROR \"\\([^ ]*\\)\"\\( (line \\([0-9]*\\))\\|\\)" nil t)
  245. (let ((efile (buffer-substring (match-beginning 1)
  246. (match-end 1)))
  247. (eline (if (match-beginning 3)
  248. (string-to-int (buffer-substring (match-beginning 3)
  249. (match-end 3)))))
  250. (emesg (buffer-substring (1+ (point))
  251. (save-excursion (end-of-line) (point)))))
  252. (pop-to-buffer haskell-hugs-process-buffer) ; show *hugs* buffer
  253. (goto-char (point-max))
  254. (recenter)
  255. (message "Hugs error %s %s"
  256. (file-name-nondirectory efile) emesg)
  257. (if (file-exists-p efile)
  258. (progn (find-file-other-window efile)
  259. (if eline (goto-line eline))
  260. (recenter)))
  261. )
  262. (pop-to-buffer haskell-hugs-process-buffer) ; show *hugs* buffer
  263. (goto-char (point-max))
  264. (message "There were no errors.")
  265. (recenter 2) ; show only the end...
  266. )
  267. )
  268. (defun haskell-hugs-show-hugs-buffer ()
  269. "Goes to the Hugs buffer."
  270. (interactive)
  271. (if (or (not haskell-hugs-process-buffer)
  272. (not (buffer-live-p haskell-hugs-process-buffer)))
  273. (haskell-hugs-start-process nil))
  274. (pop-to-buffer haskell-hugs-process-buffer)
  275. )
  276. (provide 'haskell-hugs)
  277. ;; arch-tag: c2a621e9-d743-4361-a459-983fbf1d4589
  278. ;;; haskell-hugs.el ends here