@@ -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,119 @@ 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 ()
1927
+ " Thread by one more level an existing threading macro."
1928
+ (interactive )
1929
+ (when-let* ((threading-sexp (clojure-ts--threading-sexp-node))
1930
+ ((clojure-ts--threadable-p threading-sexp))
1931
+ (sym (thread-first threading-sexp
1932
+ (treesit-node-child 0 t )
1933
+ (clojure-ts--named-node-text))))
1934
+ (let ((beg (thread-first threading-sexp
1935
+ (treesit-node-start)
1936
+ (copy-marker )))
1937
+ (end (thread-first threading-sexp
1938
+ (treesit-node-end)
1939
+ (copy-marker ))))
1940
+ (cond
1941
+ ((string-match-p (rx bol (* " some" ) " ->" eol) sym)
1942
+ (clojure-ts--thread-first))
1943
+ ((string-match-p (rx bol (* " some" ) " ->>" eol) sym)
1944
+ (clojure-ts--thread-last)))
1945
+ (indent-region beg end)
1946
+ (delete-trailing-whitespace beg end))
1947
+ t ))
1948
+
1949
+ (defun clojure-ts--thread-all (first-or-last-thread but-last )
1950
+ " Fully thread the form at point.
1951
+
1952
+ FIRST-OR-LAST-THREAD is either \" ->\" or \" ->>\" .
1953
+
1954
+ When BUT-LAST is non-nil, the last expression is not threaded. Default
1955
+ value is `clojure-ts-thread-all-but-last.' "
1956
+ (if-let* ((list-at-point (treesit-thing-at-point 'list 'nested )))
1957
+ (save-excursion
1958
+ (goto-char (treesit-node-start list-at-point))
1959
+ (insert-parentheses 1 )
1960
+ (insert first-or-last-thread)
1961
+ (while (clojure-ts-thread))
1962
+ (when (or but-last clojure-ts-thread-all-but-last)
1963
+ (clojure-ts-unwind)))
1964
+ (user-error " No list to thread at point" )))
1965
+
1966
+ (defun clojure-ts-thread-first-all (but-last )
1967
+ " Fully thread the form at point using ->.
1968
+
1969
+ When BUT-LAST is non-nil, the last expression is not threaded. Default
1970
+ value is `clojure-ts-thread-all-but-last' ."
1971
+ (interactive " P" )
1972
+ (clojure-ts--thread-all " -> " but-last))
1973
+
1974
+ (defun clojure-ts-thread-last-all (but-last )
1975
+ " Fully thread the form at point using ->>.
1976
+
1977
+ When BUT-LAST is non-nil, the last expression is not threaded. Default
1978
+ value is `clojure-ts-thread-all-but-last' ."
1979
+ (interactive " P" )
1980
+ (clojure-ts--thread-all " ->> " but-last))
1981
+
1873
1982
(defvar clojure-ts-refactor-map
1874
1983
(let ((map (make-sparse-keymap )))
1984
+ (keymap-set map " C-t" #'clojure-ts-thread )
1985
+ (keymap-set map " t" #'clojure-ts-thread )
1875
1986
(keymap-set map " C-u" #'clojure-ts-unwind )
1876
1987
(keymap-set map " u" #'clojure-ts-unwind )
1988
+ (keymap-set map " C-f" #'clojure-ts-thread-first-all )
1989
+ (keymap-set map " f" #'clojure-ts-thread-first-all )
1990
+ (keymap-set map " C-l" #'clojure-ts-thread-last-all )
1991
+ (keymap-set map " l" #'clojure-ts-thread-last-all )
1877
1992
map)
1878
1993
" Keymap for `clojure-ts-mode' refactoring commands." )
1879
1994
@@ -1886,6 +2001,10 @@ With universal argument \\[universal-argument], fully unwinds thread."
1886
2001
'(" Clojure"
1887
2002
[" Align expression" clojure-ts-align]
1888
2003
(" Refactor -> and ->>"
2004
+ [" Thread once more" clojure-ts-thread]
2005
+ [" Fully thread a form with ->" clojure-ts-thread-first-all]
2006
+ [" Fully thread a form with ->>" clojure-ts-thread-last-all]
2007
+ " --"
1889
2008
[" Unwind once" clojure-ts-unwind]
1890
2009
[" Fully unwind a threading macro" clojure-ts-unwind-all])))
1891
2010
map)
0 commit comments