|
;;; julia-mode.el --- Major mode for editing Julia source code
|
|
|
|
;; Copyright (C) 2009-2014 Julia contributors
|
|
;; URL: https://github.com/JuliaLang/julia
|
|
;; Version: 0.3
|
|
;; Keywords: languages
|
|
|
|
;;; Usage:
|
|
;; Put the following code in your .emacs, site-load.el, or other relevant file
|
|
;; (add-to-list 'load-path "path-to-julia-mode")
|
|
;; (require 'julia-mode)
|
|
|
|
;;; Commentary:
|
|
;; This is the official Emacs mode for editing Julia programs.
|
|
|
|
;;; License:
|
|
;; Permission is hereby granted, free of charge, to any person obtaining
|
|
;; a copy of this software and associated documentation files (the
|
|
;; "Software"), to deal in the Software without restriction, including
|
|
;; without limitation the rights to use, copy, modify, merge, publish,
|
|
;; distribute, sublicense, and/or sell copies of the Software, and to
|
|
;; permit persons to whom the Software is furnished to do so, subject to
|
|
;; the following conditions:
|
|
;;
|
|
;; The above copyright notice and this permission notice shall be
|
|
;; included in all copies or substantial portions of the Software.
|
|
;;
|
|
;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
;; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
;; OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
;; WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
;;; Code:
|
|
|
|
;; We can't use cl-lib whilst supporting Emacs 23 users who don't use
|
|
;; ELPA.
|
|
(with-no-warnings
|
|
(require 'cl)) ;; incf, decf, plusp
|
|
|
|
(defvar julia-mode-hook nil)
|
|
|
|
(defgroup julia ()
|
|
"Major mode for the julia programming language."
|
|
:group 'languages
|
|
:prefix "julia-")
|
|
|
|
(defcustom julia-indent-offset 4
|
|
"Number of spaces per indentation level."
|
|
:type 'integer
|
|
:group 'julia)
|
|
|
|
(defface julia-macro-face
|
|
'((t :inherit font-lock-preprocessor-face))
|
|
"Face for Julia macro invocations."
|
|
:group 'julia-mode)
|
|
|
|
(defface julia-quoted-symbol-face
|
|
'((t :inherit font-lock-preprocessor-face))
|
|
"Face for quoted Julia symbols, e.g. :foo."
|
|
:group 'julia-mode)
|
|
|
|
|
|
;;;###autoload
|
|
(add-to-list 'auto-mode-alist '("\\.jl\\'" . julia-mode))
|
|
|
|
;; define ignore-errors macro if it isn't present
|
|
;; (necessary for emacs 22 compatibility)
|
|
(when (not (fboundp 'ignore-errors))
|
|
(defmacro ignore-errors (body) `(condition-case nil ,body (error nil))))
|
|
|
|
(defun julia--regexp-opt (strings &optional paren)
|
|
"Emacs 23 provides `regexp-opt', but it does not support PAREN taking the value 'symbols.
|
|
This function provides equivalent functionality, but makes no efforts to optimise the regexp."
|
|
(cond
|
|
((>= emacs-major-version 24)
|
|
(regexp-opt strings paren))
|
|
((not (eq paren 'symbols))
|
|
(regexp-opt strings paren))
|
|
((null strings)
|
|
"")
|
|
('t
|
|
(rx-to-string `(seq symbol-start (or ,@strings) symbol-end)))))
|
|
|
|
(defvar julia-mode-syntax-table
|
|
(let ((table (make-syntax-table)))
|
|
(modify-syntax-entry ?_ "_" table)
|
|
(modify-syntax-entry ?@ "_" table)
|
|
(modify-syntax-entry ?! "_" table)
|
|
(modify-syntax-entry ?# "< 14" table) ; # single-line and multiline start
|
|
(modify-syntax-entry ?= ". 23bn" table)
|
|
(modify-syntax-entry ?\n ">" table) ; \n single-line comment end
|
|
(modify-syntax-entry ?\{ "(} " table)
|
|
(modify-syntax-entry ?\} "){ " table)
|
|
(modify-syntax-entry ?\[ "(] " table)
|
|
(modify-syntax-entry ?\] ")[ " table)
|
|
(modify-syntax-entry ?\( "() " table)
|
|
(modify-syntax-entry ?\) ")( " table)
|
|
;; Here, we treat ' as punctuation (when it's used for transpose),
|
|
;; see our use of `julia-char-regex' for handling ' as a character
|
|
;; delimiter
|
|
(modify-syntax-entry ?' "." table)
|
|
(modify-syntax-entry ?\" "\"" table)
|
|
(modify-syntax-entry ?` "\"" table)
|
|
(modify-syntax-entry ?\\ "\\" table)
|
|
|
|
(modify-syntax-entry ?. "." table)
|
|
(modify-syntax-entry ?? "." table)
|
|
(modify-syntax-entry ?$ "." table)
|
|
(modify-syntax-entry ?& "." table)
|
|
(modify-syntax-entry ?* "." table)
|
|
(modify-syntax-entry ?/ "." table)
|
|
(modify-syntax-entry ?+ "." table)
|
|
(modify-syntax-entry ?- "." table)
|
|
(modify-syntax-entry ?< "." table)
|
|
(modify-syntax-entry ?> "." table)
|
|
(modify-syntax-entry ?% "." table)
|
|
|
|
(modify-syntax-entry ?′ "w" table) ; \prime is a word constituent
|
|
table)
|
|
"Syntax table for `julia-mode'.")
|
|
|
|
(eval-when-compile
|
|
(defconst julia-char-regex
|
|
(rx (or (any "-" ";" "\\" "^" "!" "|" "?" "*" "<" "%" "," "=" ">" "+" "/" "&" "$" "~" ":")
|
|
(syntax open-parenthesis)
|
|
(syntax whitespace)
|
|
bol)
|
|
(group "'")
|
|
(group
|
|
(or (repeat 0 8 (not (any "'"))) (not (any "\\"))
|
|
"\\\\"))
|
|
(group "'"))))
|
|
|
|
(defconst julia-hanging-operator-regexp
|
|
;; taken from julia-parser.scm
|
|
(concat "^[^#\n]+ "
|
|
(regexp-opt
|
|
'( ;; conditional
|
|
"?"
|
|
;; assignment
|
|
"=" ":=" "+=" "-=" "*=" "/=" "//=" ".//=" ".*=" "./=" "\\=" ".\\="
|
|
"^=" ".^=" "÷=" ".÷=" "%=" ".%=" "|=" "&=" "$=" "=>" "<<=" ">>="
|
|
">>>=" "~" ".+=" ".-="
|
|
;; arrow
|
|
"--" "-->" "←" "→" "↔" "↚" "↛" "↠" "↣" "↦" "↮" "⇎" "⇏" "⇒" "⇔" "⇴"
|
|
"⇶" "⇷" "⇸" "⇹" "⇺" "⇻" "⇼" "⇽" "⇾" "⇿" "⟵" "⟶" "⟷" "⟷" "⟹"
|
|
"⟺" "⟻" "⟼" "⟽" "⟾" "⟿" "⤀" "⤁" "⤂" "⤃" "⤄" "⤅" "⤆" "⤇" "⤌"
|
|
"⤍" "⤎" "⤏" "⤐" "⤑" "⤔" "⤕" "⤖" "⤗" "⤘" "⤝" "⤞" "⤟" "⤠" "⥄" "⥅"
|
|
"⥆" "⥇" "⥈" "⥊" "⥋" "⥎" "⥐" "⥒" "⥓" "⥖" "⥗" "⥚" "⥛" "⥞" "⥟" "⥢"
|
|
"⥤" "⥦" "⥧" "⥨" "⥩" "⥪" "⥫" "⥬" "⥭" "⥰" "⧴" "⬱" "⬰" "⬲" "⬳" "⬴"
|
|
"⬵" "⬶" "⬷" "⬸" "⬹" "⬺" "⬻" "⬼" "⬽" "⬾" "⬿" "⭀" "⭁" "⭂" "⭃" "⭄"
|
|
"⭇" "⭈" "⭉" "⭊" "⭋" "⭌" "←" "→"
|
|
;; or and and
|
|
"&&" "||"
|
|
;; comparison
|
|
">" "<" ">=" "≥" "<=" "≤" "==" "===" "≡" "!=" "≠" "!==" "≢" ".>"
|
|
".<" ".>=" ".≥" ".<=" ".≤" ".==" ".!=" ".≠" ".=" ".!" "<:" ">:" "∈"
|
|
"∉" "∋" "∌" "⊆" "⊈" "⊂" "⊄" "⊊" "∝" "∊" "∍" "∥" "∦" "∷" "∺" "∻" "∽"
|
|
"∾" "≁" "≃" "≄" "≅" "≆" "≇" "≈" "≉" "≊" "≋" "≌" "≍" "≎" "≐" "≑" "≒"
|
|
"≓" "≔" "≕" "≖" "≗" "≘" "≙" "≚" "≛" "≜" "≝" "≞" "≟" "≣" "≦" "≧" "≨"
|
|
"≩" "≪" "≫" "≬" "≭" "≮" "≯" "≰" "≱" "≲" "≳" "≴" "≵" "≶" "≷" "≸" "≹"
|
|
"≺" "≻" "≼" "≽" "≾" "≿" "⊀" "⊁" "⊃" "⊅" "⊇" "⊉" "⊋" "⊏" "⊐" "⊑" "⊒"
|
|
"⊜" "⊩" "⊬" "⊮" "⊰" "⊱" "⊲" "⊳" "⊴" "⊵" "⊶" "⊷" "⋍" "⋐" "⋑" "⋕" "⋖"
|
|
"⋗" "⋘" "⋙" "⋚" "⋛" "⋜" "⋝" "⋞" "⋟" "⋠" "⋡" "⋢" "⋣" "⋤" "⋥" "⋦" "⋧"
|
|
"⋨" "⋩" "⋪" "⋫" "⋬" "⋭" "⋲" "⋳" "⋴" "⋵" "⋶" "⋷" "⋸" "⋹" "⋺" "⋻" "⋼"
|
|
"⋽" "⋾" "⋿" "⟈" "⟉" "⟒" "⦷" "⧀" "⧁" "⧡" "⧣" "⧤" "⧥" "⩦" "⩧" "⩪" "⩫"
|
|
"⩬" "⩭" "⩮" "⩯" "⩰" "⩱" "⩲" "⩳" "⩴" "⩵" "⩶" "⩷" "⩸" "⩹" "⩺" "⩻" "⩼"
|
|
"⩽" "⩾" "⩿" "⪀" "⪁" "⪂" "⪃" "⪄" "⪅" "⪆" "⪇" "⪈" "⪉" "⪊" "⪋" "⪌" "⪍"
|
|
"⪎" "⪏" "⪐" "⪑" "⪒" "⪓" "⪔" "⪕" "⪖" "⪗" "⪘" "⪙" "⪚" "⪛" "⪜" "⪝" "⪞"
|
|
"⪟" "⪠" "⪡" "⪢" "⪣" "⪤" "⪥" "⪦" "⪧" "⪨" "⪩" "⪪" "⪫" "⪬" "⪭" "⪮" "⪯"
|
|
"⪰" "⪱" "⪲" "⪳" "⪴" "⪵" "⪶" "⪷" "⪸" "⪹" "⪺" "⪻" "⪼" "⪽" "⪾" "⪿" "⫀"
|
|
"⫁" "⫂" "⫃" "⫄" "⫅" "⫆" "⫇" "⫈" "⫉" "⫊" "⫋" "⫌" "⫍" "⫎" "⫏" "⫐" "⫑"
|
|
"⫒" "⫓" "⫔" "⫕" "⫖" "⫗" "⫘" "⫙" "⫷" "⫸" "⫹" "⫺" "⊢" "⊣"
|
|
;; pipe, colon
|
|
"|>" "<|" ":" ".."
|
|
;; plus
|
|
"+" "-" "⊕" "⊖" "⊞" "⊟" ".+" ".-" "++" "|" "∪" "∨" "$" "⊔" "±" "∓"
|
|
"∔" "∸" "≂" "≏" "⊎" "⊻" "⊽" "⋎" "⋓" "⧺" "⧻" "⨈" "⨢" "⨣" "⨤" "⨥" "⨦"
|
|
"⨧" "⨨" "⨩" "⨪" "⨫" "⨬" "⨭" "⨮" "⨹" "⨺" "⩁" "⩂" "⩅" "⩊" "⩌" "⩏" "⩐"
|
|
"⩒" "⩔" "⩖" "⩗" "⩛" "⩝" "⩡" "⩢" "⩣"
|
|
;; bitshift
|
|
"<<" ">>" ">>>" ".<<" ".>>" ".>>>"
|
|
;; times
|
|
"*" "/" "./" "÷" ".÷" "%" "⋅" "∘" "×" ".%" ".*" "\\"
|
|
".\\" "&" "∩" "∧" "⊗" "⊘" "⊙" "⊚" "⊛" "⊠" "⊡" "⊓" "∗" "∙" "∤" "⅋"
|
|
"≀" "⊼" "⋄" "⋆" "⋇" "⋉" "⋊" "⋋" "⋌" "⋏" "⋒" "⟑" "⦸" "⦼" "⦾" "⦿" "⧶"
|
|
"⧷" "⨇" "⨰" "⨱" "⨲" "⨳" "⨴" "⨵" "⨶" "⨷" "⨸" "⨻" "⨼" "⨽" "⩀" "⩃" "⩄"
|
|
"⩋" "⩍" "⩎" "⩑" "⩓" "⩕" "⩘" "⩚" "⩜" "⩞" "⩟" "⩠" "⫛" "⊍" "▷" "⨝" "⟕"
|
|
"⟖" "⟗"
|
|
;; rational
|
|
"//" ".//"
|
|
;; power
|
|
"^" ".^" "↑" "↓" "⇵" "⟰" "⟱" "⤈" "⤉" "⤊" "⤋" "⤒" "⤓" "⥉" "⥌" "⥍"
|
|
"⥏" "⥑" "⥔" "⥕" "⥘" "⥙" "⥜" "⥝" "⥠" "⥡" "⥣" "⥥" "⥮" "⥯" "↑" "↓"
|
|
;; decl, dot
|
|
"::" "."))
|
|
(regexp-opt '(" #" " \n" "#" "\n"))))
|
|
|
|
(defconst julia-triple-quoted-string-regex
|
|
;; We deliberately put a group on the first and last delimiter, so
|
|
;; we can mark these as string delimiters for font-lock.
|
|
(rx (group "\"")
|
|
(group "\"\""
|
|
;; After the delimiter, we're a sequence of
|
|
;; non-backslashes or blackslashes paired with something.
|
|
(*? (or (not (any "\\"))
|
|
(seq "\\" anything)))
|
|
"\"\"")
|
|
(group "\"")))
|
|
|
|
(defconst julia-unquote-regex
|
|
"\\(\\s(\\|\\s-\\|-\\|[,%=<>\\+*/?&|!\\^~\\\\;:]\\|^\\)\\($[a-zA-Z0-9_]+\\)")
|
|
|
|
(defconst julia-forloop-in-regex
|
|
"for +.*[^
|
|
].* \\(in\\)\\(\\s-\\|$\\)+")
|
|
|
|
(defconst julia-function-regex
|
|
(rx line-start (* (or space "@inline" "@noinline")) symbol-start
|
|
"function"
|
|
(1+ space)
|
|
;; Don't highlight module names in function declarations:
|
|
(* (seq (1+ (or word (syntax symbol))) "."))
|
|
;; The function name itself
|
|
(group (1+ (or word (syntax symbol))))))
|
|
|
|
(defconst julia-function-assignment-regex
|
|
(rx line-start (* (or space "@inline" "@noinline")) symbol-start
|
|
(* (seq (1+ (or word (syntax symbol))) ".")) ; module name
|
|
(group (1+ (or word (syntax symbol))))
|
|
(? "{" (* (not (any "}"))) "}")
|
|
"(" (* (or
|
|
(seq "(" (* (not (any "(" ")"))) ")")
|
|
(not (any "(" ")"))))
|
|
")"
|
|
(* space)
|
|
(? "::" (* space) (1+ (not (any space))))
|
|
(* space)
|
|
(* (seq "where" (or "{" (+ space)) (+ (not (any "=")))))
|
|
"="
|
|
(not (any "="))))
|
|
|
|
(defconst julia-type-regex
|
|
(rx symbol-start (or ;;"immutable" "type" ;; remove after 0.6
|
|
"abstract type" "primitive type" "struct" "mutable struct")
|
|
(1+ space) (group (1+ (or word (syntax symbol))))))
|
|
|
|
(defconst julia-type-annotation-regex
|
|
(rx "::" (0+ space) (group (1+ (or word (syntax symbol))))))
|
|
|
|
;;(defconst julia-type-parameter-regex
|
|
;; (rx symbol-start (1+ (or (or word (syntax symbol)) ?_)) "{" (group (1+ (or (or word (syntax symbol)) ?_))) "}"))
|
|
|
|
(defconst julia-subtype-regex
|
|
(rx "<:" (0+ space) (group (1+ (or word (syntax symbol)))) (0+ space) (or "\n" "{" "}" "end")))
|
|
|
|
(defconst julia-macro-regex
|
|
(rx symbol-start (group "@" (1+ (or word (syntax symbol))))))
|
|
|
|
(defconst julia-keyword-regex
|
|
(julia--regexp-opt
|
|
'("if" "else" "elseif" "while" "for" "begin" "end" "quote"
|
|
"try" "catch" "return" "local" "function" "macro" "ccall"
|
|
"finally" "break" "continue" "global" "where"
|
|
"module" "using" "import" "export" "const" "let" "do" "in"
|
|
"baremodule"
|
|
;; "importall" ;; deprecated in 0.7
|
|
;; "immutable" "type" "bitstype" "abstract" "typealias" ;; removed in 1.0
|
|
"abstract type" "primitive type" "struct" "mutable struct")
|
|
'symbols))
|
|
|
|
(defconst julia-builtin-regex
|
|
(julia--regexp-opt
|
|
;;'("error" "throw")
|
|
'()
|
|
'symbols))
|
|
|
|
(defconst julia-builtin-types-regex
|
|
(julia--regexp-opt
|
|
'("Number" "Real" "BigInt" "Integer"
|
|
"UInt" "UInt8" "UInt16" "UInt32" "UInt64" "UInt128"
|
|
"Int" "Int8" "Int16" "Int32" "Int64" "Int128"
|
|
"BigFloat" "AbstractFloat" "Float16" "Float32" "Float64"
|
|
;;"Complex128" "Complex64" ;; replaced in 1.0
|
|
"ComplexF32" "ComplexF64"
|
|
"Bool"
|
|
"Cuchar" "Cshort" "Cushort" "Cint" "Cuint" "Clonglong" "Culonglong" "Cintmax_t" "Cuintmax_t"
|
|
"Cfloat" "Cdouble" "Cptrdiff_t" "Cssize_t" "Csize_t"
|
|
"Cchar" "Clong" "Culong" "Cwchar_t" "Cvoid"
|
|
"Cstring" "Cwstring" ;; C strings made of ordinary and wide characters
|
|
"Char" "String" "SubString"
|
|
"Array" "DArray" "AbstractArray" "AbstractVector" "AbstractMatrix" "AbstractSparseMatrix" "SubArray" "StridedArray" "StridedVector" "StridedMatrix" "VecOrMat" "StridedVecOrMat" "DenseArray" "SparseMatrixCSC" "BitArray"
|
|
"AbstractRange" "OrdinalRange" "StepRange" "UnitRange" "FloatRange"
|
|
"Tuple" "NTuple" "Vararg"
|
|
"DataType" "Symbol" "Function" "Vector" "Matrix" "Union" "Type" "Any" "Complex" "AbstractString" "Ptr" "Nothing" "Exception" "Task" "Signed" "Unsigned" "AbstractDict" "Dict" "IO" "IOStream" "Rational" "Regex" "RegexMatch" "Set" "BitSet" "Expr" "WeakRef" "ObjectIdDict"
|
|
"AbstractRNG" "MersenneTwister"
|
|
)
|
|
'symbols))
|
|
|
|
(defconst julia-quoted-symbol-regex
|
|
;; :foo and :foo2 are valid, but :123 is not.
|
|
(rx (or bol whitespace "(" "[" "," "=")
|
|
(group ":" (or letter (syntax symbol)) (0+ (or word (syntax symbol))))))
|
|
|
|
(defconst julia-font-lock-keywords
|
|
(list
|
|
;; Ensure :: and <: aren't highlighted, so we don't confuse ::Foo with :foo.
|
|
;; (in Emacs, keywords don't overlap).
|
|
(cons (rx (or "::" "<:")) ''default)
|
|
;; Highlight quoted symbols before keywords, so :function is not
|
|
;; highlighted as a keyword.
|
|
(list julia-quoted-symbol-regex 1 ''julia-quoted-symbol-face)
|
|
(cons julia-builtin-types-regex 'font-lock-type-face)
|
|
(cons julia-keyword-regex 'font-lock-keyword-face)
|
|
(cons julia-macro-regex ''julia-macro-face)
|
|
(cons
|
|
(julia--regexp-opt
|
|
'("true" "false" "C_NULL" "Inf" "NaN" "Inf32" "NaN32" "nothing" "undef")
|
|
'symbols)
|
|
'font-lock-constant-face)
|
|
(list julia-unquote-regex 2 'font-lock-constant-face)
|
|
(list julia-forloop-in-regex 1 'font-lock-keyword-face)
|
|
(list julia-function-regex 1 'font-lock-function-name-face)
|
|
(list julia-function-assignment-regex 1 'font-lock-function-name-face)
|
|
(list julia-type-regex 1 'font-lock-type-face)
|
|
(list julia-type-annotation-regex 1 'font-lock-type-face)
|
|
;;(list julia-type-parameter-regex 1 'font-lock-type-face)
|
|
(list julia-subtype-regex 1 'font-lock-type-face)
|
|
(list julia-builtin-regex 1 'font-lock-builtin-face)
|
|
))
|
|
|
|
(defconst julia-block-start-keywords
|
|
(list "if" "while" "for" "begin" "try" "function" "let" "macro"
|
|
"quote" "do" "module"
|
|
;; "immutable" "type" ;; remove after 0.6
|
|
"abstract type" "primitive type" "struct" "mutable struct"))
|
|
|
|
;; For keywords that begin a block without additional indentation
|
|
(defconst julia-block-start-keywords-no-indent
|
|
(list "module"))
|
|
|
|
(defconst julia-block-end-keywords
|
|
(list "end" "else" "elseif" "catch" "finally"))
|
|
|
|
(defun julia-stringify-triple-quote ()
|
|
"Put `syntax-table' property on triple-quoted string delimiters.
|
|
|
|
Based on `python-syntax-stringify'."
|
|
(let* ((string-start-pos (- (point) 3))
|
|
(string-end-pos (point))
|
|
(ppss (prog2
|
|
(backward-char 3)
|
|
(syntax-ppss)
|
|
(forward-char 3)))
|
|
(in-comment (nth 4 ppss))
|
|
(in-string (nth 8 ppss)))
|
|
(unless in-comment
|
|
(if in-string
|
|
;; We're in a string, so this must be the closing triple-quote.
|
|
;; Put | on the last " character.
|
|
(put-text-property (1- string-end-pos) string-end-pos
|
|
'syntax-table (string-to-syntax "|"))
|
|
;; We're not in a string, so this is the opening triple-quote.
|
|
;; Put | on the first " character.
|
|
(put-text-property string-start-pos (1+ string-start-pos)
|
|
'syntax-table (string-to-syntax "|"))))))
|
|
|
|
(defconst julia-syntax-propertize-function
|
|
(unless (< emacs-major-version 24)
|
|
(syntax-propertize-rules
|
|
("\"\"\""
|
|
(0 (ignore (julia-stringify-triple-quote))))
|
|
(julia-char-regex
|
|
(1 "\"") ; Treat ' as a string delimiter.
|
|
(2 ".") ; Don't highlight anything between.
|
|
(3 "\""))))) ; Treat the last " in """ as a string delimiter.
|
|
|
|
(defun julia-in-comment (&optional syntax-ppss)
|
|
"Return non-nil if point is inside a comment using SYNTAX-PPSS.
|
|
Handles both single-line and multi-line comments."
|
|
(nth 4 (or syntax-ppss (syntax-ppss))))
|
|
|
|
(defun julia-in-string (&optional syntax-ppss)
|
|
"Return non-nil if point is inside a string using SYNTAX-PPSS.
|
|
Note this is Emacs' notion of what is highlighted as a string.
|
|
As a result, it is true inside \"foo\", `foo` and 'f'."
|
|
(nth 3 (or syntax-ppss (syntax-ppss))))
|
|
|
|
(defun julia-in-brackets ()
|
|
"Return non-nil if point is inside square brackets."
|
|
(let ((start-pos (point))
|
|
(open-count 0))
|
|
;; Count all the [ and ] characters on the current line.
|
|
(save-excursion
|
|
(beginning-of-line)
|
|
|
|
(while (< (point) start-pos)
|
|
;; Don't count [ or ] inside strings, characters or comments.
|
|
(unless (or (julia-in-string) (julia-in-comment))
|
|
|
|
(when (looking-at (rx "["))
|
|
(incf open-count))
|
|
(when (looking-at (rx "]"))
|
|
(decf open-count)))
|
|
|
|
(forward-char 1)))
|
|
|
|
;; If we've opened more than we've closed, we're inside brackets.
|
|
(plusp open-count)))
|
|
|
|
(defun julia-at-keyword (kw-list)
|
|
"Return the word at point if it matches any keyword in KW-LIST.
|
|
KW-LIST is a list of strings. The word at point is not considered
|
|
a keyword if used as a field name, X.word, or quoted, :word."
|
|
(and (or (= (point) 1)
|
|
(and (not (equal (char-before (point)) ?.))
|
|
(not (equal (char-before (point)) ?:))))
|
|
(not (looking-at "(")) ; handle "function(" when on (
|
|
(member (current-word t) kw-list)
|
|
;; 'end' is not a keyword when used for indexing, e.g. foo[end-2]
|
|
(or (not (equal (current-word t) "end"))
|
|
(not (julia-in-brackets)))
|
|
(not (julia-in-comment))))
|
|
|
|
;; if backward-sexp gives an error, move back 1 char to move over the '('
|
|
(defun julia-safe-backward-sexp ()
|
|
(if (condition-case nil (backward-sexp) (error t))
|
|
(ignore-errors (backward-char))))
|
|
|
|
(defun julia-following-import-export-using ()
|
|
"If the current line follows an `export` or `import` keyword
|
|
with valid syntax, return the position of the keyword, otherwise
|
|
`nil`. Works by stepping backwards through comma-separated
|
|
symbol, gives up when this is not true."
|
|
;; Implementation accepts a single Module: right after the keyword, and saves
|
|
;; the module name for future use, but does not enforce that `export` has no
|
|
;; module name.
|
|
(let ((done nil) ; find keyword or give up
|
|
(module nil)) ; found "Module:"
|
|
(save-excursion
|
|
(beginning-of-line)
|
|
(while (and (not done) (< (point-min) (point)))
|
|
(julia-safe-backward-sexp)
|
|
(cond
|
|
((looking-at (rx (or "import" "export" "using")))
|
|
(setf done (point)))
|
|
((looking-at (rx (group (* (or word (syntax symbol)))) (0+ space) ":"))
|
|
(if module
|
|
(setf done 'broken)
|
|
(setf module (match-string-no-properties 1))))
|
|
((looking-at (rx (* (or word (syntax symbol))) (0+ space) ","))
|
|
(when module (setf done 'broken)))
|
|
(t (setf done 'broken)))))
|
|
(if (eq done 'broken)
|
|
nil
|
|
done)))
|
|
|
|
(defun julia-last-open-block-pos (min)
|
|
"Return the position of the last open block, if one found.
|
|
Do not move back beyond position MIN."
|
|
(save-excursion
|
|
(let ((count 0))
|
|
(while (not (or (> count 0) (<= (point) min)))
|
|
(julia-safe-backward-sexp)
|
|
(setq count
|
|
(cond ((julia-at-keyword julia-block-start-keywords)
|
|
(+ count 1))
|
|
((and (equal (current-word t) "end")
|
|
(not (julia-in-comment)))
|
|
(- count 1))
|
|
(t count))))
|
|
(if (> count 0)
|
|
(point)
|
|
nil))))
|
|
|
|
(defun julia-last-open-block (min)
|
|
"Move back and return indentation level for last open block.
|
|
Do not move back beyond MIN."
|
|
;; Ensure MIN is not before the start of the buffer.
|
|
(setq min (max min (point-min)))
|
|
(let ((pos (julia-last-open-block-pos min)))
|
|
(and pos
|
|
(progn
|
|
(goto-char pos)
|
|
(+ julia-indent-offset (current-indentation))))))
|
|
|
|
(defsubst julia--safe-backward-char ()
|
|
"Move back one character, but don't error if we're at the
|
|
beginning of the buffer."
|
|
(unless (eq (point) (point-min))
|
|
(backward-char)))
|
|
|
|
(defcustom julia-max-block-lookback 5000
|
|
"When indenting, don't look back more than this
|
|
many characters to see if there are unclosed blocks.
|
|
|
|
This variable has a moderate effect on indent performance if set too
|
|
high, but stops indenting in the middle of long blocks if set too low."
|
|
:type 'integer
|
|
:group 'julia)
|
|
|
|
(defun julia-paren-indent ()
|
|
"Return the column of the text following the innermost
|
|
containing paren before point, so we can align succeeding code
|
|
with it. Returns nil if we're not within nested parens."
|
|
(save-excursion
|
|
(beginning-of-line)
|
|
(let ((parser-state (syntax-ppss)))
|
|
(cond ((nth 3 parser-state) nil) ;; strings
|
|
((= (nth 0 parser-state) 0) nil) ;; top level
|
|
(t
|
|
(ignore-errors ;; return nil if any of these movements fail
|
|
(beginning-of-line)
|
|
(skip-syntax-forward " ")
|
|
(let ((possibly-close-paren-point (point)))
|
|
(backward-up-list)
|
|
(let ((open-paren-point (point)))
|
|
(forward-char)
|
|
(skip-syntax-forward " ")
|
|
(if (eolp)
|
|
(progn
|
|
(up-list)
|
|
(backward-char)
|
|
(let ((paren-closed (= (point) possibly-close-paren-point)))
|
|
(goto-char open-paren-point)
|
|
(beginning-of-line)
|
|
(skip-syntax-forward " ")
|
|
(+ (current-column)
|
|
(if paren-closed
|
|
0
|
|
julia-indent-offset))))
|
|
(current-column))))))))))
|
|
|
|
(defun julia-prev-line-skip-blank-or-comment ()
|
|
"Move point to beginning of previous line skipping blank lines
|
|
and lines including only comments. Returns number of lines moved.
|
|
A return of -1 signals that we moved to the first line of
|
|
the (possibly narrowed) buffer, so there is nowhere else to go."
|
|
(catch 'result
|
|
(let ((moved 0) this-move)
|
|
(while t
|
|
(setq this-move (forward-line -1))
|
|
(cond
|
|
;; moved into comment or blank
|
|
((and (= 0 this-move)
|
|
(or (looking-at-p "^\\s-*\\(?:#.*\\)*$")
|
|
(julia-in-comment)))
|
|
(incf moved))
|
|
;; success
|
|
((= 0 this-move)
|
|
(throw 'result (1+ moved)))
|
|
;; on first line and in comment
|
|
((and (bobp)
|
|
(or (looking-at-p "^\\s-*\\(?:#.*\\)*$")
|
|
(julia-in-comment)))
|
|
(throw 'result -1))
|
|
((bobp)
|
|
(throw 'result moved))
|
|
(t
|
|
(throw 'result 0)))))))
|
|
|
|
(defun julia-indent-hanging ()
|
|
"Calculate indentation for lines that follow \"hanging\"
|
|
operators (operators that end the previous line) as defined in
|
|
`julia-hanging-operator-regexp'. An assignment operator ending
|
|
the previous line increases the indent as do the other operators
|
|
unless another operator is found two lines up. Previous line
|
|
means previous line after skipping blank lines and lines with
|
|
only comments."
|
|
(let (prev-indent)
|
|
(save-excursion
|
|
(when (> (julia-prev-line-skip-blank-or-comment) 0)
|
|
(setq prev-indent (current-indentation))
|
|
(when (looking-at-p julia-hanging-operator-regexp)
|
|
(if (and (> (julia-prev-line-skip-blank-or-comment) 0)
|
|
(looking-at-p julia-hanging-operator-regexp))
|
|
;; two preceding hanging operators => indent same as line
|
|
;; above
|
|
prev-indent
|
|
;; one preceding hanging operator => increase indent from line
|
|
;; above
|
|
(+ julia-indent-offset prev-indent)))))))
|
|
|
|
(defun julia-indent-in-string ()
|
|
"Indentation inside strings with newlines is \"manual\",
|
|
meaning always increase indent on TAB and decrease on S-TAB."
|
|
(save-excursion
|
|
(beginning-of-line)
|
|
(when (julia-in-string)
|
|
(if (member this-command '(julia-latexsub-or-indent
|
|
ess-indent-or-complete))
|
|
(+ julia-indent-offset (current-indentation))
|
|
;; return the current indentation to prevent other functions from
|
|
;; indenting inside strings
|
|
(current-indentation)))))
|
|
|
|
(defun julia-indent-import-export-using ()
|
|
"Indent offset for lines that follow `import` or `export`, otherwise nil."
|
|
(when (julia-following-import-export-using)
|
|
julia-indent-offset))
|
|
|
|
(defun julia-indent-line ()
|
|
"Indent current line of julia code."
|
|
(interactive)
|
|
(let* ((point-offset (- (current-column) (current-indentation))))
|
|
(indent-line-to
|
|
(or
|
|
;; note: if this first function returns nil the beginning of the line
|
|
;; cannot be in a string
|
|
(julia-indent-in-string)
|
|
;; If we're inside an open paren, indent to line up arguments. After this,
|
|
;; we cannot be inside parens which includes brackets
|
|
(julia-paren-indent)
|
|
;; indent due to hanging operators (lines ending in an operator)
|
|
(julia-indent-hanging)
|
|
;; indent for import and export
|
|
(julia-indent-import-export-using)
|
|
;; Indent according to how many nested blocks we are in.
|
|
(save-excursion
|
|
(beginning-of-line)
|
|
;; jump out of any comments
|
|
(let ((state (syntax-ppss)))
|
|
(when (nth 4 state)
|
|
(goto-char (nth 8 state))))
|
|
(forward-to-indentation 0)
|
|
(let ((endtok (julia-at-keyword julia-block-end-keywords))
|
|
(last-open-block (julia-last-open-block (- (point) julia-max-block-lookback))))
|
|
(max 0 (+ (or last-open-block 0)
|
|
(if (or endtok
|
|
(julia-at-keyword julia-block-start-keywords-no-indent))
|
|
(- julia-indent-offset) 0)))))))
|
|
;; Point is now at the beginning of indentation, restore it
|
|
;; to its original position (relative to indentation).
|
|
(when (>= point-offset 0)
|
|
(move-to-column (+ (current-indentation) point-offset)))))
|
|
|
|
(defalias 'julia-mode-prog-mode
|
|
(if (fboundp 'prog-mode)
|
|
'prog-mode
|
|
'fundamental-mode))
|
|
|
|
|
|
;;; Navigation
|
|
;; based off python.el
|
|
(defconst julia-beginning-of-defun-regex
|
|
(concat julia-function-regex "\\|"
|
|
julia-function-assignment-regex "\\|"
|
|
"\\_<macro\\_>")
|
|
"Regex matching beginning of Julia function or macro.")
|
|
|
|
(defun julia-syntax-context-type (&optional syntax-ppss)
|
|
"Return the context type using SYNTAX-PPSS.
|
|
TYPE can be `comment', `string' or `paren'."
|
|
(let ((ppss (or syntax-ppss (syntax-ppss))))
|
|
(cond
|
|
((nth 8 ppss) (if (nth 4 ppss) 'comment 'string))
|
|
((nth 1 ppss) 'paren))))
|
|
|
|
(defsubst julia-syntax-comment-or-string-p (&optional syntax-ppss)
|
|
"Return non-nil if SYNTAX-PPSS is inside string or comment."
|
|
(nth 8 (or syntax-ppss (syntax-ppss))))
|
|
|
|
(defun julia-looking-at-beginning-of-defun (&optional syntax-ppss)
|
|
"Check if point is at `beginning-of-defun' using SYNTAX-PPSS."
|
|
(and (not (julia-syntax-comment-or-string-p (or syntax-ppss (syntax-ppss))))
|
|
(save-excursion
|
|
(beginning-of-line 1)
|
|
(looking-at julia-beginning-of-defun-regex))))
|
|
|
|
(defun julia--beginning-of-defun (&optional arg)
|
|
"Internal implementation of `julia-beginning-of-defun'.
|
|
With positive ARG search backwards, else search forwards."
|
|
(when (or (null arg) (= arg 0)) (setq arg 1))
|
|
(let* ((re-search-fn (if (> arg 0)
|
|
#'re-search-backward
|
|
#'re-search-forward))
|
|
(line-beg-pos (line-beginning-position))
|
|
(line-content-start (+ line-beg-pos (current-indentation)))
|
|
(pos (point-marker))
|
|
(beg-indentation
|
|
(and (> arg 0)
|
|
(save-excursion
|
|
(while (and (not (julia-looking-at-beginning-of-defun))
|
|
;; f(x) = ... function bodies may span multiple lines
|
|
(or (and (julia-indent-hanging)
|
|
(forward-line -1))
|
|
;; inside dangling parameter list
|
|
(and (eq 'paren (julia-syntax-context-type))
|
|
(backward-up-list))
|
|
(julia-last-open-block (point-min)))))
|
|
(or (and (julia-looking-at-beginning-of-defun)
|
|
(+ (current-indentation) julia-indent-offset))
|
|
0))))
|
|
(found
|
|
(progn
|
|
(when (and (< arg 0)
|
|
(julia-looking-at-beginning-of-defun))
|
|
(end-of-line 1))
|
|
(while (and (funcall re-search-fn
|
|
julia-beginning-of-defun-regex nil t)
|
|
(or (julia-syntax-comment-or-string-p)
|
|
;; handle nested defuns when moving backwards
|
|
;; by checking matching indentation
|
|
(and (> arg 0)
|
|
(not (= (current-indentation) 0))
|
|
(>= (current-indentation) beg-indentation)))))
|
|
(and (julia-looking-at-beginning-of-defun)
|
|
(or (not (= (line-number-at-pos pos)
|
|
(line-number-at-pos)))
|
|
(and (>= (point) line-beg-pos)
|
|
(<= (point) line-content-start)
|
|
(> pos line-content-start)))))))
|
|
(if found
|
|
(or (beginning-of-line 1) (point))
|
|
(and (goto-char pos) nil))))
|
|
|
|
(defun julia-beginning-of-defun (&optional arg)
|
|
"Move point to `beginning-of-defun'.
|
|
With positive ARG search backwards else search forward.
|
|
ARG nil or 0 defaults to 1. When searching backwards,
|
|
nested defuns are handled depending on current point position.
|
|
Return non-nil (point) if point moved to `beginning-of-defun'."
|
|
(when (or (null arg) (= arg 0)) (setq arg 1))
|
|
(let ((found))
|
|
(while (and (not (= arg 0))
|
|
(let ((keep-searching-p
|
|
(julia--beginning-of-defun arg)))
|
|
(when (and keep-searching-p (null found))
|
|
(setq found t))
|
|
keep-searching-p))
|
|
(setq arg (if (> arg 0) (1- arg) (1+ arg))))
|
|
found))
|
|
|
|
(defun julia-end-of-defun (&optional arg)
|
|
"Move point to the end of the current function.
|
|
Return nil if point is not in a function, otherwise point."
|
|
(interactive)
|
|
(let ((beg-defun-indent)
|
|
(beg-pos (point)))
|
|
(when (or (julia-looking-at-beginning-of-defun)
|
|
(julia-beginning-of-defun 1)
|
|
(julia-beginning-of-defun -1))
|
|
(beginning-of-line)
|
|
(if (looking-at-p julia-function-assignment-regex)
|
|
;; f(x) = ...
|
|
(progn
|
|
;; skip any dangling lines
|
|
(while (and (forward-line)
|
|
(not (eobp))
|
|
(or (julia-indent-hanging)
|
|
;; dangling closing paren
|
|
(and (eq 'paren (julia-syntax-context-type))
|
|
(search-forward ")"))))))
|
|
;; otherwise skip forward to matching indentation (not in string/comment)
|
|
(setq beg-defun-indent (current-indentation))
|
|
(while (and (not (eobp))
|
|
(forward-line 1)
|
|
(or (julia-syntax-comment-or-string-p)
|
|
(> (current-indentation) beg-defun-indent)))))
|
|
(end-of-line)
|
|
(point))))
|
|
|
|
;;; IMENU
|
|
(defvar julia-imenu-generic-expression
|
|
;; don't use syntax classes, screws egrep
|
|
'(("Function (_)" "[ \t]*function[ \t]+\\(_[^ \t\n]*\\)" 1)
|
|
("Function" "^[ \t]*function[ \t]+\\([^_][^\t\n]*\\)" 1)
|
|
("Const" "[ \t]*const \\([^ \t\n]*\\)" 1)
|
|
("Type" "^[ \t]*[a-zA-Z0-9_]*type[a-zA-Z0-9_]* \\([^ \t\n]*\\)" 1)
|
|
("Require" " *\\(\\brequire\\)(\\([^ \t\n)]*\\)" 2)
|
|
("Include" " *\\(\\binclude\\)(\\([^ \t\n)]*\\)" 2)
|
|
;; ("Classes" "^.*setClass(\\(.*\\)," 1)
|
|
;; ("Coercions" "^.*setAs(\\([^,]+,[^,]*\\)," 1) ; show from and to
|
|
;; ("Generics" "^.*setGeneric(\\([^,]*\\)," 1)
|
|
;; ("Methods" "^.*set\\(Group\\|Replace\\)?Method(\"\\(.+\\)\"," 2)
|
|
;; ;;[ ]*\\(signature=\\)?(\\(.*,?\\)*\\)," 1)
|
|
;; ;;
|
|
;; ;;("Other" "^\\(.+\\)\\s-*<-[ \t\n]*[^\\(function\\|read\\|.*data\.frame\\)]" 1)
|
|
;; ("Package" "^.*\\(library\\|require\\)(\\(.*\\)," 2)
|
|
;; ("Data" "^\\(.+\\)\\s-*<-[ \t\n]*\\(read\\|.*data\.frame\\).*(" 1)))
|
|
))
|
|
|
|
;;;###autoload
|
|
(define-derived-mode julia-mode julia-mode-prog-mode "Julia"
|
|
"Major mode for editing julia code."
|
|
(set-syntax-table julia-mode-syntax-table)
|
|
(set (make-local-variable 'comment-start) "# ")
|
|
(set (make-local-variable 'comment-start-skip) "#+\\s-*")
|
|
(set (make-local-variable 'font-lock-defaults) '(julia-font-lock-keywords))
|
|
(if (< emacs-major-version 24)
|
|
;; Emacs 23 doesn't have syntax-propertize-function
|
|
(set (make-local-variable 'font-lock-syntactic-keywords)
|
|
(list
|
|
`(,julia-char-regex
|
|
(1 "\"") ; Treat ' as a string delimiter.
|
|
(2 ".") ; Don't highlight anything between the open and close '.
|
|
(3 "\"")) ; Treat the close ' as a string delimiter.
|
|
`(,julia-triple-quoted-string-regex
|
|
(1 "\"") ; Treat the first " in """ as a string delimiter.
|
|
(2 ".") ; Don't highlight anything between.
|
|
(3 "\"")))) ; Treat the last " in """ as a string delimiter.
|
|
;; Emacs 24 and later has syntax-propertize-function, so use that instead.
|
|
(set (make-local-variable 'syntax-propertize-function)
|
|
julia-syntax-propertize-function))
|
|
(set (make-local-variable 'indent-line-function) 'julia-indent-line)
|
|
(set (make-local-variable 'beginning-of-defun-function) #'julia-beginning-of-defun)
|
|
(set (make-local-variable 'end-of-defun-function) #'julia-end-of-defun)
|
|
(setq indent-tabs-mode nil)
|
|
(setq imenu-generic-expression julia-imenu-generic-expression)
|
|
(imenu-add-to-menubar "Imenu"))
|
|
|
|
(defun julia-manual-deindent ()
|
|
"Deindent by `julia-indent-offset' regardless of current
|
|
indentation context. To be used to manually indent inside
|
|
strings."
|
|
(interactive)
|
|
(indent-line-to (max 0 (- (current-indentation) julia-indent-offset))))
|
|
(define-key julia-mode-map (kbd "<backtab>") 'julia-manual-deindent)
|
|
|
|
(defvar julia-latexsubs (make-hash-table :test 'equal))
|
|
|
|
(defun julia-latexsub ()
|
|
"Perform a LaTeX-like Unicode symbol substitution."
|
|
(interactive "*i")
|
|
(let ((orig-pt (point)))
|
|
(while (not (or (bobp) (= ?\\ (char-before))
|
|
(= ?\s (char-syntax (char-before)))))
|
|
(backward-char))
|
|
(if (and (not (bobp)) (= ?\\ (char-before)))
|
|
(progn
|
|
(backward-char)
|
|
(let ((sub (gethash (buffer-substring (point) orig-pt) julia-latexsubs)))
|
|
(if sub
|
|
(progn
|
|
(delete-region (point) orig-pt)
|
|
(insert sub))
|
|
(goto-char orig-pt))))
|
|
(goto-char orig-pt))))
|
|
|
|
(defalias 'latexsub 'julia-latexsub)
|
|
|
|
(defun julia-latexsub-or-indent (arg)
|
|
"Either indent according to mode or perform a LaTeX-like symbol substution"
|
|
(interactive "*i")
|
|
(if (latexsub)
|
|
(indent-for-tab-command arg)))
|
|
(define-key julia-mode-map (kbd "TAB") 'julia-latexsub-or-indent)
|
|
|
|
(defalias 'latexsub-or-indent 'julia-latexsub-or-indent)
|
|
|
|
;;; populate LaTeX symbols hash table from a generated file.
|
|
;;; (See Julia issue #8947 for why we don't use the Emacs tex input mode.)
|
|
(load (expand-file-name "julia-latexsubs" (file-name-directory load-file-name)))
|
|
|
|
;; Math insertion in julia. Use it with
|
|
;; (add-hook 'julia-mode-hook 'julia-math-mode)
|
|
;; (add-hook 'inferior-julia-mode-hook 'julia-math-mode)
|
|
|
|
(when (require 'latex nil t)
|
|
(declare-function LaTeX-math-abbrev-prefix "latex")
|
|
|
|
(defun julia-math-insert (s)
|
|
"Inserts math symbol given by `s'"
|
|
(when s
|
|
(let ((sym (gethash (concat "\\" s) julia-latexsubs)))
|
|
(when sym
|
|
(insert sym)))))
|
|
|
|
(with-no-warnings
|
|
(define-minor-mode julia-math-mode
|
|
"A minor mode with easy access to TeX math commands. The
|
|
command is only entered if it is supported in Julia. The
|
|
following commands are defined:
|
|
|
|
\\{LaTeX-math-mode-map}"
|
|
nil nil (list (cons (LaTeX-math-abbrev-prefix) LaTeX-math-keymap))
|
|
(if julia-math-mode
|
|
(set (make-local-variable 'LaTeX-math-insert-function)
|
|
'julia-math-insert)))))
|
|
|
|
;; Code for `inferior-julia-mode'
|
|
(require 'comint)
|
|
|
|
(defcustom julia-program "julia"
|
|
"Path to the program used by `inferior-julia'."
|
|
:type 'string
|
|
:group 'julia)
|
|
|
|
(defcustom julia-arguments '("-i" "--color=yes")
|
|
"Commandline arguments to pass to `julia-program'."
|
|
:type '(repeat (string :tag "argument"))
|
|
:group 'julia)
|
|
|
|
(defvar julia-prompt-regexp "^\\w*> "
|
|
"Regexp for matching `inferior-julia' prompt.")
|
|
|
|
(defvar inferior-julia-mode-map
|
|
(let ((map (nconc (make-sparse-keymap) comint-mode-map)))
|
|
;; example definition
|
|
(define-key map (kbd "TAB") 'julia-latexsub-or-indent)
|
|
map)
|
|
"Basic mode map for `inferior-julia-mode'.")
|
|
|
|
;;;###autoload
|
|
(defun inferior-julia ()
|
|
"Run an inferior instance of `julia' inside Emacs."
|
|
(interactive)
|
|
(let ((julia-program julia-program)
|
|
(buffer (get-buffer-create "*Julia*")))
|
|
(when (not (comint-check-proc "*Julia*"))
|
|
(apply #'make-comint-in-buffer "Julia" "*Julia*"
|
|
julia-program nil julia-arguments))
|
|
(pop-to-buffer-same-window "*Julia*")
|
|
(inferior-julia-mode)))
|
|
|
|
(defun inferior-julia--initialize ()
|
|
"Helper function to initialize `inferior-julia'."
|
|
(setq comint-use-prompt-regexp t))
|
|
|
|
(define-derived-mode inferior-julia-mode comint-mode "Julia"
|
|
"Major mode for `inferior-julia'.
|
|
|
|
\\<inferior-julia-mode-map>"
|
|
nil "Julia"
|
|
(setq comint-prompt-regexp julia-prompt-regexp)
|
|
(setq comint-prompt-read-only t)
|
|
(set (make-local-variable 'font-lock-defaults) '(julia-font-lock-keywords t))
|
|
(set (make-local-variable 'paragraph-start) julia-prompt-regexp)
|
|
(set (make-local-variable 'indent-line-function) 'julia-indent-line))
|
|
|
|
(add-hook 'inferior-julia-mode-hook 'inferior-julia--initialize)
|
|
|
|
;;;###autoload
|
|
(defalias 'run-julia #'inferior-julia
|
|
"Run an inferior instance of `julia' inside Emacs.")
|
|
|
|
(provide 'julia-mode)
|
|
|
|
;; Local Variables:
|
|
;; coding: utf-8
|
|
;; byte-compile-warnings: (not obsolete)
|
|
;; End:
|
|
;;; julia-mode.el ends here
|