16
16
;; This program is free software; you can redistribute it and/or
17
17
;; modify it under the terms of the GNU General Public License as
18
;; published by the Free Software Foundation; either version 3, any later version.
20
;; This program is distributed in the hope that it will be useful, but WITHOUT
21
;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
22
;; FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
25
;; You should have received a copy of the GNU General Public License along with
26
;; this program; see the file COPYING. If not, write to the Free Software
27
;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
18
;; published by the Free Software Foundation; either version 3, or
19
;; (at your option) any later version.
21
;; This file is distributed in the hope that it will be useful,
22
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
23
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24
;; GNU General Public License for more details.
26
;; You should have received a copy of the GNU General Public License
27
;; along with GNU Emacs; see the file COPYING. If not, write to
28
;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
31
31
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
34
;; customise inferior-julia-program-name to point to your julia-release-basic
34
;; customise inferior-julia-program-name to point to your julia-basic
35
35
;; and start the inferior with M-x julia.
37
37
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
39
41
(require 'compile); for compilation-* below
43
(defvar julia-mode-hook nil)
45
(add-to-list 'auto-mode-alist '("\\.jl\\'" . julia-mode))
44
(autoload 'inferior-ess "ess-inf" "Run an ESS process.")
45
(autoload 'ess-mode "ess-mode" "Edit an ESS process.")
47
47
(defvar julia-syntax-table
48
48
(let ((table (make-syntax-table)))
129
123
(defconst julia-block-start-keywords
130
124
(list "if" "while" "for" "begin" "try" "function" "type" "let" "macro"
125
"quote" "do" "immutable"))
133
127
(defconst julia-block-other-keywords
134
128
(list "else" "elseif"))
136
130
(defconst julia-block-end-keywords
137
(list "end" "else" "elseif" "catch"))
139
(defun ess-inside-brackets-p (&optional pos)
141
(let* ((pos (or pos (point)))
142
(beg (re-search-backward "\\[" (max (point-min) (- pos 1000)) t))
143
(end (re-search-forward "\\]" (min (point-max) (+ pos 1000)) t)))
144
(and beg end (> pos beg) (> end pos)))))
131
(list "end" "else" "elseif" "catch" "finally"))
146
133
(defun julia-at-keyword (kw-list)
147
; not a keyword if used as a field name, X.word, or quoted, :word
134
"Return the word at point if it matches any keyword in KW-LIST.
135
KW-LIST is a list of strings. The word at point is not considered
136
a keyword if used as a field name, X.word, or quoted, :word."
148
137
(and (or (= (point) 1)
149
138
(and (not (equal (char-before (point)) ?.))
150
139
(not (equal (char-before (point)) ?:))))
195
187
(defun julia-paren-indent ()
196
(let* ((p (parse-partial-sexp (save-excursion
197
;; only indent by paren if the last open
198
;; paren is closer than the last open
200
(or (julia-last-open-block-pos (point-min))
202
(progn (beginning-of-line)
188
"Return indent by last opening paren."
189
(let* ((p (parse-partial-sexp
191
;; only indent by paren if the last open
192
;; paren is closer than the last open
194
(or (julia-last-open-block-pos (point-min))
196
(progn (beginning-of-line)
205
199
(if (or (= 0 (car p)) (null pos))
207
201
(progn (goto-char pos) (+ 1 (current-column))))))
210
; (let ((pos (condition-case nil
211
; (scan-lists (point) -1 1)
214
; (progn (goto-char pos) (+ 1 (current-column)))
217
203
(defun julia-indent-line ()
218
"Indent current line of julia code"
204
"Indent current line of julia code."
223
(or (and (ess-inside-string-p (point-at-bol)) 0)
224
(save-excursion (ignore-errors (julia-form-indent)))
225
(save-excursion (ignore-errors (julia-paren-indent)))
226
;; previous line ends in =
229
(skip-chars-backward " \t\n")
230
(when (eql (char-before) ?=)
231
(+ julia-basic-offset (current-indentation))))
235
(forward-to-indentation 0)
236
(julia-at-keyword julia-block-end-keywords))))
237
(ignore-errors (+ (julia-last-open-block (point-min))
238
(if endtok (- julia-basic-offset) 0)))))
239
;; take same indentation as previous line
240
(save-excursion (forward-line -1)
241
(current-indentation))
243
(when (julia-at-keyword julia-block-end-keywords)
208
(or (and (ess-inside-string-p (point-at-bol)) 0)
209
(save-excursion (ignore-errors (julia-form-indent)))
210
(save-excursion (ignore-errors (julia-paren-indent)))
211
;; previous line ends in =
214
(skip-chars-backward " \t\n")
215
(when (eql (char-before) ?=)
216
(+ julia-basic-offset (current-indentation))))
220
(forward-to-indentation 0)
221
(julia-at-keyword julia-block-end-keywords))))
222
(ignore-errors (+ (julia-last-open-block (point-min))
223
(if endtok (- julia-basic-offset) 0)))))
224
;; take same indentation as previous line
225
(save-excursion (forward-line -1)
226
(current-indentation))
228
(when (julia-at-keyword julia-block-end-keywords)
246
231
(defvar julia-editing-alist
247
232
'((paragraph-start . (concat "\\s-*$\\|" page-delimiter))
252
237
(comment-add . 1)
253
238
(comment-start-skip . "#+\\s-*")
254
239
(comment-column . 40)
255
;;(comment-indent-function . 'S-comment-indent)
256
;;(ess-comment-indent . 'S-comment-indent)
257
;; (ess-indent-line . 'S-indent-line)
258
;;(ess-calculate-indent . 'ess-calculate-indent)
259
240
(ess-indent-line-function . 'julia-indent-line)
260
241
(indent-line-function . 'julia-indent-line)
261
242
(parse-sexp-ignore-comments . t)
262
243
(ess-style . ess-default-style) ;; ignored
263
244
(ess-local-process-name . nil)
264
;;(ess-keep-dump-files . 'ask)
265
245
(ess-mode-syntax-table . julia-syntax-table)
266
;; For Changelog add, require ' ' before <- : "attr<-" is a function name :
267
;; (add-log-current-defun-header-regexp . "^\\(.+\\)\\s-+=[ \t\n]*function")
268
246
(add-log-current-defun-header-regexp . "^.*function[ \t]*\\([^ \t(]*\\)[ \t]*(")
269
(font-lock-defaults . '(julia-font-lock-defaults
270
nil nil ((?\_ . "w"))))
247
(font-lock-defaults . '(julia-font-lock-keywords nil nil ((?\_ . "w"))))
272
249
"General options for julia source files.")
274
(autoload 'inferior-ess "ess-inf" "Run an ESS process.")
275
(autoload 'ess-mode "ess-mode" "Edit an ESS process.")
277
251
(defun julia-send-string-function (process string visibly)
252
"Send the Julia STRING to the PROCESS.
253
VISIBLY is not currently used."
278
254
(let ((file (concat temporary-file-directory "julia_eval_region.jl")))
279
255
(with-temp-file file
281
(process-send-string process (format inferior-ess-load-command file))))
257
(process-send-string process (format ess-load-command file))))
283
261
(defun julia-get-help-topics (&optional proc)
284
(ess-get-words-from-vector "ESS.all_help_topics()\n"))
262
(append (ess-get-words-from-vector "ESS.all_help_topics()\n")
263
(julia--get-objects)))
285
264
;; (ess-command com)))
287
(defvar julia-help-command "help(\"%s\")\n")
289
(defvar ess-julia-error-regexp-alist '(julia-in julia-at)
266
(defun julia--retrive-topics (url)
267
(with-current-buffer (url-retrieve-synchronously url)
269
(goto-char (point-min))
271
(while (re-search-forward "toctree.*href=\"\\(.+\\)\">\\(.+\\)</a" nil t)
272
(push (propertize (match-string 2)
273
:manual (concat url (match-string 1)))
278
(defvar julia--manual-topics nil)
279
(defun julia-manual-lookup-function (&rest args) ; args are not used
281
"Look up topics at http://docs.julialang.org/en/latest/manual/"
282
;; <li class="toctree-l1"><a class="reference internal" href="introduction/">Introduction</a></li>
283
(let* ((pages (or julia--manual-topics
284
(setq julia--manual-topics
285
(julia--retrive-topics "http://docs.julialang.org/en/latest/manual/"))))
286
(page (ess-completing-read "Lookup:" pages nil t)))
287
(browse-url (get-text-property 1 :manual page))))
289
(defvar julia--reference-topics nil)
290
(defun julia-reference-lookup-function (&rest args) ; args are not used
292
"Look up reference topics"
293
;; <li class="toctree-l1"><a class="reference internal" href="introduction/">Introduction</a></li>
294
(let* ((pages (ess-get-words-from-vector "ESS.help_categories()\n")))
295
(ess-display-help-on-object
296
(ess-completing-read "Category" pages nil t))))
301
(defun julia-object-completion ()
302
"Return completions at point in a format required by `completion-at-point-functions'. "
303
(let ((proc (ess-get-next-available-process ess-dialect t))
304
(beg (ess-symbol-start)))
307
(let* ((prefix (buffer-substring-no-properties beg (point)))
308
(obj (and (string-match "\\(.*\\)\\..*$" prefix)
309
(match-string 1 prefix)))
311
(+ beg 1 (length obj))
314
(nreverse (mapcar 'car (julia--get-objects proc obj)))
316
(when (string-match "complet" (symbol-name last-command))
317
(message "No ESS process of dialect %s started" ess-dialect)
320
(defun julia--get-objects (&optional proc obj)
321
"Return all available objects.
322
Local caching might be used. If MODULE is givven, return only
323
objects from that MODULE."
325
(get-process ess-local-process-name)))
326
(when (process-live-p proc)
327
(let ((objects (process-get proc 'objects)))
328
(if (process-get proc 'busy)
331
(process-get proc 'objects))
333
(or (cdr (assoc obj objects))
334
;; don't cache composite objects and datatypes
335
(julia--get-components proc obj))
336
;; this segment is entered when user completon at top level is
337
;; requested, either Tab or AC. Hence Main is always updated.
338
(let ((modules (ess-get-words-from-vector
339
"ESS.main_modules()\n" nil nil proc))
340
(loc (process-get proc 'last-objects-cache))
341
(lev (process-get proc 'last-eval)))
343
(mapcan (lambda (mod)
344
;; we are caching all modules, and reinit Main every
345
;; time user enters commands
347
(or (and (or (not (equal mod "Main"))
348
(ignore-errors (time-less-p lev loc)))
349
(cdr (assoc mod objects)))
350
(julia--get-components proc mod t))))
352
(process-put proc 'last-objects-cache (current-time)))))))))
354
(defun julia--get-components (proc obj &optional cache?)
355
(with-current-buffer (ess-command (format "ESS.components(%s)\n" obj)
356
nil nil nil nil proc)
357
(goto-char (point-min))
359
(while (re-search-forward
360
"^\\([^ \t\n]+\\) +\\([^ \t\n]+\\)$" nil t)
361
(push (cons (match-string 1) (match-string 2)) list))
363
(let ((objects (process-get proc 'objects)))
364
(push (cons obj list) objects)
365
(process-put proc 'objects objects)))
370
(defvar ac-source-julia-objects
371
'((prefix . ess-symbol-start)
373
(candidates . ess-ac-julia-objects)
374
(document . ess-ac-help-object)
376
"Auto-completion source for julia objects")
378
(defun ess-ac-julia-objects ()
379
"Get all cached objects"
380
(let ((aprf ac-prefix))
381
(let ((proc (ess-get-next-available-process nil t)))
383
(if (string-match "\\(.*\\)\\..*$" aprf)
384
(let ((module (match-string 1 aprf)))
385
(mapcar (lambda (el) (concat module "." (car el)))
386
(julia--get-objects proc module)))
387
(julia--get-objects proc))))))
392
(defvar julia-error-regexp-alist '(julia-in julia-at)
290
393
"List of symbols which are looked up in `compilation-error-regexp-alist-alist'.")
292
395
(add-to-list 'compilation-error-regexp-alist-alist
294
397
(add-to-list 'compilation-error-regexp-alist-alist
295
398
'(julia-at "^\\S-+\\s-+\\(at \\(.*\\):\\([0-9]+\\)\\)" 2 3 nil 2 1))
403
(defun julia-eldoc-function ()
404
"Return the doc string, or nil.
405
If an ESS process is not associated with the buffer, do not try
406
to look up any doc strings."
408
(when (and (ess-process-live-p)
409
(not (ess-process-get 'busy)))
410
(let ((funname (or (and ess-eldoc-show-on-symbol ;; aggressive completion
412
(car (ess--funname.start)))))
414
(let* ((args (copy-sequence (nth 2 (ess-function-arguments funname))))
415
(W (- (window-width (minibuffer-window)) (+ 4 (length funname))))
416
(doc (concat (propertize funname 'face font-lock-function-name-face) ": ")))
418
(setq args (sort args (lambda (s1 s2)
419
(< (length s1) (length s2)))))
420
(setq doc (concat doc (pop args)))
421
(while (and args (< (+ (length doc) (length (car args))) W))
422
(setq doc (concat doc " "
424
(when (and args (< (length doc) W))
425
(setq doc (concat doc " {--}"))))
430
(defvar julia-imenu-generic-expression
431
;; don't use syntax classes, screws egrep
432
'(("Function (_)" "[ \t]*function[ \t]+\\(_[^ \t\n]*\\)" 1)
433
("Function" "^[ \t]*function[ \t]+\\([^_][^\t\n]*\\)" 1)
434
("Const" "[ \t]*const \\([^ \t\n]*\\)" 1)
435
("Type" "^[ \t]*[a-zA-Z0-9_]*type[a-zA-Z0-9_]* \\([^ \t\n]*\\)" 1)
436
("Require" " *\\(\\brequire\\)(\\([^ \t\n)]*\\)" 2)
437
("Include" " *\\(\\binclude\\)(\\([^ \t\n)]*\\)" 2)
297
442
(defvar julia-customize-alist
298
443
'((comint-use-prompt-regexp . t)
299
(ess-eldoc-function . 'ess-julia-eldoc-function)
444
(ess-eldoc-function . 'julia-eldoc-function)
300
445
(inferior-ess-primary-prompt . "a> ") ;; from julia>
301
446
(inferior-ess-secondary-prompt . nil)
302
447
(inferior-ess-prompt . "\\w*> ")
303
448
(ess-local-customize-alist . 'julia-customize-alist)
304
449
(inferior-ess-program . inferior-julia-program-name)
305
(inferior-ess-font-lock-defaults . julia-font-lock-defaults)
450
(inferior-ess-font-lock-defaults . julia-font-lock-keywords)
306
451
(ess-get-help-topics-function . 'julia-get-help-topics)
307
452
(ess-help-web-search-command . "http://docs.julialang.org/en/latest/search/?q=%s")
308
(inferior-ess-load-command . "include(\"%s\")\n")
453
(ess-manual-lookup-command . 'julia-manual-lookup-function)
454
(ess-reference-lookup-command . 'julia-reference-lookup-function)
455
(ess-load-command . "include(\"%s\")\n")
309
456
(ess-funargs-command . "ESS.fun_args(\"%s\")\n")
310
457
(ess-dump-error-re . "in \\w* at \\(.*\\):[0-9]+")
311
458
(ess-error-regexp . "\\(^\\s-*at\\s-*\\(?3:.*\\):\\(?2:[0-9]+\\)\\)")
312
(ess-error-regexp-alist . ess-julia-error-regexp-alist)
459
(ess-error-regexp-alist . julia-error-regexp-alist)
313
460
(ess-send-string-function . nil);'julia-send-string-function)
314
461
(ess-imenu-generic-expression . julia-imenu-generic-expression)
315
462
;; (inferior-ess-objects-command . inferior-R-objects-command)
316
463
;; (inferior-ess-search-list-command . "search()\n")
317
(inferior-ess-help-command . julia-help-command)
464
(inferior-ess-help-command . "ESS.help(\"%s\")\n")
318
465
;; (inferior-ess-help-command . "help(\"%s\")\n")
319
466
(ess-language . "julia")
320
467
(ess-dialect . "julia")
321
468
(ess-suffix . "jl")
469
(ess-ac-sources . '(ac-source-julia-objects))
322
470
(ess-dump-filename-template . (ess-replace-regexp-in-string
323
471
"S$" ess-suffix ; in the one from custom:
324
472
ess-dump-filename-template-proto))
419
568
(goto-char (point-max))
420
569
(ess--inject-code-from-file (format "%sess-julia.jl" ess-etc-directory))
421
570
(with-ess-process-buffer nil
422
(run-mode-hooks 'ess-julia-post-run-hook))
571
(run-mode-hooks 'julia-post-run-hook))
427
(defun ess-julia-eldoc-function ()
428
"Return the doc string, or nil.
429
If an ESS process is not associated with the buffer, do not try
430
to look up any doc strings."
432
(when (and (ess-process-live-p)
433
(not (ess-process-get 'busy)))
434
(let ((funname (or (and ess-eldoc-show-on-symbol ;; aggressive completion
436
(car (ess--funname.start)))))
438
(let* ((args (copy-sequence (nth 2 (ess-function-arguments funname))))
439
(W (- (window-width (minibuffer-window)) (+ 4 (length funname))))
440
(doc (concat (propertize funname 'face font-lock-function-name-face) ": ")))
442
(setq args (sort args (lambda (s1 s2)
443
(< (length s1) (length s2)))))
444
(setq doc (concat doc (pop args)))
445
(while (and args (< (length doc) W))
446
(setq doc (concat doc " "
448
(when (and args (< (length doc) W))
449
(setq doc (concat doc " {--}"))))
454
(defvar julia-imenu-generic-expression
455
;; don't use syntax classes, screws egrep
456
'(("Function (_)" "[ \t]*function[ \t]+\\(_[^ \t\n]*\\)" 1)
457
("Function" "[ \t]*function[ \t]+\\([^_][^\t\n]*\\)" 1)
458
("Const" "[ \t]*const \\([^ \t\n]*\\)" 1)
459
("Type" "^[ \t]*[a-zA-Z0-9_]*type[a-zA-Z0-9_]* \\([^ \t\n]*\\)" 1)
460
("Require" " *\\(\\brequire\\)(\\([^ \t\n)]*\\)" 2)
461
("Include" " *\\(\\binclude\\)(\\([^ \t\n)]*\\)" 2)
462
;; ("Classes" "^.*setClass(\\(.*\\)," 1)
463
;; ("Coercions" "^.*setAs(\\([^,]+,[^,]*\\)," 1) ; show from and to
464
;; ("Generics" "^.*setGeneric(\\([^,]*\\)," 1)
465
;; ("Methods" "^.*set\\(Group\\|Replace\\)?Method(\"\\(.+\\)\"," 2)
466
;; ;;[ ]*\\(signature=\\)?(\\(.*,?\\)*\\)," 1)
468
;; ;;("Other" "^\\(.+\\)\\s-*<-[ \t\n]*[^\\(function\\|read\\|.*data\.frame\\)]" 1)
469
;; ("Package" "^.*\\(library\\|require\\)(\\(.*\\)," 2)
470
;; ("Data" "^\\(.+\\)\\s-*<-[ \t\n]*\\(read\\|.*data\.frame\\).*(" 1)))
574
(add-to-list 'auto-mode-alist '("\\.jl\\'" . julia-mode))
473
576
(provide 'ess-julia)
578
;;; ess-julia.el ends here