diff --git a/CHANGELOG.md b/CHANGELOG.md index c167910..3f0f61a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ - [#42]: Fix font locking of definitions with metadata - [#42]: Fix indentation of definitions with metadata - Fix semantic indentation of quoted functions +- Add custom `fill-paragraph-function` which respects docstrings similar to + `clojure-mode`. ## 0.2.2 (2024-02-16) diff --git a/README.md b/README.md index 62d8414..a3d762c 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,17 @@ To make forms inside of `(comment ...)` forms appear as top-level forms for eval (setq clojure-ts-toplevel-inside-comment-form t) ``` +### Fill paragraph + +To change the maximal line length used by `M-x prog-fill-reindent-defun` (also +bound to `M-q` by default) to reformat docstrings and comments it's possible to +customize `clojure-ts-fill-paragraph` variable (by default set to the value of +Emacs' `fill-paragraph` value). + +Every new line in the docstrings is indented by +`clojure-ts-docstring-fill-prefix-width` number of spaces (set to 2 by default +which matches the `clojure-mode` settings). + ## Rationale [clojure-mode](https://github.com/clojure-emacs/clojure-mode) has served us well diff --git a/clojure-ts-mode.el b/clojure-ts-mode.el index 2c56c19..1d6fa21 100644 --- a/clojure-ts-mode.el +++ b/clojure-ts-mode.el @@ -97,6 +97,21 @@ itself." :safe #'booleanp :package-version '(clojure-ts-mode . "0.2.1")) +(defcustom clojure-ts-docstring-fill-column fill-column + "Value of `fill-column' to use when filling a docstring." + :type 'integer + :safe #'integerp + :package-version '(clojure-ts-mode . "0.2.3")) + +(defcustom clojure-ts-docstring-fill-prefix-width 2 + "Width of `fill-prefix' when filling a docstring. +The default value conforms with the de facto convention for +Clojure docstrings, aligning the second line with the opening +double quotes on the third column." + :type 'integer + :safe #'integerp + :package-version '(clojure-ts-mode . "0.2.3")) + (defvar clojure-ts--debug nil "Enables debugging messages, shows current node in mode-line. Only intended for use at development time.") @@ -863,6 +878,27 @@ forms like deftype, defrecord, reify, proxy, etc." '(semantic fixed) clojure-ts-indent-style))))) +(defun clojure-ts--docstring-fill-prefix () + "The prefix string used by `clojure-ts--fill-paragraph'. +It is simply `clojure-ts-docstring-fill-prefix-width' number of spaces." + (make-string clojure-ts-docstring-fill-prefix-width ? )) + +(defun clojure-ts--fill-paragraph (&optional justify) + "Like `fill-paragraph', but can handler Clojure docstrings. +If JUSTIFY is non-nil, justify as well as fill the paragraph." + (let ((current-node (treesit-node-at (point)))) + (if (clojure-ts--match-docstring nil current-node nil) + (let ((fill-column (or clojure-ts-docstring-fill-column fill-column)) + (fill-prefix (clojure-ts--docstring-fill-prefix)) + (beg-doc (treesit-node-start current-node)) + (end-doc (treesit-node-end current-node))) + (save-restriction + (narrow-to-region beg-doc end-doc) + (fill-paragraph justify))) + (or (fill-comment-paragraph justify) + (fill-paragraph justify))) + t)) + (defconst clojure-ts--sexp-nodes '("#_" ;; transpose-sexp near a discard macro moves it around. "num_lit" "sym_lit" "kwd_lit" "nil_lit" "bool_lit" @@ -963,6 +999,7 @@ See `clojure-ts--font-lock-settings' for usage of MARKDOWN-AVAILABLE." (keyword string char symbol builtin type) (constant number quote metadata doc) (bracket deref function regex tagged-literals))) + (setq-local fill-paragraph-function #'clojure-ts--fill-paragraph) (when (boundp 'treesit-thing-settings) ;; Emacs 30+ (setq-local treesit-thing-settings clojure-ts--thing-settings))) diff --git a/test/clojure-ts-mode-fill-paragraph-test.el b/test/clojure-ts-mode-fill-paragraph-test.el new file mode 100644 index 0000000..a7e2dde --- /dev/null +++ b/test/clojure-ts-mode-fill-paragraph-test.el @@ -0,0 +1,62 @@ +;;; clojure-ts-mode-fill-paragraph-test.el --- -*- lexical-binding: t; -*- + +;; Copyright (C) 2025 Roman Rudakov + +;; 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 TS Mode + +;;; Code: + +(require 'clojure-ts-mode) +(require 'buttercup) + +(describe "clojure-ts--fill-paragraph" + (it "should reformat only the docstring" + (with-clojure-ts-buffer "(ns foo) + +(defn hello-world + \"This is a very long docstring that should be reformatted using fill-paragraph function.\" + [] + (pringln \"Hello world\"))" + (goto-char 40) + (prog-fill-reindent-defun) + (expect (buffer-substring-no-properties (point-min) (point-max)) + :to-equal + "(ns foo) + +(defn hello-world + \"This is a very long docstring that should be reformatted using + fill-paragraph function.\" + [] + (pringln \"Hello world\"))"))) + + (it "should reformat normal comments properly" + (with-clojure-ts-buffer "(ns foo) + +;; This is a very long comment that should be reformatted using fill-paragraph function." + (goto-char 20) + (prog-fill-reindent-defun) + (expect (buffer-substring-no-properties (point-min) (point-max)) + :to-equal + "(ns foo) + +;; This is a very long comment that should be reformatted using +;; fill-paragraph function.")))) + +;;; clojure-ts-mode-fill-paragraph-test.el ends here