aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS3
-rw-r--r--snakemake-mode.el54
-rw-r--r--snakemake-test.el106
3 files changed, 163 insertions, 0 deletions
diff --git a/NEWS b/NEWS
index 3bef2db..1f73c4f 100644
--- a/NEWS
+++ b/NEWS
@@ -16,6 +16,9 @@ NEWS -- history of user-visible changes -*- mode: org; -*-
- ".rules" has been added as a suffix for Snakemake files.
+- ~beginning-of-defun~ and ~end-of-defun~ implementations for rule
+ blocks are now defined.
+
* v0.4.0
** New features
diff --git a/snakemake-mode.el b/snakemake-mode.el
index 853f356..a598aca 100644
--- a/snakemake-mode.el
+++ b/snakemake-mode.el
@@ -158,6 +158,54 @@
(when (<= rule-start pos rule-end)
(cons rule-start rule-end))))))))
+(defun snakemake-beginning-of-block (&optional arg)
+ "Move to beginning of rule block.
+With ARG, do it that many times. Negative ARG signals to move
+forward rather than backward."
+ (when (or (null arg) (= arg 0))
+ (setq arg 1))
+ (funcall (if (> arg 0)
+ #'re-search-backward
+ (lambda (&rest args)
+ (end-of-line)
+ (prog1 (apply #'re-search-forward args)
+ (beginning-of-line))))
+ snakemake-rule-or-subworkflow-re
+ nil 'move (abs arg))
+ (looking-at-p snakemake-rule-or-subworkflow-re))
+
+(defun snakemake-end-of-block ()
+ "Move to end of rule or subworkflow block."
+ (let ((bounds (snakemake-block-bounds)))
+ (when bounds
+ (goto-char (cdr bounds)))))
+
+(defun snakemake-beginning-of-defun (&optional arg)
+ "Move to beginning of current rule block or function.
+With ARG, do it that many times. Negative ARG signals to move
+forward rather than backward."
+ (when (or (null arg) (= arg 0))
+ (setq arg 1))
+ (let ((choose-fn (if (> arg 0) #'max #'min))
+ (cands (delq nil
+ (mapcar
+ (lambda (f)
+ (save-excursion (and (funcall f arg) (point))))
+ (list #'snakemake-beginning-of-block
+ #'python-nav-beginning-of-defun)))))
+ (cond (cands
+ (goto-char (apply choose-fn cands)))
+ ((> arg 0)
+ (goto-char (point-min))
+ nil)
+ (t
+ (goto-char (point-max))
+ nil))))
+
+(defun snakemake-end-of-defun ()
+ "Move to end of current rule block or function."
+ (or (snakemake-end-of-block)
+ (python-nav-end-of-defun)))
;;; Indentation
@@ -347,6 +395,12 @@ embedded R, you need to set mmm-global-mode to a non-nil value such as 'maybe.")
#'snakemake-imenu-create-index)
(set (make-local-variable 'indent-line-function) 'snakemake-indent-line-function)
(set (make-local-variable 'indent-region-function) nil)
+
+ (set (make-local-variable 'beginning-of-defun-function)
+ #'snakemake-beginning-of-defun)
+ (set (make-local-variable 'end-of-defun-function)
+ #'snakemake-end-of-defun)
+
(set (make-local-variable 'font-lock-defaults)
`(,(append snakemake-font-lock-keywords python-font-lock-keywords))))
diff --git a/snakemake-test.el b/snakemake-test.el
index d6f3d81..d725345 100644
--- a/snakemake-test.el
+++ b/snakemake-test.el
@@ -575,6 +575,112 @@ rule abc:
output: 'file'"
(snakemake-block-bounds))))
+(ert-deftest snakemake-test-beginning-of-block ()
+ (should
+ (string= "
+rule abc:
+ output: 'file'"
+ (snakemake-with-temp-text
+ "
+<point>rule abc:
+ output: 'file'"
+ (snakemake-beginning-of-block)
+ (buffer-substring (point) (point-max)))))
+ (should
+ (string= "rule abc:
+ output: 'file'"
+ (snakemake-with-temp-text
+ "
+rule abc:
+ output: <point>'file'"
+ (snakemake-beginning-of-block)
+ (buffer-substring (point) (point-max)))))
+ (should
+ (string= "rule abc:
+ output: 'file'
+
+"
+ (snakemake-with-temp-text
+ "
+rule abc:
+ output: 'file'
+
+<point>"
+ (snakemake-beginning-of-block)
+ (buffer-substring (point) (point-max)))))
+ (should
+ (string= "rule abc:
+ \"\"\"docstring header
+
+ docstring line
+ \"\"\"
+ output: 'file'"
+ (snakemake-with-temp-text
+ "
+rule abc:
+ \"\"\"docstring header
+
+ docstring line
+ \"\"\"
+ output: 'file'<point>"
+ (snakemake-beginning-of-block)
+ (buffer-substring (point) (point-max)))))
+ (should
+ (string= "subworkflow otherworkflow:
+ workdir: '../path/to/otherworkflow'
+ snakefile: '../path/to/otherworkflow/Snakefile'"
+ (snakemake-with-temp-text
+ "
+subworkflow otherworkflow:
+<point> workdir: '../path/to/otherworkflow'
+ snakefile: '../path/to/otherworkflow/Snakefile'"
+ (snakemake-beginning-of-block)
+ (buffer-substring (point) (point-max))))))
+
+(ert-deftest snakemake-test-end-of-block ()
+ (should
+ (string= "
+rule abc:
+ output: 'file'
+
+
+"
+ (snakemake-with-temp-text
+ "
+rule abc:
+<point> output: 'file'
+
+
+"
+ (snakemake-end-of-block)
+ (buffer-substring (point-min) (point)))))
+ (should
+ (string= "
+rule abc:
+ output: 'file'
+"
+ (snakemake-with-temp-text
+ "
+rule abc:<point>
+ output: 'file'
+
+rule xyz:
+ input: 'file'"
+ (snakemake-end-of-block)
+ (buffer-substring (point-min) (point)))))
+ (should
+ (string= "
+rule abc:
+ output: 'file'"
+ (snakemake-with-temp-text
+ "
+rule abc:<point>
+ output: 'file'
+rule xyz:
+ input: 'file'"
+ (snakemake-end-of-block)
+ (buffer-substring (point-min) (point))))))
+
;;; snakemake.el