@@ -266,6 +266,12 @@ values like this:
266
266
:safe #'listp
267
267
:type '(repeat string))
268
268
269
+ (defcustom clojure-ts-completion-enabled t
270
+ " Enable built-in completion feature."
271
+ :package-version '(clojure-ts-mode . " 0.5" )
272
+ :safe #'booleanp
273
+ :type 'boolean )
274
+
269
275
(defvar clojure-ts-mode-remappings
270
276
'((clojure-mode . clojure-ts-mode)
271
277
(clojurescript-mode . clojure-ts-clojurescript-mode)
@@ -1561,26 +1567,28 @@ function literal."
1561
1567
" map_lit" " ns_map_lit" " vec_lit" " set_lit" )
1562
1568
" A regular expression that matches nodes that can be treated as lists." )
1563
1569
1570
+ (defconst clojure-ts--defun-symbols-regex
1571
+ (rx bol
1572
+ (or " def"
1573
+ " defn"
1574
+ " defn-"
1575
+ " definline"
1576
+ " defrecord"
1577
+ " defmacro"
1578
+ " defmulti"
1579
+ " defonce"
1580
+ " defprotocol"
1581
+ " deftest"
1582
+ " deftest-"
1583
+ " ns"
1584
+ " definterface"
1585
+ " deftype"
1586
+ " defstruct" )
1587
+ eol))
1588
+
1564
1589
(defun clojure-ts--defun-node-p (node )
1565
1590
" Return TRUE if NODE is a function or a var definition."
1566
- (clojure-ts--list-node-sym-match-p node
1567
- (rx bol
1568
- (or " def"
1569
- " defn"
1570
- " defn-"
1571
- " definline"
1572
- " defrecord"
1573
- " defmacro"
1574
- " defmulti"
1575
- " defonce"
1576
- " defprotocol"
1577
- " deftest"
1578
- " deftest-"
1579
- " ns"
1580
- " definterface"
1581
- " deftype"
1582
- " defstruct" )
1583
- eol)))
1591
+ (clojure-ts--list-node-sym-match-p node clojure-ts--defun-symbols-regex))
1584
1592
1585
1593
(defconst clojure-ts--markdown-inline-sexp-nodes
1586
1594
'(" inline_link" " full_reference_link" " collapsed_reference_link"
@@ -2512,6 +2520,126 @@ before DELIM-OPEN."
2512
2520
map)
2513
2521
" Keymap for `clojure-ts-mode' ." )
2514
2522
2523
+ ; ;; Completion
2524
+
2525
+ (defconst clojure-ts--completion-query-defuns
2526
+ (treesit-query-compile 'clojure
2527
+ `((source
2528
+ (list_lit
2529
+ ((sym_lit) @sym
2530
+ (:match , clojure-ts--defun-symbols-regex @sym))
2531
+ :anchor [(comment) (meta_lit) (old_meta_lit)] :*
2532
+ :anchor ((sym_lit) @defun-candidate)))))
2533
+ " Query that matches top-level definitions." )
2534
+
2535
+ (defconst clojure-ts--completion-defn-with-args-sym-regex
2536
+ (rx bol
2537
+ (or " defn"
2538
+ " defn-"
2539
+ " fn"
2540
+ " fn*"
2541
+ " defmacro"
2542
+ " defmethod" )
2543
+ eol)
2544
+ " Regexp that matches a symbol of definition with arguments vector." )
2545
+
2546
+ (defconst clojure-ts--completion-let-like-sym-regex
2547
+ (rx bol
2548
+ (or " let"
2549
+ " if-let"
2550
+ " when-let"
2551
+ " if-some"
2552
+ " when-some"
2553
+ " loop"
2554
+ " with-open"
2555
+ " dotimes"
2556
+ " with-local-vars" )
2557
+ eol)
2558
+ " Regexp that matches a symbol of let-like form." )
2559
+
2560
+ (defconst clojure-ts--completion-locals-query
2561
+ (treesit-query-compile 'clojure `((vec_lit (sym_lit) @local-candidate)
2562
+ (map_lit (sym_lit) @local-candidate)))
2563
+ " Query that matches a local binding symbol.
2564
+
2565
+ Symbold must be a direct child of a vector or a map. This query covers
2566
+ bindings vector as well as destructuring syntax." )
2567
+
2568
+ (defconst clojure-ts--completion-annotations
2569
+ (list 'defun-candidate " Definition"
2570
+ 'local-candidate " Local variable" )
2571
+ " Property list of completion candidate type and annotation string." )
2572
+
2573
+ (defun clojure-ts--completion-annotation-function (candidate )
2574
+ " Return annotation for a completion CANDIDATE."
2575
+ (thread-last minibuffer-completion-table
2576
+ (alist-get candidate)
2577
+ (plist-get clojure-ts--completion-annotations)))
2578
+
2579
+ (defun clojure-ts--completion-defun-with-args-node-p (node )
2580
+ " Return non-nil if NODE is a function definition with arguments."
2581
+ (when-let* ((sym-name (clojure-ts--list-node-sym-text node)))
2582
+ (string-match-p clojure-ts--completion-defn-with-args-sym-regex sym-name)))
2583
+
2584
+ (defun clojure-ts--completion-fn-args-nodes ()
2585
+ " Return a list of captured nodes that represent function arguments.
2586
+
2587
+ The function traverses the syntax tree upwards and returns nodes from
2588
+ all functions along the way."
2589
+ (let ((parent-defun (clojure-ts--parent-until #'clojure-ts--completion-defun-with-args-node-p ))
2590
+ (captured-nodes))
2591
+ (while parent-defun
2592
+ (when-let* ((args-vec (clojure-ts--node-child parent-defun " vec_lit" )))
2593
+ (setq captured-nodes
2594
+ (append captured-nodes
2595
+ (treesit-query-capture args-vec clojure-ts--completion-locals-query))
2596
+ parent-defun (treesit-parent-until parent-defun
2597
+ #'clojure-ts--completion-defun-with-args-node-p ))))
2598
+ captured-nodes))
2599
+
2600
+ (defun clojure-ts--completion-let-like-node-p (node )
2601
+ " Return non-nil if NODE is a let-like form."
2602
+ (when-let* ((sym-name (clojure-ts--list-node-sym-text node)))
2603
+ (string-match-p clojure-ts--completion-let-like-sym-regex sym-name)))
2604
+
2605
+ (defun clojure-ts--completion-let-locals-nodes ()
2606
+ " Return a list of captured nodes that represent bindings in let forms.
2607
+
2608
+ The function tranverses the syntax tree upwards and returns nodes from
2609
+ all let bindings found along the way."
2610
+ (let ((parent-let (clojure-ts--parent-until #'clojure-ts--completion-let-like-node-p ))
2611
+ (captured-nodes))
2612
+ (while parent-let
2613
+ (when-let* ((bindings-vec (clojure-ts--node-child parent-let " vec_lit" )))
2614
+ (setq captured-nodes
2615
+ (append captured-nodes
2616
+ (treesit-query-capture bindings-vec clojure-ts--completion-locals-query))
2617
+ parent-let (treesit-parent-until parent-let
2618
+ #'clojure-ts--completion-let-like-node-p ))))
2619
+ captured-nodes))
2620
+
2621
+ (defun clojure-ts-completion-at-point-function ()
2622
+ " Return a completion table for the symbol around point."
2623
+ (when-let* ((bounds (bounds-of-thing-at-point 'symbol ))
2624
+ (source (treesit-buffer-root-node 'clojure ))
2625
+ (nodes (append (treesit-query-capture source clojure-ts--completion-query-defuns)
2626
+ (clojure-ts--completion-fn-args-nodes)
2627
+ (clojure-ts--completion-let-locals-nodes))))
2628
+ (list (car bounds)
2629
+ (cdr bounds)
2630
+ (thread-last nodes
2631
+ ; ; Remove node at point
2632
+ (seq-remove (lambda (item ) (= (treesit-node-end (cdr item)) (point ))))
2633
+ ; ; Remove unwanted captured nodes
2634
+ (seq-filter (lambda (item )
2635
+ (not (member (car item) '(sym kwd)))))
2636
+ ; ; Produce alist of candidates
2637
+ (seq-map (lambda (item ) (cons (treesit-node-text (cdr item) t ) (car item))))
2638
+ ; ; Remove duplicated candidates
2639
+ (seq-uniq ))
2640
+ :exclusive 'no
2641
+ :annotation-function #'clojure-ts--completion-annotation-function )))
2642
+
2515
2643
(defvar clojure-ts-clojurescript-mode-map
2516
2644
(let ((map (make-sparse-keymap )))
2517
2645
(set-keymap-parent map clojure-ts-mode-map)
@@ -2670,7 +2798,10 @@ REGEX-AVAILABLE."
2670
2798
clojure-ts--imenu-settings)
2671
2799
2672
2800
(when (boundp 'treesit-thing-settings ) ; ; Emacs 30+
2673
- (setq-local treesit-thing-settings clojure-ts--thing-settings)))
2801
+ (setq-local treesit-thing-settings clojure-ts--thing-settings))
2802
+
2803
+ (when clojure-ts-completion-enabled
2804
+ (add-to-list 'completion-at-point-functions #'clojure-ts-completion-at-point-function )))
2674
2805
2675
2806
;;;### autoload
2676
2807
(define-derived-mode clojure-ts-mode prog-mode " Clojure[TS]"
0 commit comments