branch: elpa/julia-mode commit b800403fada2b83bc98d6006cfd370e0cfd7876f Author: Adam B <adam_...@thebeckmeyers.xyz> Commit: GitHub <nore...@github.com>
Font lock tweaks (#102) * Remove unused type-parameter regex This regex was designed to operate on type parameters (e.g. Vector{T} has T as a type parameter) to fontify them as types. However type parameters can be values and not just types (e.g. NTuple{2,Int}) so fontifying them as such is not correct. * Remove font-lock-type-face for types at point of use In the elisp manual, font-lock-type-face is described as being "for the names of user-defined data types.". It is intended to font-lock the point of type definition rather than use. By using this font-lock on a list of builtin types, a visual distinction is created between builtin and library/user-defined types when none exists in reality. * font-lock quoted symbols as constants instead of preprocessor This fits better with a quoted symbols purpose in source-code. You could also consider font-lock-string-face. Closes #4. * Remove "in" from font-lock keywords When in a for-loop or comprehension, "in" is already font-lock-keyword-face because of julia-forloop-in-regex. * Remove unused font-locking for builtins In the future, we could consider using font-lock-builtin-face for everything returned by names(Base), but given that none of these are truly builtin (can be overridden in a baremodule), I think it's best to not use this face. * Add missing to font-lock-constant-face * Use font-lock-keyword face for ternary ? and : * Add baremodule to block start * Use terser setq-local in place of make-local-variable This is equivalent to the current implementation with make-local-variable, but setq-local is only available in emacs 24.3+. * Remove regex term matching obsolete parametric function spelling Pre Julia 1.0, parametric functions could be spelled as f{T}(args...) but now the only correct spelling is f(args...) where T. * Use font-lock-keyword-face for = and ∈ in loops Previously, only ∈ was font-locked and only if it had whitespace around it. This new regex correctly font-locks "for i=1:10" as well as "for i = 1:10". This commit also adds tests for whether font-lock-keyword-face is being correctly applied in for loops. * Apply reviewer comments from PR 102 See also the previous commit for addition of forloop font-lock tests. --- CHANGELOG.md | 4 +++ julia-mode-tests.el | 27 +++++++++++++++ julia-mode.el | 96 ++++++++++++++++++++++------------------------------- 3 files changed, 70 insertions(+), 57 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 930c5ca..856dabe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ - remove `latexsub` alias for `julia-latexsub` [#101](https://github.com/JuliaEditorSupport/julia-emacs/pull/101) +- correctly font-lock for-loops and ternary expressions as keywords [#102](https://github.com/JuliaEditorSupport/julia-emacs/pull/102) + +- use font-lock-constant-face instead of font-lock-preprocessor-face for quoted symbols [#102](https://github.com/JuliaEditorSupport/julia-emacs/pull/102) + # 0.3 This is the first actual release, and the last one to support Emacs 23. diff --git a/julia-mode-tests.el b/julia-mode-tests.el index cc41fcb..09b8e13 100644 --- a/julia-mode-tests.el +++ b/julia-mode-tests.el @@ -465,6 +465,33 @@ end") (julia--should-font-lock string pos font-lock-string-face)) (julia--should-font-lock string (length string) font-lock-keyword-face))) +(ert-deftest julia--test-ternary-font-lock () + "? and : in ternary expression font-locked as keywords" + (let ((string "true ? 1 : 2")) + (julia--should-font-lock string 6 font-lock-keyword-face) + (julia--should-font-lock string 10 font-lock-keyword-face)) + (let ((string "true ?\n 1 :\n 2")) + (julia--should-font-lock string 6 font-lock-keyword-face) + (julia--should-font-lock string 14 font-lock-keyword-face))) + +(ert-deftest julia--test-forloop-font-lock () + "for and in/=/∈ font-locked as keywords in loops and comprehensions" + (let ((string "for i=1:10\nprintln(i)\nend")) + (julia--should-font-lock string 1 font-lock-keyword-face) + (julia--should-font-lock string 6 font-lock-keyword-face)) + (let ((string "for i in 1:10\nprintln(i)\nend")) + (julia--should-font-lock string 3 font-lock-keyword-face) + (julia--should-font-lock string 7 font-lock-keyword-face)) + (let ((string "for i∈1:10\nprintln(i)\nend")) + (julia--should-font-lock string 2 font-lock-keyword-face) + (julia--should-font-lock string 6 font-lock-keyword-face)) + (let ((string "[i for i in 1:10]")) + (julia--should-font-lock string 4 font-lock-keyword-face) + (julia--should-font-lock string 10 font-lock-keyword-face)) + (let ((string "(i for i in 1:10)")) + (julia--should-font-lock string 4 font-lock-keyword-face) + (julia--should-font-lock string 10 font-lock-keyword-face))) + ;;; Movement (ert-deftest julia--test-beginning-of-defun-assn-1 () "Point moves to beginning of single-line assignment function." diff --git a/julia-mode.el b/julia-mode.el index 112db64..9f7234d 100644 --- a/julia-mode.el +++ b/julia-mode.el @@ -57,7 +57,7 @@ :group 'julia-mode) (defface julia-quoted-symbol-face - '((t :inherit font-lock-preprocessor-face)) + '((t :inherit font-lock-constant-face)) "Face for quoted Julia symbols, e.g. :foo." :group 'julia-mode) @@ -197,8 +197,19 @@ (defconst julia-forloop-in-regex "for +.*[^ -].* \\(in\\|∈\\)\\(\\s-\\|$\\)+") +].* \\(in\\)\\(\\s-\\|$\\)+") + +(defconst julia--forloop-=-regex + "for +.*[^ +].*\\(=\\|∈\\)") +(defconst julia-ternary-regex + " +\\(\\?\\)[ + ]+[^ +]* +\\(:\\)[ + ]+") + +;; functions of form "function f(x) nothing end" (defconst julia-function-regex (rx line-start (* (or space "@inline" "@noinline")) symbol-start "function" @@ -208,11 +219,11 @@ ;; The function name itself (group (1+ (or word (syntax symbol)))))) +;; functions of form "f(x) = nothing" (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 "(" ")")))) @@ -232,9 +243,6 @@ (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"))) @@ -246,46 +254,21 @@ '("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" + "module" "using" "import" "export" "const" "let" "do" "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 - (regexp-opt - ;;'("error" "throw") - '() - 'symbols)) - -(defconst julia-builtin-types-regex - (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 + ;; font-lock-builtin-face intentionally unused since any name from + ;; names(Base) can be aliased in a baremodule. (list ;; Ensure :: and <: aren't highlighted, so we don't confuse ::Foo with :foo. ;; (in Emacs, keywords don't overlap). @@ -293,33 +276,34 @@ ;; 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 (regexp-opt - '("true" "false" "C_NULL" "Inf" "NaN" "Inf32" "NaN32" "nothing" "undef") + '("true" "false" "C_NULL" "Inf" "NaN" "Inf32" "NaN32" "nothing" "undef" "missing") '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--forloop-=-regex 1 'font-lock-keyword-face) + (list julia-ternary-regex (list 1 'font-lock-keyword-face) (list 2 '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) + ;; font-lock-type-face is for the point of type definition rather + ;; than usage, but using for type annotations is an acceptable pun. (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))) + (list julia-subtype-regex 1 'font-lock-type-face))) (defconst julia-block-start-keywords (list "if" "while" "for" "begin" "try" "function" "let" "macro" - "quote" "do" "module" + "quote" "do" "module" "baremodule" ;; "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")) + (list "module" "baremodule")) (defconst julia-block-end-keywords (list "end" "else" "elseif" "catch" "finally")) @@ -760,14 +744,13 @@ Return nil if point is not in a function, otherwise point." (define-derived-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)) - (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-local comment-start "# ") + (setq-local comment-start-skip "#+\\s-*") + (setq-local font-lock-defaults '(julia-font-lock-keywords)) + (setq-local syntax-propertize-function julia-syntax-propertize-function) + (setq-local indent-line-function #'julia-indent-line) + (setq-local beginning-of-defun-function #'julia-beginning-of-defun) + (setq-local 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")) @@ -829,8 +812,7 @@ 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))))) + (setq-local LaTeX-math-insert-function #'julia-math-insert))))) ;; Code for `inferior-julia-mode' (require 'comint) @@ -875,13 +857,13 @@ following commands are defined: \\<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)) + (setq-local comint-prompt-regexp julia-prompt-regexp) + (setq-local comint-prompt-read-only t) + (setq-local font-lock-defaults '(julia-font-lock-keywords t)) + (setq-local paragraph-start julia-prompt-regexp) + (setq-local indent-line-function #'julia-indent-line)) -(add-hook 'inferior-julia-mode-hook 'inferior-julia--initialize) +(add-hook 'inferior-julia-mode-hook #'inferior-julia--initialize) ;;;###autoload (defalias 'run-julia #'inferior-julia