performance

pull/2257/head^2
filip mertens 3 years ago committed by yann300
parent 5b5650889c
commit 0f4f6aae6a
  1. 13
      libs/remix-ui/search/src/lib/components/CancelSearch.tsx
  2. 9
      libs/remix-ui/search/src/lib/components/Exclude.tsx
  3. 7
      libs/remix-ui/search/src/lib/components/Include.tsx
  4. 28
      libs/remix-ui/search/src/lib/components/Search.tsx
  5. 21
      libs/remix-ui/search/src/lib/components/results/ResultItem.tsx
  6. 22
      libs/remix-ui/search/src/lib/components/results/Results.tsx
  7. 186
      libs/remix-ui/search/src/lib/context/context.tsx
  8. 30
      libs/remix-ui/search/src/lib/reducers/Reducer.ts
  9. 5
      libs/remix-ui/search/src/lib/search.css
  10. 6
      libs/remix-ui/search/src/lib/types/index.ts

@ -0,0 +1,13 @@
import React from "react"
import { useContext } from "react"
import { SearchContext } from "../context/context"
export const CacncelSearch = () => {
const { cancelSearch } = useContext(SearchContext)
const cancel = async () => {
await cancelSearch()
}
return (
<a onClick={async () => await cancel()}>stop</a>
)
}

@ -2,12 +2,13 @@ import React, { useContext, useEffect, useRef, useState } from 'react'
import { SearchContext } from '../context/context'
export const Exclude = props => {
const { setExclude, state } = useContext(SearchContext)
const [excludeInput, setExcludeInput] = useState<string>('.git/**/*,.deps/**/*')
const { setExclude, cancelSearch } = useContext(SearchContext)
const [excludeInput, setExcludeInput] = useState<string>('.*/**/*')
const timeOutId = useRef(null)
const change = e => {
const change = async e => {
setExcludeInput(e.target.value)
clearTimeout(timeOutId.current)
await cancelSearch()
timeOutId.current = setTimeout(() => setExclude(e.target.value), 500)
}
@ -23,7 +24,7 @@ export const Exclude = props => {
id='search_exclude'
placeholder="Exclude ie .git/**/*"
className="form-control"
onChange={change}
onChange={async(e) => change(e)}
value={excludeInput}
></input>
</div>

@ -2,12 +2,13 @@ import React, { useContext, useRef, useState } from 'react'
import { SearchContext } from '../context/context'
export const Include = props => {
const { setInclude } = useContext(SearchContext)
const { setInclude, cancelSearch } = useContext(SearchContext)
const [includeInput, setIncludeInput] = useState<string>('')
const timeOutId = useRef(null)
const change = e => {
const change = async e => {
setIncludeInput(e.target.value)
clearTimeout(timeOutId.current)
await cancelSearch()
timeOutId.current = setTimeout(() => setInclude(e.target.value), 500)
}
@ -19,7 +20,7 @@ export const Include = props => {
id='search_include'
placeholder="Include ie contracts/**/*.sol"
className="form-control"
onChange={change}
onChange={async(e) => change(e)}
value={includeInput}
></input>
</div>

@ -1,30 +1,28 @@
import React from 'react'
import React, { useContext } from 'react'
import { SearchProvider } from '../context/context'
import { Find } from './Find'
import { Results } from './results/Results'
import '../search.css'
import { Include } from './Include'
import { Exclude } from './Exclude'
import { Replace } from './Replace'
import { OverWriteCheck } from './OverWriteCheck'
import { FindContainer } from './FindContainer'
import { Undo } from './Undo'
import { CacncelSearch } from './CancelSearch'
export const SearchTab = props => {
const plugin = props.plugin
const plugin = props.plugin
return (
return (
<>
<div className="search_plugin_search_tab px-2">
<SearchProvider plugin={plugin}>
<FindContainer></FindContainer>
<Include></Include>
<Exclude></Exclude>
<Undo></Undo>
<Results></Results>
</SearchProvider>
</div>
<div className="search_plugin_search_tab px-2">
<SearchProvider plugin={plugin}>
<FindContainer></FindContainer>
<Include></Include>
<Exclude></Exclude>
<Undo></Undo>
<Results></Results>
</SearchProvider>
</div>
</>
)
}

@ -7,6 +7,7 @@ import { ResultSummary } from './ResultSummary'
interface ResultItemProps {
file: SearchResult
index: number
}
export const ResultItem = (props: ResultItemProps) => {
@ -17,6 +18,7 @@ export const ResultItem = (props: ResultItemProps) => {
const [lines, setLines] = useState<SearchResultLine[]>([])
const [toggleExpander, setToggleExpander] = useState<boolean>(false)
const reloadTimeOut = useRef(null)
const loadTimeout = useRef(null)
const subscribed = useRef(true)
const { modal } = useDialogDispatchers()
@ -27,6 +29,7 @@ export const ResultItem = (props: ResultItemProps) => {
useEffect(() => {
if (props.file.forceReload) {
clearTimeout(reloadTimeOut.current)
clearTimeout(loadTimeout.current)
reloadTimeOut.current = setTimeout(() => reload(), 1000)
}
}, [props.file.forceReload])
@ -35,14 +38,11 @@ export const ResultItem = (props: ResultItemProps) => {
setToggleExpander(!toggleExpander)
}
useEffect(() => {
reload()
}, [state.find])
useEffect(() => {
subscribed.current = true
return () => {
updateCount(0, props.file.filename)
clearTimeout(reloadTimeOut.current)
clearTimeout(loadTimeout.current)
subscribed.current = false
}
}, [])
@ -64,7 +64,7 @@ export const ResultItem = (props: ResultItemProps) => {
}
}
const reload = () => {
const doLoad = () => {
findText(props.file.filename).then(res => {
if (subscribed.current) {
setLines(res)
@ -78,9 +78,15 @@ export const ResultItem = (props: ResultItemProps) => {
setLoading(false)
disableForceReload(props.file.filename)
}
}).catch((e) => {
console.error(e)
})
}
const reload = () => {
loadTimeout.current = setTimeout(doLoad, 150 * props.index)
}
return (
<>
{lines && lines.length ? (
@ -110,13 +116,12 @@ export const ResultItem = (props: ResultItemProps) => {
</div>
:null}
{lines.map((line, index) => (
index < state.maxLines ?
<ResultSummary
setLoading={setLoading}
key={index}
searchResult={props.file}
line={line}
/>: null
/>
))}
</div>
) : null}

@ -5,12 +5,26 @@ import { ResultItem } from './ResultItem'
export const Results = () => {
const { state } = useContext(SearchContext)
return (
<div data-id='search_results' className='mt-2'>
{state.find ? <div className='search_plugin_result_count_number badge badge-pill badge-secondary'>showing {state.count} results {state.fileCount} in files</div>: null}
{state.find && state.clipped? <div className='alert alert-warning mt-1'>The result set only shows a subset of all matches<br></br>Please narrow down your search.</div>: null}
<div data-id="search_results" className="mt-2">
<div className="search_plugin_search_indicator py-1">
{' '}
{state.searching && !state.clipped
? `searching in ${state.searching}`
: null}
</div>
{state.find && !state.clipped ? (
<div className="search_plugin_result_count_number badge badge-pill badge-secondary">
showing {state.count} results {state.fileCount} in files
</div>
) : null}
{state.find && state.clipped ? (
<div className="alert alert-warning mt-1">
Too many resuls to display...<br></br>Please narrow down your search.
</div>
) : null}
{state.searchResults &&
state.searchResults.map((result, index) => {
return index <state.maxFiles? <ResultItem key={index} file={result} />: null
return <ResultItem index={index} key={index} file={result} />
})}
</div>
)

@ -1,4 +1,4 @@
import React, { useEffect, useRef } from 'react'
import React, { useEffect, useRef, useState } from 'react'
import { createContext, useReducer } from 'react'
import {
findLinesInStringWithMatch,
@ -31,7 +31,10 @@ export interface SearchingStateInterface {
setSearchResults: (value: SearchResult[]) => void
findText: (path: string) => Promise<SearchResultLine[]>
hightLightInPath: (result: SearchResult, line: SearchResultLineLine) => void
replaceText: (result: SearchResult, line: SearchResultLineLine) => Promise<void>
replaceText: (
result: SearchResult,
line: SearchResultLineLine
) => Promise<void>
reloadFile: (file: string) => void
toggleCaseSensitive: () => void
toggleMatchWholeWord: () => void
@ -39,9 +42,10 @@ export interface SearchingStateInterface {
setReplaceWithoutConfirmation: (value: boolean) => void
disableForceReload: (file: string) => void
updateCount: (count: number, file: string) => void
replaceAllInFile: (result: SearchResult) => Promise<void>
replaceAllInFile: (result: SearchResult) => Promise<void>
undoReplace: (buffer: undoBufferRecord) => Promise<void>
clearUndo: () => void
cancelSearch: () => Promise<void>
}
export const SearchContext = createContext<SearchingStateInterface>(null)
@ -53,11 +57,12 @@ export const SearchProvider = ({
plugin = undefined
} = {}) => {
const [state, dispatch] = useReducer(reducer, initialState)
const reloadTimeOut = useRef(null)
const [files, setFiles] = useState([])
const clearSearchingTimeout = useRef(null)
const value = {
state,
setFind: (value: string) => {
plugin.cancel('fileManager')
dispatch({
type: 'SET_FIND',
payload: value
@ -165,14 +170,30 @@ export const SearchProvider = ({
payload: { count, file }
})
},
setSearching(file: string) {
dispatch({
type: 'SET_SEARCHING',
payload: file
})
},
findText: async (path: string) => {
if (!plugin) return
try {
if (state.find.length < 1) return
value.setSearching(path)
const text = await plugin.call('fileManager', 'readFile', path)
const result: SearchResultLine[] = findLinesInStringWithMatch(text, createRegExFromFind())
const result: SearchResultLine[] = findLinesInStringWithMatch(
text,
createRegExFromFind()
)
clearTimeout(clearSearchingTimeout.current)
clearSearchingTimeout.current = setTimeout(() => value.setSearching(null), 500)
return result
} catch (e) { }
} catch (e) {
value.setSearching(null)
// do nothing
}
},
hightLightInPath: async (
result: SearchResult,
@ -180,7 +201,14 @@ export const SearchProvider = ({
) => {
await plugin.call('editor', 'discardHighlight')
await plugin.call('editor', 'highlight', line.position, result.path)
await plugin.call('editor', 'revealRange', line.position.start.line, line.position.start.column, line.position.end.line, line.position.end.column)
await plugin.call(
'editor',
'revealRange',
line.position.start.line,
line.position.start.column,
line.position.end.line,
line.position.end.column
)
},
replaceText: async (result: SearchResult, line: SearchResultLineLine) => {
try {
@ -192,12 +220,7 @@ export const SearchProvider = ({
result.path
)
const replaced = replaceTextInLine(content, line, state.replace)
await plugin.call(
'fileManager',
'setFile',
result.path,
replaced
)
await plugin.call('fileManager', 'setFile', result.path, replaced)
setUndoState(content, replaced, result.path)
} catch (e) {
throw new Error(e)
@ -205,26 +228,17 @@ export const SearchProvider = ({
},
replaceAllInFile: async (result: SearchResult) => {
await plugin.call('editor', 'discardHighlight')
const content = await plugin.call(
'fileManager',
'readFile',
result.path
)
const replaced = replaceAllInFile(content, createRegExFromFind(), state.replace)
await plugin.call(
'fileManager',
'setFile',
result.path,
replaced
)
await plugin.call(
'fileManager',
'open',
result.path
const content = await plugin.call('fileManager', 'readFile', result.path)
const replaced = replaceAllInFile(
content,
createRegExFromFind(),
state.replace
)
await plugin.call('fileManager', 'setFile', result.path, replaced)
await plugin.call('fileManager', 'open', result.path)
setUndoState(content, replaced, result.path)
},
setUndoEnabled: (path:string, workspace: string, content: string) => {
setUndoEnabled: (path: string, workspace: string, content: string) => {
dispatch({
type: 'SET_UNDO_ENABLED',
payload: {
@ -235,11 +249,7 @@ export const SearchProvider = ({
})
},
undoReplace: async (buffer: undoBufferRecord) => {
const content = await plugin.call(
'fileManager',
'readFile',
buffer.path
)
const content = await plugin.call('fileManager', 'readFile', buffer.path)
if (buffer.newContent !== content) {
throw new Error('Can not undo replace, file has been changed.')
}
@ -249,41 +259,58 @@ export const SearchProvider = ({
buffer.path,
buffer.oldContent
)
await plugin.call(
'fileManager',
'open',
buffer.path
)
await plugin.call('fileManager', 'open', buffer.path)
},
clearUndo: () => {
dispatch ({
dispatch({
type: 'CLEAR_UNDO',
payload: undefined
})
}
}
},
clearStats: () => {
dispatch({
type: 'CLEAR_STATS',
payload: undefined
})
},
cancelSearch: async () => {
plugin.cancel('fileManager')
value.clearStats()
},
setClipped: (value: boolean) => {
dispatch({
type: 'SET_CLIPPED',
payload: value
})
}
}
const reloadStateForFile = async (file: string) => {
await value.reloadFile(file)
}
useEffect(() => {
plugin.on('filePanel', 'setWorkspace', async (workspace) => {
plugin.on('filePanel', 'setWorkspace', async workspace => {
value.setSearchResults(null)
value.clearUndo()
value.setCurrentWorkspace(workspace.name)
setFiles(await getDirectory('/', plugin))
})
plugin.on('fileManager', 'fileSaved', async file => {
await reloadStateForFile(file)
await checkUndoState(file)
})
plugin.on('fileManager', 'fileAdded', async file => {
setFiles(await getDirectory('/', plugin))
})
plugin.on('fileManager', 'currentFileChanged', async file => {
value.setCurrentFile(file)
await checkUndoState(file)
})
return () => {
plugin.off('fileManager', 'fileChanged')
plugin.off('filePanel', 'setWorkspace')
@ -296,7 +323,8 @@ export const SearchProvider = ({
paths.split(',').forEach(path => {
path = path.trim()
if (path.startsWith('*.')) path = path.replace(/(\*\.)/g, '**/*.')
if (path.endsWith('/*') && !path.endsWith('/**/*')) path = path.replace(/(\*)/g, '**/*.*')
if (path.endsWith('/*') && !path.endsWith('/**/*'))
path = path.replace(/(\*)/g, '**/*.*')
results.push(path)
})
return results
@ -305,19 +333,19 @@ export const SearchProvider = ({
const checkUndoState = async (path: string) => {
if (!plugin) return
try {
const content = await plugin.call(
'fileManager',
'readFile',
path
)
const content = await plugin.call('fileManager', 'readFile', path)
const workspace = await plugin.call('filePanel', 'getCurrentWorkspace')
value.setUndoEnabled(path, workspace.name, content)
} catch (e) {
} catch (e) {
console.log(e)
}
}
const setUndoState = async (oldContent: string, newContent: string, path: string) => {
const setUndoState = async (
oldContent: string,
newContent: string,
path: string
) => {
const workspace = await plugin.call('filePanel', 'getCurrentWorkspace')
const undo = {
oldContent,
@ -341,29 +369,41 @@ export const SearchProvider = ({
return re
}
useEffect(() => {
if(state.count>500) {
value.setClipped(true)
value.cancelSearch()
}
}, [state.count])
useEffect(() => {
if (state.find) {
(async () => {
const files = await getDirectory('/', plugin)
const pathFilter: any = {}
if (state.include) {
pathFilter.include = setGlobalExpression(state.include)
}
if (state.exclude) {
pathFilter.exclude = setGlobalExpression(state.exclude)
}
const filteredFiles = files.filter(filePathFilter(pathFilter)).map(file => {
const r: SearchResult = {
filename: file,
lines: [],
path: file,
timeStamp: Date.now(),
forceReload: false,
count: 0
try {
const pathFilter: any = {}
if (state.include) {
pathFilter.include = setGlobalExpression(state.include)
}
if (state.exclude) {
pathFilter.exclude = setGlobalExpression(state.exclude)
}
return r
})
value.setSearchResults(filteredFiles)
const filteredFiles = files
.filter(filePathFilter(pathFilter))
.map(file => {
const r: SearchResult = {
filename: file,
lines: [],
path: file,
timeStamp: Date.now(),
forceReload: false,
count: 0
}
return r
})
value.setSearchResults(filteredFiles)
} catch (e) {
console.log(e)
}
})()
}
}, [state.timeStamp])

@ -5,6 +5,7 @@ export const SearchReducer = (state: SearchState = SearchingInitialState, action
case 'SET_FIND':
return {
...state,
searchResults: null,
find: action.payload,
timeStamp: Date.now()
}
@ -64,6 +65,21 @@ export const SearchReducer = (state: SearchState = SearchingInitialState, action
...state,
}
}
case 'CLEAR_STATS':
return {
...state,
count: 0,
fileCount: 0,
searchResults: null,
searching: null
}
case 'SET_SEARCHING':
return {
...state,
searching: action.payload,
}
case 'CLEAR_UNDO': {
state.undoBuffer = []
return {
@ -81,12 +97,7 @@ export const SearchReducer = (state: SearchState = SearchingInitialState, action
}
state.searchResults.forEach(file => {
if (file.count) {
if(file.count > state.maxLines) {
clipped = true
count += state.maxLines
}else{
count += file.count
}
count += file.count
fileCount++
}
})
@ -99,6 +110,13 @@ export const SearchReducer = (state: SearchState = SearchingInitialState, action
} else {
return state
}
case 'SET_CLIPPED':
return {
...state,
clipped: action.payload
}
case 'TOGGLE_CASE_SENSITIVE':
return {
...state,

@ -133,4 +133,9 @@
white-space: pre;
text-overflow: ellipsis;
overflow: hidden;
}
.search_plugin_search_indicator{
white-space: pre;
text-overflow: ellipsis;
overflow: hidden;
}

@ -63,7 +63,8 @@ export interface SearchState {
clipped: boolean,
undoBuffer: Record<string, undoBufferRecord>[],
currentFile: string,
workspace: string
workspace: string,
searching: string | null,
}
export const SearchingInitialState: SearchState = {
@ -85,5 +86,6 @@ export const SearchingInitialState: SearchState = {
clipped: false,
undoBuffer: null,
currentFile: '',
workspace: ''
workspace: '',
searching: null
}
Loading…
Cancel
Save