Mercurial > hg > Members > kokubo > emacs
comparison .emacs.d/haskell-mode/haskell-session.el @ 0:2764b4f45f9f
1st commit
author | Shohei KOKUBO <e105744@ie.u-ryukyu.ac.jp> |
---|---|
date | Mon, 21 Apr 2014 04:30:59 +0900 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:2764b4f45f9f |
---|---|
1 ;;; haskell-session.el --- Haskell sessions | |
2 | |
3 ;; Copyright (C) 2011-2012 Chris Done | |
4 | |
5 ;; Author: Chris Done <chrisdone@gmail.com> | |
6 | |
7 ;; This file is not part of GNU Emacs. | |
8 | |
9 ;; This file is free software; you can redistribute it and/or modify | |
10 ;; it under the terms of the GNU General Public License as published by | |
11 ;; the Free Software Foundation; either version 3, or (at your option) | |
12 ;; any later version. | |
13 | |
14 ;; This file is distributed in the hope that it will be useful, | |
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 ;; GNU General Public License for more details. | |
18 | |
19 ;; You should have received a copy of the GNU General Public License | |
20 ;; along with GNU Emacs; see the file COPYING. If not, write to | |
21 ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | |
22 ;; Boston, MA 02110-1301, USA. | |
23 | |
24 ;;; Commentary: | |
25 | |
26 ;;; Todo: | |
27 | |
28 ;;; Code: | |
29 | |
30 (require 'haskell-cabal) | |
31 (require 'haskell-string) | |
32 (require 'haskell-mode) | |
33 (with-no-warnings (require 'cl)) | |
34 | |
35 (declare-function haskell-interactive-mode "haskell-interactive-mode" ()) | |
36 (declare-function haskell-kill-session-process "haskell-process" (&optional session)) | |
37 (declare-function haskell-process-start "haskell-process" (session)) | |
38 (declare-function haskell-process-cd "haskell-process" (&optional not-interactive)) | |
39 | |
40 ;; Dynamically scoped variables. | |
41 (defvar haskell-process-type) | |
42 | |
43 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
44 ;; Globals | |
45 | |
46 (defvar haskell-sessions (list) | |
47 "All Haskell sessions in the Emacs session.") | |
48 | |
49 (defun haskell-session-tags-filename (session) | |
50 "Get the filename for the TAGS file." | |
51 (concat (haskell-session-cabal-dir session) "/TAGS")) | |
52 | |
53 ;;;###autoload | |
54 (defun haskell-session-all-modules (&optional dontcreate) | |
55 "Get all modules -- installed or in the current project. | |
56 If DONTCREATE is non-nil don't create a new session." | |
57 (append (haskell-session-installed-modules dontcreate) | |
58 (haskell-session-project-modules dontcreate))) | |
59 | |
60 ;;;###autoload | |
61 (defun haskell-session-installed-modules (&optional dontcreate) | |
62 "Get the modules installed in the current package set. | |
63 If DONTCREATE is non-nil don't create a new session." | |
64 ;; TODO: Again, this makes HEAVY use of unix utilities. It'll work | |
65 ;; fine in Linux, probably okay on OS X, and probably not at all on | |
66 ;; Windows. Again, if someone wants to test on Windows and come up | |
67 ;; with alternatives that's OK. | |
68 ;; | |
69 ;; Ideally all these package queries can be provided by a Haskell | |
70 ;; program based on the Cabal API. Possibly as a nice service. Such | |
71 ;; a service could cache and do nice things like that. For now, this | |
72 ;; simple shell script takes us far. | |
73 ;; | |
74 ;; Probably also we can take the code from inferior-haskell-mode. | |
75 ;; | |
76 ;; Ugliness aside, if it saves us time to type it's a winner. | |
77 ;; | |
78 ;; FIXME/TODO: add support for (eq 'cabal-repl haskell-process-type) | |
79 (require 'haskell-process) ; hack for accessing haskell-process-type | |
80 (let ((modules (shell-command-to-string | |
81 (format "%s | %s | %s" | |
82 (if (eq 'cabal-dev haskell-process-type) | |
83 (if (or (not dontcreate) (haskell-session-maybe)) | |
84 (format "cabal-dev -s %s/cabal-dev ghc-pkg dump" | |
85 (haskell-session-cabal-dir (haskell-session))) | |
86 "echo ''") | |
87 "ghc-pkg dump") | |
88 "egrep '^(exposed-modules: | )[A-Z]'" | |
89 "cut -c18-")))) | |
90 (split-string modules))) | |
91 | |
92 (defun haskell-session-project-modules (&optional dontcreate) | |
93 "Get the modules of the current project. | |
94 If DONTCREATE is non-nil don't create a new session." | |
95 (if (or (not dontcreate) (haskell-session-maybe)) | |
96 (let* ((session (haskell-session)) | |
97 (modules | |
98 (shell-command-to-string | |
99 (format "%s && %s" | |
100 (format "cd %s" (haskell-session-cabal-dir session)) | |
101 ;; TODO: Use a different, better source. Possibly hasktags or some such. | |
102 ;; TODO: At least make it cross-platform. Linux | |
103 ;; (and possibly OS X) have egrep, Windows | |
104 ;; doesn't -- or does it via Cygwin or MinGW? | |
105 ;; This also doesn't handle module\nName. But those gits can just cut it out! | |
106 "egrep '^module[\t\r ]+[^(\t\r ]+' . -r -I --include='*.*hs' --include='*.hsc' -s -o -h | sed 's/^module[\t\r ]*//' | sort | uniq")))) | |
107 (split-string modules)))) | |
108 | |
109 (defun haskell-session-kill (&optional leave-interactive-buffer) | |
110 "Kill the session process and buffer, delete the session. | |
111 0. Prompt to kill all associated buffers. | |
112 1. Kill the process. | |
113 2. Kill the interactive buffer. | |
114 3. Walk through all the related buffers and set their haskell-session to nil. | |
115 4. Remove the session from the sessions list." | |
116 (interactive) | |
117 (let* ((session (haskell-session)) | |
118 (name (haskell-session-name session)) | |
119 (also-kill-buffers (y-or-n-p (format "Killing `%s'. Also kill all associated buffers?" name)))) | |
120 (haskell-kill-session-process session) | |
121 (unless leave-interactive-buffer | |
122 (kill-buffer (haskell-session-interactive-buffer session))) | |
123 (loop for buffer in (buffer-list) | |
124 do (with-current-buffer buffer | |
125 (when (and (boundp 'haskell-session) | |
126 (string= (haskell-session-name haskell-session) name)) | |
127 (setq haskell-session nil) | |
128 (when also-kill-buffers | |
129 (kill-buffer))))) | |
130 (setq haskell-sessions | |
131 (remove-if (lambda (session) | |
132 (string= (haskell-session-name session) | |
133 name)) | |
134 haskell-sessions)))) | |
135 | |
136 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
137 ;; Finding/clearing the session | |
138 | |
139 ;; Used internally | |
140 (defvar haskell-session) | |
141 | |
142 ;;;###autoload | |
143 (defun haskell-session-maybe () | |
144 "Maybe get the Haskell session, return nil if there isn't one." | |
145 (if (default-boundp 'haskell-session) | |
146 haskell-session | |
147 (setq haskell-session nil))) | |
148 | |
149 ;;;###autoload | |
150 (defun haskell-session () | |
151 "Get the Haskell session, prompt if there isn't one or fail." | |
152 (or (haskell-session-maybe) | |
153 (haskell-session-assign | |
154 (or (haskell-session-from-buffer) | |
155 (haskell-session-new-assume-from-cabal) | |
156 (haskell-session-choose) | |
157 (haskell-session-new))))) | |
158 | |
159 (defun haskell-session-new-assume-from-cabal () | |
160 "Prompt to create a new project based on a guess from the nearest Cabal file." | |
161 (when (y-or-n-p (format "Start a new project named ā%sā? " | |
162 (haskell-session-default-name))) | |
163 (haskell-session-make (haskell-session-default-name)))) | |
164 | |
165 (defun haskell-session-from-buffer () | |
166 "Get the session based on the buffer." | |
167 (when (and (buffer-file-name) | |
168 (consp haskell-sessions)) | |
169 (reduce (lambda (acc a) | |
170 (if (haskell-is-prefix-of (haskell-session-cabal-dir a) | |
171 (file-name-directory (buffer-file-name))) | |
172 (if acc | |
173 (if (and | |
174 (> (length (haskell-session-cabal-dir a)) | |
175 (length (haskell-session-cabal-dir acc)))) | |
176 a | |
177 acc) | |
178 a) | |
179 acc)) | |
180 haskell-sessions | |
181 :initial-value nil))) | |
182 | |
183 (defun haskell-session-new () | |
184 "Make a new session." | |
185 (let ((name (read-from-minibuffer "Project name: " (haskell-session-default-name)))) | |
186 (when (not (string= name "")) | |
187 (haskell-session-make name)))) | |
188 | |
189 (defun haskell-session-default-name () | |
190 "Generate a default project name for the new project prompt." | |
191 (let ((file (haskell-cabal-find-file))) | |
192 (or (when file | |
193 (downcase (file-name-sans-extension | |
194 (file-name-nondirectory file)))) | |
195 "haskell"))) | |
196 | |
197 (defun haskell-session-assign (session) | |
198 "Set the current session." | |
199 (set (make-local-variable 'haskell-session) session)) | |
200 | |
201 (defun haskell-session-choose () | |
202 "Find a session by choosing from a list of the current sessions." | |
203 (when haskell-sessions | |
204 (let* ((session-name (funcall haskell-completing-read-function | |
205 "Choose Haskell session: " | |
206 (mapcar 'haskell-session-name haskell-sessions))) | |
207 (session (find-if (lambda (session) | |
208 (string= (haskell-session-name session) | |
209 session-name)) | |
210 haskell-sessions))) | |
211 session))) | |
212 | |
213 (defun haskell-session-clear () | |
214 "Clear the buffer of any Haskell session choice." | |
215 (set (make-local-variable 'haskell-session) nil)) | |
216 | |
217 (defun haskell-session-change () | |
218 "Change the session for the current buffer." | |
219 (interactive) | |
220 (haskell-session-clear) | |
221 (haskell-session-assign (or (haskell-session-new-assume-from-cabal) | |
222 (haskell-session-choose) | |
223 (haskell-session-new)))) | |
224 | |
225 (defun haskell-session-change-target (target) | |
226 "Set the build target for cabal repl" | |
227 (interactive "sNew build target:") | |
228 (let* ((session haskell-session) | |
229 (old-target (haskell-session-get session 'target))) | |
230 (when session | |
231 (haskell-session-set-target session target) | |
232 (when (and (not (string= old-target target)) | |
233 (y-or-n-p "Target changed, restart haskell process?")) | |
234 (haskell-process-start session))))) | |
235 | |
236 (defun haskell-session-strip-dir (session file) | |
237 "Strip the load dir from the file path." | |
238 (let ((cur-dir (haskell-session-current-dir session))) | |
239 (if (> (length file) (length cur-dir)) | |
240 (if (string= (substring file 0 (length cur-dir)) | |
241 cur-dir) | |
242 (replace-regexp-in-string | |
243 "^[/\\]" "" | |
244 (substring file | |
245 (length cur-dir))) | |
246 file) | |
247 file))) | |
248 | |
249 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
250 ;; Building the session | |
251 | |
252 (defun haskell-session-make (name) | |
253 "Make a Haskell session." | |
254 (let ((session (set (make-local-variable 'haskell-session) | |
255 (list (cons 'name name))))) | |
256 (add-to-list 'haskell-sessions session) | |
257 (haskell-process-start session) | |
258 session)) | |
259 | |
260 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
261 ;; Accessing the session | |
262 | |
263 (defun haskell-session-name (s) | |
264 "Get the session name." | |
265 (haskell-session-get s 'name)) | |
266 | |
267 (defun haskell-session-target (s) | |
268 "Get the session build target." | |
269 (let* ((maybe-target (haskell-session-get s 'target)) | |
270 (target (if maybe-target maybe-target | |
271 (let ((new-target | |
272 (read-string "build target (empty for default):"))) | |
273 (haskell-session-set-target s new-target))))) | |
274 (if (not (string= target "")) target nil))) | |
275 | |
276 (defun haskell-session-set-target (s target) | |
277 "Set the session build target." | |
278 (haskell-session-set s 'target target)) | |
279 | |
280 (defun haskell-session-interactive-buffer (s) | |
281 "Get the session interactive buffer." | |
282 (let ((buffer (haskell-session-get s 'interactive-buffer))) | |
283 (if (and buffer (buffer-live-p buffer)) | |
284 buffer | |
285 (let ((buffer (get-buffer-create (format "*%s*" (haskell-session-name s))))) | |
286 (haskell-session-set-interactive-buffer s buffer) | |
287 (with-current-buffer buffer | |
288 (haskell-interactive-mode) | |
289 (haskell-session-assign s)) | |
290 (switch-to-buffer-other-window buffer) | |
291 buffer)))) | |
292 | |
293 (defun haskell-session-set-interactive-buffer (s v) | |
294 "Set the session interactive buffer." | |
295 (haskell-session-set s 'interactive-buffer v)) | |
296 | |
297 (defun haskell-session-set-process (s v) | |
298 "Set the session process." | |
299 (haskell-session-set s 'process v)) | |
300 | |
301 ;;;###autoload | |
302 (defun haskell-session-process (s) | |
303 "Get the session process." | |
304 (haskell-session-get s 'process)) | |
305 | |
306 (defun haskell-session-set-cabal-dir (s v) | |
307 "Set the session cabal-dir." | |
308 (let ((true-path (file-truename v))) | |
309 (haskell-session-set s 'cabal-dir true-path) | |
310 (haskell-session-set-cabal-checksum s true-path))) | |
311 | |
312 (defun haskell-session-set-current-dir (s v) | |
313 "Set the session current directory." | |
314 (let ((true-path (file-truename v))) | |
315 (haskell-session-set s 'current-dir true-path))) | |
316 | |
317 (defun haskell-session-set-cabal-checksum (s cabal-dir) | |
318 "Set the session checksum of .cabal files" | |
319 (haskell-session-set s 'cabal-checksum | |
320 (haskell-cabal-compute-checksum cabal-dir))) | |
321 | |
322 (defun haskell-session-current-dir (s) | |
323 "Get the session current directory." | |
324 (let ((dir (haskell-session-get s 'current-dir))) | |
325 (or dir | |
326 (haskell-process-cd t)))) | |
327 | |
328 (defun haskell-session-cabal-dir (s) | |
329 "Get the session cabal-dir." | |
330 (let ((dir (haskell-session-get s 'cabal-dir))) | |
331 (if dir | |
332 dir | |
333 (let ((set-dir (haskell-cabal-get-dir))) | |
334 (if set-dir | |
335 (progn (haskell-session-set-cabal-dir s set-dir) | |
336 set-dir) | |
337 (haskell-session-cabal-dir s)))))) | |
338 | |
339 (defun haskell-session-modify (session key update) | |
340 "Update the value at KEY in SESSION with UPDATE." | |
341 (haskell-session-set | |
342 session | |
343 key | |
344 (funcall update | |
345 (haskell-session-get session key)))) | |
346 | |
347 (defun haskell-session-get (session key) | |
348 "Get the SESSION's KEY value. | |
349 Returns nil if KEY not set." | |
350 (cdr (assq key session))) | |
351 | |
352 (defun haskell-session-set (session key value) | |
353 "Set the SESSION's KEY to VALUE. | |
354 Returns newly set VALUE." | |
355 (let ((cell (assq key session))) | |
356 (if cell | |
357 (setcdr cell value) ; modify cell in-place | |
358 (setcdr session (cons (cons key value) (cdr session))) ; new cell | |
359 value))) | |
360 | |
361 (provide 'haskell-session) | |
362 | |
363 ;; Local Variables: | |
364 ;; byte-compile-warnings: (not cl-functions) | |
365 ;; End: | |
366 | |
367 ;;; haskell-session.el ends here |