|
85 | 85 | (cond |
86 | 86 | is-broken offset |
87 | 87 | is-forward start |
88 | | - :default (max 0 (dec end)))] |
| 88 | + :else (max 0 (dec end)))] |
89 | 89 | (->IhxSelection caret anchor offset in-append))) |
90 | 90 |
|
91 | 91 |
|
|
249 | 249 | "select:" |
250 | 250 | "Select in selections" |
251 | 251 | (Messages/getQuestionIcon)) |
252 | | - pattern (when (not (empty? input)) (re-pattern input)) |
| 252 | + pattern (when (seq input) (re-pattern input)) |
253 | 253 | matches |
254 | 254 | (and pattern |
255 | 255 | (->> (.getAllCarets model) |
|
506 | 506 | (defn next-match |
507 | 507 | [text offset opener target] |
508 | 508 | (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))))))) |
518 | 519 |
|
519 | 520 |
|
520 | 521 | (defn previous-match |
|
523 | 524 | idx (- len offset 1) |
524 | 525 | text (-> (StringBuilder. text) .reverse) |
525 | 526 | res (next-match text idx opener target)] |
526 | | - (- len res 1))) |
| 527 | + (when res (- len res 1)))) |
527 | 528 |
|
528 | 529 |
|
529 | 530 | (defn get-open-close-chars |
|
619 | 620 | :open (next-match text (inc offset) opener match) |
620 | 621 | :close (previous-match text (dec offset) opener match))] |
621 | 622 | (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