mirror of
https://github.com/logseq/logseq.git
synced 2026-04-24 14:14:55 +00:00
Fix wrong cursor posistion (#10838)
* fix unable to use 'del' to delete the last character if the cursor is at the beginning of the block * fix right arrow calculate character length incorrect if the cursor is at the beginning of the block * fix moving up or down across blocks may enter the middle of a character * fix moving up or down inside a block may enter the middle of a character
This commit is contained in:
@@ -652,10 +652,10 @@
|
||||
:visibility "hidden"
|
||||
:top 0
|
||||
:left 0}}
|
||||
(let [content (str content "0")]
|
||||
(for [[idx c] (map-indexed
|
||||
vector
|
||||
(string/split content ""))]
|
||||
(let [content (str content "0")
|
||||
graphemes (util/split-graphemes content)
|
||||
graphemes-char-index (reductions #(+ %1 (count %2)) 0 graphemes)]
|
||||
(for [[idx c] (zipmap graphemes-char-index graphemes)]
|
||||
(if (= c "\n")
|
||||
[:span {:id (str "mock-text_" idx)
|
||||
:key idx} "0" [:br]]
|
||||
|
||||
@@ -306,18 +306,6 @@
|
||||
:own-order-list-index own-order-list-index
|
||||
:own-order-number-list? (= own-order-list-type "number"))))
|
||||
|
||||
(defn- text-range-by-lst-fst-line [content [direction pos]]
|
||||
(case direction
|
||||
:up
|
||||
(let [last-new-line (or (string/last-index-of content \newline) -1)
|
||||
end (+ last-new-line pos 1)]
|
||||
(subs content 0 end))
|
||||
:down
|
||||
(-> (string/split-lines content)
|
||||
first
|
||||
(or "")
|
||||
(subs 0 pos))))
|
||||
|
||||
(defn mark-last-input-time!
|
||||
[repo]
|
||||
(when repo
|
||||
@@ -394,7 +382,7 @@
|
||||
content-length (count content)
|
||||
text-range (cond
|
||||
(vector? pos)
|
||||
(text-range-by-lst-fst-line content pos)
|
||||
(util/get-text-range content (last pos) (= (first pos) :down))
|
||||
|
||||
(and (> tail-len 0) (>= (count content) tail-len))
|
||||
(subs content 0 (- (count content) tail-len))
|
||||
|
||||
@@ -2586,7 +2586,7 @@
|
||||
(defn- move-cross-boundary-up-down
|
||||
[direction move-opts]
|
||||
(let [input (state/get-input)
|
||||
line-pos (util/get-first-or-last-line-pos input)
|
||||
line-pos (util/get-line-pos (.-value input) (util/get-selection-start input))
|
||||
repo (state/get-current-repo)
|
||||
f (case direction
|
||||
:up util/get-prev-block-non-collapsed
|
||||
|
||||
@@ -414,12 +414,49 @@
|
||||
(when input
|
||||
(.-selectionDirection input)))
|
||||
|
||||
(defn get-first-or-last-line-pos
|
||||
[input]
|
||||
(let [pos (get-selection-start input)
|
||||
value (.-value input)
|
||||
last-newline-pos (or (string/last-index-of value \newline (dec pos)) -1)]
|
||||
(- pos last-newline-pos 1)))
|
||||
#?(:cljs
|
||||
(defn split-graphemes
|
||||
[s]
|
||||
(let [^js splitter (GraphemeSplitter.)]
|
||||
(.splitGraphemes splitter s))))
|
||||
|
||||
#?(:cljs
|
||||
(defn get-graphemes-pos
|
||||
"Return the length of the substrings in s between start and from-index.
|
||||
|
||||
multi-char count as 1, like emoji characters"
|
||||
[s from-index]
|
||||
(let [^js splitter (GraphemeSplitter.)]
|
||||
(.countGraphemes splitter (subs s 0 from-index)))))
|
||||
|
||||
#?(:cljs
|
||||
(defn get-line-pos
|
||||
"Return the length of the substrings in s between the last index of newline
|
||||
in s searching backward from from-newline-index and from-newline-index.
|
||||
|
||||
multi-char count as 1, like emoji characters"
|
||||
[s from-newline-index]
|
||||
(let [^js splitter (GraphemeSplitter.)
|
||||
last-newline-pos (string/last-index-of s \newline (dec from-newline-index))
|
||||
before-last-newline-length (or last-newline-pos -1)
|
||||
last-newline-content (subs s (inc before-last-newline-length) from-newline-index)]
|
||||
(.countGraphemes splitter last-newline-content))))
|
||||
|
||||
#?(:cljs
|
||||
(defn get-text-range
|
||||
"Return the substring of the first grapheme-num characters of s if first-line? is true,
|
||||
otherwise return the substring of s before the last \n and the first grapheme-num characters.
|
||||
|
||||
grapheme-num treats multi-char as 1, like emoji characters"
|
||||
[s grapheme-num first-line?]
|
||||
(let [newline-pos (if first-line?
|
||||
0
|
||||
(inc (or (string/last-index-of s \newline) -1)))
|
||||
^js splitter (GraphemeSplitter.)
|
||||
^js newline-graphemes (.splitGraphemes splitter (subs s newline-pos))
|
||||
^js newline-graphemes (.slice newline-graphemes 0 grapheme-num)
|
||||
content (.join newline-graphemes "")]
|
||||
(subs s 0 (+ newline-pos (count content))))))
|
||||
|
||||
#?(:cljs
|
||||
(defn stop [e]
|
||||
@@ -684,7 +721,7 @@
|
||||
(defn safe-dec-current-pos-from-end
|
||||
[input current-pos]
|
||||
(if-let [len (and (string? input) (.-length input))]
|
||||
(when-let [input (and (>= len 2) (<= current-pos len)
|
||||
(if-let [input (and (>= len 2) (<= current-pos len)
|
||||
(.substring input (max (- current-pos 20) 0) current-pos))]
|
||||
(try
|
||||
(let [^js splitter (GraphemeSplitter.)
|
||||
@@ -692,7 +729,8 @@
|
||||
(- current-pos (.-length (.pop input))))
|
||||
(catch :default e
|
||||
(js/console.error e)
|
||||
(dec current-pos))))
|
||||
(dec current-pos)))
|
||||
(dec current-pos))
|
||||
(dec current-pos))))
|
||||
|
||||
#?(:cljs
|
||||
@@ -700,7 +738,7 @@
|
||||
(defn safe-inc-current-pos-from-start
|
||||
[input current-pos]
|
||||
(if-let [len (and (string? input) (.-length input))]
|
||||
(when-let [input (and (>= len 2) (<= current-pos len)
|
||||
(if-let [input (and (>= len 2) (<= current-pos len)
|
||||
(.substr input current-pos 20))]
|
||||
(try
|
||||
(let [^js splitter (GraphemeSplitter.)
|
||||
@@ -708,7 +746,8 @@
|
||||
(+ current-pos (.-length (.shift input))))
|
||||
(catch :default e
|
||||
(js/console.error e)
|
||||
(inc current-pos))))
|
||||
(inc current-pos)))
|
||||
(inc current-pos))
|
||||
(inc current-pos))))
|
||||
|
||||
#?(:cljs
|
||||
@@ -1334,10 +1373,11 @@
|
||||
scroll-top (.-scrollTop main-node)
|
||||
|
||||
current-pos (get-selection-start el)
|
||||
grapheme-pos (get-graphemes-pos (.-value (.textContent el)) current-pos)
|
||||
mock-text (some-> (gdom/getElement "mock-text")
|
||||
gdom/getChildren
|
||||
array-seq
|
||||
(nth-safe current-pos))
|
||||
(nth-safe grapheme-pos))
|
||||
offset-top (and mock-text (.-offsetTop mock-text))
|
||||
offset-height (and mock-text (.-offsetHeight mock-text))
|
||||
|
||||
|
||||
@@ -31,12 +31,13 @@
|
||||
([input] (get-caret-pos input (util/get-selection-start input)))
|
||||
([input pos]
|
||||
(when input
|
||||
(let [rect (bean/->clj (.. input (getBoundingClientRect) (toJSON)))]
|
||||
(let [rect (bean/->clj (.. input (getBoundingClientRect) (toJSON)))
|
||||
grapheme-pos (util/get-graphemes-pos (.-value input) pos)]
|
||||
(try
|
||||
(some-> (gdom/getElement "mock-text")
|
||||
gdom/getChildren
|
||||
array-seq
|
||||
(util/nth-safe pos)
|
||||
(util/nth-safe grapheme-pos)
|
||||
mock-char-pos
|
||||
(assoc :rect rect))
|
||||
(catch :default e
|
||||
@@ -71,9 +72,8 @@
|
||||
([input n]
|
||||
(when input
|
||||
(let [{:keys [pos]} (get-caret-pos input)
|
||||
pos (if (and (= n 1) (not (zero? pos)))
|
||||
(or (util/safe-inc-current-pos-from-start (.-value input) pos)
|
||||
(inc pos))
|
||||
pos (if (= n 1)
|
||||
(util/safe-inc-current-pos-from-start (.-value input) pos)
|
||||
(+ pos n))]
|
||||
(move-cursor-to input pos)))))
|
||||
|
||||
@@ -85,8 +85,7 @@
|
||||
(let [{:keys [pos]} (get-caret-pos input)
|
||||
pos (if (= n 1)
|
||||
(util/safe-dec-current-pos-from-end (.-value input) pos)
|
||||
(- pos n))
|
||||
pos (max 0 (or pos (dec pos)))]
|
||||
(- pos n))]
|
||||
(move-cursor-to input pos)))))
|
||||
|
||||
(defn- get-input-content&pos
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
(is (= 3 (util/safe-dec-current-pos-from-end "abc😀d" 5)))
|
||||
(is (= 3 (util/safe-dec-current-pos-from-end "abc😀" 5)))
|
||||
(is (= 0 (util/safe-dec-current-pos-from-end "😀" 2)))
|
||||
(is (= 0 (util/safe-dec-current-pos-from-end "a" 1)))
|
||||
(is (= 4 (util/safe-dec-current-pos-from-end "abcde" 5)))
|
||||
(is (= 1 (util/safe-dec-current-pos-from-end "中文" 2))))
|
||||
|
||||
@@ -20,8 +21,40 @@
|
||||
(is (= 5 (util/safe-inc-current-pos-from-start "abc😀d" 3)))
|
||||
(is (= 2 (util/safe-inc-current-pos-from-start "😀" 0)))
|
||||
(is (= 2 (util/safe-inc-current-pos-from-start "abcde" 1)))
|
||||
(is (= 1 (util/safe-inc-current-pos-from-start "a" 0)))
|
||||
(is (= 1 (util/safe-inc-current-pos-from-start "中文" 0)))))
|
||||
|
||||
(deftest test-get-line-pos
|
||||
(testing "get-line-pos"
|
||||
(is (= 3 (util/get-line-pos "abcde" 3)))
|
||||
(is (= 4 (util/get-line-pos "abcd\ne" 4)))
|
||||
(is (= 0 (util/get-line-pos "abcd\ne" 5)))
|
||||
(is (= 4 (util/get-line-pos "abc😀d" 5)))
|
||||
(is (= 1 (util/get-line-pos "abc\nde" 5)))
|
||||
(is (= 1 (util/get-line-pos "abc\n😀d" 6)))
|
||||
(is (= 2 (util/get-line-pos "ab\nc😀d" 6)))
|
||||
(is (= 1 (util/get-line-pos "abc\nde\nf" 5)))
|
||||
(is (= 1 (util/get-line-pos "abc\n😀d\ne" 6)))
|
||||
(is (= 2 (util/get-line-pos "ab\nc😀d\ne" 6)))))
|
||||
|
||||
(deftest test-get-text-range
|
||||
(testing "get-text-range"
|
||||
(is (= "" (util/get-text-range "abcdefg" 0 true)))
|
||||
(is (= "" (util/get-text-range "abcdefg" 0 false)))
|
||||
(is (= "abcdefg" (util/get-text-range "abcdefg" 10 true)))
|
||||
(is (= "abcdefg" (util/get-text-range "abcdefg" 10 false)))
|
||||
(is (= "abc" (util/get-text-range "abcdefg" 3 true)))
|
||||
(is (= "abc" (util/get-text-range "abcdefg" 3 false)))
|
||||
(is (= "abc" (util/get-text-range "abcdefg\nhijklmn" 3 true)))
|
||||
(is (= "abcdefg\nhij" (util/get-text-range "abcdefg\nhijklmn" 3 false)))
|
||||
(is (= "abcdefg\nhijklmn" (util/get-text-range "abcdefg\nhijklmn" 10 false)))
|
||||
(is (= "abcdefg\nhijklmn\nopq" (util/get-text-range "abcdefg\nhijklmn\nopqrst" 3 false)))
|
||||
(is (= "a😀b" (util/get-text-range "a😀bcdefg" 3 true)))
|
||||
(is (= "a😀b" (util/get-text-range "a😀bcdefg" 3 false)))
|
||||
(is (= "a😀b" (util/get-text-range "a😀bcdefg\nhijklmn" 3 true)))
|
||||
(is (= "a😀bcdefg\nhij" (util/get-text-range "a😀bcdefg\nhijklmn" 3 false)))
|
||||
(is (= "a😀bcdefg\nh😀i" (util/get-text-range "a😀bcdefg\nh😀ijklmn" 3 false)))))
|
||||
|
||||
(deftest test-memoize-last
|
||||
(testing "memoize-last add test"
|
||||
(let [actual-ops (atom 0)
|
||||
|
||||
Reference in New Issue
Block a user