Skip to content

Commit ec8677b

Browse files
authored
Merge pull request #42 from BetterThanTomorrow:41-unbalance-refusal
41-unbalance-refusal * Fixes #41
2 parents 8f596a9 + 3a7f971 commit ec8677b

File tree

9 files changed

+175
-154
lines changed

9 files changed

+175
-154
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ Changes to Calva Backseat Driver
44

55
## [Unreleased]
66

7+
- [Avoid using potentially incorrectly auto-balanced code in evaluations and edits](https://github.com/BetterThanTomorrow/calva-backseat-driver/issues/41)
8+
79
## [v0.0.22] - 2025-10-01
810

911
- Fix: [Evaluation tool not available in v0.0.21](https://github.com/BetterThanTomorrow/calva-backseat-driver/issues/39)

e2e-test-ws/.joyride/src/tests/mcp/server_test.cljs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
(ns tests.mcp.server-test
22
(:require
3-
["fs" :as fs]
43
["net" :as net]
54
["path" :as path]
65
["vscode" :as vscode]

e2e-test-ws/.joyride/src/tests/ws_ready/example_test.cljs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
(ns tests.ws-ready.example-test
2-
(:require [cljs.test :refer [deftest testing is]]
2+
(:require [cljs.test :refer [deftest is]]
33
["vscode" :as vscode]
44
workspace-activate))
55

scripts/tasks.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
(ns tasks
2-
(:require [babashka.process :as p]
2+
(:require #_[babashka.process :as p]
33
[clojure.string :as string]
44
[babashka.fs :as fs]
55
publish

src/calva_backseat_driver/bracket_balance.cljs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,13 @@
11
(ns calva-backseat-driver.bracket-balance
2-
(:require ["parinfer" :as parinfer]))
3-
4-
(defn infer-parens
5-
[code]
6-
(some-> (parinfer/indentMode code #js {:partialResult true})
7-
(js->clj :keywordize-keys true)))
2+
(:require [calva-backseat-driver.integrations.parinfer :as parinfer]))
83

94
(defn infer-parens-response
105
"Infer parens from the indentation"
116
[{:ex/keys [dispatch!]
127
:calva/keys [text]}]
138
(dispatch! [[:app/ax.log :debug "[Server] Infering brackets for:" text]])
149
(try
15-
(let [result (infer-parens text)]
10+
(let [result (parinfer/infer-brackets text)]
1611
(clj->js
1712
(if (:success result)
1813
(let [new-text (:text result)]

src/calva_backseat_driver/integrations/calva/editor.cljs

Lines changed: 86 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -174,37 +174,31 @@
174174
:file-context file-context})
175175

176176
;; Validation done. Proceed with form editing
177-
(p/let [balance-result (some-> (parinfer/infer-brackets new-form)
178-
(js->clj :keywordize-keys true))
179-
form-data (get-ranges-form-data-by-line file-path final-line-number ranges-fn-key)
180-
diagnostics-before-edit (get-diagnostics-for-file file-path)]
181-
(if (:success balance-result)
182-
(p/let [balanced-form (:text balance-result)
183-
balancing-occurred? (not= new-form balanced-form)
184-
text (if (= :insertionPoint ranges-fn-key)
185-
(str (string/trim balanced-form) "\n\n")
186-
balanced-form)
187-
edit-result (edit-replace-range file-path
188-
(first (:ranges-object form-data))
189-
text)
190-
_ (p/delay 1000) ;; TODO: Consider subscribing on diagnistics changes instead
191-
diagnostics-after-edit (get-diagnostics-for-file file-path)]
192-
(if edit-result
193-
(do
194-
(.save vscode-document)
195-
(cond-> {:success true
196-
:diagnostics-before-edit diagnostics-before-edit
197-
:diagnostics-after-edit diagnostics-after-edit}
198-
(not= final-line-number line-number)
199-
(assoc :actual-line-used final-line-number)
200-
201-
balancing-occurred?
202-
(merge
203-
{:balancing-note "The code provided for editing had unbalanced brackets. The code was automatically balanced before editing. Use the code in the `balanced-code` to correct your code on record."
204-
:balanced-code balanced-form})))
205-
{:success false
206-
:diagnostics-before-edit diagnostics-before-edit}))
207-
balance-result))))))
177+
(let [validation-result (parinfer/validate-brackets new-form)]
178+
(if-not (:valid? validation-result)
179+
;; REFUSE to edit - return validation failure
180+
(p/resolved (assoc validation-result :success false))
181+
;; Valid brackets - proceed with edit
182+
(p/let [form-data (get-ranges-form-data-by-line file-path final-line-number ranges-fn-key)
183+
diagnostics-before-edit (get-diagnostics-for-file file-path)]
184+
(p/let [text (if (= :insertionPoint ranges-fn-key)
185+
(str (string/trim new-form) "\n\n")
186+
new-form)
187+
edit-result (edit-replace-range file-path
188+
(first (:ranges-object form-data))
189+
text)
190+
_ (p/delay 1000)
191+
diagnostics-after-edit (get-diagnostics-for-file file-path)]
192+
(if edit-result
193+
(do
194+
(.save vscode-document)
195+
(cond-> {:success true
196+
:diagnostics-before-edit diagnostics-before-edit
197+
:diagnostics-after-edit diagnostics-after-edit}
198+
(not= final-line-number line-number)
199+
(assoc :actual-line-used final-line-number)))
200+
{:success false
201+
:diagnostics-before-edit diagnostics-before-edit})))))))))
208202
(p/catch (fn [e]
209203
{:success false
210204
:error (.-message e)})))
@@ -265,75 +259,68 @@
265259
(defn structural-create-file+
266260
"Create a new Clojure file with exact content using vscode/workspace.fs API"
267261
[file-path content]
268-
(let [inferred (parinfer/infer-brackets content)
269-
infer-result (when inferred (js->clj inferred :keywordize-keys true))
270-
balanced-content (if (:success infer-result)
271-
(:text infer-result)
272-
content)
273-
balancing-occurred? (not= content balanced-content)]
274-
(p/let [uri (vscode/Uri.file file-path)
275-
normalized-content (normalize-file-content balanced-content)
276-
content-bytes (.encode (js/TextEncoder.) normalized-content)
277-
directory-path (get-directory-from-path file-path)
278-
directory-uri (vscode/Uri.file directory-path)]
279-
(p/-> (vscode/workspace.fs.createDirectory directory-uri)
280-
(p/catch (fn [_] nil)) ;; Ignore if directory already exists
281-
(p/then (fn [_]
282-
(vscode/workspace.fs.writeFile uri content-bytes)))
283-
(p/then (fn [_]
284-
(p/let [_ (p/delay 1000) ;; Wait for diagnostics to update
285-
diagnostics-after-edit (get-diagnostics-for-file file-path)]
286-
(cond-> {:success true
287-
:file-path file-path
288-
:message "File created successfully"
289-
:diagnostics-after-edit diagnostics-after-edit}
290-
balancing-occurred?
291-
(merge {:balancing-note "The code provided had unbalanced brackets. The code was automatically balanced before creating file."
292-
:balanced-code balanced-content})))))
293-
(p/catch (fn [error]
294-
{:success false
295-
:error (.-message error)
296-
:file-path file-path}))))))
262+
(let [validation-result (parinfer/validate-brackets content)]
263+
(if-not (:valid? validation-result)
264+
;; REFUSE to create - return validation failure
265+
(p/resolved (assoc validation-result
266+
:file-path file-path))
267+
;; Valid brackets - proceed with file creation
268+
(p/let [uri (vscode/Uri.file file-path)
269+
normalized-content (normalize-file-content content)
270+
content-bytes (.encode (js/TextEncoder.) normalized-content)
271+
directory-path (get-directory-from-path file-path)
272+
directory-uri (vscode/Uri.file directory-path)]
273+
(p/-> (vscode/workspace.fs.createDirectory directory-uri)
274+
(p/catch (fn [_] nil))
275+
(p/then (fn [_]
276+
(vscode/workspace.fs.writeFile uri content-bytes)))
277+
(p/then (fn [_]
278+
(p/let [_ (p/delay 1000)
279+
diagnostics-after-edit (get-diagnostics-for-file file-path)]
280+
{:success true
281+
:file-path file-path
282+
:message "File created successfully"
283+
:diagnostics-after-edit diagnostics-after-edit})))
284+
(p/catch (fn [error]
285+
{:success false
286+
:error (.-message error)
287+
:file-path file-path})))))))
297288

298289
(defn append-code+
299290
"Append a top-level form to the end of a file at guaranteed top level"
300291
[file-path code]
301-
(let [inferred (parinfer/infer-brackets code)
302-
infer-result (when inferred (js->clj inferred :keywordize-keys true))
303-
balanced-form (if (:success infer-result)
304-
(:text infer-result)
305-
code)
306-
balancing-occurred? (not= code balanced-form)]
307-
(-> (p/let [uri (vscode/Uri.file file-path)
308-
^js vscode-document (vscode/workspace.openTextDocument uri)
309-
diagnostics-before-edit (get-diagnostics-for-file file-path)
310-
last-line-number (.-lineCount vscode-document)
311-
end-position (vscode/Position. last-line-number 0)
312-
last-line-text (if (pos? last-line-number)
313-
(.-text (.lineAt vscode-document (dec last-line-number)))
314-
"")
315-
needs-spacing? (and (pos? last-line-number)
316-
(not (string/blank? last-line-text)))
317-
spacing (if needs-spacing? "\n\n" "\n")
318-
append-text (str spacing (string/trim balanced-form) "\n")
319-
edit (vscode/TextEdit.insert end-position append-text)
320-
workspace-edit (vscode/WorkspaceEdit.)
321-
_ (.set workspace-edit uri #js [edit])
322-
edit-result (vscode/workspace.applyEdit workspace-edit)
323-
_ (p/delay 1000) ;; Wait for diagnostics to update
324-
diagnostics-after-edit (get-diagnostics-for-file file-path)]
325-
(.save vscode-document)
326-
(if edit-result
327-
(cond-> {:success true
328-
:appended-at-end true
329-
:diagnostics-before-edit diagnostics-before-edit
330-
:diagnostics-after-edit diagnostics-after-edit}
331-
balancing-occurred?
332-
(merge {:balancing-note "The code provided had unbalanced brackets. The code was automatically balanced before appending."
333-
:balanced-code balanced-form}))
334-
{:success false
335-
:diagnostics-before-edit diagnostics-before-edit
336-
:error "Failed to apply workspace edit"}))
337-
(p/catch (fn [error]
338-
{:success false
339-
:error (.-message error)})))))
292+
(let [validation-result (parinfer/validate-brackets code)]
293+
(if-not (:valid? validation-result)
294+
;; REFUSE to append - return validation failure
295+
(p/resolved validation-result)
296+
;; Valid brackets - proceed with append
297+
(-> (p/let [uri (vscode/Uri.file file-path)
298+
^js vscode-document (vscode/workspace.openTextDocument uri)
299+
diagnostics-before-edit (get-diagnostics-for-file file-path)
300+
last-line-number (.-lineCount vscode-document)
301+
end-position (vscode/Position. last-line-number 0)
302+
last-line-text (if (pos? last-line-number)
303+
(.-text (.lineAt vscode-document (dec last-line-number)))
304+
"")
305+
needs-spacing? (and (pos? last-line-number)
306+
(not (string/blank? last-line-text)))
307+
spacing (if needs-spacing? "\n\n" "\n")
308+
append-text (str spacing (string/trim code) "\n")
309+
edit (vscode/TextEdit.insert end-position append-text)
310+
workspace-edit (vscode/WorkspaceEdit.)
311+
_ (.set workspace-edit uri #js [edit])
312+
edit-result (vscode/workspace.applyEdit workspace-edit)
313+
_ (p/delay 1000)
314+
diagnostics-after-edit (get-diagnostics-for-file file-path)]
315+
(.save vscode-document)
316+
(if edit-result
317+
{:success true
318+
:appended-at-end true
319+
:diagnostics-before-edit diagnostics-before-edit
320+
:diagnostics-after-edit diagnostics-after-edit}
321+
{:success false
322+
:diagnostics-before-edit diagnostics-before-edit
323+
:error "Failed to apply workspace edit"}))
324+
(p/catch (fn [error]
325+
{:success false
326+
:error (.-message error)}))))))

src/calva_backseat_driver/integrations/calva/features.cljs

Lines changed: 36 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
(ns calva-backseat-driver.integrations.calva.features
22
(:require
33
["vscode" :as vscode]
4-
[calva-backseat-driver.bracket-balance :as balance]
54
[calva-backseat-driver.integrations.calva.api :as calva]
65
[calva-backseat-driver.integrations.calva.editor :as editor]
7-
[promesa.core :as p]))
6+
[promesa.core :as p]
7+
[calva-backseat-driver.integrations.parinfer :as parinfer]))
88

99
(def ^:private no-ns-eval-note
1010
"When evaluating without providing a namespace argument the evaluation is performed in the `user` namespace. Most often this is not what you want, and instead you should be evaluating providing the namespace argument. If it is the first time you are using a namespace, evaluate its ns-form first.")
@@ -22,46 +22,40 @@
2222
Takes a string of code to evaluate and a session key (clj/cljs/cljc), js/undefined means current session."
2323
[{:ex/keys [dispatch!]
2424
:calva/keys [code repl-session-key ns]}]
25-
(let [inferred (balance/infer-parens code)
26-
balancded-code (if (:success inferred)
27-
(:text inferred)
28-
code)
29-
balancing-ocurred? (not= code balancded-code)]
30-
(when balancing-ocurred?
31-
(dispatch! [[:app/ax.log :debug "[Server] Code was unbalanced:" code "balancded-code:" balancded-code]]))
32-
(p/let [evaluate (get-in calva/calva-api [:repl :evaluateCode])
33-
result (-> (p/let [^js evaluation+ (if ns
34-
(evaluate repl-session-key balancded-code ns)
35-
(evaluate repl-session-key balancded-code))]
36-
(dispatch! [[:app/ax.log :debug "[Server] Evaluating code:" balancded-code]])
37-
(cond-> {:result (.-result evaluation+)
38-
:ns (.-ns evaluation+)
39-
:stdout (.-output evaluation+)
40-
:stderr (.-errorOutput evaluation+)
41-
:session-key (.-replSessionKey evaluation+)
42-
:note "Remember to check the output tool now and then to see what's happening in the application."}
43-
44-
balancing-ocurred?
45-
(merge
46-
{:balancing-note "The code provided for evaluation had unbalanced brackets. The code was automatically balanced before evaluation. Use the code in the `balanced-code` to correct your code on record."
47-
:balanced-code balancded-code})
48-
49-
(.-error evaluation+)
50-
(merge {:error (.-error evaluation+)
51-
:stacktrace (.-stacktrace evaluation+)})
52-
53-
(not ns)
54-
(merge {:note no-ns-eval-note})
55-
56-
(= "" (.-result evaluation+))
57-
(merge {:note empty-result-note})))
58-
(p/catch (fn [err]
59-
(dispatch! [[:app/ax.log :debug "[Server] Evaluation failed:"
60-
err]])
61-
{:result "nil"
62-
:stderr (pr-str err)
63-
:note error-result-note})))]
64-
(clj->js result))))
25+
(let [{:keys [valid? balanced-code]
26+
:as validation} (parinfer/validate-brackets code)]
27+
(when-not valid?
28+
(dispatch! [[:app/ax.log :debug "[Server] Code was unbalanced:" code "balanced-code:" balanced-code]]))
29+
(if-not valid?
30+
(p/resolved validation)
31+
(p/let [evaluate (get-in calva/calva-api [:repl :evaluateCode])
32+
result (-> (p/let [^js evaluation+ (if ns
33+
(evaluate repl-session-key code ns)
34+
(evaluate repl-session-key code))]
35+
(dispatch! [[:app/ax.log :debug "[Server] Evaluating code:" code]])
36+
(cond-> {:result (.-result evaluation+)
37+
:ns (.-ns evaluation+)
38+
:stdout (.-output evaluation+)
39+
:stderr (.-errorOutput evaluation+)
40+
:session-key (.-replSessionKey evaluation+)
41+
:note "Remember to check the output tool now and then to see what's happening in the application."}
42+
43+
(.-error evaluation+)
44+
(merge {:error (.-error evaluation+)
45+
:stacktrace (.-stacktrace evaluation+)})
46+
47+
(not ns)
48+
(merge {:note no-ns-eval-note})
49+
50+
(= "" (.-result evaluation+))
51+
(merge {:note empty-result-note})))
52+
(p/catch (fn [err]
53+
(dispatch! [[:app/ax.log :debug "[Server] Evaluation failed:"
54+
err]])
55+
{:result "nil"
56+
:stderr (pr-str err)
57+
:note error-result-note})))]
58+
(clj->js result)))))
6559

6660
(defn get-clojuredocs+ [{:ex/keys [dispatch!]
6761
:calva/keys [clojure-symbol]}]

0 commit comments

Comments
 (0)