@@ -150,6 +150,16 @@ three or more semicolons will be treated as outline headings. If set to
150
150
:type 'string
151
151
:package-version '(clojure-ts-mode . " 0.4" ))
152
152
153
+ (defcustom clojure-ts-thread-all-but-last nil
154
+ " Non-nil means do not thread the last expression.
155
+
156
+ This means that `clojure-ts-thread-first-all' and
157
+ `clojure-ts-thread-last-all' not thread the deepest sexp inside the
158
+ current sexp."
159
+ :package-version '(clojure-ts-mode . " 0.4.0" )
160
+ :safe #'booleanp
161
+ :type 'boolean )
162
+
153
163
(defcustom clojure-ts-align-reader-conditionals nil
154
164
" Whether to align reader conditionals, as if they were maps."
155
165
:package-version '(clojure-ts-mode . " 0.4" )
@@ -1291,9 +1301,9 @@ according to the rule. If NODE is nil, use next node after BOL."
1291
1301
(clojure-ts--anon-fn-node-p parent))
1292
1302
; ; Can the following two clauses be replaced by checking indexes?
1293
1303
; ; Does the second child exist, and is it not equal to the current node?
1294
- (treesit- node-child parent 1 t )
1295
- (not (treesit-node-eq (treesit- node-child parent 1 t ) node))
1296
- (let ((first-child (treesit- node-child parent 0 t )))
1304
+ (clojure-ts-- node-child-skip-metadata parent 1 )
1305
+ (not (treesit-node-eq (clojure-ts-- node-child-skip-metadata parent 1 ) node))
1306
+ (let ((first-child (clojure-ts-- node-child-skip-metadata parent 0 )))
1297
1307
(or (clojure-ts--symbol-node-p first-child)
1298
1308
(clojure-ts--keyword-node-p first-child)
1299
1309
(clojure-ts--var-node-p first-child)))))
@@ -1381,17 +1391,11 @@ if NODE has metadata and its parent has type NODE-TYPE."
1381
1391
(treesit-node-type
1382
1392
(clojure-ts--node-with-metadata-parent node)))))
1383
1393
1384
- (defun clojure-ts--anchor-nth-sibling (n &optional named )
1385
- " Return the start of the Nth child of PARENT.
1386
-
1387
- NAMED non-nil means count only named nodes.
1388
-
1389
- NOTE: This is a replacement for built-in `nth-sibling' anchor preset,
1390
- which doesn't work properly for named nodes (see the bug
1391
- https://debbugs.gnu.org/cgi/bugreport.cgi?bug=78065)"
1394
+ (defun clojure-ts--anchor-nth-sibling (n )
1395
+ " Return the start of the Nth child of PARENT skipping metadata."
1392
1396
(lambda (_n parent &rest _ )
1393
1397
(treesit-node-start
1394
- (treesit- node-child parent n named ))))
1398
+ (clojure-ts-- node-child-skip-metadata parent n))))
1395
1399
1396
1400
(defun clojure-ts--semantic-indent-rules ()
1397
1401
" Return a list of indentation rules for `treesit-simple-indent-rules' ."
@@ -1423,7 +1427,7 @@ https://debbugs.gnu.org/cgi/bugreport.cgi?bug=78065)"
1423
1427
; ; https://guide.clojure.style/#threading-macros-alignment
1424
1428
(clojure-ts--match-threading-macro-arg prev-sibling 0 )
1425
1429
; ; https://guide.clojure.style/#vertically-align-fn-args
1426
- (clojure-ts--match-function-call-arg ,(clojure-ts--anchor-nth-sibling 1 t ) 0 )
1430
+ (clojure-ts--match-function-call-arg ,(clojure-ts--anchor-nth-sibling 1 ) 0 )
1427
1431
; ; https://guide.clojure.style/#one-space-indent
1428
1432
((parent-is " list_lit" ) parent 1 ))))
1429
1433
@@ -1539,8 +1543,8 @@ BOUND bounds the whitespace search."
1539
1543
(and (not (treesit-node-child-by-field-name cur-sexp " value" ))
1540
1544
(string-empty-p (clojure-ts--named-node-text cur-sexp))))
1541
1545
(treesit-end-of-thing 'sexp 2 'restricted )
1542
- (treesit-end-of-thing 'sexp 1 'restrict ))
1543
- (when (looking-at " ," )
1546
+ (treesit-end-of-thing 'sexp 1 'restricted ))
1547
+ (when (looking-at-p " ," )
1544
1548
(forward-char ))
1545
1549
; ; Move past any whitespace or comment.
1546
1550
(search-forward-regexp regex bound)
@@ -1744,7 +1748,7 @@ Forms between BEG and END are aligned according to
1744
1748
(goto-char first-child-start)
1745
1749
(treesit-beginning-of-thing 'sexp -1 )
1746
1750
(let ((contents (clojure-ts--delete-and-extract-sexp)))
1747
- (when (looking-at " *\n " )
1751
+ (when (looking-at-p " *\n " )
1748
1752
(join-line 'following ))
1749
1753
(just-one-space )
1750
1754
(goto-char first-child-start)
@@ -1753,9 +1757,11 @@ Forms between BEG and END are aligned according to
1753
1757
(clojure-ts--ensure-parens-around-function-name)
1754
1758
(down-list )
1755
1759
(forward-sexp )
1756
- (insert " " contents)
1757
- (when multiline-p
1758
- (insert " \n " )))))))
1760
+ (cond
1761
+ ((and multiline-p (looking-at-p " *\n " ))
1762
+ (insert " \n " contents))
1763
+ (multiline-p (insert " " contents " \n " ))
1764
+ (t (insert " " contents))))))))
1759
1765
1760
1766
(defun clojure-ts--unwind-thread-last ()
1761
1767
" Unwind a thread last macro once."
@@ -1768,7 +1774,7 @@ Forms between BEG and END are aligned according to
1768
1774
(goto-char first-child-start)
1769
1775
(treesit-beginning-of-thing 'sexp -1 )
1770
1776
(let ((contents (clojure-ts--delete-and-extract-sexp)))
1771
- (when (looking-at " *\n " )
1777
+ (when (looking-at-p " *\n " )
1772
1778
(join-line 'following ))
1773
1779
(just-one-space )
1774
1780
(goto-char first-child-start)
@@ -1870,10 +1876,125 @@ With universal argument \\[universal-argument], fully unwinds thread."
1870
1876
(interactive )
1871
1877
(clojure-ts-unwind '(4 )))
1872
1878
1879
+ (defun clojure-ts--remove-superfluous-parens ()
1880
+ " Remove extra parens from a form."
1881
+ (when-let* ((node (treesit-thing-at-point 'sexp 'nested ))
1882
+ ((clojure-ts--list-node-p node))
1883
+ ((= 1 (treesit-node-child-count node t ))))
1884
+ (let ((delete-pair-blink-delay 0 ))
1885
+ (delete-pair ))))
1886
+
1887
+ (defun clojure-ts--thread-first ()
1888
+ " Thread a sexp using ->."
1889
+ (save-excursion
1890
+ (clojure-ts--skip-first-child (clojure-ts--threading-sexp-node))
1891
+ (down-list )
1892
+ (treesit-beginning-of-thing 'sexp -1 )
1893
+ (let ((contents (clojure-ts--delete-and-extract-sexp)))
1894
+ (delete-char -1 )
1895
+ (when (looking-at-p " *\n " )
1896
+ (join-line 'following ))
1897
+ (backward-up-list )
1898
+ (insert contents " \n " )
1899
+ (clojure-ts--remove-superfluous-parens))))
1900
+
1901
+ (defun clojure-ts--thread-last ()
1902
+ " Thread a sexp using ->>."
1903
+ (save-excursion
1904
+ (clojure-ts--skip-first-child (clojure-ts--threading-sexp-node))
1905
+ (treesit-end-of-thing 'sexp )
1906
+ (down-list -1 )
1907
+ (treesit-beginning-of-thing 'sexp )
1908
+ (let ((contents (clojure-ts--delete-and-extract-sexp)))
1909
+ (delete-char -1 )
1910
+ (treesit-end-of-thing 'sexp -1 'restricted )
1911
+ (when (looking-at-p " *\n " )
1912
+ (join-line 'following ))
1913
+ (backward-up-list )
1914
+ (insert contents " \n " )
1915
+ (clojure-ts--remove-superfluous-parens))))
1916
+
1917
+ (defun clojure-ts--threadable-p (node )
1918
+ " Return non-nil if expression NODE can be threaded.
1919
+
1920
+ First argument after threading symbol itself should be a list and it
1921
+ should have more than one named child."
1922
+ (let ((second-child (treesit-node-child node 1 t )))
1923
+ (and (clojure-ts--list-node-p second-child)
1924
+ (> (treesit-node-child-count second-child t ) 1 ))))
1925
+
1926
+ (defun clojure-ts-thread (&optional called-by-user-p )
1927
+ " Thread by one more level an existing threading macro.
1928
+
1929
+ If CALLED-BY-USER-P is non-nil (which is always TRUE when called
1930
+ interactively), the function signals a `user-error' if threading form
1931
+ cannot be found."
1932
+ (interactive " p" )
1933
+ (if-let* ((threading-sexp (clojure-ts--threading-sexp-node))
1934
+ ((clojure-ts--threadable-p threading-sexp))
1935
+ (sym (thread-first threading-sexp
1936
+ (treesit-node-child 0 t )
1937
+ (clojure-ts--named-node-text))))
1938
+ (let ((beg (thread-first threading-sexp
1939
+ (treesit-node-start)
1940
+ (copy-marker )))
1941
+ (end (thread-first threading-sexp
1942
+ (treesit-node-end)
1943
+ (copy-marker ))))
1944
+ (cond
1945
+ ((string-match-p (rx bol (* " some" ) " ->" eol) sym)
1946
+ (clojure-ts--thread-first))
1947
+ ((string-match-p (rx bol (* " some" ) " ->>" eol) sym)
1948
+ (clojure-ts--thread-last)))
1949
+ (indent-region beg end)
1950
+ (delete-trailing-whitespace beg end)
1951
+ t )
1952
+ (when called-by-user-p
1953
+ (user-error " No threading form at point" ))))
1954
+
1955
+ (defun clojure-ts--thread-all (first-or-last-thread but-last )
1956
+ " Fully thread the form at point.
1957
+
1958
+ FIRST-OR-LAST-THREAD is either \" ->\" or \" ->>\" .
1959
+
1960
+ When BUT-LAST is non-nil, the last expression is not threaded. Default
1961
+ value is `clojure-ts-thread-all-but-last.' "
1962
+ (if-let* ((list-at-point (treesit-thing-at-point 'list 'nested )))
1963
+ (save-excursion
1964
+ (goto-char (treesit-node-start list-at-point))
1965
+ (insert-parentheses 1 )
1966
+ (insert first-or-last-thread)
1967
+ (while (clojure-ts-thread))
1968
+ (when (or but-last clojure-ts-thread-all-but-last)
1969
+ (clojure-ts-unwind)))
1970
+ (user-error " No list to thread at point" )))
1971
+
1972
+ (defun clojure-ts-thread-first-all (but-last )
1973
+ " Fully thread the form at point using ->.
1974
+
1975
+ When BUT-LAST is non-nil, the last expression is not threaded. Default
1976
+ value is `clojure-ts-thread-all-but-last' ."
1977
+ (interactive " P" )
1978
+ (clojure-ts--thread-all " -> " but-last))
1979
+
1980
+ (defun clojure-ts-thread-last-all (but-last )
1981
+ " Fully thread the form at point using ->>.
1982
+
1983
+ When BUT-LAST is non-nil, the last expression is not threaded. Default
1984
+ value is `clojure-ts-thread-all-but-last' ."
1985
+ (interactive " P" )
1986
+ (clojure-ts--thread-all " ->> " but-last))
1987
+
1873
1988
(defvar clojure-ts-refactor-map
1874
1989
(let ((map (make-sparse-keymap )))
1990
+ (keymap-set map " C-t" #'clojure-ts-thread )
1991
+ (keymap-set map " t" #'clojure-ts-thread )
1875
1992
(keymap-set map " C-u" #'clojure-ts-unwind )
1876
1993
(keymap-set map " u" #'clojure-ts-unwind )
1994
+ (keymap-set map " C-f" #'clojure-ts-thread-first-all )
1995
+ (keymap-set map " f" #'clojure-ts-thread-first-all )
1996
+ (keymap-set map " C-l" #'clojure-ts-thread-last-all )
1997
+ (keymap-set map " l" #'clojure-ts-thread-last-all )
1877
1998
map)
1878
1999
" Keymap for `clojure-ts-mode' refactoring commands." )
1879
2000
@@ -1886,6 +2007,10 @@ With universal argument \\[universal-argument], fully unwinds thread."
1886
2007
'(" Clojure"
1887
2008
[" Align expression" clojure-ts-align]
1888
2009
(" Refactor -> and ->>"
2010
+ [" Thread once more" clojure-ts-thread]
2011
+ [" Fully thread a form with ->" clojure-ts-thread-first-all]
2012
+ [" Fully thread a form with ->>" clojure-ts-thread-last-all]
2013
+ " --"
1889
2014
[" Unwind once" clojure-ts-unwind]
1890
2015
[" Fully unwind a threading macro" clojure-ts-unwind-all])))
1891
2016
map)
0 commit comments