|
42 | 42 |
|
43 | 43 | (s/def ::dry-run (s/nilable #{"diff" "new-source"})) |
44 | 44 |
|
| 45 | +(s/def ::form-col pos-int?) |
| 46 | + |
45 | 47 | (s/def ::context |
46 | 48 | (s/keys :req [::file-path] |
47 | 49 | :opt [::source ::old-content ::new-source-code ::top-level-def-name |
48 | 50 | ::top-level-def-type ::edit-type ::error ::message |
49 | 51 | ::zloc ::offsets ::lint-result ::docstring |
50 | 52 | ::comment-substring ::new-content ::expand-symbols |
51 | | - ::diff ::type ::output-source ::nrepl-client-atom ::dry-run])) |
| 53 | + ::diff ::type ::output-source ::nrepl-client-atom ::dry-run |
| 54 | + ::form-col])) |
52 | 55 |
|
53 | 56 | ;; Pipeline helper functions |
54 | 57 |
|
|
229 | 232 | (str error-msg suggestion-msg) |
230 | 233 | error-msg)})))) |
231 | 234 |
|
| 235 | +(defn capture-form-position |
| 236 | + "Captures the column position of the found form for partial formatting. |
| 237 | + Must be called after find-form, before edit-form. |
| 238 | + Saves ::form-col (1-based column) to the context when position is available. |
| 239 | + Leaves ::form-col absent when z/position returns nil so downstream code |
| 240 | + can fall back to full-file formatting rather than mis-indenting. |
| 241 | +
|
| 242 | + Requires ::zloc in the context (pointing to the found form)." |
| 243 | + [ctx] |
| 244 | + (let [zloc (::zloc ctx)] |
| 245 | + (if-let [pos (z/position zloc)] |
| 246 | + (assoc ctx ::form-col (second pos)) ;; [row col] -> col |
| 247 | + ctx))) ;; leave ::form-col absent — downstream falls back to full-file formatting |
| 248 | + |
| 249 | +(defn format-new-source-partial |
| 250 | + "Formats the new source code in isolation when cljfmt is set to :partial |
| 251 | + and position tracking succeeded (::form-col is present in context). |
| 252 | + Formats ::new-source-code using cljfmt, then re-indents to match the |
| 253 | + target column position. This is done BEFORE insertion so the formatted |
| 254 | + content replaces the old form without touching the rest of the file. |
| 255 | +
|
| 256 | + When ::form-col is absent (position tracking unavailable), passes through |
| 257 | + unchanged so format-source falls back to full-file formatting. |
| 258 | +
|
| 259 | + Requires ::new-source-code, ::form-col, and ::nrepl-client-atom in context." |
| 260 | + [ctx] |
| 261 | + (let [nrepl-client-map (some-> ctx ::nrepl-client-atom deref) |
| 262 | + cljfmt-setting (config/get-cljfmt nrepl-client-map)] |
| 263 | + (if (and (= :partial cljfmt-setting) (::form-col ctx)) |
| 264 | + (let [new-source (::new-source-code ctx) |
| 265 | + target-col (::form-col ctx) |
| 266 | + formatting-options (core/project-formatting-options nrepl-client-map) |
| 267 | + formatted (core/format-form-in-isolation new-source target-col formatting-options)] |
| 268 | + (assoc ctx ::new-source-code formatted)) |
| 269 | + ;; Not :partial mode, or position unavailable — pass through |
| 270 | + ctx))) |
| 271 | + |
232 | 272 | (defn edit-form |
233 | 273 | "Edits the form according to the specified edit type. |
234 | 274 | Requires ::zloc, ::top-level-def-type, ::top-level-def-name, |
|
269 | 309 | "Formats the source code using the formatter. |
270 | 310 | If formatting fails but the source is syntactically valid, |
271 | 311 | returns the original source unchanged. |
272 | | - |
| 312 | +
|
| 313 | + When cljfmt is :partial AND the form was already formatted in isolation |
| 314 | + (indicated by ::form-col in context), skips whole-file formatting. |
| 315 | + Otherwise falls back to full-file formatting (e.g., for sexp-edit-pipeline |
| 316 | + which doesn't do pre-insertion partial formatting). |
| 317 | +
|
273 | 318 | Requires ::output-source in the context. |
274 | 319 | Updates ::output-source with the formatted code (or unchanged if formatting fails)." |
275 | 320 | [ctx] |
276 | 321 | (let [nrepl-client-map (some-> ctx ::nrepl-client-atom deref) |
277 | | - cljfmt-enabled (config/get-cljfmt nrepl-client-map)] |
278 | | - (if cljfmt-enabled |
| 322 | + cljfmt-setting (config/get-cljfmt nrepl-client-map)] |
| 323 | + (cond |
| 324 | + ;; :partial mode with pre-formatted form - skip whole-file formatting |
| 325 | + ;; ::form-col presence means format-new-source-partial already ran |
| 326 | + (and (= :partial cljfmt-setting) (::form-col ctx)) |
| 327 | + ctx |
| 328 | + |
| 329 | + ;; true (or :partial without pre-formatting) - full-file formatting |
| 330 | + cljfmt-setting |
279 | 331 | (try |
280 | 332 | (let [source (::output-source ctx) |
281 | 333 | formatting-options (core/project-formatting-options nrepl-client-map) |
|
289 | 341 | ;; Only propagate error if we don't have valid source |
290 | 342 | {::error true |
291 | 343 | ::message (str "Failed to format source: " (.getMessage e))}))) |
292 | | - ;; If cljfmt is disabled, return ctx unchanged |
| 344 | + |
| 345 | + ;; false - formatting disabled, return ctx unchanged |
| 346 | + :else |
293 | 347 | ctx))) |
294 | 348 |
|
295 | 349 | (defn determine-file-type |
|
425 | 479 | enhance-defmethod-name |
426 | 480 | parse-source |
427 | 481 | find-form |
| 482 | + capture-form-position |
| 483 | + format-new-source-partial |
428 | 484 | edit-form |
429 | 485 | zloc->output-source |
430 | 486 | format-source |
|
0 commit comments