[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[nongnu] elpa/julia-mode 10b0eff 286/352: beginning/end of defun functio
From: |
ELPA Syncer |
Subject: |
[nongnu] elpa/julia-mode 10b0eff 286/352: beginning/end of defun functions |
Date: |
Sun, 29 Aug 2021 11:23:03 -0400 (EDT) |
branch: elpa/julia-mode
commit 10b0eff520eab0248b3687be3b4125dd743c47cf
Author: nverno <noah.v.peart@gmail.com>
Commit: nverno <noah.v.peart@gmail.com>
beginning/end of defun functions
---
julia-mode-tests.el | 219 ++++++++++++++++++++++++++++++++++++++++++++--------
julia-mode.el | 135 ++++++++++++++++++++++++++++++--
2 files changed, 315 insertions(+), 39 deletions(-)
diff --git a/julia-mode-tests.el b/julia-mode-tests.el
index 4f24513..f5436c7 100644
--- a/julia-mode-tests.el
+++ b/julia-mode-tests.el
@@ -65,14 +65,37 @@
(font-lock-fontify-buffer)))
(should (eq ,face (get-text-property ,pos 'face)))))
+(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)))))
+
(ert-deftest julia--test-indent-if ()
"We should indent inside if bodies."
(julia--should-indent
- "
+ "
if foo
bar
end"
- "
+ "
if foo
bar
end"))
@@ -80,13 +103,13 @@ end"))
(ert-deftest julia--test-indent-else ()
"We should indent inside else bodies."
(julia--should-indent
- "
+ "
if foo
bar
else
baz
end"
- "
+ "
if foo
bar
else
@@ -96,23 +119,23 @@ end"))
(ert-deftest julia--test-indent-toplevel ()
"We should not indent toplevel expressions. "
(julia--should-indent
- "
+ "
foo()
bar()"
- "
+ "
foo()
bar()"))
(ert-deftest julia--test-indent-nested-if ()
"We should indent for each level of indentation."
(julia--should-indent
- "
+ "
if foo
if bar
bar
end
end"
- "
+ "
if foo
if bar
bar
@@ -151,11 +174,11 @@ end"))
(ert-deftest julia--test-indent-function ()
"We should indent function bodies."
(julia--should-indent
- "
+ "
function foo()
bar
end"
- "
+ "
function foo()
bar
end"))
@@ -163,11 +186,11 @@ end"))
(ert-deftest julia--test-indent-begin ()
"We should indent after a begin keyword."
(julia--should-indent
- "
+ "
@async begin
bar
end"
- "
+ "
@async begin
bar
end"))
@@ -175,10 +198,10 @@ end"))
(ert-deftest julia--test-indent-paren ()
"We should indent to line up with the text after an open paren."
(julia--should-indent
- "
+ "
foobar(bar,
baz)"
- "
+ "
foobar(bar,
baz)"))
@@ -186,31 +209,31 @@ foobar(bar,
"We should indent to line up with the text after an open
paren, even if there are additional spaces."
(julia--should-indent
- "
+ "
foobar( bar,
baz )"
- "
+ "
foobar( bar,
baz )"))
(ert-deftest julia--test-indent-paren-newline ()
"python-mode-like indentation."
(julia--should-indent
- "
+ "
foobar(
bar,
baz)"
- "
+ "
foobar(
bar,
baz)")
(julia--should-indent
- "
+ "
foobar(
bar,
baz
)"
- "
+ "
foobar(
bar,
baz
@@ -219,10 +242,10 @@ foobar(
(ert-deftest julia--test-indent-equals ()
"We should increase indent on a trailing =."
(julia--should-indent
- "
+ "
foo() =
bar"
- "
+ "
foo() =
bar"))
@@ -244,12 +267,12 @@ qux"))
(ert-deftest julia--test-indent-ignores-blank-lines ()
"Blank lines should not affect indentation of non-blank lines."
(julia--should-indent
- "
+ "
if foo
bar
end"
- "
+ "
if foo
bar
@@ -258,11 +281,11 @@ end"))
(ert-deftest julia--test-indent-comment-equal ()
"`=` at the end of comment should not increase indent level."
(julia--should-indent
- "
+ "
# a =
# b =
c"
- "
+ "
# a =
# b =
c"))
@@ -270,32 +293,32 @@ c"))
(ert-deftest julia--test-indent-leading-paren ()
"`(` at the beginning of a line should not affect indentation."
(julia--should-indent
- "
+ "
\(1)"
- "
+ "
\(1)"))
(ert-deftest julia--test-top-level-following-paren-indent ()
"`At the top level, a previous line indented due to parens should not affect
indentation."
(julia--should-indent
- "y1 = f(x,
+ "y1 = f(x,
z)
y2 = g(x)"
- "y1 = f(x,
+ "y1 = f(x,
z)
y2 = g(x)"))
(ert-deftest julia--test-indentation-of-multi-line-strings ()
"Indentation should only affect the first line of a multi-line string."
- (julia--should-indent
- " a = \"\"\"
+ (julia--should-indent
+ " a = \"\"\"
description
begin
foo
bar
end
\"\"\""
- "a = \"\"\"
+ "a = \"\"\"
description
begin
foo
@@ -442,6 +465,136 @@ end")
(julia--should-font-lock string pos font-lock-string-face))
(julia--should-font-lock string (length string) font-lock-keyword-face)))
+;;; Movement
+(ert-deftest julia--test-beginning-of-defun-assn-1 ()
+ "Point moves to beginning of single-line assignment function."
+ (julia--should-move-point
+ "f() = \"a + b\"" 'beginning-of-defun "a \\+" 1))
+
+(ert-deftest julia--test-beginning-of-defun-assn-2 ()
+ "Point moves to beginning of multi-line assignment function."
+ (julia--should-move-point
+ "f(x)=
+x*
+x" 'beginning-of-defun "\\*\nx" 1))
+
+(ert-deftest julia--test-beginning-of-defun-assn-3 ()
+ "Point moves to beginning of multi-line assignment function adjoining
+another function."
+ (julia--should-move-point
+ "f( x
+)::Int16 = x / 2
+f2(y)=
+y*y" 'beginning-of-defun "2" 1))
+
+(ert-deftest julia--test-beginning-of-defun-assn-4 ()
+ "Point moves to beginning of 2nd multi-line assignment function adjoining
+another function."
+ (julia--should-move-point
+ "f( x
+)::Int16 =
+x /
+2
+f2(y) =
+y*y" 'beginning-of-defun "\\*y" "f2"))
+
+(ert-deftest julia--test-beginning-of-defun-assn-5 ()
+ "Point moves to beginning of 1st multi-line assignment function adjoining
+another function with prefix arg."
+ (julia--should-move-point
+ "f( x
+)::Int16 =
+x /
+2
+f2(y) =
+y*y" 'beginning-of-defun "y\\*y" 1 nil 2))
+
+(ert-deftest julia--test-beginning-of-macro ()
+ "Point moves to beginning of macro."
+ (julia--should-move-point
+ "macro current_module()
+return VERSION >= v\"0.7-\" :(@__MODULE__) : :(current_module())))
+end" 'beginning-of-defun "@" 1))
+
+(ert-deftest julia--test-beginning-of-defun-1 ()
+ "Point moves to beginning of defun in 'function's."
+ (julia--should-move-point
+ "function f(a, b)
+a + b
+end" 'beginning-of-defun "f(" 1))
+
+(ert-deftest julia--test-beginning-of-defun-nested-1 ()
+ "Point moves to beginning of nested function."
+ (julia--should-move-point
+ "function f(x)
+
+function fact(n)
+if n == 0
+return 1
+else
+return n * fact(n-1)
+end
+end
+
+return fact(x)
+end" 'beginning-of-defun "fact(n" "function fact"))
+
+(ert-deftest julia--test-beginning-of-defun-nested-2 ()
+ "Point moves to beginning of outermost function with prefix arg."
+ (julia--should-move-point
+ "function f(x)
+
+function fact(n)
+if n == 0
+return 1
+else
+return n * fact(n-1)
+end
+end
+
+return fact(x)
+end" 'beginning-of-defun "n \\*" 1 nil 2))
+
+(ert-deftest julia--test-beginning-of-defun-no-move ()
+ "Point shouldn't move if there is no previous function."
+ (julia--should-move-point
+ "1 + 1
+f(x) = x + 1" 'beginning-of-defun "\\+" 4))
+
+(ert-deftest julia--test-end-of-defun-assn-1 ()
+ "Point should move to end of assignment function."
+ (julia--should-move-point
+ "f(x)::Int8 =
+x *x" 'end-of-defun "(" "*x" 'end))
+
+(ert-deftest julia--test-end-of-defun-nested-1 ()
+ "Point should move to end of inner function when called from inner."
+ (julia--should-move-point
+ "function f(x)
+function fact(n)
+if n == 0
+return 1
+else
+return n * fact(n-1)
+end
+end
+return fact(x)
+end" 'end-of-defun "n == 0" "end[ \n]+end\n" 'end))
+
+(ert-deftest julia--test-end-of-defun-nested-2 ()
+ "Point should move to end of outer function when called from inner with
prefix."
+ (julia--should-move-point
+ "function f(x)
+function fact(n)
+if n == 0
+return 1
+else
+return n * fact(n-1)
+end
+end
+return fact(x)
+end" 'end-of-defun "n == 0" "return fact(x)[ \n]+end" 'end 2))
+
(defun julia--run-tests ()
(interactive)
(if (featurep 'ert)
diff --git a/julia-mode.el b/julia-mode.el
index 4e5960c..ff4d7d8 100644
--- a/julia-mode.el
+++ b/julia-mode.el
@@ -373,16 +373,16 @@ Based on `python-syntax-stringify'."
(2 ".") ; Don't highlight anything between.
(3 "\""))))) ; Treat the last " in """ as a string delimiter.
-(defun julia-in-comment ()
- "Return non-nil if point is inside a comment.
+(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 (syntax-ppss)))
+ (nth 4 (or syntax-ppss (syntax-ppss))))
-(defun julia-in-string ()
- "Return non-nil if point is inside a string.
+(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 (syntax-ppss)))
+ (nth 3 (or syntax-ppss (syntax-ppss))))
(defun julia-in-brackets ()
"Return non-nil if point is inside square brackets."
@@ -637,6 +637,127 @@ meaning always increase indent on TAB and decrease on
S-TAB."
'prog-mode
'fundamental-mode))
+
+;;; Navigation
+;; based off python.el
+(defconst julia-beginning-of-defun-regex
+ (eval-when-compile (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
@@ -680,6 +801,8 @@ meaning always increase indent on TAB and decrease on
S-TAB."
(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"))
- [nongnu] elpa/julia-mode b800403 326/352: Font lock tweaks (#102), (continued)
- [nongnu] elpa/julia-mode b800403 326/352: Font lock tweaks (#102), ELPA Syncer, 2021/08/29
- [nongnu] elpa/julia-mode 8bfc709 325/352: bump version for release, ELPA Syncer, 2021/08/29
- [nongnu] elpa/julia-mode a31868d 340/352: font-lock ccall as a builtin, ELPA Syncer, 2021/08/29
- [nongnu] elpa/julia-mode a4eeb6c 343/352: Merge pull request #134 from non-Jedi/font-lock-builtin-face, ELPA Syncer, 2021/08/29
- [nongnu] elpa/julia-mode a1ba9a0 352/352: add logo, README tweaks, add section on testing (#163), ELPA Syncer, 2021/08/29
- [nongnu] elpa/julia-mode dc17943 351/352: Indent imports from submodule correctly (#154), ELPA Syncer, 2021/08/29
- [nongnu] elpa/julia-mode af5eb63 273/352: short function syntax: support return type declaration, ELPA Syncer, 2021/08/29
- [nongnu] elpa/julia-mode 1eacdc6 298/352: Create FUNDING.yml, ELPA Syncer, 2021/08/29
- [nongnu] elpa/julia-mode 023df24 322/352: add latest PR to changelog, ELPA Syncer, 2021/08/29
- [nongnu] elpa/julia-mode 140369e 337/352: Simplify char-regex, ELPA Syncer, 2021/08/29
- [nongnu] elpa/julia-mode 10b0eff 286/352: beginning/end of defun functions,
ELPA Syncer <=
- [nongnu] elpa/julia-mode 6408b96 306/352: Transition to cl-lib, drop support for Emacs 23., ELPA Syncer, 2021/08/29
- [nongnu] elpa/julia-mode 4f024cb 313/352: make loading "julia-latexsubs" work when using `eval-buffer` (#83), ELPA Syncer, 2021/08/29
- [nongnu] elpa/julia-mode 10ce821 310/352: Add MELPA badge, clarify installation., ELPA Syncer, 2021/08/29
- [nongnu] elpa/julia-mode 088fb0b 314/352: Load latexsub table as a feature, ELPA Syncer, 2021/08/29
- [nongnu] elpa/julia-mode 66ef2db 317/352: Merge branch 'master' into tp/drop-emacs-23, ELPA Syncer, 2021/08/29
- [nongnu] elpa/julia-mode b295e36 345/352: Merge pull request #133 from non-Jedi/triple-quote-cmd, ELPA Syncer, 2021/08/29
- [nongnu] elpa/julia-mode e10a730 056/352: FFT of real vectors as well, ELPA Syncer, 2021/08/29
- [nongnu] elpa/julia-mode 7065601 138/352: julia-mode symbol-table update from #7018, ELPA Syncer, 2021/08/29
- [nongnu] elpa/julia-mode 5d693ab 349/352: Fix CI using actions. (#160), ELPA Syncer, 2021/08/29
- [nongnu] elpa/julia-mode 307b42b 189/352: If the previous line has a trailing =, that should take precedence over blocks., ELPA Syncer, 2021/08/29