diff --git a/CHANGELOG.md b/CHANGELOG.md index 898ac017..7bfe1ff5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ * [#508](https://github.com/clojure-emacs/clojure-mode/issues/508): Fix font lock for namespaces with metadata * [#506](https://github.com/clojure-emacs/clojure-mode/issues/506): `clojure-mode-display-version` correctly displays the package's version * [#445](https://github.com/clojure-emacs/clojure-mode/issues/445), [#405](https://github.com/clojure-emacs/clojure-mode/issues/405), [#469](https://github.com/clojure-emacs/clojure-mode/issues/469): Correct font-lock on string definitions with docstrings, e.g: `(def foo "doc" "value")`. Correct indentation as well. +* [#518](https://github.com/clojure-emacs/clojure-mode/issues/518): Fix clojure-find-ns when there's an `ns` form inside a string ## 5.10.0 (2019-01-05) diff --git a/clojure-mode.el b/clojure-mode.el index b434c98c..5d06272c 100644 --- a/clojure-mode.el +++ b/clojure-mode.el @@ -1886,6 +1886,19 @@ the cached value will be updated automatically." (defvar-local clojure-cached-ns nil "A buffer ns cache used to speed up ns-related operations.") +(defun clojure--find-ns-in-direction (direction) + "Return the nearest namespace in a specific DIRECTION. +DIRECTION is `forward' or `backward'." + (let ((candidate) + (fn (if (eq direction 'forward) + #'search-forward-regexp + #'search-backward-regexp))) + (while (and (not candidate) + (funcall fn clojure-namespace-name-regex nil t)) + (unless (or (clojure--in-string-p) (clojure--in-comment-p)) + (setq candidate (match-string-no-properties 4)))) + candidate)) + (defun clojure-find-ns () "Return the namespace of the current Clojure buffer. Return the namespace closest to point and above it. If there are @@ -1901,12 +1914,8 @@ The results will be cached if `clojure-cache-ns' is set to t." ;; Move to top-level to avoid searching from inside ns (ignore-errors (while t (up-list nil t t))) - ;; The closest ns form above point. - (when (or (re-search-backward clojure-namespace-name-regex nil t) - ;; Or any form at all. - (and (goto-char (point-min)) - (re-search-forward clojure-namespace-name-regex nil t))) - (match-string-no-properties 4)))))) + (or (clojure--find-ns-in-direction 'backward) + (clojure--find-ns-in-direction 'forward)))))) (setq clojure-cached-ns ns) ns))) @@ -2384,6 +2393,10 @@ See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-cycle-privacy" "Check whether the point is currently in a string." (nth 3 (syntax-ppss))) +(defun clojure--in-comment-p () + "Check whether the point is currently in a comment." + (nth 4 (syntax-ppss))) + (defun clojure--goto-if () "Find the first surrounding if or if-not expression." (when (clojure--in-string-p) diff --git a/test/clojure-mode-sexp-test.el b/test/clojure-mode-sexp-test.el index a15a3194..f8a97e3c 100644 --- a/test/clojure-mode-sexp-test.el +++ b/test/clojure-mode-sexp-test.el @@ -146,23 +146,38 @@ and point left there." ;; we should not cache the results of `clojure-find-ns' here (let ((clojure-cache-ns nil)) (with-temp-buffer - (insert "(ns ^{:doc \"Some docs\"}\nfoo-bar)") - (newline) - (newline) - (insert "(in-ns 'baz-quux)") - (clojure-mode) - - ;; From inside docstring of first ns - (goto-char 18) - (should (equal "foo-bar" (clojure-find-ns))) - - ;; From inside first ns's name, on its own line - (goto-char 29) - (should (equal "foo-bar" (clojure-find-ns))) - - ;; From inside second ns's name - (goto-char 42) - (should (equal "baz-quux" (clojure-find-ns)))))) + (insert "(ns ^{:doc \"Some docs\"}\nfoo-bar)") + (newline) + (newline) + (insert "(in-ns 'baz-quux)") + (clojure-mode) + + ;; From inside docstring of first ns + (goto-char 18) + (should (equal "foo-bar" (clojure-find-ns))) + + ;; From inside first ns's name, on its own line + (goto-char 29) + (should (equal "foo-bar" (clojure-find-ns))) + + ;; From inside second ns's name + (goto-char 42) + (should (equal "baz-quux" (clojure-find-ns)))) + (let ((data + '(("\"\n(ns foo-bar)\"\n" "(in-ns 'baz-quux)" "baz-quux") + (";(ns foo-bar)\n" "(in-ns 'baz-quux)" "baz-quux") + ("(ns foo-bar)\n" "\"\n(in-ns 'baz-quux)\"" "foo-bar") + ("(ns foo-bar)\n" ";(in-ns 'baz-quux)" "foo-bar")))) + (pcase-dolist (`(,form1 ,form2 ,expected) data) + (with-temp-buffer + (insert form1) + (save-excursion (insert form2)) + (clojure-mode) + ;; Between the two namespaces + (should (equal expected (clojure-find-ns))) + ;; After both namespaces + (goto-char (point-max)) + (should (equal expected (clojure-find-ns)))))))) (provide 'clojure-mode-sexp-test)