Skip to content

Commit

Permalink
编辑器查找时支持显示当前高亮项索引
Browse files Browse the repository at this point in the history
  • Loading branch information
fgt1t5y committed Oct 10, 2024
1 parent 306fe6f commit d6ceaa9
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 32 deletions.
33 changes: 24 additions & 9 deletions src/components/editor/EditorFindAndReplace.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,6 @@
</button>
</div>
<div class="FloatPanelBody">
<div class="MatchingInfo">
<span>
<Checkbox v-model="enableReplace" input-id="enableReplace" binary />
<label for="enableReplace">启用替换</label>
</span>
<span>匹配数目:{{ matchingCount }}</span>
</div>
<div class="SearchInfo">
<InputText
v-model="keyword"
Expand All @@ -25,18 +18,28 @@
<button
title="上一个"
:disabled="!keyword || matchingCount === 0"
@click="editor.findPrev()"
@click="findNextOrPrev(-1)"
>
<i class="i i-arrow-up3"></i>
</button>
<button
title="下一个"
:disabled="!keyword || matchingCount === 0"
@click="editor.findNext()"
@click="findNextOrPrev(1)"
>
<i class="i i-arrow-down3"></i>
</button>
</div>
<div class="MatchingInfo">
<span>
<Checkbox v-model="enableReplace" input-id="enableReplace" binary />
<label for="enableReplace">启用替换</label>
</span>
<div>
<span v-show="matchingIndex">第 {{ matchingIndex }} 个,</span>
<span>共 {{ matchingCount }} 个</span>
</div>
</div>
<div v-show="enableReplace" class="SearchInfo">
<InputText
v-model="replace"
Expand Down Expand Up @@ -80,6 +83,7 @@ const props = defineProps<{
const keyword = ref<string>("");
const replace = ref<string>("");
const matchingIndex = ref<number>(0);
const matchingCount = ref<number>(0);
const enableReplace = ref<boolean>(false);
Expand All @@ -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();
Expand All @@ -115,6 +129,7 @@ watchEffect(() => {
onUnmounted(() => {
props.editor.clearFind();
props.editor.off("update", updateMatchingCount);
props.editor.focus();
});
onMounted(() => {
Expand Down
36 changes: 14 additions & 22 deletions src/components/editor/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@ import {
getSearchState,
getMatchHighlights,
SearchQuery,
findNext,
findPrev,
replaceCurrent,
replaceAll,
findPrevNoWrap,
findNextNoWrap,
getSearchMatchingRanges,
} from "./search";

interface EventArgument {
Expand Down Expand Up @@ -343,46 +344,37 @@ export class Editor {

public clearFind() {
this.find("");

this.view!.focus();
}

public getSearchState() {
return getSearchState(this.state);
}

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() {
Expand Down
23 changes: 23 additions & 0 deletions src/components/editor/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
4 changes: 3 additions & 1 deletion src/styles/Default.css
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down

0 comments on commit d6ceaa9

Please sign in to comment.