@tooee/search 0.2.4 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,62 +1,62 @@
1
- import { useCallback, useEffect, useRef, useState } from "react"
2
- import { useSelector } from "@xstate/store-react"
3
- import { useCommand, useMode, useSetMode, type Mode } from "@tooee/commands"
1
+ import { useCallback, useEffect, useRef, useState } from "react";
2
+ import { useSelector } from "@xstate/store-react";
3
+ import { useCommand, useMode, useSetMode } from "@tooee/commands";
4
+ import type { Mode } from "@tooee/commands";
4
5
  import {
5
6
  createNavSearchStore,
6
7
  selectCurrentMatchIndex,
7
8
  selectMatches,
8
9
  selectSearchActive,
9
10
  selectSearchQuery,
10
- type NavSearchDeps,
11
- type NavSearchStore,
12
- type RowKey,
13
- } from "./nav-search-store.js"
11
+ } from "./nav-search-store.js";
12
+ import type { NavSearchDeps, NavSearchStore, RowKey } from "./nav-search-store.js";
14
13
 
15
14
  export interface UseSearchOptions {
16
- match: (query: string) => number[]
17
- onJump: (index: number) => void
18
- enabled?: boolean
19
- deps?: readonly unknown[]
15
+ match: (query: string) => number[];
16
+ onJump: (index: number) => void;
17
+ enabled?: boolean;
18
+ deps?: readonly unknown[];
20
19
  }
21
20
 
22
21
  export interface SearchState {
23
- searchQuery: string
24
- searchActive: boolean
25
- setSearchQuery: (query: string) => void
26
- matchingLines: number[]
27
- currentMatchIndex: number
28
- submitSearch: () => void
22
+ searchQuery: string;
23
+ searchActive: boolean;
24
+ setSearchQuery: (query: string) => void;
25
+ matchingLines: number[];
26
+ currentMatchIndex: number;
27
+ submitSearch: () => void;
29
28
  }
30
29
 
31
- const CURSOR_MODES: Mode[] = ["cursor"]
32
- const ALL_MODES: Mode[] = ["cursor", "select", "insert"]
30
+ const CURSOR_MODES: Mode[] = ["cursor"];
31
+ const ALL_MODES: Mode[] = ["cursor", "select", "insert"];
33
32
 
34
- export function useNavSearchStore(options: {
35
- keys: readonly RowKey[]
36
- isSelectable?: (index: number) => boolean
37
- preserveCursorByKey?: boolean
33
+ export const useNavSearchStore = function useNavSearchStore(options: {
34
+ keys: readonly RowKey[];
35
+ isSelectable?: (index: number) => boolean;
36
+ preserveCursorByKey?: boolean;
38
37
  }): NavSearchStore {
39
- const depsRef = useRef<NavSearchDeps>({ isSelectable: options.isSelectable ?? (() => true) })
40
- depsRef.current.isSelectable = options.isSelectable ?? (() => true)
41
- const [store] = useState(() =>
42
- createNavSearchStore({ keys: options.keys, deps: depsRef.current }),
43
- )
38
+ const depsRef = useRef<NavSearchDeps>({ isSelectable: options.isSelectable ?? (() => true) });
39
+ depsRef.current.isSelectable = options.isSelectable ?? (() => true);
40
+ const [store, setStore] = useState(() =>
41
+ createNavSearchStore({ deps: depsRef.current, keys: options.keys }),
42
+ );
43
+ void setStore;
44
44
  useEffect(() => {
45
- const currentKeys = store.getSnapshot().context.rowKeys
45
+ const currentKeys = store.getSnapshot().context.rowKeys;
46
46
  const keysChanged =
47
47
  currentKeys.length !== options.keys.length ||
48
- currentKeys.some((key, index) => !Object.is(key, options.keys[index]))
48
+ currentKeys.some((key, index) => !Object.is(key, options.keys[index]));
49
49
  if (keysChanged) {
50
50
  store.trigger.rowsChanged({
51
51
  keys: options.keys,
52
52
  preserveCursorByKey: options.preserveCursorByKey,
53
- })
53
+ });
54
54
  }
55
- }, [store, options.keys, options.preserveCursorByKey])
56
- return store
57
- }
55
+ }, [store, options.keys, options.preserveCursorByKey]);
56
+ return store;
57
+ };
58
58
 
59
- export function useSearchBindings(
59
+ export const useSearchBindings = function useSearchBindings(
60
60
  store: NavSearchStore,
61
61
  {
62
62
  match,
@@ -65,101 +65,119 @@ export function useSearchBindings(
65
65
  deps = [],
66
66
  }: Omit<UseSearchOptions, "onJump"> & { onJump?: (index: number) => void },
67
67
  ): SearchState {
68
- const mode = useMode()
69
- const setMode = useSetMode()
70
- const matchRef = useRef(match)
71
- matchRef.current = match
72
- const onJumpRef = useRef(onJump)
73
- onJumpRef.current = onJump
68
+ const mode = useMode();
69
+ const setMode = useSetMode();
70
+ const matchRef = useRef(match);
71
+ matchRef.current = match;
72
+ const onJumpRef = useRef(onJump);
73
+ onJumpRef.current = onJump;
74
74
 
75
75
  useEffect(() => {
76
- const subscription = store.on("jumped", ({ index }) => onJumpRef.current?.(index))
77
- return () => subscription.unsubscribe()
78
- }, [store])
76
+ const subscription = store.on("jumped", ({ index }) => onJumpRef.current?.(index));
77
+ return () => {
78
+ subscription.unsubscribe();
79
+ };
80
+ }, [store]);
79
81
  useEffect(() => {
80
- const subscription = store.on("restoreMode", ({ mode: restored }) => setMode(restored))
81
- return () => subscription.unsubscribe()
82
- }, [store, setMode])
82
+ const subscription = store.on("restoreMode", ({ mode: restored }) => {
83
+ setMode(restored);
84
+ });
85
+ return () => {
86
+ subscription.unsubscribe();
87
+ };
88
+ }, [store, setMode]);
83
89
 
84
- const searchQuery = useSelector(store, (snapshot) => selectSearchQuery(snapshot.context))
85
- const searchActive = useSelector(store, (snapshot) => selectSearchActive(snapshot.context))
86
- const matchingLines = useSelector(store, (snapshot) =>
87
- selectMatches(snapshot.context),
88
- ) as number[]
90
+ const searchQuery = useSelector(store, (snapshot) => selectSearchQuery(snapshot.context));
91
+ const searchActive = useSelector(store, (snapshot) => selectSearchActive(snapshot.context));
92
+ const selectedMatches = useSelector(store, (snapshot) => selectMatches(snapshot.context));
93
+ // Deferred(lint-sweep): redesign the public selector result as readonly (separate stream)
94
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- preserve the existing mutable public SearchState type
95
+ const matchingLines = selectedMatches as number[];
89
96
  const currentMatchIndex = useSelector(store, (snapshot) =>
90
97
  selectCurrentMatchIndex(snapshot.context),
91
- )
98
+ );
92
99
 
93
100
  const updateSearchQuery = useCallback(
94
101
  (query: string) => {
95
- store.trigger.searchChanged({ query, matches: query ? matchRef.current(query) : [] })
102
+ store.trigger.searchChanged({ matches: query ? matchRef.current(query) : [], query });
96
103
  },
97
104
  [store],
98
- )
105
+ );
99
106
 
100
107
  // Re-match a committed query once when the caller-declared content changes.
101
- const depsRef = useRef(deps)
108
+ const depsRef = useRef(deps);
102
109
  useEffect(() => {
103
110
  if (
104
111
  depsRef.current.every((value, index) => Object.is(value, deps[index])) &&
105
112
  depsRef.current.length === deps.length
106
- )
107
- return
108
- depsRef.current = deps
109
- const query = selectSearchQuery(store.getSnapshot().context)
110
- if (query) store.trigger.searchChanged({ query, matches: matchRef.current(query) })
111
- }, [deps, store])
113
+ ) {
114
+ return;
115
+ }
116
+ depsRef.current = deps;
117
+ const query = selectSearchQuery(store.getSnapshot().context);
118
+ if (query) {
119
+ store.trigger.searchChanged({ matches: matchRef.current(query), query });
120
+ }
121
+ }, [deps, store]);
112
122
 
113
123
  useCommand({
114
- id: "cursor-search-start",
115
- title: "Search",
124
+ enabled,
125
+ handler: () => {
126
+ store.trigger.searchStarted({ mode });
127
+ setMode("insert");
128
+ },
116
129
  hotkey: "/",
130
+ id: "cursor-search-start",
117
131
  modes: CURSOR_MODES,
132
+ title: "Search",
133
+ });
134
+ useCommand({
118
135
  enabled,
119
136
  handler: () => {
120
- store.trigger.searchStarted({ mode })
121
- setMode("insert")
137
+ store.trigger.searchNext({});
122
138
  },
123
- })
124
- useCommand({
125
- id: "cursor-search-next",
126
- title: "Next match",
127
139
  hotkey: "n",
140
+ id: "cursor-search-next",
128
141
  modes: CURSOR_MODES,
129
- enabled,
142
+ title: "Next match",
130
143
  when: () => !selectSearchActive(store.getSnapshot().context),
131
- handler: () => store.trigger.searchNext({}),
132
- })
144
+ });
133
145
  useCommand({
134
- id: "cursor-search-prev",
135
- title: "Previous match",
146
+ enabled,
147
+ handler: () => {
148
+ store.trigger.searchPrevious({});
149
+ },
136
150
  hotkey: "shift+n",
151
+ id: "cursor-search-prev",
137
152
  modes: CURSOR_MODES,
138
- enabled,
153
+ title: "Previous match",
139
154
  when: () => !selectSearchActive(store.getSnapshot().context),
140
- handler: () => store.trigger.searchPrevious({}),
141
- })
155
+ });
142
156
  useCommand({
143
- id: "search-cancel",
144
- title: "Cancel search",
157
+ enabled,
158
+ handler: () => {
159
+ store.trigger.searchCancelled({});
160
+ },
145
161
  hotkey: "escape",
162
+ id: "search-cancel",
146
163
  modes: ALL_MODES,
147
- enabled,
164
+ title: "Cancel search",
148
165
  when: () => selectSearchActive(store.getSnapshot().context),
149
- handler: () => store.trigger.searchCancelled({}),
150
- })
166
+ });
151
167
 
152
168
  return {
153
- searchQuery,
169
+ currentMatchIndex,
170
+ matchingLines,
154
171
  searchActive,
172
+ searchQuery,
155
173
  setSearchQuery: updateSearchQuery,
156
- matchingLines,
157
- currentMatchIndex,
158
- submitSearch: () => store.trigger.searchSubmitted({}),
159
- }
160
- }
174
+ submitSearch: () => {
175
+ store.trigger.searchSubmitted({});
176
+ },
177
+ };
178
+ };
161
179
 
162
- export function useSearch(options: UseSearchOptions): SearchState {
163
- const store = useNavSearchStore({ keys: [] })
164
- return useSearchBindings(store, options)
165
- }
180
+ export const useSearch = function useSearch(options: UseSearchOptions): SearchState {
181
+ const store = useNavSearchStore({ keys: [] });
182
+ return useSearchBindings(store, options);
183
+ };
package/src/search.ts CHANGED
@@ -1,18 +1,20 @@
1
- export function findMatchingLines(text: string, query: string): number[] {
2
- if (!query) return []
3
- const lowerQuery = query.toLowerCase()
4
- const results: number[] = []
5
- let lineStart = 0
6
- let lineNum = 0
7
- for (let i = 0; i <= text.length; i++) {
1
+ export const findMatchingLines = function findMatchingLines(text: string, query: string): number[] {
2
+ if (!query) {
3
+ return [];
4
+ }
5
+ const lowerQuery = query.toLowerCase();
6
+ const results: number[] = [];
7
+ let lineStart = 0;
8
+ let lineNum = 0;
9
+ for (let i = 0; i <= text.length; i += 1) {
8
10
  if (i === text.length || text[i] === "\n") {
9
- const line = text.slice(lineStart, i).toLowerCase()
11
+ const line = text.slice(lineStart, i).toLowerCase();
10
12
  if (line.includes(lowerQuery)) {
11
- results.push(lineNum)
13
+ results.push(lineNum);
12
14
  }
13
- lineStart = i + 1
14
- lineNum++
15
+ lineStart = i + 1;
16
+ lineNum += 1;
15
17
  }
16
18
  }
17
- return results
18
- }
19
+ return results;
20
+ };
@@ -1 +0,0 @@
1
- {"version":3,"file":"SearchBar.d.ts","sourceRoot":"","sources":["../src/SearchBar.tsx"],"names":[],"mappings":"AAEA,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACtC,QAAQ,EAAE,MAAM,IAAI,CAAA;IACpB,QAAQ,EAAE,MAAM,IAAI,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,aAAa,EACb,QAAQ,EACR,QAAQ,EAAE,SAAS,EACnB,UAAU,EACV,YAAY,GACb,EAAE,cAAc,6BAoChB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"SearchBar.js","sourceRoot":"","sources":["../src/SearchBar.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAWxC,MAAM,UAAU,SAAS,CAAC,EACxB,KAAK,EACL,aAAa,EACb,QAAQ,EACR,QAAQ,EAAE,SAAS,EACnB,UAAU,EACV,YAAY,GACG;IACf,MAAM,EAAE,KAAK,EAAE,GAAG,QAAQ,EAAE,CAAA;IAE5B,MAAM,YAAY,GAChB,UAAU,KAAK,SAAS,IAAI,UAAU,GAAG,CAAC;QACxC,CAAC,CAAC,GAAG,CAAC,YAAY,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,UAAU,EAAE;QAC5C,CAAC,CAAC,UAAU,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;YACpC,CAAC,CAAC,YAAY;YACd,CAAC,CAAC,EAAE,CAAA;IAEV,OAAO,CACL,eACE,KAAK,EAAE;YACL,aAAa,EAAE,KAAK;YACpB,UAAU,EAAE,CAAC;YACb,eAAe,EAAE,KAAK,CAAC,eAAe;YACtC,WAAW,EAAE,CAAC;YACd,YAAY,EAAE,CAAC;SAChB,aAED,eAAM,OAAO,EAAC,GAAG,EAAC,KAAK,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,MAAM,EAAE,GAAI,EACjD,gBACE,KAAK,EAAE,KAAK,EACZ,OAAO,QACP,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,QAAQ,EAClB,eAAe,EAAC,aAAa,EAC7B,sBAAsB,EAAC,aAAa,EACpC,SAAS,EAAE,KAAK,CAAC,IAAI,EACrB,WAAW,EAAE,KAAK,CAAC,MAAM,EACzB,WAAW,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,EAC9C,KAAK,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,GACtB,EACD,YAAY,CAAC,CAAC,CAAC,eAAM,OAAO,EAAE,IAAI,YAAY,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,SAAS,EAAE,GAAI,CAAC,CAAC,CAAC,IAAI,IACxF,CACP,CAAA;AACH,CAAC"}