Skip to content

Commit 8b503a6

Browse files
authored
feat: Match replace (#49)
1 parent 209248f commit 8b503a6

File tree

2 files changed

+128
-41
lines changed

2 files changed

+128
-41
lines changed

src/main/clojure/fominok/ideahelix/editor.clj

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,6 @@
307307
((:shift \B)
308308
"Select WORD backward" :scroll
309309
[state document caret]
310-
311310
(dotimes [_ (get-prefix state)]
312311
(-> (ihx-selection document caret)
313312
(ihx-long-word-backward! document false)
@@ -648,7 +647,7 @@
648647
(:match-inside
649648
(_
650649
"Select inside"
651-
[project state document caret char]
650+
[document caret char]
652651
(-> (ihx-selection document caret)
653652
(ihx-select-inside document char)
654653
(ihx-apply-selection! document))
@@ -657,7 +656,7 @@
657656
(:select-match-inside
658657
(_
659658
"Select inside"
660-
[project state document caret char]
659+
[document caret char]
661660
(-> (ihx-selection document caret)
662661
(ihx-select-inside document char)
663662
(ihx-apply-selection! document))
@@ -666,7 +665,7 @@
666665
(:match-around
667666
(_
668667
"Select around"
669-
[project state document caret char]
668+
[document caret char]
670669
(-> (ihx-selection document caret)
671670
(ihx-select-around document char)
672671
(ihx-apply-selection! document))
@@ -675,40 +674,23 @@
675674
(:select-match-around
676675
(_
677676
"Select around"
678-
[project state document caret char]
677+
[document caret char]
679678
(-> (ihx-selection document caret)
680679
(ihx-select-around document char)
681680
(ihx-apply-selection! document))))
682681

683682
((:or :match :select-match)
684683
(\s
685-
[state] (assoc state :mode :match-surround-add)))
686-
687-
(:match-surround-add
688-
(_
689-
"Surround add" :write
690-
[project state document caret char]
691-
(-> (ihx-selection document caret)
692-
(ihx-surround-add document char)
693-
(ihx-apply-selection! document))))
694-
695-
((:or :match :select-match)
684+
[state] (assoc state :mode :match-surround-add))
696685
(\d
697-
[state] (assoc state :mode :match-surround-delete)))
698-
699-
(:match-surround-delete
700-
(_
701-
"Surround delete" :write
702-
[project document caret char]
703-
(-> (ihx-selection document caret)
704-
(ihx-surround-delete document char)
705-
(ihx-apply-selection! document))
706-
[state] (assoc state :mode :normal)))
686+
[state] (assoc state :mode :match-surround-delete))
687+
(\r
688+
[state] (assoc state :mode :match-find)))
707689

708690
(:match
709691
(\m
710692
"Goto matching bracket"
711-
[project document editor caret]
693+
[document caret]
712694
(-> (ihx-selection document caret)
713695
(ihx-goto-matching document)
714696
ihx-shrink-selection
@@ -718,11 +700,41 @@
718700
(:select-match
719701
(\m
720702
"Goto matching bracket"
721-
[project document editor caret]
703+
[document caret]
722704
(-> (ihx-selection document caret)
723705
(ihx-goto-matching document)
724706
(ihx-apply-selection! document))
725-
[state] (assoc state :mode :select))))
707+
[state] (assoc state :mode :select)))
708+
709+
(:match-surround-add
710+
(_
711+
"Surround add" :write
712+
[document caret char]
713+
(-> (ihx-selection document caret)
714+
(ihx-surround-add document char)
715+
(ihx-apply-selection! document))
716+
[state] (assoc state :mode :normal)))
717+
718+
(:match-surround-delete
719+
(_
720+
"Surround delete" :write
721+
[document caret char]
722+
(-> (ihx-selection document caret)
723+
(ihx-surround-delete document char)
724+
(ihx-apply-selection! document))
725+
[state] (assoc state :mode :normal)))
726+
727+
(:match-find
728+
(_
729+
"Find the matching brackets"
730+
[state editor document char]
731+
(ihx-surround-find state editor document char)))
732+
733+
(:match-replace
734+
(_
735+
"Replace the matching brackets" :write
736+
[state editor document char]
737+
(ihx-surround-replace state editor document char))))
726738

727739

728740
(defn handle-editor-event

src/main/clojure/fominok/ideahelix/editor/selection.clj

Lines changed: 87 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@
8585
(cond
8686
is-broken offset
8787
is-forward start
88-
:default (max 0 (dec end)))]
88+
:else (max 0 (dec end)))]
8989
(->IhxSelection caret anchor offset in-append)))
9090

9191

@@ -249,7 +249,7 @@
249249
"select:"
250250
"Select in selections"
251251
(Messages/getQuestionIcon))
252-
pattern (when (not (empty? input)) (re-pattern input))
252+
pattern (when (seq input) (re-pattern input))
253253
matches
254254
(and pattern
255255
(->> (.getAllCarets model)
@@ -506,15 +506,16 @@
506506
(defn next-match
507507
[text offset opener target]
508508
(loop [to-find 1 text (.subSequence text offset (.length text)) acc-offset offset]
509-
(let [target-offset (find-next-occurrence text {:pos (set [opener target])})
510-
found (.charAt text target-offset)
511-
text (.subSequence text (inc target-offset) (.length text))
512-
acc-offset (inc acc-offset)]
513-
(if (= found target) ; must check first if we found match in case match = char
514-
(if (= to-find 1)
515-
(+ acc-offset target-offset -1)
516-
(recur (dec to-find) text (+ acc-offset target-offset)))
517-
(recur (inc to-find) text (+ acc-offset target-offset))))))
509+
(let [target-offset (find-next-occurrence text {:pos (set [opener target])})]
510+
(when target-offset
511+
(let [found (.charAt text target-offset)
512+
text (.subSequence text (inc target-offset) (.length text))
513+
acc-offset (+ acc-offset target-offset 1)]
514+
(if (= found target)
515+
(if (= to-find 1)
516+
(dec acc-offset)
517+
(recur (dec to-find) text acc-offset))
518+
(recur (inc to-find) text acc-offset)))))))
518519

519520

520521
(defn previous-match
@@ -523,7 +524,7 @@
523524
idx (- len offset 1)
524525
text (-> (StringBuilder. text) .reverse)
525526
res (next-match text idx opener target)]
526-
(- len res 1)))
527+
(when res (- len res 1))))
527528

528529

529530
(defn get-open-close-chars
@@ -619,3 +620,77 @@
619620
:open (next-match text (inc offset) opener match)
620621
:close (previous-match text (dec offset) opener match))]
621622
(assoc selection :offset offset)))))
623+
624+
625+
(defn- find-bracket-pair
626+
"Finds left and right bracket positions for the given offset, text, and chars."
627+
[text offset open-char close-char]
628+
(when-let [curr-char (and (< offset (count text)) (.charAt text offset))]
629+
(let [pair (if (and (not= open-char curr-char) (not= close-char curr-char))
630+
[(previous-match text offset close-char open-char)
631+
(next-match text offset open-char close-char)]
632+
(cond
633+
(= open-char curr-char) [offset (next-match text (inc offset) open-char close-char)]
634+
(= close-char curr-char) [(previous-match text (dec offset) close-char open-char) offset]))]
635+
(when (and (first pair) (second pair))
636+
pair))))
637+
638+
639+
(defn- add-caret-at
640+
"Adds a caret at the given offset (or uses primary if specified), sets single-char selection."
641+
[model editor offset use-primary?]
642+
(let [caret (if use-primary?
643+
(.getPrimaryCaret model)
644+
(.addCaret model (.offsetToVisualPosition editor offset) false))]
645+
(when caret
646+
(.setSelection caret offset (inc offset))
647+
(.moveToOffset caret offset))))
648+
649+
650+
(defn ihx-surround-find
651+
[state editor document char]
652+
(if-not (printable-char? char)
653+
state
654+
(let [model (.getCaretModel editor)
655+
original-carets (.getAllCarets model)
656+
original-selections (mapv #(ihx-selection document % :insert-mode false) original-carets)
657+
text (.getCharsSequence document)
658+
{:keys [open-char close-char]} (get-open-close-chars char)
659+
finder (fn [acc sel]
660+
(let [pair (find-bracket-pair text (:offset sel) open-char close-char)]
661+
(if pair (conj acc pair) acc)))
662+
pairs (reduce finder [] original-selections)]
663+
(if (empty? pairs)
664+
(assoc state :mode :normal)
665+
(do
666+
(.removeSecondaryCarets model)
667+
(loop [remaining-pairs pairs
668+
is-first? true]
669+
(when-let [[left right] (first remaining-pairs)]
670+
(add-caret-at model editor left is-first?)
671+
(add-caret-at model editor right false)
672+
(recur (rest remaining-pairs) false)))
673+
(assoc state :pre-match-selections original-selections
674+
:match-pairs pairs
675+
:mode :match-replace))))))
676+
677+
678+
(defn ihx-surround-replace
679+
[state editor document char]
680+
(if-not (printable-char? char)
681+
state
682+
(let [{:keys [open-char close-char]} (get-open-close-chars char)
683+
pairs (:match-pairs state)
684+
model (.getCaretModel editor)]
685+
(when (seq pairs)
686+
(doseq [[left right] pairs]
687+
(.replaceString document left (inc left) (str open-char))
688+
(.replaceString document right (inc right) (str close-char)))
689+
(.removeSecondaryCarets model)
690+
(doseq [[idx sel] (map-indexed vector (:pre-match-selections state))]
691+
(let [caret (if (zero? idx)
692+
(.getPrimaryCaret model)
693+
(.addCaret model (.offsetToVisualPosition editor (:offset sel)) false))]
694+
(when caret
695+
(ihx-apply-selection! (assoc sel :caret caret) document)))))
696+
(assoc state :mode :normal :pre-match-selections nil :match-pairs nil))))

0 commit comments

Comments
 (0)