diff .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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.emacs.d/haskell-mode/haskell-session.el	Mon Apr 21 04:30:59 2014 +0900
@@ -0,0 +1,367 @@
+;;; haskell-session.el --- Haskell sessions
+
+;; Copyright (C) 2011-2012  Chris Done
+
+;; Author: Chris Done <chrisdone@gmail.com>
+
+;; 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.
+
+;;; Commentary:
+
+;;; Todo:
+
+;;; Code:
+
+(require 'haskell-cabal)
+(require 'haskell-string)
+(require 'haskell-mode)
+(with-no-warnings (require 'cl))
+
+(declare-function haskell-interactive-mode "haskell-interactive-mode" ())
+(declare-function haskell-kill-session-process "haskell-process" (&optional session))
+(declare-function haskell-process-start "haskell-process" (session))
+(declare-function haskell-process-cd "haskell-process" (&optional not-interactive))
+
+;; Dynamically scoped variables.
+(defvar haskell-process-type)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Globals
+
+(defvar haskell-sessions (list)
+  "All Haskell sessions in the Emacs session.")
+
+(defun haskell-session-tags-filename (session)
+  "Get the filename for the TAGS file."
+  (concat (haskell-session-cabal-dir session) "/TAGS"))
+
+;;;###autoload
+(defun haskell-session-all-modules (&optional dontcreate)
+  "Get all modules -- installed or in the current project.
+If DONTCREATE is non-nil don't create a new session."
+  (append (haskell-session-installed-modules dontcreate)
+          (haskell-session-project-modules dontcreate)))
+
+;;;###autoload
+(defun haskell-session-installed-modules (&optional dontcreate)
+  "Get the modules installed in the current package set.
+If DONTCREATE is non-nil don't create a new session."
+  ;; TODO: Again, this makes HEAVY use of unix utilities. It'll work
+  ;; fine in Linux, probably okay on OS X, and probably not at all on
+  ;; Windows. Again, if someone wants to test on Windows and come up
+  ;; with alternatives that's OK.
+  ;;
+  ;; Ideally all these package queries can be provided by a Haskell
+  ;; program based on the Cabal API. Possibly as a nice service. Such
+  ;; a service could cache and do nice things like that. For now, this
+  ;; simple shell script takes us far.
+  ;;
+  ;; Probably also we can take the code from inferior-haskell-mode.
+  ;;
+  ;; Ugliness aside, if it saves us time to type it's a winner.
+  ;;
+  ;; FIXME/TODO: add support for (eq 'cabal-repl haskell-process-type)
+  (require 'haskell-process) ; hack for accessing haskell-process-type
+  (let ((modules (shell-command-to-string
+                  (format "%s | %s | %s"
+                          (if (eq 'cabal-dev haskell-process-type)
+                              (if (or (not dontcreate) (haskell-session-maybe))
+                                  (format "cabal-dev -s %s/cabal-dev ghc-pkg dump"
+                                          (haskell-session-cabal-dir (haskell-session)))
+                                "echo ''")
+                            "ghc-pkg dump")
+                          "egrep '^(exposed-modules: |                 )[A-Z]'"
+                          "cut -c18-"))))
+    (split-string modules)))
+
+(defun haskell-session-project-modules (&optional dontcreate)
+  "Get the modules of the current project.
+If DONTCREATE is non-nil don't create a new session."
+  (if (or (not dontcreate) (haskell-session-maybe))
+      (let* ((session (haskell-session))
+             (modules
+              (shell-command-to-string
+               (format "%s && %s"
+                       (format "cd %s" (haskell-session-cabal-dir session))
+                       ;; TODO: Use a different, better source. Possibly hasktags or some such.
+                       ;; TODO: At least make it cross-platform. Linux
+                       ;; (and possibly OS X) have egrep, Windows
+                       ;; doesn't -- or does it via Cygwin or MinGW?
+                       ;; This also doesn't handle module\nName. But those gits can just cut it out!
+                       "egrep '^module[\t\r ]+[^(\t\r ]+' . -r -I --include='*.*hs' --include='*.hsc' -s -o -h | sed 's/^module[\t\r ]*//' | sort | uniq"))))
+        (split-string modules))))
+
+(defun haskell-session-kill (&optional leave-interactive-buffer)
+  "Kill the session process and buffer, delete the session.
+0. Prompt to kill all associated buffers.
+1. Kill the process.
+2. Kill the interactive buffer.
+3. Walk through all the related buffers and set their haskell-session to nil.
+4. Remove the session from the sessions list."
+  (interactive)
+  (let* ((session (haskell-session))
+         (name (haskell-session-name session))
+         (also-kill-buffers (y-or-n-p (format "Killing `%s'. Also kill all associated buffers?" name))))
+    (haskell-kill-session-process session)
+    (unless leave-interactive-buffer
+      (kill-buffer (haskell-session-interactive-buffer session)))
+    (loop for buffer in (buffer-list)
+          do (with-current-buffer buffer
+               (when (and (boundp 'haskell-session)
+                          (string= (haskell-session-name haskell-session) name))
+                 (setq haskell-session nil)
+                 (when also-kill-buffers
+                   (kill-buffer)))))
+    (setq haskell-sessions
+          (remove-if (lambda (session)
+                       (string= (haskell-session-name session)
+                                name))
+                     haskell-sessions))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Finding/clearing the session
+
+;; Used internally
+(defvar haskell-session)
+
+;;;###autoload
+(defun haskell-session-maybe ()
+  "Maybe get the Haskell session, return nil if there isn't one."
+  (if (default-boundp 'haskell-session)
+      haskell-session
+    (setq haskell-session nil)))
+
+;;;###autoload
+(defun haskell-session ()
+  "Get the Haskell session, prompt if there isn't one or fail."
+  (or (haskell-session-maybe)
+      (haskell-session-assign
+       (or (haskell-session-from-buffer)
+           (haskell-session-new-assume-from-cabal)
+           (haskell-session-choose)
+           (haskell-session-new)))))
+
+(defun haskell-session-new-assume-from-cabal ()
+  "Prompt to create a new project based on a guess from the nearest Cabal file."
+  (when (y-or-n-p (format "Start a new project named ā€œ%sā€? "
+                          (haskell-session-default-name)))
+    (haskell-session-make (haskell-session-default-name))))
+
+(defun haskell-session-from-buffer ()
+  "Get the session based on the buffer."
+  (when (and (buffer-file-name)
+             (consp haskell-sessions))
+    (reduce (lambda (acc a)
+              (if (haskell-is-prefix-of (haskell-session-cabal-dir a)
+                                        (file-name-directory (buffer-file-name)))
+                  (if acc
+                      (if (and
+                           (> (length (haskell-session-cabal-dir a))
+                              (length (haskell-session-cabal-dir acc))))
+                          a
+                        acc)
+                    a)
+                acc))
+            haskell-sessions
+            :initial-value nil)))
+
+(defun haskell-session-new ()
+  "Make a new session."
+  (let ((name (read-from-minibuffer "Project name: " (haskell-session-default-name))))
+    (when (not (string= name ""))
+      (haskell-session-make name))))
+
+(defun haskell-session-default-name ()
+  "Generate a default project name for the new project prompt."
+  (let ((file (haskell-cabal-find-file)))
+    (or (when file
+          (downcase (file-name-sans-extension
+                     (file-name-nondirectory file))))
+        "haskell")))
+
+(defun haskell-session-assign (session)
+  "Set the current session."
+  (set (make-local-variable 'haskell-session) session))
+
+(defun haskell-session-choose ()
+  "Find a session by choosing from a list of the current sessions."
+  (when haskell-sessions
+    (let* ((session-name (funcall haskell-completing-read-function
+                          "Choose Haskell session: "
+                          (mapcar 'haskell-session-name haskell-sessions)))
+           (session (find-if (lambda (session)
+                               (string= (haskell-session-name session)
+                                        session-name))
+                             haskell-sessions)))
+      session)))
+
+(defun haskell-session-clear ()
+  "Clear the buffer of any Haskell session choice."
+  (set (make-local-variable 'haskell-session) nil))
+
+(defun haskell-session-change ()
+  "Change the session for the current buffer."
+  (interactive)
+  (haskell-session-clear)
+  (haskell-session-assign (or (haskell-session-new-assume-from-cabal)
+                              (haskell-session-choose)
+                              (haskell-session-new))))
+
+(defun haskell-session-change-target (target)
+  "Set the build target for cabal repl"
+  (interactive "sNew build target:")
+  (let* ((session haskell-session)
+         (old-target (haskell-session-get session 'target)))
+    (when session
+      (haskell-session-set-target session target)
+      (when (and (not (string= old-target target))
+                 (y-or-n-p "Target changed, restart haskell process?"))
+        (haskell-process-start session)))))
+
+(defun haskell-session-strip-dir (session file)
+  "Strip the load dir from the file path."
+  (let ((cur-dir (haskell-session-current-dir session)))
+    (if (> (length file) (length cur-dir))
+        (if (string= (substring file 0 (length cur-dir))
+                     cur-dir)
+            (replace-regexp-in-string
+             "^[/\\]" ""
+             (substring file
+                        (length cur-dir)))
+          file)
+      file)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Building the session
+
+(defun haskell-session-make (name)
+  "Make a Haskell session."
+  (let ((session (set (make-local-variable 'haskell-session)
+                      (list (cons 'name name)))))
+    (add-to-list 'haskell-sessions session)
+    (haskell-process-start session)
+    session))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Accessing the session
+
+(defun haskell-session-name (s)
+  "Get the session name."
+  (haskell-session-get s 'name))
+
+(defun haskell-session-target (s)
+  "Get the session build target."
+  (let* ((maybe-target (haskell-session-get s 'target))
+         (target (if maybe-target maybe-target
+                   (let ((new-target
+                          (read-string "build target (empty for default):")))
+                     (haskell-session-set-target s new-target)))))
+    (if (not (string= target "")) target nil)))
+
+(defun haskell-session-set-target (s target)
+  "Set the session build target."
+  (haskell-session-set s 'target target))
+
+(defun haskell-session-interactive-buffer (s)
+  "Get the session interactive buffer."
+  (let ((buffer (haskell-session-get s 'interactive-buffer)))
+    (if (and buffer (buffer-live-p buffer))
+        buffer
+      (let ((buffer (get-buffer-create (format "*%s*" (haskell-session-name s)))))
+        (haskell-session-set-interactive-buffer s buffer)
+        (with-current-buffer buffer
+          (haskell-interactive-mode)
+          (haskell-session-assign s))
+        (switch-to-buffer-other-window buffer)
+        buffer))))
+
+(defun haskell-session-set-interactive-buffer (s v)
+  "Set the session interactive buffer."
+  (haskell-session-set s 'interactive-buffer v))
+
+(defun haskell-session-set-process (s v)
+  "Set the session process."
+  (haskell-session-set s 'process v))
+
+;;;###autoload
+(defun haskell-session-process (s)
+  "Get the session process."
+  (haskell-session-get s 'process))
+
+(defun haskell-session-set-cabal-dir (s v)
+  "Set the session cabal-dir."
+  (let ((true-path (file-truename v)))
+    (haskell-session-set s 'cabal-dir true-path)
+    (haskell-session-set-cabal-checksum s true-path)))
+
+(defun haskell-session-set-current-dir (s v)
+  "Set the session current directory."
+  (let ((true-path (file-truename v)))
+    (haskell-session-set s 'current-dir true-path)))
+
+(defun haskell-session-set-cabal-checksum (s cabal-dir)
+  "Set the session checksum of .cabal files"
+  (haskell-session-set s 'cabal-checksum
+                       (haskell-cabal-compute-checksum cabal-dir)))
+
+(defun haskell-session-current-dir (s)
+  "Get the session current directory."
+  (let ((dir (haskell-session-get s 'current-dir)))
+    (or dir
+        (haskell-process-cd t))))
+
+(defun haskell-session-cabal-dir (s)
+  "Get the session cabal-dir."
+  (let ((dir (haskell-session-get s 'cabal-dir)))
+    (if dir
+        dir
+      (let ((set-dir (haskell-cabal-get-dir)))
+        (if set-dir
+            (progn (haskell-session-set-cabal-dir s set-dir)
+                   set-dir)
+          (haskell-session-cabal-dir s))))))
+
+(defun haskell-session-modify (session key update)
+  "Update the value at KEY in SESSION with UPDATE."
+  (haskell-session-set
+   session
+   key
+   (funcall update
+            (haskell-session-get session key))))
+
+(defun haskell-session-get (session key)
+  "Get the SESSION's KEY value.
+Returns nil if KEY not set."
+  (cdr (assq key session)))
+
+(defun haskell-session-set (session key value)
+  "Set the SESSION's KEY to VALUE.
+Returns newly set VALUE."
+  (let ((cell (assq key session)))
+    (if cell
+        (setcdr cell value) ; modify cell in-place
+      (setcdr session (cons (cons key value) (cdr session))) ; new cell
+      value)))
+
+(provide 'haskell-session)
+
+;; Local Variables:
+;; byte-compile-warnings: (not cl-functions)
+;; End:
+
+;;; haskell-session.el ends here