regex support

pull/5370/head
filip mertens 3 years ago
parent 6e35fc6fca
commit 57814f7522
  1. 33
      libs/remix-ui/search/src/lib/components/Find.tsx
  2. 2
      libs/remix-ui/search/src/lib/components/Search.tsx
  3. 23
      libs/remix-ui/search/src/lib/components/results/ResultFileName.tsx
  4. 6
      libs/remix-ui/search/src/lib/components/results/ResultSummary.tsx
  5. 3
      libs/remix-ui/search/src/lib/components/results/SearchHelper.ts
  6. 92
      libs/remix-ui/search/src/lib/context/context.tsx
  7. 6
      libs/remix-ui/search/src/lib/reducers/Reducer.ts
  8. 7
      libs/remix-ui/search/src/lib/search.css
  9. 2
      libs/remix-ui/search/src/lib/types/index.ts

@ -2,7 +2,13 @@ import React, { useContext } from 'react'
import { SearchContext } from '../context/context'
export const Find = props => {
const { setFind, state, toggleCaseSensitive, toggleMatchWholeWord } = useContext(SearchContext)
const {
setFind,
state,
toggleCaseSensitive,
toggleMatchWholeWord,
toggleUseRegex
} = useContext(SearchContext)
let timeOutId: any = null
const change = e => {
clearTimeout(timeOutId)
@ -21,11 +27,13 @@ export const Find = props => {
></input>
<div className="controls">
<div
title="Match Case (⌥⌘C)"
className={`monaco-custom-checkbox codicon codicon-case-sensitive ${state.casesensitive ? 'checked' : ''}`}
title="Match Case"
className={`monaco-custom-checkbox codicon codicon-case-sensitive ${
state.casesensitive ? 'checked' : ''
}`}
role="checkbox"
aria-checked="false"
aria-label="Match Case (⌥⌘C)"
aria-label="Match Case"
aria-disabled="false"
onClick={() => {
toggleCaseSensitive()
@ -33,7 +41,9 @@ export const Find = props => {
></div>
<div
title="Match Whole Word"
className={`monaco-custom-checkbox codicon codicon-whole-word ${state.matchWord ? 'checked' : ''}`}
className={`monaco-custom-checkbox codicon codicon-whole-word ${
state.matchWord ? 'checked' : ''
}`}
role="checkbox"
aria-checked="false"
aria-label="Match Whole Word"
@ -42,6 +52,19 @@ export const Find = props => {
toggleMatchWholeWord()
}}
></div>
<div
title="Use Regular Expression"
className={`monaco-custom-checkbox codicon codicon-regex ${
state.useRegExp ? 'checked' : ''
}`}
role="checkbox"
aria-checked="false"
aria-label="Use Regular Expression"
aria-disabled="false"
onClick={() => {
toggleUseRegex()
}}
></div>
</div>
</div>
</div>

@ -13,7 +13,7 @@ const plugin = props.plugin
return (
<>
<div className="search_tab">
<div className="search_tab pl-2 pr-2">
<SearchProvider plugin={plugin}>
<Find></Find>
<Replace></Replace>

@ -1,18 +1,25 @@
import React from 'react'
import React, { useEffect, useState } from 'react'
import { SearchResult } from '../../types'
import { getPathIcon } from '@remix-ui/helper'
interface ResultItemProps {
file: SearchResult
}
export const ResultFileName = (props: ResultItemProps) => {
const [icon, setIcon] = useState<string>('')
useEffect(() => {
if (props.file && props.file.path) {
setIcon(getPathIcon(props.file.path))
}
}, [props.file])
return (
<div title={props.file.filename} className="input-group udapp_nameNbuts">
<div className="udapp_titleText input-group-prepend">
<span className="input-group-text udapp_spanTitleText">
{props.file.filename}
</span>
<>
{icon ? <div className={`${icon} caret caret_tv`}></div> : null}
<div className="search_file_name ml-2">
{props.file.filename}
</div>
</div>
</>
)
}

@ -17,7 +17,11 @@ export const ResultSummary = (props: ResultSummaryProps) => {
const replace = async (line: SearchResultLineLine) => {
props.setLoading(true)
await replaceText(props.searchResult, line)
try{
await replaceText(props.searchResult, line)
}catch(e){
props.setLoading(false)
}
}
return (

@ -104,5 +104,8 @@ export const replaceTextInLine = (str: string, searchResultLine: SearchResultLin
}).join(getEOL(str))
}
export function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
}

@ -1,28 +1,38 @@
import React, { useEffect } from 'react'
import { createContext, useReducer } from 'react'
import { findLinesInStringWithMatch, getDirectory, replaceTextInLine } from '../components/results/SearchHelper'
import {
SearchReducer,
} from '../reducers/Reducer'
import { SearchState, SearchResult, SearchResultLine, SearchResultLineLine, SearchingInitialState } from '../types'
findLinesInStringWithMatch,
getDirectory,
replaceTextInLine
} from '../components/results/SearchHelper'
import { SearchReducer } from '../reducers/Reducer'
import {
SearchState,
SearchResult,
SearchResultLine,
SearchResultLineLine,
SearchingInitialState
} from '../types'
import { filePathFilter } from '@jsdevtools/file-path-filter'
import { escapeRegExp } from 'lodash'
export interface SearchingStateInterface {
state: SearchState
setFind: (value: string) => void
setReplace: (value: string) => void
setInclude: (value: string) => void,
setExclude: (value: string) => void,
setCaseSensitive: (value: boolean) => void,
setRegex: (value: boolean) => void,
setWholeWord: (value: boolean) => void,
setSearchResults: (value: SearchResult[]) => void,
findText: (path: string) => Promise<SearchResultLine[]>,
hightLightInPath: (result:SearchResult, line:SearchResultLineLine) => void,
replaceText: (result: SearchResult, line: SearchResultLineLine) => void,
reloadFile: (file:string) => void,
toggleCaseSensitive: () => void,
toggleMatchWholeWord: () => void,
setInclude: (value: string) => void
setExclude: (value: string) => void
setCaseSensitive: (value: boolean) => void
setRegex: (value: boolean) => void
setWholeWord: (value: boolean) => void
setSearchResults: (value: SearchResult[]) => void
findText: (path: string) => Promise<SearchResultLine[]>
hightLightInPath: (result: SearchResult, line: SearchResultLineLine) => void
replaceText: (result: SearchResult, line: SearchResultLineLine) => void
reloadFile: (file: string) => void
toggleCaseSensitive: () => void
toggleMatchWholeWord: () => void
toggleUseRegex: () => void
}
export const SearchContext = createContext<SearchingStateInterface>(null)
@ -86,12 +96,18 @@ export const SearchProvider = ({
payload: value
})
},
reloadFile: async (file:string) => {
reloadFile: async (file: string) => {
dispatch({
type: 'RELOAD_FILE',
payload: file
})
},
toggleUseRegex: () => {
dispatch({
type: 'TOGGLE_USE_REGEX',
payload: undefined
})
},
toggleCaseSensitive: () => {
dispatch({
type: 'TOGGLE_CASE_SENSITIVE',
@ -104,29 +120,47 @@ export const SearchProvider = ({
payload: undefined
})
},
findText : async (path: string) => {
if(!plugin) return
findText: async (path: string) => {
if (!plugin) return
try {
if(state.find.length < 3) return
if (state.find.length < 3) return
const text = await plugin.call('fileManager', 'readFile', path)
let flags = 'g'
let find = state.find
if(!state.casesensitive) flags += 'i'
if(state.matchWord) find = `\\b${find}\\b`
if (!state.casesensitive) flags += 'i'
if (state.matchWord) find = `\\b${find}\\b`
if (!state.useRegExp) find = escapeRegExp(find)
const re = new RegExp(find, flags)
const result: SearchResultLine[] = findLinesInStringWithMatch(text, re)
return result
} catch (e) {}
},
hightLightInPath: async(result: SearchResult, line: SearchResultLineLine) => {
hightLightInPath: async (
result: SearchResult,
line: SearchResultLineLine
) => {
await plugin.call('editor', 'discardHighlight')
await plugin.call('editor', 'highlight', line.position, result.path)
},
replaceText: async(result: SearchResult, line: SearchResultLineLine) => {
await plugin.call('editor', 'discardHighlight')
await plugin.call('editor', 'highlight', line.position, result.path)
const content = await plugin.call('fileManager', 'readFile', result.path)
await plugin.call('fileManager','setFile', result.path, replaceTextInLine(content, line, state.replace))
replaceText: async (result: SearchResult, line: SearchResultLineLine) => {
try {
await plugin.call('editor', 'discardHighlight')
await plugin.call('editor', 'highlight', line.position, result.path)
const content = await plugin.call(
'fileManager',
'readFile',
result.path
)
await plugin.call(
'fileManager',
'setFile',
result.path,
replaceTextInLine(content, line, state.replace)
)
} catch (e) {
throw new Error(e)
}
}
}
@ -173,8 +207,6 @@ export const SearchProvider = ({
}
}, [state.timeStamp])
return (
<>
<SearchContext.Provider value={value}>{children}</SearchContext.Provider>

@ -40,6 +40,12 @@ export const SearchReducer = (state: SearchState = SearchingInitialState, action
casesensitive: !state.casesensitive,
timeStamp: Date.now()
}
case 'TOGGLE_USE_REGEX':
return {
...state,
useRegExp: !state.useRegExp,
timeStamp: Date.now()
}
case 'TOGGLE_MATCH_WHOLE_WORD':
return {
...state,

@ -5,6 +5,7 @@
-ms-user-select: none; /* IE10+/Edge */
user-select: none; /* Standard */
cursor: pointer;
align-items: center;
}
.wrap_summary {
@ -86,3 +87,9 @@
.search_tab .checked {
background-color: var(--secondary);
}
.search_tab .search_file_name {
text-overflow: ellipsis;
overflow: hidden;
text-transform: uppercase;
}

@ -40,6 +40,7 @@ export interface SearchState {
exclude: string,
casesensitive: boolean,
matchWord: boolean,
useRegExp: boolean,
timeStamp: number
}
@ -51,5 +52,6 @@ export const SearchingInitialState: SearchState = {
searchResults: [],
casesensitive: false,
matchWord: false,
useRegExp: false,
timeStamp: 0
}
Loading…
Cancel
Save