From 4be68439c96cfb369bcc758b49c20a898447877c Mon Sep 17 00:00:00 2001 From: Lars Andersen Date: Wed, 29 Jul 2015 15:00:22 +0200 Subject: [PATCH] [Fix #310,#311] clojure-expected-ns with src/cljc When the source path is src/clj{,c,s,x} instead of just src/ clojure-expected ns would create namespaces like clj.my-project.my-ns whereas what's wanted is my-project.my-ns. Reading boot.clj or project.clj to find out the user's src dirs is out of scope for clojure-mode, so we use the simply heuristic that no namespace should start with clj, cljc or cljs because these are the idiomatic source directories in multi-source projects. When improving clojure-expected-ns I extracted out two utilities, clojure-project-dir and clojure-project-relative-path. These utilities already exist in clj-refactor so I opted to make them public rather than private, as they are generally useful. --- CHANGELOG.md | 1 + clojure-mode.el | 37 ++++++++++++++++-------- test/clojure-mode-util-test.el | 53 ++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 12 deletions(-) create mode 100644 test/clojure-mode-util-test.el diff --git a/CHANGELOG.md b/CHANGELOG.md index edf2ba00..b4c15cb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ ### Bugs fixed +* [#310](https://github.com/clojure-emacs/clojure-mode/issues/310) and [#311](https://github.com/clojure-emacs/clojure-mode/issues/311) Fix `clojure-expected-ns` in multi-source projects. * [#307](https://github.com/clojure-emacs/clojure-mode/issues/307): Don't highlight `handle` and `handler-case` as keywords. ## 4.1.0 (20/06/2015) diff --git a/clojure-mode.el b/clojure-mode.el index a1d0f576..083ddb29 100644 --- a/clojure-mode.el +++ b/clojure-mode.el @@ -979,18 +979,31 @@ nil." -(defun clojure-expected-ns () - "Return the namespace name that the file should have." - (let* ((project-dir (file-truename - (or (locate-dominating-file default-directory - "project.clj") - (locate-dominating-file default-directory - "build.boot")))) - (relative (substring (file-truename (buffer-file-name)) - (length project-dir) - (- (length (file-name-extension (buffer-file-name) t)))))) - (replace-regexp-in-string - "_" "-" (mapconcat 'identity (cdr (split-string relative "/")) ".")))) +(defun clojure-project-dir () + "Return the absolute path to the project's root directory." + (file-truename + (or (locate-dominating-file default-directory + "project.clj") + (locate-dominating-file default-directory + "build.boot")))) + +(defun clojure-project-relative-path (path) + "Denormalize PATH by making it relative to the project root." + (file-relative-name path (clojure-project-dir))) + +(defun clojure-expected-ns (&optional path) + "Return the namespace matching PATH. + +PATH is expected to be an absolute file path. + +If PATH is nil, use the path to the file backing the current buffer." + (let* ((relative (clojure-project-relative-path + (or path (file-truename (buffer-file-name))))) + (sans-file-type (substring relative 0 (- (length (file-name-extension path t))))) + (sans-file-sep (mapconcat 'identity (cdr (split-string sans-file-type "/")) ".")) + (sans-underscores (replace-regexp-in-string "_" "-" sans-file-sep))) + ;; Drop prefix from ns for projects with structure src/{clj,cljs,cljc} + (replace-regexp-in-string "\\`clj[scx]?\\." "" sans-underscores))) (defun clojure-insert-ns-form-at-point () "Insert a namespace form at point." diff --git a/test/clojure-mode-util-test.el b/test/clojure-mode-util-test.el new file mode 100644 index 00000000..37367578 --- /dev/null +++ b/test/clojure-mode-util-test.el @@ -0,0 +1,53 @@ +;;; clojure-mode-util-test.el --- Clojure Mode: util test suite -*- lexical-binding: t; -*- + +;; Copyright (C) 2014-2015 Bozhidar Batsov + +;; This file is not part of GNU Emacs. + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; The unit test suite of Clojure Mode + +;;; Code: +(require 'clojure-mode) +(require 'cl-lib) +(require 'ert) + +(let ((project-dir "/home/user/projects/my-project/") + (clj-file-path "/home/user/projects/my-project/src/clj/my_project/my_ns/my_file.clj") + (project-relative-clj-file-path "src/clj/my_project/my_ns/my_file.clj") + (clj-file-ns "my-project.my-ns.my-file")) + + (ert-deftest project-relative-path () + :tags '(utils) + (cl-letf (((symbol-function 'clojure-project-dir) (lambda () project-dir))) + (should (string= (clojure-project-relative-path clj-file-path) + project-relative-clj-file-path)))) + + (ert-deftest expected-ns () + :tags '(utils) + (cl-letf (((symbol-function 'clojure-project-relative-path) + (lambda (&optional current-buffer-file-name) + project-relative-clj-file-path))) + (should (string= (clojure-expected-ns clj-file-path) clj-file-ns))))) + +(provide 'clojure-mode-util-test) + +;; Local Variables: +;; indent-tabs-mode: nil +;; End: + +;;; clojure-mode-util-test.el ends here