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))
 

Reply via email to