From d6ceaa91c49c96ddd3b8a193a5b26afbf8da703a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=9C=9B?= <1501034130@qq.com>
Date: Thu, 10 Oct 2024 12:53:16 +0800
Subject: [PATCH] =?UTF-8?q?=E7=BC=96=E8=BE=91=E5=99=A8=E6=9F=A5=E6=89=BE?=
=?UTF-8?q?=E6=97=B6=E6=94=AF=E6=8C=81=E6=98=BE=E7=A4=BA=E5=BD=93=E5=89=8D?=
=?UTF-8?q?=E9=AB=98=E4=BA=AE=E9=A1=B9=E7=B4=A2=E5=BC=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../editor/EditorFindAndReplace.vue | 33 ++++++++++++-----
src/components/editor/index.ts | 36 ++++++++-----------
src/components/editor/search.ts | 23 ++++++++++++
src/styles/Default.css | 4 ++-
4 files changed, 64 insertions(+), 32 deletions(-)
diff --git a/src/components/editor/EditorFindAndReplace.vue b/src/components/editor/EditorFindAndReplace.vue
index b14f21a..eaec4e7 100644
--- a/src/components/editor/EditorFindAndReplace.vue
+++ b/src/components/editor/EditorFindAndReplace.vue
@@ -7,13 +7,6 @@
-
-
-
-
-
- 匹配数目:{{ matchingCount }}
-
+
+
+
+
+
+
+ 第 {{ matchingIndex }} 个,
+ 共 {{ matchingCount }} 个
+
+
("");
const replace = ref("");
+const matchingIndex = ref(0);
const matchingCount = ref(0);
const enableReplace = ref(false);
@@ -92,9 +96,19 @@ const _queryString = (keyword: string, replace?: string) => {
props.editor.find(keyword);
}
+ matchingIndex.value = props.editor.getCurrentMatchIndex() + 1;
matchingCount.value = props.editor.getMatchCount();
};
+const findNextOrPrev = (dir: -1 | 1) => {
+ if (dir > 0) props.editor.findNext();
+ else props.editor.findPrev();
+
+ props.editor.focus();
+
+ matchingIndex.value = props.editor.getCurrentMatchIndex() + 1;
+};
+
const replaceCurrent = () => {
props.editor.replaceCurrent();
props.editor.findNext();
@@ -115,6 +129,7 @@ watchEffect(() => {
onUnmounted(() => {
props.editor.clearFind();
props.editor.off("update", updateMatchingCount);
+ props.editor.focus();
});
onMounted(() => {
diff --git a/src/components/editor/index.ts b/src/components/editor/index.ts
index 9d3b09c..4b05e03 100644
--- a/src/components/editor/index.ts
+++ b/src/components/editor/index.ts
@@ -23,10 +23,11 @@ import {
getSearchState,
getMatchHighlights,
SearchQuery,
- findNext,
- findPrev,
replaceCurrent,
replaceAll,
+ findPrevNoWrap,
+ findNextNoWrap,
+ getSearchMatchingRanges,
} from "./search";
interface EventArgument {
@@ -343,8 +344,6 @@ export class Editor {
public clearFind() {
this.find("");
-
- this.view!.focus();
}
public getSearchState() {
@@ -352,37 +351,30 @@ export class Editor {
}
public getMatchCount() {
- if (!this.getSearchState()) return 0;
-
- const decos = getMatchHighlights(this.state);
- let i = 0;
- let sum = 0;
+ return getSearchMatchingRanges(this.state)?.length || 0;
+ }
- // @ts-ignore
- const decoArray = decos.children as any[];
+ public getCurrentMatchIndex() {
+ const ranges = getSearchMatchingRanges(this.state);
+ const selection = this.state.selection;
- for (; i < decoArray.length; i += 1) {
- if (typeof decoArray[i] === "number") continue;
- sum += decoArray[i].local.length;
- }
+ if (!ranges) return -1;
- return sum;
+ return ranges.findIndex(
+ ({ from, to }) => from === selection.from && to === selection.to
+ );
}
public findPrev() {
if (!this.getSearchState()) return;
- this.view!.focus();
-
- return findPrev(this.state, this.view!.dispatch);
+ return findPrevNoWrap(this.state, this.view!.dispatch);
}
public findNext() {
if (!this.getSearchState()) return;
- this.view!.focus();
-
- return findNext(this.state, this.view!.dispatch);
+ return findNextNoWrap(this.state, this.view!.dispatch);
}
public replaceCurrent() {
diff --git a/src/components/editor/search.ts b/src/components/editor/search.ts
index a4231ec..0d63fb6 100644
--- a/src/components/editor/search.ts
+++ b/src/components/editor/search.ts
@@ -540,6 +540,29 @@ export function getSearchState(state: EditorState):
return searchKey.getState(state);
}
+/// Get the ranges of matching texts in the current active search.
+/// Will return `undefined` is the search plugin isn't active.
+export function getSearchMatchingRanges(state: EditorState): { from: number, to: number }[] | undefined {
+ const searchState = searchKey.getState(state)
+ return searchState && searchState.deco.find().map(({ from, to }) => ({ from, to }))
+}
+
+/// Get the number of matching texts in the current active search.
+/// Will return `undefined` is the search plugin isn't active.
+export function getSearchMatchesCount(state: EditorState): number | undefined {
+ return getSearchMatchingRanges(state)?.length
+}
+
+/// Get the index of the match of the current active search
+/// that has the same range of the current selection.
+/// Will return -1 if no matching text matches the selection,
+/// or `undefined` is the search plugin isn't active.
+export function getSearchCurrentMatchIndex(state: EditorState): number | undefined {
+ const ranges = getSearchMatchingRanges(state)
+ const { from: selFrom, to: selTo } = state.selection
+ return ranges && ranges.findIndex(({ from, to }) => from === selFrom && to === selTo)
+}
+
/// Access the decoration set holding the currently highlighted search
/// matches in the document.
export function getMatchHighlights(state: EditorState) {
diff --git a/src/styles/Default.css b/src/styles/Default.css
index f292f92..4eaf1f4 100644
--- a/src/styles/Default.css
+++ b/src/styles/Default.css
@@ -509,10 +509,12 @@ label {
.ProseMirror-search-match {
background-color: var(--p-primary-color);
+ color: white;
}
.ProseMirror-active-search-match {
- background-color: var(--p-primary-color);
+ background-color: blue;
+ color: white;
}
/* Make sure li selections wrap around markers */