-
Notifications
You must be signed in to change notification settings - Fork 62
/
inf-fsharp-mode.el
199 lines (164 loc) · 7.61 KB
/
inf-fsharp-mode.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
;;; inf-fsharp-mode.el --- Support for F# interactive
;; Copyright (C) 1997 INRIA
;; Author: 1993-1997 Xavier Leroy, Jacques Garrigue
;; 2010-2011 Laurent Le Brun <[email protected]>
;; Maintainer: Robin Neatherway <[email protected]>
;; Keywords: languages
;; 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.
(require 'tramp)
(require 'comint)
(require 'fsharp-mode-util)
(require 'cl-lib)
;; User modifiable variables
;; Whether you want the output buffer to be diplayed when you send a phrase
(defvar fsharp-display-when-eval t
"*If true, display the inferior fsharp buffer when evaluating expressions.")
(defvar inferior-fsharp-program
(cond
((executable-find "dotnet") "dotnet fsi --readline-")
(fsharp-ac-using-mono "fsharpi --readline-")
(t (concat "\"" (fsharp-mode--executable-find "fsi.exe") "\" --fsi-server-input-codepage:65001")))
"Inferior F# command.")
;; End of User modifiable variables
(defvar inferior-fsharp-mode-map
(let ((map (copy-keymap comint-mode-map)))
(define-key map [M-return] 'fsharp-comint-send)
map))
;; Augment fsharp mode, so you can process fsharp code in the source files.
(define-derived-mode inferior-fsharp-mode comint-mode "Inferior fsharp"
"Major mode for interacting with an inferior fsharp process.
Runs a fsharp toplevel as a subprocess of Emacs, with I/O through an
Emacs buffer. A history of input phrases is maintained. Phrases can
be sent from another buffer in fsharp mode.
\\{inferior-fsharp-mode-map}"
(setq comint-prompt-regexp "^> ?")
(setq comint-prompt-read-only t)
(set (make-local-variable 'paragraph-start) (concat "^$\\|" page-delimiter))
(set (make-local-variable 'paragraph-separate) paragraph-start)
(set (make-local-variable 'paragraph-ignore-fill-prefix) t)
(set (make-local-variable 'require-final-newline) t)
(set (make-local-variable 'comment-start) "(*")
(set (make-local-variable 'comment-end) "*)")
(set (make-local-variable 'comment-column) 40)
(set (make-local-variable 'comment-start-skip) "(\\*+ *")
(set (make-local-variable 'parse-sexp-ignore-comments) nil)
(set (make-local-variable 'comint-process-echoes) t)
(run-hooks 'inferior-fsharp-mode-hooks)
;; use compilation mode to parse errors, but RET and C-cC-c should still be from comint-mode
(compilation-minor-mode)
(make-local-variable 'minor-mode-map-alist)
(setq minor-mode-map-alist (assq-delete-all 'compilation-minor-mode (cl-copy-seq minor-mode-map-alist))))
(defconst inferior-fsharp-buffer-subname "inferior-fsharp")
(defconst inferior-fsharp-buffer-name
(concat "*" inferior-fsharp-buffer-subname "*"))
(defun fsharp--localname (file)
"Return localname of a Tramp filename.
If FILE is not a Tramp filename return FILENAME"
(if (tramp-tramp-file-p file)
(with-parsed-tramp-file-name file nil
localname)
file))
(defun fsharp-run-process-if-needed (&optional cmd)
"Launch fsi if needed, using CMD if supplied."
(unless (comint-check-proc inferior-fsharp-buffer-name)
(setq inferior-fsharp-program
(or cmd (read-from-minibuffer "fsharp toplevel to run: "
inferior-fsharp-program)))
(let ((cmdlist (inferior-fsharp-args-to-list inferior-fsharp-program))
(process-connection-type 'pty))
(with-current-buffer (apply (function make-comint)
inferior-fsharp-buffer-subname
(car cmdlist) nil
(cdr cmdlist))
(when (eq system-type 'windows-nt)
(set-process-coding-system (get-buffer-process (current-buffer))
'utf-8 'utf-8))
(inferior-fsharp-mode))
(display-buffer inferior-fsharp-buffer-name))))
;;;###autoload
(defun run-fsharp (&optional cmd)
"Run an inferior fsharp process.
Input and output via buffer `*inferior-fsharp*'."
(interactive
(list (if (not (comint-check-proc inferior-fsharp-buffer-name))
(read-from-minibuffer "fsharp toplevel to run: "
inferior-fsharp-program))))
(fsharp-run-process-if-needed cmd)
(switch-to-buffer-other-window inferior-fsharp-buffer-name))
;; split the command line (e.g. "mono fsi" -> ("mono" "fsi"))
;; we double the \ before unquoting, so that the user doesn't have to
(defun inferior-fsharp-args-to-list (string)
(split-string-and-unquote (replace-regexp-in-string "\\\\" "\\\\\\\\" string)))
(defun inferior-fsharp-show-subshell ()
(interactive)
(fsharp-run-process-if-needed)
(display-buffer inferior-fsharp-buffer-name)
(let ((buf (current-buffer))
(fsharp-buf (get-buffer inferior-fsharp-buffer-name))
(count 0))
(while
(and (< count 10)
(not (equal (buffer-name (current-buffer))
inferior-fsharp-buffer-name)))
(next-multiframe-window)
(setq count (+ count 1)))
(if (equal (buffer-name (current-buffer))
inferior-fsharp-buffer-name)
(goto-char (point-max)))
(while
(> count 0)
(previous-multiframe-window)
(setq count (- count 1)))))
(defun inferior-fsharp-eval-region (start end)
"Send the current region to the inferior fsharp process."
(interactive "r")
(fsharp-run-process-if-needed)
;; send location to fsi
(let* ((name (file-truename (buffer-file-name (current-buffer))))
(dir (fsharp--localname (file-name-directory name)))
(line (number-to-string (line-number-at-pos start)))
(loc (concat "# " line " \"" name "\"\n"))
(movedir (concat "#silentCd @\"" dir "\";;\n")))
(comint-send-string inferior-fsharp-buffer-name movedir)
(comint-send-string inferior-fsharp-buffer-name loc))
(save-excursion
(goto-char end)
(comint-send-region inferior-fsharp-buffer-name start (point))
;; normally, ";;" are part of the region
(if (and (>= (point) 2)
(prog2 (backward-char 2) (looking-at ";;")))
(comint-send-string inferior-fsharp-buffer-name "\n")
(comint-send-string inferior-fsharp-buffer-name "\n;;\n"))
;; the user may not want to see the output buffer
(if fsharp-display-when-eval
(display-buffer inferior-fsharp-buffer-name t))))
(defvar fsharp-previous-output nil
"tells the beginning of output in the shell-output buffer, so that the
output can be retreived later, asynchronously.")
;; To insert the last output from fsharp at point
(defun fsharp-insert-last-output ()
"Insert the result of the evaluation of previous phrase"
(interactive)
(let ((pos (process-mark (get-buffer-process inferior-fsharp-buffer-name))))
(insert-buffer-substring inferior-fsharp-buffer-name
fsharp-previous-output (- pos 2))))
(defun fsharp-simple-send (proc string)
(comint-simple-send proc (concat string ";;")))
(defun fsharp-comint-send ()
(interactive)
(let ((comint-input-sender 'fsharp-simple-send))
(comint-send-input)))
(provide 'inf-fsharp-mode)
;;; inf-sharp-mode.el ends here