Skip to content

Fix "Configuration files for cljfmt are ignored" (tests passing) #23

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/clojure_mcp/tools/file_edit/pipeline.clj
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,14 @@
- Updated context with formatted content for Clojure files, or unchanged for other file types"
[ctx]
(let [file-path (::form-pipeline/file-path ctx)
output-source (::form-pipeline/output-source ctx)]
output-source (::form-pipeline/output-source ctx)
nrepl-client-map @(::form-pipeline/nrepl-client-atom ctx)
formatting-options (form-edit-core/project-formatting-options nrepl-client-map)]
(if (and (file-write-core/is-clojure-file? file-path) output-source)
(try
(let [formatted-source (form-edit-core/format-source-string output-source)]
(let [formatted-source (form-edit-core/format-source-string
output-source
formatting-options)]
(assoc ctx ::form-pipeline/output-source formatted-source))
(catch Exception e
ctx))
Expand Down
9 changes: 5 additions & 4 deletions src/clojure_mcp/tools/file_write/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,14 @@

Returns:
- A map with :error, :type, :file-path, and :diff keys"
[file-path content]
[nrepl-client-atom file-path content]
(let [file (io/file file-path)
file-exists? (.exists file)
old-content (if file-exists? (slurp file) "")

;; Create a context map for the pipeline
initial-ctx {::pipeline/file-path file-path
initial-ctx {::pipeline/nrepl-client-atom nrepl-client-atom
::pipeline/file-path file-path
::pipeline/source old-content
::pipeline/new-source-code content
::pipeline/old-content old-content
Expand Down Expand Up @@ -104,7 +105,7 @@

Returns:
- A map with :error, :type, :file-path, and :diff keys"
[file-path content]
[nrepl-client-atom file-path content]
(if (is-clojure-file? file-path)
(write-clojure-file file-path content)
(write-clojure-file nrepl-client-atom file-path content)
(write-text-file file-path content)))
2 changes: 1 addition & 1 deletion src/clojure_mcp/tools/file_write/tool.clj
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ Before using this tool:

(defmethod tool-system/execute-tool :file-write [{:keys [nrepl-client-atom]} inputs]
(let [{:keys [file-path content]} inputs
result (core/write-file file-path content)]
result (core/write-file nrepl-client-atom file-path content)]
;; Update the timestamp if write was successful and we have a client atom
(when (and nrepl-client-atom (not (:error result)))
(file-timestamps/update-file-timestamp-to-current-mtime! nrepl-client-atom file-path))
Expand Down
42 changes: 29 additions & 13 deletions src/clojure_mcp/tools/form_edit/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
[rewrite-clj.node :as n]
[rewrite-clj.paredit :as par]
[cljfmt.core :as fmt]
[cljfmt.config :as cljfmt-config]
[clojure-mcp.config :as config]
[clojure.string :as str]
[clojure.java.io :as io]))

Expand Down Expand Up @@ -617,26 +619,40 @@

;; Source code formatting

(def default-formatting-options
{:indentation? true
:remove-surrounding-whitespace? true
:remove-trailing-whitespace? true
:insert-missing-whitespace? true
:remove-consecutive-blank-lines? true
:remove-multiple-non-indenting-spaces? true
:split-keypairs-over-multiple-lines? false
:sort-ns-references? false
:function-arguments-indentation :community
:indents fmt/default-indents})

(defn- load-cljfmt-config [nrepl-user-dir]
(let [path (cljfmt-config/find-config-file nrepl-user-dir)]
(->> (some-> path cljfmt-config/read-config)
(merge default-formatting-options)
(cljfmt-config/convert-legacy-keys))))

(defn project-formatting-options [nrepl-client-map]
(let [nrepl-user-dir (config/get-nrepl-user-dir nrepl-client-map)]
(load-cljfmt-config nrepl-user-dir)))

(defn format-source-string
"Formats a source code string using cljfmt with comprehensive formatting options.
"Formats a source code string using cljfmt. Use the project-formatting-options
function to get comprehensive formatting options for the current project.

Arguments:
- source-str: The source code string to format
- formatting-options: Options for cljfmt

Returns:
- The formatted source code string"
[source-str]
(let [formatting-options {:indentation? true
:remove-surrounding-whitespace? true
:remove-trailing-whitespace? true
:insert-missing-whitespace? true
:remove-consecutive-blank-lines? true
:remove-multiple-non-indenting-spaces? true
:split-keypairs-over-multiple-lines? false
:sort-ns-references? false
:function-arguments-indentation :community
:indents fmt/default-indents}]
(fmt/reformat-string source-str formatting-options)))
[source-str formatting-options]
(fmt/reformat-string source-str formatting-options))

;; File operations

Expand Down
4 changes: 3 additions & 1 deletion src/clojure_mcp/tools/form_edit/pipeline.clj
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,9 @@
[ctx]
(try
(let [source (::output-source ctx)
formatted (core/format-source-string source)]
nrepl-client-map @(::nrepl-client-atom ctx)
formatting-options (core/project-formatting-options nrepl-client-map)
formatted (core/format-source-string source formatting-options)]
(assoc ctx ::output-source formatted))
(catch Exception e
;; Instead of failing, use the original source if available
Expand Down
42 changes: 34 additions & 8 deletions test/clojure_mcp/tools/file_write/core_test.clj
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
(ns clojure-mcp.tools.file-write.core-test
(:require
[clojure.test :refer [deftest is testing use-fixtures]]
[clojure-mcp.config :as config]
[clojure-mcp.tools.file-write.core :as file-write-core]
[clojure-mcp.tools.test-utils :as test-utils]
[clojure.java.io :as io]
[clojure.string :as str]))

Expand All @@ -28,6 +30,7 @@
(binding [*test-dir* test-dir
*test-clj-file* test-clj-file
*test-txt-file* test-txt-file]
(config/set-config! test-utils/*nrepl-client-atom* :nrepl-user-dir test-dir)
(try
(f)
(finally
Expand All @@ -37,6 +40,7 @@
(.delete file)))
(.delete test-dir))))))

(use-fixtures :once test-utils/test-nrepl-fixture)
(use-fixtures :each create-test-files-fixture)

(deftest is-clojure-file-test
Expand Down Expand Up @@ -79,7 +83,10 @@
(testing "Creating a new Clojure file"
(let [new-file (io/file *test-dir* "new-file.clj")
content "(ns new.namespace)\n\n(defn new-function [x]\n (+ x 10))"
result (file-write-core/write-clojure-file (.getPath new-file) content)]
result (file-write-core/write-clojure-file
test-utils/*nrepl-client-atom*
(.getPath new-file)
content)]
(is (not (:error result)))
(is (= "create" (:type result)))
(is (= (.getPath new-file) (:file-path result)))
Expand All @@ -90,7 +97,10 @@
(let [path (.getPath *test-clj-file*)
original-content (slurp *test-clj-file*)
new-content "(ns test.namespace)\n\n(defn test-function [x]\n (+ x 10))"
result (file-write-core/write-clojure-file path new-content)]
result (file-write-core/write-clojure-file
test-utils/*nrepl-client-atom*
path
new-content)]
(is (not (:error result)))
(is (= "update" (:type result)))
(is (= path (:file-path result)))
Expand All @@ -100,7 +110,10 @@
(testing "Formatting Clojure code during write"
(let [path (.getPath *test-clj-file*)
unformatted-content "(ns test.namespace)( defn poorly-formatted-fn[x]( + x 5) )"
result (file-write-core/write-clojure-file path unformatted-content)]
result (file-write-core/write-clojure-file
test-utils/*nrepl-client-atom*
path
unformatted-content)]
(is (not (:error result)))
(is (= "update" (:type result)))
(is (not (str/includes? (slurp *test-clj-file*) "poorly-formatted-fn[x]")))
Expand All @@ -109,7 +122,10 @@
(testing "Auto-repairs missing closing parenthesis"
(let [path (.getPath *test-clj-file*)
content-with-missing-paren "(ns test.namespace)\n\n(defn repaired-function [x]\n (+ x 10)"
result (file-write-core/write-clojure-file path content-with-missing-paren)]
result (file-write-core/write-clojure-file
test-utils/*nrepl-client-atom*
path
content-with-missing-paren)]
(is (not (:error result)))
(is (= "update" (:type result)))
(let [saved-content (slurp *test-clj-file*)]
Expand All @@ -120,7 +136,10 @@
(testing "Auto-repairs unbalanced brackets"
(let [path (.getPath *test-clj-file*)
content-with-mismatched-brackets "(ns test.namespace)\n\n(defn bracket-fn [x]\n (let [y (+ x 1)]\n (println y]))"
result (file-write-core/write-clojure-file path content-with-mismatched-brackets)]
result (file-write-core/write-clojure-file
test-utils/*nrepl-client-atom*
path
content-with-mismatched-brackets)]
(is (not (:error result)))
(is (= "update" (:type result)))
(let [saved-content (slurp *test-clj-file*)]
Expand All @@ -132,7 +151,10 @@
(let [path (.getPath *test-clj-file*)
content-with-syntax-error "(ns test.namespace)\n\n(defn broken-function a123 [x 11)\n (+ x 10))"
original-content (slurp *test-clj-file*)
result (file-write-core/write-clojure-file path content-with-syntax-error)]
result (file-write-core/write-clojure-file
test-utils/*nrepl-client-atom*
path
content-with-syntax-error)]
;; The test should fail with a specific error
(is (:error result) "Should have error for non-repairable syntax error")
(when (:message result)
Expand All @@ -145,7 +167,9 @@
(testing "Write dispatcher for Clojure files"
(let [clj-file (io/file *test-dir* "dispatch-test.clj")
content "(ns dispatch.test)"
result (file-write-core/write-file (.getPath clj-file) content)]
result (file-write-core/write-file test-utils/*nrepl-client-atom*
(.getPath clj-file)
content)]
(is (not (:error result)))
(is (= "create" (:type result)))
(is (.exists clj-file))
Expand All @@ -154,7 +178,9 @@
(testing "Write dispatcher for text files"
(let [txt-file (io/file *test-dir* "dispatch-test.txt")
content "Plain text content"
result (file-write-core/write-file (.getPath txt-file) content)]
result (file-write-core/write-file test-utils/*nrepl-client-atom*
(.getPath txt-file)
content)]
(is (not (:error result)))
(is (= "create" (:type result)))
(is (.exists txt-file))
Expand Down
17 changes: 16 additions & 1 deletion test/clojure_mcp/tools/form_edit/core_test.clj
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
(ns clojure-mcp.tools.form-edit.core-test
(:require
[clojure-mcp.tools.test-utils :as test-utils]
[clojure-mcp.config :as config]
[clojure.test :refer [deftest testing is use-fixtures]]
[clojure-mcp.tools.form-edit.core :as sut]
[rewrite-clj.zip :as z]
Expand All @@ -18,12 +20,14 @@
(spit test-file "(ns test.core)\n\n(defn example-fn [x y]\n #_(println \"debug value:\" x)\n (+ x y))\n\n(def a 1)\n\n#_(def unused-value 42)\n\n(comment\n (example-fn 1 2))\n\n;; Test comment\n;; spans multiple lines")
(binding [*test-dir* test-dir
*test-file* test-file]
(config/set-config! test-utils/*nrepl-client-atom* :nrepl-user-dir test-dir)
(try
(f)
(finally
(when (.exists test-file) (.delete test-file))
(when (.exists test-dir) (.delete test-dir))))))))

(use-fixtures :once test-utils/test-nrepl-fixture)
(use-fixtures :each create-test-files-fixture)

;; Test helper functions
Expand Down Expand Up @@ -305,10 +309,21 @@
(deftest format-source-string-test
(testing "format-source-string correctly formats source code"
(let [unformatted "(defn example-fn[x y] (+ x y) )"
formatted (sut/format-source-string unformatted)]
formatted (sut/format-source-string
unformatted
sut/default-formatting-options)]
;; Compare as EDN to ignore whitespace differences
(is (= (read-string unformatted) (read-string formatted))))))

(deftest project-formatting-options-test
(testing "format-source-string correctly formats source code"
(let [custom-options {:function-arguments-indentation :cursive}
_ (spit (io/file *test-dir* "cljfmt.edn") (pr-str custom-options))
formatting-options (sut/project-formatting-options
@test-utils/*nrepl-client-atom*)]
(is (= (merge sut/default-formatting-options custom-options)
formatting-options)))))

(deftest load-file-content-test
(testing "load-file-content loads existing file"
(let [path (.getAbsolutePath *test-file*)
Expand Down
6 changes: 4 additions & 2 deletions test/clojure_mcp/tools/form_edit/pipeline_test.clj
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
(ns clojure-mcp.tools.form-edit.pipeline-test
(:require
[clojure.test :refer [deftest testing is use-fixtures]]
[clojure-mcp.config :as config]
[clojure-mcp.tools.form-edit.pipeline :as sut]
[clojure-mcp.tools.form-edit.core :as core]
[clojure-mcp.tools.test-utils :as test-utils]
Expand All @@ -11,7 +12,6 @@
;; Test fixtures
(def ^:dynamic *test-dir* nil)
(def ^:dynamic *test-file* nil)

(def ^:dynamic *nrepl-client-atom* nil)

(defn create-test-files-fixture [f]
Expand All @@ -29,6 +29,7 @@
test-file-content)]
(binding [*test-dir* test-dir
*test-file* (io/file test-file-path)]
(config/set-config! test-utils/*nrepl-client-atom* :nrepl-user-dir test-dir)
(try
(f)
(finally
Expand Down Expand Up @@ -175,7 +176,8 @@
(testing "format-source formats the source code"
;; Manual setup to avoid dependency on zloc->output-source
(let [source "(ns test.core)\n\n(defn example-fn[x y] (+ x y))"
ctx {::sut/output-source source} ;; Directly use the source as output source
ctx {::sut/nrepl-client-atom *nrepl-client-atom*
::sut/output-source source} ;; Directly use the source as output source
result (sut/format-source ctx)]
(is (string? (::sut/output-source result)))
(is (not (str/includes? (::sut/output-source result) " example-fn[x y]")))
Expand Down
1 change: 1 addition & 0 deletions test/clojure_mcp/tools/test_utils.clj
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
(ns clojure-mcp.tools.test-utils
"Utility functions for testing the tool-system based tools."
(:require
[clojure-mcp.config :as config]
[clojure-mcp.nrepl :as nrepl]
[nrepl.server :as nrepl-server]
[clojure-mcp.tool-system :as tool-system]
Expand Down