Browse Source

Implement the find method.

oldj 4 years ago
parent
commit
d9a8b68657

+ 4 - 0
src/common/types.d.ts

@@ -23,9 +23,13 @@ export interface IPopupMenuOption {
 }
 
 export interface IFindResultItem {
+  item_title: string;
   item_id: string;
   item_type: HostsType;
   line: number;
   start: number;
   end: number;
+  before: string;
+  match: string;
+  after: string;
 }

+ 9 - 18
src/main/actions/find/findBy.ts

@@ -7,26 +7,13 @@ import getContentOfHosts from '@main/actions/hosts/getContent'
 import { flatten } from '@root/common/hostsFn'
 import { IFindResultItem } from '@root/common/types'
 import { getList } from '../index'
+import findInContent from './findInContent'
 
-interface IFindOptions {
+export interface IFindOptions {
   is_regexp: boolean;
   is_ignore_case: boolean;
 }
 
-const findInContent = (content: string, exp: string | RegExp, options: IFindOptions): Omit<IFindResultItem, 'item_id' | 'item_type'>[] => {
-  let result_items: IFindResultItem[] = []
-
-  let result = {
-    line: -1,
-    start: -1,
-    end: -1,
-  }
-
-  // todo ...
-
-  return result_items
-}
-
 export default async (keyword: string, options: IFindOptions): Promise<IFindResultItem[]> => {
   console.log(keyword)
   let result_items: IFindResultItem[] = []
@@ -34,9 +21,12 @@ export default async (keyword: string, options: IFindOptions): Promise<IFindResu
   let tree = await getList()
   let items = flatten(tree)
 
-  let exp: string | RegExp = keyword
+  let exp: RegExp
   if (options.is_regexp) {
-    exp = new RegExp(exp, options.is_ignore_case ? 'ig' : 'g')
+    exp = new RegExp(keyword, options.is_ignore_case ? 'ig' : 'g')
+  } else {
+    let kw = keyword.replace(/([.^$([?*+])/ig, '\\$1')
+    exp = new RegExp(kw, options.is_ignore_case ? 'ig' : 'g')
   }
 
   for (let item of items) {
@@ -45,9 +35,10 @@ export default async (keyword: string, options: IFindOptions): Promise<IFindResu
       continue
     }
     let content = await getContentOfHosts(item.id)
-    let found = findInContent(content, exp, options)
+    let found = findInContent(content, exp)
     result_items = [...result_items, ...found.map(i => ({
       ...i,
+      item_title: item.title || '',
       item_id: item.id,
       item_type,
     }))]

+ 49 - 0
src/main/actions/find/findInContent.ts

@@ -0,0 +1,49 @@
+/**
+ * @author: oldj
+ * @homepage: https://oldj.net
+ */
+
+import { IFindResultItem } from '@root/common/types'
+
+type MatchResult = Pick<IFindResultItem, 'line' | 'start' | 'end' | 'before' | 'match' | 'after'>
+
+export default (content: string, exp: RegExp): MatchResult[] => {
+  let result_items: MatchResult[] = []
+
+  let m = content.match(exp)
+  if (!m) {
+    return []
+  }
+
+  let line = 1
+  let start = 0
+
+  let cnt = content
+  for (let i of m) {
+    let idx = cnt.indexOf(i)
+    if (idx === -1) continue
+
+    let head = cnt.slice(0, idx)
+    cnt = cnt.slice(idx + i.length)
+
+    let head_lines = head.split('\n')
+    line += head_lines.length - 1
+    start += head.length
+    let before_lines = content.slice(0, start).split('\n')
+    let before = before_lines[before_lines.length - 1]
+    let after = cnt.split('\n')[0]
+
+    result_items.push({
+      line,
+      start,
+      end: start + i.length,
+      before,
+      match: i,
+      after,
+    })
+
+    start += i.length
+  }
+
+  return result_items
+}

+ 14 - 1
src/renderer/pages/find.tsx

@@ -20,7 +20,7 @@ import useOnBroadcast from '@renderer/core/useOnBroadcast'
 import { IFindResultItem } from '@root/common/types'
 import { useDebounce } from 'ahooks'
 import lodash from 'lodash'
-import React, { useEffect, useState } from 'react'
+import React, { useEffect, useRef, useState } from 'react'
 import { IoSearch } from 'react-icons/io5'
 import styles from './find.less'
 
@@ -38,6 +38,7 @@ const find = (props: Props) => {
   const [is_ignore_case, setIsIgnoreCase] = useState(false)
   const [find_result, setFindResult] = useState<IFindResultItem[]>([])
   const debounced_keyword = useDebounce(keyword, { wait: 500 })
+  const ipt_kw = useRef<HTMLInputElement>(null)
 
   const init = async () => {
     if (!configs) return
@@ -68,6 +69,17 @@ const find = (props: Props) => {
     doFind(debounced_keyword)
   }, [debounced_keyword, is_regexp, is_ignore_case])
 
+  useEffect(() => {
+    const onFocus = () => {
+      if (ipt_kw.current) {
+        ipt_kw.current.focus()
+      }
+    }
+
+    window.addEventListener('focus', onFocus, false)
+    return () => window.removeEventListener('focus', onFocus, false)
+  }, [ipt_kw])
+
   useOnBroadcast('config_updated', loadConfigs)
 
   const doFind = lodash.debounce(async (v: string) => {
@@ -104,6 +116,7 @@ const find = (props: Props) => {
             onChange={(e) => {
               setKeyword(e.target.value)
             }}
+            ref={ipt_kw}
           />
         </InputGroup>
 

+ 55 - 0
test/main/findInContent.test.ts

@@ -0,0 +1,55 @@
+/**
+ * @author: oldj
+ * @homepage: https://oldj.net
+ */
+
+import assert = require('assert')
+import { default as findInContent } from 'src/main/actions/find/findInContent'
+
+describe('find in content test', () => {
+  it('basic test 1', () => {
+    let content = `abc12 abc123 abc`
+    let m = findInContent(content, /bc/ig)
+    assert(m.length === 3)
+    assert(m[0].line === 1)
+    assert(m[0].start === 1)
+    assert(m[0].end === 3)
+    assert(m[0].before === 'a')
+    assert(m[0].match === 'bc')
+    assert(typeof m[0].after === 'string')
+
+    assert(m[1].line === 1)
+    assert(m[1].start === 7)
+    assert(m[1].end === 9)
+    assert(m[1].before === 'abc12 a')
+    assert(m[1].match === 'bc')
+    assert(m[1].after === '123 abc')
+
+    assert(m[2].line === 1)
+    assert(m[2].start === 14)
+    assert(m[2].end === 16)
+    assert(m[2].before === 'abc12 abc123 a')
+    assert(m[2].match === 'bc')
+    assert(m[2].after === '')
+  })
+
+  it.only('basic test 2', () => {
+    let content = `abc12 abc123 abc\nxyza3b`
+    let m = findInContent(content, /a\w*3/ig)
+    console.log(m)
+    assert(m.length === 2)
+    assert(m[1].line === 1)
+    assert(m[1].start === 6)
+    assert(m[1].end === 12)
+    assert(m[1].before === 'abc12 ')
+    assert(m[1].match === 'abc123')
+    assert(m[1].after === ' abc')
+
+    assert(m[2].line === 2)
+    assert(m[2].start === 20)
+    assert(m[2].end === 22)
+    assert(m[2].before === 'xyz')
+    assert(m[2].match === 'a3')
+    assert(m[2].after === 'b')
+  })
+})