Klimi's new dotfiles with stow.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

946 lines
39 KiB

пре 5 година
  1. ;;; julia-mode.el --- Major mode for editing Julia source code
  2. ;; Copyright (C) 2009-2014 Julia contributors
  3. ;; URL: https://github.com/JuliaLang/julia
  4. ;; Version: 0.3
  5. ;; Keywords: languages
  6. ;;; Usage:
  7. ;; Put the following code in your .emacs, site-load.el, or other relevant file
  8. ;; (add-to-list 'load-path "path-to-julia-mode")
  9. ;; (require 'julia-mode)
  10. ;;; Commentary:
  11. ;; This is the official Emacs mode for editing Julia programs.
  12. ;;; License:
  13. ;; Permission is hereby granted, free of charge, to any person obtaining
  14. ;; a copy of this software and associated documentation files (the
  15. ;; "Software"), to deal in the Software without restriction, including
  16. ;; without limitation the rights to use, copy, modify, merge, publish,
  17. ;; distribute, sublicense, and/or sell copies of the Software, and to
  18. ;; permit persons to whom the Software is furnished to do so, subject to
  19. ;; the following conditions:
  20. ;;
  21. ;; The above copyright notice and this permission notice shall be
  22. ;; included in all copies or substantial portions of the Software.
  23. ;;
  24. ;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  25. ;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  26. ;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  27. ;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  28. ;; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  29. ;; OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  30. ;; WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  31. ;;; Code:
  32. ;; We can't use cl-lib whilst supporting Emacs 23 users who don't use
  33. ;; ELPA.
  34. (with-no-warnings
  35. (require 'cl)) ;; incf, decf, plusp
  36. (defvar julia-mode-hook nil)
  37. (defgroup julia ()
  38. "Major mode for the julia programming language."
  39. :group 'languages
  40. :prefix "julia-")
  41. (defcustom julia-indent-offset 4
  42. "Number of spaces per indentation level."
  43. :type 'integer
  44. :group 'julia)
  45. (defface julia-macro-face
  46. '((t :inherit font-lock-preprocessor-face))
  47. "Face for Julia macro invocations."
  48. :group 'julia-mode)
  49. (defface julia-quoted-symbol-face
  50. '((t :inherit font-lock-preprocessor-face))
  51. "Face for quoted Julia symbols, e.g. :foo."
  52. :group 'julia-mode)
  53. ;;;###autoload
  54. (add-to-list 'auto-mode-alist '("\\.jl\\'" . julia-mode))
  55. ;; define ignore-errors macro if it isn't present
  56. ;; (necessary for emacs 22 compatibility)
  57. (when (not (fboundp 'ignore-errors))
  58. (defmacro ignore-errors (body) `(condition-case nil ,body (error nil))))
  59. (defun julia--regexp-opt (strings &optional paren)
  60. "Emacs 23 provides `regexp-opt', but it does not support PAREN taking the value 'symbols.
  61. This function provides equivalent functionality, but makes no efforts to optimise the regexp."
  62. (cond
  63. ((>= emacs-major-version 24)
  64. (regexp-opt strings paren))
  65. ((not (eq paren 'symbols))
  66. (regexp-opt strings paren))
  67. ((null strings)
  68. "")
  69. ('t
  70. (rx-to-string `(seq symbol-start (or ,@strings) symbol-end)))))
  71. (defvar julia-mode-syntax-table
  72. (let ((table (make-syntax-table)))
  73. (modify-syntax-entry ?_ "_" table)
  74. (modify-syntax-entry ?@ "_" table)
  75. (modify-syntax-entry ?! "_" table)
  76. (modify-syntax-entry ?# "< 14" table) ; # single-line and multiline start
  77. (modify-syntax-entry ?= ". 23bn" table)
  78. (modify-syntax-entry ?\n ">" table) ; \n single-line comment end
  79. (modify-syntax-entry ?\{ "(} " table)
  80. (modify-syntax-entry ?\} "){ " table)
  81. (modify-syntax-entry ?\[ "(] " table)
  82. (modify-syntax-entry ?\] ")[ " table)
  83. (modify-syntax-entry ?\( "() " table)
  84. (modify-syntax-entry ?\) ")( " table)
  85. ;; Here, we treat ' as punctuation (when it's used for transpose),
  86. ;; see our use of `julia-char-regex' for handling ' as a character
  87. ;; delimiter
  88. (modify-syntax-entry ?' "." table)
  89. (modify-syntax-entry ?\" "\"" table)
  90. (modify-syntax-entry ?` "\"" table)
  91. (modify-syntax-entry ?\\ "\\" table)
  92. (modify-syntax-entry ?. "." table)
  93. (modify-syntax-entry ?? "." table)
  94. (modify-syntax-entry ?$ "." table)
  95. (modify-syntax-entry ?& "." table)
  96. (modify-syntax-entry ?* "." table)
  97. (modify-syntax-entry ?/ "." table)
  98. (modify-syntax-entry ?+ "." table)
  99. (modify-syntax-entry ?- "." table)
  100. (modify-syntax-entry ?< "." table)
  101. (modify-syntax-entry ?> "." table)
  102. (modify-syntax-entry ?% "." table)
  103. (modify-syntax-entry ?′ "w" table) ; \prime is a word constituent
  104. table)
  105. "Syntax table for `julia-mode'.")
  106. (eval-when-compile
  107. (defconst julia-char-regex
  108. (rx (or (any "-" ";" "\\" "^" "!" "|" "?" "*" "<" "%" "," "=" ">" "+" "/" "&" "$" "~" ":")
  109. (syntax open-parenthesis)
  110. (syntax whitespace)
  111. bol)
  112. (group "'")
  113. (group
  114. (or (repeat 0 8 (not (any "'"))) (not (any "\\"))
  115. "\\\\"))
  116. (group "'"))))
  117. (defconst julia-hanging-operator-regexp
  118. ;; taken from julia-parser.scm
  119. (concat "^[^#\n]+ "
  120. (regexp-opt
  121. '( ;; conditional
  122. "?"
  123. ;; assignment
  124. "=" ":=" "+=" "-=" "*=" "/=" "//=" ".//=" ".*=" "./=" "\\=" ".\\="
  125. "^=" ".^=" "÷=" ".÷=" "%=" ".%=" "|=" "&=" "$=" "=>" "<<=" ">>="
  126. ">>>=" "~" ".+=" ".-="
  127. ;; arrow
  128. "--" "-->" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
  129. "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
  130. "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
  131. "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
  132. "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
  133. "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
  134. "" "" "" "" "" "" "" "" "" "" "⬿" "" "" "" "" ""
  135. "" "" "" "" "" "" "" ""
  136. ;; or and and
  137. "&&" "||"
  138. ;; comparison
  139. ">" "<" ">=" "" "<=" "" "==" "===" "" "!=" "" "!==" "" ".>"
  140. ".<" ".>=" ".≥" ".<=" ".≤" ".==" ".!=" ".≠" ".=" ".!" "<:" ">:" ""
  141. "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
  142. "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
  143. "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
  144. "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
  145. "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
  146. "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
  147. "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
  148. "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
  149. "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
  150. "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
  151. "" "" "⩿" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
  152. "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
  153. "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
  154. "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "⪿" ""
  155. "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
  156. "" "" "" "" "" "" "" "" "" "" "" "" "" ""
  157. ;; pipe, colon
  158. "|>" "<|" ":" ".."
  159. ;; plus
  160. "+" "-" "" "" "" "" ".+" ".-" "++" "|" "" "" "$" "" "±" ""
  161. "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
  162. "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
  163. "" "" "" "" "" "" "" "" ""
  164. ;; bitshift
  165. "<<" ">>" ">>>" ".<<" ".>>" ".>>>"
  166. ;; times
  167. "*" "/" "./" "÷" "" "%" "" "" "×" ".%" ".*" "\\"
  168. ".\\" "&" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
  169. "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "⦿" ""
  170. "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
  171. "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
  172. "" ""
  173. ;; rational
  174. "//" ".//"
  175. ;; power
  176. "^" ".^" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
  177. "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
  178. ;; decl, dot
  179. "::" "."))
  180. (regexp-opt '(" #" " \n" "#" "\n"))))
  181. (defconst julia-triple-quoted-string-regex
  182. ;; We deliberately put a group on the first and last delimiter, so
  183. ;; we can mark these as string delimiters for font-lock.
  184. (rx (group "\"")
  185. (group "\"\""
  186. ;; After the delimiter, we're a sequence of
  187. ;; non-backslashes or blackslashes paired with something.
  188. (*? (or (not (any "\\"))
  189. (seq "\\" anything)))
  190. "\"\"")
  191. (group "\"")))
  192. (defconst julia-unquote-regex
  193. "\\(\\s(\\|\\s-\\|-\\|[,%=<>\\+*/?&|!\\^~\\\\;:]\\|^\\)\\($[a-zA-Z0-9_]+\\)")
  194. (defconst julia-forloop-in-regex
  195. "for +.*[^
  196. ].* \\(in\\)\\(\\s-\\|$\\)+")
  197. (defconst julia-function-regex
  198. (rx line-start (* (or space "@inline" "@noinline")) symbol-start
  199. "function"
  200. (1+ space)
  201. ;; Don't highlight module names in function declarations:
  202. (* (seq (1+ (or word (syntax symbol))) "."))
  203. ;; The function name itself
  204. (group (1+ (or word (syntax symbol))))))
  205. (defconst julia-function-assignment-regex
  206. (rx line-start (* (or space "@inline" "@noinline")) symbol-start
  207. (* (seq (1+ (or word (syntax symbol))) ".")) ; module name
  208. (group (1+ (or word (syntax symbol))))
  209. (? "{" (* (not (any "}"))) "}")
  210. "(" (* (or
  211. (seq "(" (* (not (any "(" ")"))) ")")
  212. (not (any "(" ")"))))
  213. ")"
  214. (* space)
  215. (? "::" (* space) (1+ (not (any space))))
  216. (* space)
  217. (* (seq "where" (or "{" (+ space)) (+ (not (any "=")))))
  218. "="
  219. (not (any "="))))
  220. (defconst julia-type-regex
  221. (rx symbol-start (or ;;"immutable" "type" ;; remove after 0.6
  222. "abstract type" "primitive type" "struct" "mutable struct")
  223. (1+ space) (group (1+ (or word (syntax symbol))))))
  224. (defconst julia-type-annotation-regex
  225. (rx "::" (0+ space) (group (1+ (or word (syntax symbol))))))
  226. ;;(defconst julia-type-parameter-regex
  227. ;; (rx symbol-start (1+ (or (or word (syntax symbol)) ?_)) "{" (group (1+ (or (or word (syntax symbol)) ?_))) "}"))
  228. (defconst julia-subtype-regex
  229. (rx "<:" (0+ space) (group (1+ (or word (syntax symbol)))) (0+ space) (or "\n" "{" "}" "end")))
  230. (defconst julia-macro-regex
  231. (rx symbol-start (group "@" (1+ (or word (syntax symbol))))))
  232. (defconst julia-keyword-regex
  233. (julia--regexp-opt
  234. '("if" "else" "elseif" "while" "for" "begin" "end" "quote"
  235. "try" "catch" "return" "local" "function" "macro" "ccall"
  236. "finally" "break" "continue" "global" "where"
  237. "module" "using" "import" "export" "const" "let" "do" "in"
  238. "baremodule"
  239. ;; "importall" ;; deprecated in 0.7
  240. ;; "immutable" "type" "bitstype" "abstract" "typealias" ;; removed in 1.0
  241. "abstract type" "primitive type" "struct" "mutable struct")
  242. 'symbols))
  243. (defconst julia-builtin-regex
  244. (julia--regexp-opt
  245. ;;'("error" "throw")
  246. '()
  247. 'symbols))
  248. (defconst julia-builtin-types-regex
  249. (julia--regexp-opt
  250. '("Number" "Real" "BigInt" "Integer"
  251. "UInt" "UInt8" "UInt16" "UInt32" "UInt64" "UInt128"
  252. "Int" "Int8" "Int16" "Int32" "Int64" "Int128"
  253. "BigFloat" "AbstractFloat" "Float16" "Float32" "Float64"
  254. ;;"Complex128" "Complex64" ;; replaced in 1.0
  255. "ComplexF32" "ComplexF64"
  256. "Bool"
  257. "Cuchar" "Cshort" "Cushort" "Cint" "Cuint" "Clonglong" "Culonglong" "Cintmax_t" "Cuintmax_t"
  258. "Cfloat" "Cdouble" "Cptrdiff_t" "Cssize_t" "Csize_t"
  259. "Cchar" "Clong" "Culong" "Cwchar_t" "Cvoid"
  260. "Cstring" "Cwstring" ;; C strings made of ordinary and wide characters
  261. "Char" "String" "SubString"
  262. "Array" "DArray" "AbstractArray" "AbstractVector" "AbstractMatrix" "AbstractSparseMatrix" "SubArray" "StridedArray" "StridedVector" "StridedMatrix" "VecOrMat" "StridedVecOrMat" "DenseArray" "SparseMatrixCSC" "BitArray"
  263. "AbstractRange" "OrdinalRange" "StepRange" "UnitRange" "FloatRange"
  264. "Tuple" "NTuple" "Vararg"
  265. "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"
  266. "AbstractRNG" "MersenneTwister"
  267. )
  268. 'symbols))
  269. (defconst julia-quoted-symbol-regex
  270. ;; :foo and :foo2 are valid, but :123 is not.
  271. (rx (or bol whitespace "(" "[" "," "=")
  272. (group ":" (or letter (syntax symbol)) (0+ (or word (syntax symbol))))))
  273. (defconst julia-font-lock-keywords
  274. (list
  275. ;; Ensure :: and <: aren't highlighted, so we don't confuse ::Foo with :foo.
  276. ;; (in Emacs, keywords don't overlap).
  277. (cons (rx (or "::" "<:")) ''default)
  278. ;; Highlight quoted symbols before keywords, so :function is not
  279. ;; highlighted as a keyword.
  280. (list julia-quoted-symbol-regex 1 ''julia-quoted-symbol-face)
  281. (cons julia-builtin-types-regex 'font-lock-type-face)
  282. (cons julia-keyword-regex 'font-lock-keyword-face)
  283. (cons julia-macro-regex ''julia-macro-face)
  284. (cons
  285. (julia--regexp-opt
  286. '("true" "false" "C_NULL" "Inf" "NaN" "Inf32" "NaN32" "nothing" "undef")
  287. 'symbols)
  288. 'font-lock-constant-face)
  289. (list julia-unquote-regex 2 'font-lock-constant-face)
  290. (list julia-forloop-in-regex 1 'font-lock-keyword-face)
  291. (list julia-function-regex 1 'font-lock-function-name-face)
  292. (list julia-function-assignment-regex 1 'font-lock-function-name-face)
  293. (list julia-type-regex 1 'font-lock-type-face)
  294. (list julia-type-annotation-regex 1 'font-lock-type-face)
  295. ;;(list julia-type-parameter-regex 1 'font-lock-type-face)
  296. (list julia-subtype-regex 1 'font-lock-type-face)
  297. (list julia-builtin-regex 1 'font-lock-builtin-face)
  298. ))
  299. (defconst julia-block-start-keywords
  300. (list "if" "while" "for" "begin" "try" "function" "let" "macro"
  301. "quote" "do" "module"
  302. ;; "immutable" "type" ;; remove after 0.6
  303. "abstract type" "primitive type" "struct" "mutable struct"))
  304. ;; For keywords that begin a block without additional indentation
  305. (defconst julia-block-start-keywords-no-indent
  306. (list "module"))
  307. (defconst julia-block-end-keywords
  308. (list "end" "else" "elseif" "catch" "finally"))
  309. (defun julia-stringify-triple-quote ()
  310. "Put `syntax-table' property on triple-quoted string delimiters.
  311. Based on `python-syntax-stringify'."
  312. (let* ((string-start-pos (- (point) 3))
  313. (string-end-pos (point))
  314. (ppss (prog2
  315. (backward-char 3)
  316. (syntax-ppss)
  317. (forward-char 3)))
  318. (in-comment (nth 4 ppss))
  319. (in-string (nth 8 ppss)))
  320. (unless in-comment
  321. (if in-string
  322. ;; We're in a string, so this must be the closing triple-quote.
  323. ;; Put | on the last " character.
  324. (put-text-property (1- string-end-pos) string-end-pos
  325. 'syntax-table (string-to-syntax "|"))
  326. ;; We're not in a string, so this is the opening triple-quote.
  327. ;; Put | on the first " character.
  328. (put-text-property string-start-pos (1+ string-start-pos)
  329. 'syntax-table (string-to-syntax "|"))))))
  330. (defconst julia-syntax-propertize-function
  331. (unless (< emacs-major-version 24)
  332. (syntax-propertize-rules
  333. ("\"\"\""
  334. (0 (ignore (julia-stringify-triple-quote))))
  335. (julia-char-regex
  336. (1 "\"") ; Treat ' as a string delimiter.
  337. (2 ".") ; Don't highlight anything between.
  338. (3 "\""))))) ; Treat the last " in """ as a string delimiter.
  339. (defun julia-in-comment (&optional syntax-ppss)
  340. "Return non-nil if point is inside a comment using SYNTAX-PPSS.
  341. Handles both single-line and multi-line comments."
  342. (nth 4 (or syntax-ppss (syntax-ppss))))
  343. (defun julia-in-string (&optional syntax-ppss)
  344. "Return non-nil if point is inside a string using SYNTAX-PPSS.
  345. Note this is Emacs' notion of what is highlighted as a string.
  346. As a result, it is true inside \"foo\", `foo` and 'f'."
  347. (nth 3 (or syntax-ppss (syntax-ppss))))
  348. (defun julia-in-brackets ()
  349. "Return non-nil if point is inside square brackets."
  350. (let ((start-pos (point))
  351. (open-count 0))
  352. ;; Count all the [ and ] characters on the current line.
  353. (save-excursion
  354. (beginning-of-line)
  355. (while (< (point) start-pos)
  356. ;; Don't count [ or ] inside strings, characters or comments.
  357. (unless (or (julia-in-string) (julia-in-comment))
  358. (when (looking-at (rx "["))
  359. (incf open-count))
  360. (when (looking-at (rx "]"))
  361. (decf open-count)))
  362. (forward-char 1)))
  363. ;; If we've opened more than we've closed, we're inside brackets.
  364. (plusp open-count)))
  365. (defun julia-at-keyword (kw-list)
  366. "Return the word at point if it matches any keyword in KW-LIST.
  367. KW-LIST is a list of strings. The word at point is not considered
  368. a keyword if used as a field name, X.word, or quoted, :word."
  369. (and (or (= (point) 1)
  370. (and (not (equal (char-before (point)) ?.))
  371. (not (equal (char-before (point)) ?:))))
  372. (not (looking-at "(")) ; handle "function(" when on (
  373. (member (current-word t) kw-list)
  374. ;; 'end' is not a keyword when used for indexing, e.g. foo[end-2]
  375. (or (not (equal (current-word t) "end"))
  376. (not (julia-in-brackets)))
  377. (not (julia-in-comment))))
  378. ;; if backward-sexp gives an error, move back 1 char to move over the '('
  379. (defun julia-safe-backward-sexp ()
  380. (if (condition-case nil (backward-sexp) (error t))
  381. (ignore-errors (backward-char))))
  382. (defun julia-following-import-export-using ()
  383. "If the current line follows an `export` or `import` keyword
  384. with valid syntax, return the position of the keyword, otherwise
  385. `nil`. Works by stepping backwards through comma-separated
  386. symbol, gives up when this is not true."
  387. ;; Implementation accepts a single Module: right after the keyword, and saves
  388. ;; the module name for future use, but does not enforce that `export` has no
  389. ;; module name.
  390. (let ((done nil) ; find keyword or give up
  391. (module nil)) ; found "Module:"
  392. (save-excursion
  393. (beginning-of-line)
  394. (while (and (not done) (< (point-min) (point)))
  395. (julia-safe-backward-sexp)
  396. (cond
  397. ((looking-at (rx (or "import" "export" "using")))
  398. (setf done (point)))
  399. ((looking-at (rx (group (* (or word (syntax symbol)))) (0+ space) ":"))
  400. (if module
  401. (setf done 'broken)
  402. (setf module (match-string-no-properties 1))))
  403. ((looking-at (rx (* (or word (syntax symbol))) (0+ space) ","))
  404. (when module (setf done 'broken)))
  405. (t (setf done 'broken)))))
  406. (if (eq done 'broken)
  407. nil
  408. done)))
  409. (defun julia-last-open-block-pos (min)
  410. "Return the position of the last open block, if one found.
  411. Do not move back beyond position MIN."
  412. (save-excursion
  413. (let ((count 0))
  414. (while (not (or (> count 0) (<= (point) min)))
  415. (julia-safe-backward-sexp)
  416. (setq count
  417. (cond ((julia-at-keyword julia-block-start-keywords)
  418. (+ count 1))
  419. ((and (equal (current-word t) "end")
  420. (not (julia-in-comment)))
  421. (- count 1))
  422. (t count))))
  423. (if (> count 0)
  424. (point)
  425. nil))))
  426. (defun julia-last-open-block (min)
  427. "Move back and return indentation level for last open block.
  428. Do not move back beyond MIN."
  429. ;; Ensure MIN is not before the start of the buffer.
  430. (setq min (max min (point-min)))
  431. (let ((pos (julia-last-open-block-pos min)))
  432. (and pos
  433. (progn
  434. (goto-char pos)
  435. (+ julia-indent-offset (current-indentation))))))
  436. (defsubst julia--safe-backward-char ()
  437. "Move back one character, but don't error if we're at the
  438. beginning of the buffer."
  439. (unless (eq (point) (point-min))
  440. (backward-char)))
  441. (defcustom julia-max-block-lookback 5000
  442. "When indenting, don't look back more than this
  443. many characters to see if there are unclosed blocks.
  444. This variable has a moderate effect on indent performance if set too
  445. high, but stops indenting in the middle of long blocks if set too low."
  446. :type 'integer
  447. :group 'julia)
  448. (defun julia-paren-indent ()
  449. "Return the column of the text following the innermost
  450. containing paren before point, so we can align succeeding code
  451. with it. Returns nil if we're not within nested parens."
  452. (save-excursion
  453. (beginning-of-line)
  454. (let ((parser-state (syntax-ppss)))
  455. (cond ((nth 3 parser-state) nil) ;; strings
  456. ((= (nth 0 parser-state) 0) nil) ;; top level
  457. (t
  458. (ignore-errors ;; return nil if any of these movements fail
  459. (beginning-of-line)
  460. (skip-syntax-forward " ")
  461. (let ((possibly-close-paren-point (point)))
  462. (backward-up-list)
  463. (let ((open-paren-point (point)))
  464. (forward-char)
  465. (skip-syntax-forward " ")
  466. (if (eolp)
  467. (progn
  468. (up-list)
  469. (backward-char)
  470. (let ((paren-closed (= (point) possibly-close-paren-point)))
  471. (goto-char open-paren-point)
  472. (beginning-of-line)
  473. (skip-syntax-forward " ")
  474. (+ (current-column)
  475. (if paren-closed
  476. 0
  477. julia-indent-offset))))
  478. (current-column))))))))))
  479. (defun julia-prev-line-skip-blank-or-comment ()
  480. "Move point to beginning of previous line skipping blank lines
  481. and lines including only comments. Returns number of lines moved.
  482. A return of -1 signals that we moved to the first line of
  483. the (possibly narrowed) buffer, so there is nowhere else to go."
  484. (catch 'result
  485. (let ((moved 0) this-move)
  486. (while t
  487. (setq this-move (forward-line -1))
  488. (cond
  489. ;; moved into comment or blank
  490. ((and (= 0 this-move)
  491. (or (looking-at-p "^\\s-*\\(?:#.*\\)*$")
  492. (julia-in-comment)))
  493. (incf moved))
  494. ;; success
  495. ((= 0 this-move)
  496. (throw 'result (1+ moved)))
  497. ;; on first line and in comment
  498. ((and (bobp)
  499. (or (looking-at-p "^\\s-*\\(?:#.*\\)*$")
  500. (julia-in-comment)))
  501. (throw 'result -1))
  502. ((bobp)
  503. (throw 'result moved))
  504. (t
  505. (throw 'result 0)))))))
  506. (defun julia-indent-hanging ()
  507. "Calculate indentation for lines that follow \"hanging\"
  508. operators (operators that end the previous line) as defined in
  509. `julia-hanging-operator-regexp'. An assignment operator ending
  510. the previous line increases the indent as do the other operators
  511. unless another operator is found two lines up. Previous line
  512. means previous line after skipping blank lines and lines with
  513. only comments."
  514. (let (prev-indent)
  515. (save-excursion
  516. (when (> (julia-prev-line-skip-blank-or-comment) 0)
  517. (setq prev-indent (current-indentation))
  518. (when (looking-at-p julia-hanging-operator-regexp)
  519. (if (and (> (julia-prev-line-skip-blank-or-comment) 0)
  520. (looking-at-p julia-hanging-operator-regexp))
  521. ;; two preceding hanging operators => indent same as line
  522. ;; above
  523. prev-indent
  524. ;; one preceding hanging operator => increase indent from line
  525. ;; above
  526. (+ julia-indent-offset prev-indent)))))))
  527. (defun julia-indent-in-string ()
  528. "Indentation inside strings with newlines is \"manual\",
  529. meaning always increase indent on TAB and decrease on S-TAB."
  530. (save-excursion
  531. (beginning-of-line)
  532. (when (julia-in-string)
  533. (if (member this-command '(julia-latexsub-or-indent
  534. ess-indent-or-complete))
  535. (+ julia-indent-offset (current-indentation))
  536. ;; return the current indentation to prevent other functions from
  537. ;; indenting inside strings
  538. (current-indentation)))))
  539. (defun julia-indent-import-export-using ()
  540. "Indent offset for lines that follow `import` or `export`, otherwise nil."
  541. (when (julia-following-import-export-using)
  542. julia-indent-offset))
  543. (defun julia-indent-line ()
  544. "Indent current line of julia code."
  545. (interactive)
  546. (let* ((point-offset (- (current-column) (current-indentation))))
  547. (indent-line-to
  548. (or
  549. ;; note: if this first function returns nil the beginning of the line
  550. ;; cannot be in a string
  551. (julia-indent-in-string)
  552. ;; If we're inside an open paren, indent to line up arguments. After this,
  553. ;; we cannot be inside parens which includes brackets
  554. (julia-paren-indent)
  555. ;; indent due to hanging operators (lines ending in an operator)
  556. (julia-indent-hanging)
  557. ;; indent for import and export
  558. (julia-indent-import-export-using)
  559. ;; Indent according to how many nested blocks we are in.
  560. (save-excursion
  561. (beginning-of-line)
  562. ;; jump out of any comments
  563. (let ((state (syntax-ppss)))
  564. (when (nth 4 state)
  565. (goto-char (nth 8 state))))
  566. (forward-to-indentation 0)
  567. (let ((endtok (julia-at-keyword julia-block-end-keywords))
  568. (last-open-block (julia-last-open-block (- (point) julia-max-block-lookback))))
  569. (max 0 (+ (or last-open-block 0)
  570. (if (or endtok
  571. (julia-at-keyword julia-block-start-keywords-no-indent))
  572. (- julia-indent-offset) 0)))))))
  573. ;; Point is now at the beginning of indentation, restore it
  574. ;; to its original position (relative to indentation).
  575. (when (>= point-offset 0)
  576. (move-to-column (+ (current-indentation) point-offset)))))
  577. (defalias 'julia-mode-prog-mode
  578. (if (fboundp 'prog-mode)
  579. 'prog-mode
  580. 'fundamental-mode))
  581. ;;; Navigation
  582. ;; based off python.el
  583. (defconst julia-beginning-of-defun-regex
  584. (concat julia-function-regex "\\|"
  585. julia-function-assignment-regex "\\|"
  586. "\\_<macro\\_>")
  587. "Regex matching beginning of Julia function or macro.")
  588. (defun julia-syntax-context-type (&optional syntax-ppss)
  589. "Return the context type using SYNTAX-PPSS.
  590. TYPE can be `comment', `string' or `paren'."
  591. (let ((ppss (or syntax-ppss (syntax-ppss))))
  592. (cond
  593. ((nth 8 ppss) (if (nth 4 ppss) 'comment 'string))
  594. ((nth 1 ppss) 'paren))))
  595. (defsubst julia-syntax-comment-or-string-p (&optional syntax-ppss)
  596. "Return non-nil if SYNTAX-PPSS is inside string or comment."
  597. (nth 8 (or syntax-ppss (syntax-ppss))))
  598. (defun julia-looking-at-beginning-of-defun (&optional syntax-ppss)
  599. "Check if point is at `beginning-of-defun' using SYNTAX-PPSS."
  600. (and (not (julia-syntax-comment-or-string-p (or syntax-ppss (syntax-ppss))))
  601. (save-excursion
  602. (beginning-of-line 1)
  603. (looking-at julia-beginning-of-defun-regex))))
  604. (defun julia--beginning-of-defun (&optional arg)
  605. "Internal implementation of `julia-beginning-of-defun'.
  606. With positive ARG search backwards, else search forwards."
  607. (when (or (null arg) (= arg 0)) (setq arg 1))
  608. (let* ((re-search-fn (if (> arg 0)
  609. #'re-search-backward
  610. #'re-search-forward))
  611. (line-beg-pos (line-beginning-position))
  612. (line-content-start (+ line-beg-pos (current-indentation)))
  613. (pos (point-marker))
  614. (beg-indentation
  615. (and (> arg 0)
  616. (save-excursion
  617. (while (and (not (julia-looking-at-beginning-of-defun))
  618. ;; f(x) = ... function bodies may span multiple lines
  619. (or (and (julia-indent-hanging)
  620. (forward-line -1))
  621. ;; inside dangling parameter list
  622. (and (eq 'paren (julia-syntax-context-type))
  623. (backward-up-list))
  624. (julia-last-open-block (point-min)))))
  625. (or (and (julia-looking-at-beginning-of-defun)
  626. (+ (current-indentation) julia-indent-offset))
  627. 0))))
  628. (found
  629. (progn
  630. (when (and (< arg 0)
  631. (julia-looking-at-beginning-of-defun))
  632. (end-of-line 1))
  633. (while (and (funcall re-search-fn
  634. julia-beginning-of-defun-regex nil t)
  635. (or (julia-syntax-comment-or-string-p)
  636. ;; handle nested defuns when moving backwards
  637. ;; by checking matching indentation
  638. (and (> arg 0)
  639. (not (= (current-indentation) 0))
  640. (>= (current-indentation) beg-indentation)))))
  641. (and (julia-looking-at-beginning-of-defun)
  642. (or (not (= (line-number-at-pos pos)
  643. (line-number-at-pos)))
  644. (and (>= (point) line-beg-pos)
  645. (<= (point) line-content-start)
  646. (> pos line-content-start)))))))
  647. (if found
  648. (or (beginning-of-line 1) (point))
  649. (and (goto-char pos) nil))))
  650. (defun julia-beginning-of-defun (&optional arg)
  651. "Move point to `beginning-of-defun'.
  652. With positive ARG search backwards else search forward.
  653. ARG nil or 0 defaults to 1. When searching backwards,
  654. nested defuns are handled depending on current point position.
  655. Return non-nil (point) if point moved to `beginning-of-defun'."
  656. (when (or (null arg) (= arg 0)) (setq arg 1))
  657. (let ((found))
  658. (while (and (not (= arg 0))
  659. (let ((keep-searching-p
  660. (julia--beginning-of-defun arg)))
  661. (when (and keep-searching-p (null found))
  662. (setq found t))
  663. keep-searching-p))
  664. (setq arg (if (> arg 0) (1- arg) (1+ arg))))
  665. found))
  666. (defun julia-end-of-defun (&optional arg)
  667. "Move point to the end of the current function.
  668. Return nil if point is not in a function, otherwise point."
  669. (interactive)
  670. (let ((beg-defun-indent)
  671. (beg-pos (point)))
  672. (when (or (julia-looking-at-beginning-of-defun)
  673. (julia-beginning-of-defun 1)
  674. (julia-beginning-of-defun -1))
  675. (beginning-of-line)
  676. (if (looking-at-p julia-function-assignment-regex)
  677. ;; f(x) = ...
  678. (progn
  679. ;; skip any dangling lines
  680. (while (and (forward-line)
  681. (not (eobp))
  682. (or (julia-indent-hanging)
  683. ;; dangling closing paren
  684. (and (eq 'paren (julia-syntax-context-type))
  685. (search-forward ")"))))))
  686. ;; otherwise skip forward to matching indentation (not in string/comment)
  687. (setq beg-defun-indent (current-indentation))
  688. (while (and (not (eobp))
  689. (forward-line 1)
  690. (or (julia-syntax-comment-or-string-p)
  691. (> (current-indentation) beg-defun-indent)))))
  692. (end-of-line)
  693. (point))))
  694. ;;; IMENU
  695. (defvar julia-imenu-generic-expression
  696. ;; don't use syntax classes, screws egrep
  697. '(("Function (_)" "[ \t]*function[ \t]+\\(_[^ \t\n]*\\)" 1)
  698. ("Function" "^[ \t]*function[ \t]+\\([^_][^\t\n]*\\)" 1)
  699. ("Const" "[ \t]*const \\([^ \t\n]*\\)" 1)
  700. ("Type" "^[ \t]*[a-zA-Z0-9_]*type[a-zA-Z0-9_]* \\([^ \t\n]*\\)" 1)
  701. ("Require" " *\\(\\brequire\\)(\\([^ \t\n)]*\\)" 2)
  702. ("Include" " *\\(\\binclude\\)(\\([^ \t\n)]*\\)" 2)
  703. ;; ("Classes" "^.*setClass(\\(.*\\)," 1)
  704. ;; ("Coercions" "^.*setAs(\\([^,]+,[^,]*\\)," 1) ; show from and to
  705. ;; ("Generics" "^.*setGeneric(\\([^,]*\\)," 1)
  706. ;; ("Methods" "^.*set\\(Group\\|Replace\\)?Method(\"\\(.+\\)\"," 2)
  707. ;; ;;[ ]*\\(signature=\\)?(\\(.*,?\\)*\\)," 1)
  708. ;; ;;
  709. ;; ;;("Other" "^\\(.+\\)\\s-*<-[ \t\n]*[^\\(function\\|read\\|.*data\.frame\\)]" 1)
  710. ;; ("Package" "^.*\\(library\\|require\\)(\\(.*\\)," 2)
  711. ;; ("Data" "^\\(.+\\)\\s-*<-[ \t\n]*\\(read\\|.*data\.frame\\).*(" 1)))
  712. ))
  713. ;;;###autoload
  714. (define-derived-mode julia-mode julia-mode-prog-mode "Julia"
  715. "Major mode for editing julia code."
  716. (set-syntax-table julia-mode-syntax-table)
  717. (set (make-local-variable 'comment-start) "# ")
  718. (set (make-local-variable 'comment-start-skip) "#+\\s-*")
  719. (set (make-local-variable 'font-lock-defaults) '(julia-font-lock-keywords))
  720. (if (< emacs-major-version 24)
  721. ;; Emacs 23 doesn't have syntax-propertize-function
  722. (set (make-local-variable 'font-lock-syntactic-keywords)
  723. (list
  724. `(,julia-char-regex
  725. (1 "\"") ; Treat ' as a string delimiter.
  726. (2 ".") ; Don't highlight anything between the open and close '.
  727. (3 "\"")) ; Treat the close ' as a string delimiter.
  728. `(,julia-triple-quoted-string-regex
  729. (1 "\"") ; Treat the first " in """ as a string delimiter.
  730. (2 ".") ; Don't highlight anything between.
  731. (3 "\"")))) ; Treat the last " in """ as a string delimiter.
  732. ;; Emacs 24 and later has syntax-propertize-function, so use that instead.
  733. (set (make-local-variable 'syntax-propertize-function)
  734. julia-syntax-propertize-function))
  735. (set (make-local-variable 'indent-line-function) 'julia-indent-line)
  736. (set (make-local-variable 'beginning-of-defun-function) #'julia-beginning-of-defun)
  737. (set (make-local-variable 'end-of-defun-function) #'julia-end-of-defun)
  738. (setq indent-tabs-mode nil)
  739. (setq imenu-generic-expression julia-imenu-generic-expression)
  740. (imenu-add-to-menubar "Imenu"))
  741. (defun julia-manual-deindent ()
  742. "Deindent by `julia-indent-offset' regardless of current
  743. indentation context. To be used to manually indent inside
  744. strings."
  745. (interactive)
  746. (indent-line-to (max 0 (- (current-indentation) julia-indent-offset))))
  747. (define-key julia-mode-map (kbd "<backtab>") 'julia-manual-deindent)
  748. (defvar julia-latexsubs (make-hash-table :test 'equal))
  749. (defun julia-latexsub ()
  750. "Perform a LaTeX-like Unicode symbol substitution."
  751. (interactive "*i")
  752. (let ((orig-pt (point)))
  753. (while (not (or (bobp) (= ?\\ (char-before))
  754. (= ?\s (char-syntax (char-before)))))
  755. (backward-char))
  756. (if (and (not (bobp)) (= ?\\ (char-before)))
  757. (progn
  758. (backward-char)
  759. (let ((sub (gethash (buffer-substring (point) orig-pt) julia-latexsubs)))
  760. (if sub
  761. (progn
  762. (delete-region (point) orig-pt)
  763. (insert sub))
  764. (goto-char orig-pt))))
  765. (goto-char orig-pt))))
  766. (defalias 'latexsub 'julia-latexsub)
  767. (defun julia-latexsub-or-indent (arg)
  768. "Either indent according to mode or perform a LaTeX-like symbol substution"
  769. (interactive "*i")
  770. (if (latexsub)
  771. (indent-for-tab-command arg)))
  772. (define-key julia-mode-map (kbd "TAB") 'julia-latexsub-or-indent)
  773. (defalias 'latexsub-or-indent 'julia-latexsub-or-indent)
  774. ;;; populate LaTeX symbols hash table from a generated file.
  775. ;;; (See Julia issue #8947 for why we don't use the Emacs tex input mode.)
  776. (load (expand-file-name "julia-latexsubs" (file-name-directory load-file-name)))
  777. ;; Math insertion in julia. Use it with
  778. ;; (add-hook 'julia-mode-hook 'julia-math-mode)
  779. ;; (add-hook 'inferior-julia-mode-hook 'julia-math-mode)
  780. (when (require 'latex nil t)
  781. (declare-function LaTeX-math-abbrev-prefix "latex")
  782. (defun julia-math-insert (s)
  783. "Inserts math symbol given by `s'"
  784. (when s
  785. (let ((sym (gethash (concat "\\" s) julia-latexsubs)))
  786. (when sym
  787. (insert sym)))))
  788. (with-no-warnings
  789. (define-minor-mode julia-math-mode
  790. "A minor mode with easy access to TeX math commands. The
  791. command is only entered if it is supported in Julia. The
  792. following commands are defined:
  793. \\{LaTeX-math-mode-map}"
  794. nil nil (list (cons (LaTeX-math-abbrev-prefix) LaTeX-math-keymap))
  795. (if julia-math-mode
  796. (set (make-local-variable 'LaTeX-math-insert-function)
  797. 'julia-math-insert)))))
  798. ;; Code for `inferior-julia-mode'
  799. (require 'comint)
  800. (defcustom julia-program "julia"
  801. "Path to the program used by `inferior-julia'."
  802. :type 'string
  803. :group 'julia)
  804. (defcustom julia-arguments '("-i" "--color=yes")
  805. "Commandline arguments to pass to `julia-program'."
  806. :type '(repeat (string :tag "argument"))
  807. :group 'julia)
  808. (defvar julia-prompt-regexp "^\\w*> "
  809. "Regexp for matching `inferior-julia' prompt.")
  810. (defvar inferior-julia-mode-map
  811. (let ((map (nconc (make-sparse-keymap) comint-mode-map)))
  812. ;; example definition
  813. (define-key map (kbd "TAB") 'julia-latexsub-or-indent)
  814. map)
  815. "Basic mode map for `inferior-julia-mode'.")
  816. ;;;###autoload
  817. (defun inferior-julia ()
  818. "Run an inferior instance of `julia' inside Emacs."
  819. (interactive)
  820. (let ((julia-program julia-program)
  821. (buffer (get-buffer-create "*Julia*")))
  822. (when (not (comint-check-proc "*Julia*"))
  823. (apply #'make-comint-in-buffer "Julia" "*Julia*"
  824. julia-program nil julia-arguments))
  825. (pop-to-buffer-same-window "*Julia*")
  826. (inferior-julia-mode)))
  827. (defun inferior-julia--initialize ()
  828. "Helper function to initialize `inferior-julia'."
  829. (setq comint-use-prompt-regexp t))
  830. (define-derived-mode inferior-julia-mode comint-mode "Julia"
  831. "Major mode for `inferior-julia'.
  832. \\<inferior-julia-mode-map>"
  833. nil "Julia"
  834. (setq comint-prompt-regexp julia-prompt-regexp)
  835. (setq comint-prompt-read-only t)
  836. (set (make-local-variable 'font-lock-defaults) '(julia-font-lock-keywords t))
  837. (set (make-local-variable 'paragraph-start) julia-prompt-regexp)
  838. (set (make-local-variable 'indent-line-function) 'julia-indent-line))
  839. (add-hook 'inferior-julia-mode-hook 'inferior-julia--initialize)
  840. ;;;###autoload
  841. (defalias 'run-julia #'inferior-julia
  842. "Run an inferior instance of `julia' inside Emacs.")
  843. (provide 'julia-mode)
  844. ;; Local Variables:
  845. ;; coding: utf-8
  846. ;; byte-compile-warnings: (not obsolete)
  847. ;; End:
  848. ;;; julia-mode.el ends here