branch: elpa/julia-mode commit 4a1a68cefa8759961f017243a8fbd1d2ec9a0f12 Merge: 7b00566570 c19d10efee Author: Tamás K. Papp <tkp...@gmail.com> Commit: Tamás K. Papp <tkp...@gmail.com>
Merge branch 'master' into tp/repl-style-completion --- .github/workflows/CI.yml | 10 +++++-- julia-mode-tests.el | 73 ++++++++++++++++++++++++++++++++++-------------- julia-mode.el | 51 ++++++++++++++++++++------------- 3 files changed, 90 insertions(+), 44 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 7092bc5e7d..72fcde1a53 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -24,11 +24,15 @@ jobs: - 27.2 - 28.1 - 28.2 + - 29.1 + - 29.2 + - 29.3 ignore_warnings: - true - include: - - emacs_version: snapshot - ignore_warnings: true + # NOTE commented out because failure notifications are a distraction + # include: + # - emacs_version: snapshot + # ignore_warnings: true steps: - uses: actions/checkout@v2 - uses: purcell/setup-emacs@master diff --git a/julia-mode-tests.el b/julia-mode-tests.el index e77cf03818..45db3c7810 100644 --- a/julia-mode-tests.el +++ b/julia-mode-tests.el @@ -36,11 +36,6 @@ ;;; 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 - (require 'julia-mode) (require 'ert) @@ -69,28 +64,36 @@ "Assert that TEXT at position POS gets font-locked with FACE in `julia-mode'." `(should (eq ,face (julia--get-font-lock ,text ,pos)))) +(defun julia--should-move-point-helper (text fun from to &optional end arg) + "Takes the same arguments as `julia--should-move-point', returns a cons of the expected and the actual point." + (with-temp-buffer + (julia-mode) + (insert text) + (indent-region (point-min) (point-max)) + (goto-char (point-min)) + (if (stringp from) + (re-search-forward from) + (goto-char from)) + (funcall fun arg) + (let ((actual-to (point)) + (expected-to + (if (stringp to) + (progn (goto-char (point-min)) + (re-search-forward to) + (if end (goto-char (match-end 0)) + (goto-char (match-beginning 0)) + (point-at-bol))) + to))) + (cons expected-to actual-to)))) + (defmacro julia--should-move-point (text fun from to &optional end arg) "With TEXT in `julia-mode', after calling FUN, the point should move FROM\ to TO. If FROM is a string, move the point to matching string before calling function FUN. If TO is a string, match resulting point to point a beginning of matching line or end of match if END is non-nil. Optional ARG is passed to FUN." (declare (indent defun)) - `(with-temp-buffer - (julia-mode) - (insert ,text) - (indent-region (point-min) (point-max)) - (goto-char (point-min)) - (if (stringp ,from) - (re-search-forward ,from) - (goto-char ,from)) - (funcall ,fun ,arg) - (should (eq (point) (if (stringp ,to) - (progn (goto-char (point-min)) - (re-search-forward ,to) - (if ,end (goto-char (match-end 0)) - (goto-char (match-beginning 0)) - (point-at-bol))) - ,to))))) + `(let ((positions (julia--should-move-point-helper ,text ,fun ,from ,to ,end ,arg))) + (should (eq (car positions) (cdr positions))))) ;;; indent tests @@ -749,6 +752,32 @@ var = func(begin (julia--should-font-lock c (- (length c) 1) font-lock-string-face) (julia--should-font-lock c (length c) nil))))) +(ert-deftest julia--test-const-def-font-lock () + (let ((string "const foo = \"bar\"")) + (julia--should-font-lock string 1 font-lock-keyword-face) ; const + (julia--should-font-lock string 5 font-lock-keyword-face) ; const + (julia--should-font-lock string 7 font-lock-variable-name-face) ; foo + (julia--should-font-lock string 9 font-lock-variable-name-face) ; foo + (julia--should-font-lock string 11 nil) ; = + )) + +(ert-deftest julia--test-const-def-font-lock-underscores () + (let ((string "@macro const foo_bar = \"bar\"")) + (julia--should-font-lock string 8 font-lock-keyword-face) ; const + (julia--should-font-lock string 12 font-lock-keyword-face) ; const + (julia--should-font-lock string 14 font-lock-variable-name-face) ; foo + (julia--should-font-lock string 17 font-lock-variable-name-face) ; _ + (julia--should-font-lock string 20 font-lock-variable-name-face) ; bar + (julia--should-font-lock string 22 nil) ; = + )) + +(ert-deftest julia--test-!-font-lock () + (let ((string "!@macro foo()")) + (julia--should-font-lock string 1 nil) + (julia--should-font-lock string 2 'julia-macro-face) + (julia--should-font-lock string 7 'julia-macro-face) + (julia--should-font-lock string 8 nil))) + ;;; Movement (ert-deftest julia--test-beginning-of-defun-assn-1 () "Point moves to beginning of single-line assignment function." @@ -937,6 +966,8 @@ end" 'end-of-defun "n == 0" "return fact(x)[ \n]+end" 'end 2)) (should (equal (julia--call-latexsub-exit-function "\\kappa\\alpha(" 7 13 "\\alpha" t) "\\kappaα(")) + ;; Test that whitespace is stripped from `:exit-function' NAME for compatibility with helm + (should (equal (julia--call-latexsub-exit-function "x\\alpha " 2 8 "\\alpha " t) "xα ")) ;; test that LaTeX not expanded when `julia-automatic-latexsub' is nil (should (equal (julia--call-latexsub-exit-function "\\alpha" 1 7 "\\alpha" nil) "\\alpha")) (should (equal (julia--call-latexsub-exit-function "x\\alpha " 2 8 "\\alpha" nil) "x\\alpha ")) diff --git a/julia-mode.el b/julia-mode.el index e18b9ab863..a5ba9848b0 100644 --- a/julia-mode.el +++ b/julia-mode.el @@ -126,6 +126,10 @@ partial match for LaTeX completion, or `nil' when not applicable." (let ((table (make-syntax-table))) (modify-syntax-entry ?_ "_" table) (modify-syntax-entry ?@ "_" table) + + ;; "!" can be part of both operators (!=) and variable names (append!). Here, we treat + ;; it as being part of a variable name. Care must be taken to account for the special + ;; case where "!" prefixes a variable name and acts as an operator (e.g. !any(...)). (modify-syntax-entry ?! "_" table) (modify-syntax-entry ?# "< 14" table) ; # single-line and multiline start (modify-syntax-entry ?= ". 23bn" table) @@ -278,6 +282,8 @@ partial match for LaTeX completion, or `nil' when not applicable." ;; The function name itself (group (1+ (or word (syntax symbol)))))) +;; TODO: function definitions of form "x + y = 5" or "!x = true" not currently highlighted + ;; functions of form "f(x) = nothing" (defconst julia-function-assignment-regex (rx line-start (* (or space "@inline" "@noinline")) symbol-start @@ -299,6 +305,13 @@ partial match for LaTeX completion, or `nil' when not applicable." "abstract type" "primitive type" "struct" "mutable struct") (1+ space) (group (1+ (or word (syntax symbol)))))) +(defconst julia-const-def-regex + (rx + symbol-start "const" (1+ space) + (group (minimal-match (seq symbol-start (one-or-more anything) symbol-end))) + (zero-or-more space) + "=")) + (defconst julia-type-annotation-regex (rx "::" (0+ space) (group (1+ (or word (syntax symbol)))))) @@ -306,7 +319,7 @@ partial match for LaTeX completion, or `nil' when not applicable." (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)))))) + (rx symbol-start (0+ ?!) (group "@" (1+ (or word (syntax symbol)))))) (defconst julia-keyword-regex (regexp-opt @@ -333,7 +346,7 @@ partial match for LaTeX completion, or `nil' when not applicable." ;; highlighted as a keyword. (list julia-quoted-symbol-regex 1 ''julia-quoted-symbol-face) (cons julia-keyword-regex 'font-lock-keyword-face) - (cons julia-macro-regex ''julia-macro-face) + (list julia-macro-regex 1 ''julia-macro-face) (cons (regexp-opt ;; constants defined in Core plus true/false @@ -348,6 +361,10 @@ partial match for LaTeX completion, or `nil' when not applicable." (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) + ;; Per the elisp manual, font-lock-variable-name-face is for variables being defined or + ;; declared. It is difficult identify this consistently in julia (see issue #2). For now, + ;; we only font-lock constant definitions. + (list julia-const-def-regex 1 'font-lock-variable-name-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) @@ -802,22 +819,12 @@ Return nil if point is not in a function, otherwise 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))) - )) + `(("Function" ,julia-function-regex 1) + ("Function" ,julia-function-assignment-regex 1) + ("Const" ,julia-const-def-regex 1) + ("Type" ,julia-type-regex 1) + ("Require" " *\\(\\brequire\\)(\\([^ \t\n)]*\\)" 2) + ("Include" " *\\(\\binclude\\)(\\([^ \t\n)]*\\)" 2))) ;;;###autoload (define-derived-mode julia-mode prog-mode "Julia" @@ -914,8 +921,12 @@ buffer where the LaTeX symbol starts." ;; <https://github.com/abo-abo/swiper/issues/2345>). Instead of automatic ;; expansion, user can either enable `abbrev-mode' or call `expand-abbrev'. (when-let (((eq status 'finished)) - (symb (abbrev-symbol name julia-latexsub-abbrev-table)) - (end (+ beg (length name)))) + ;; helm-mode passes NAME with an extra whitespace at the end. Since + ;; `julia--latexsub-start-symbol' won't include whitespace, we can safely + ;; strip whitespace. + (clean-name (string-trim-right name)) + (symb (abbrev-symbol clean-name julia-latexsub-abbrev-table)) + (end (+ beg (length clean-name)))) (abbrev-insert symb name beg end))) #'ignore))