parent
c9dd9fb9ab
commit
fe21ad5b64
@ -0,0 +1,26 @@ |
||||
import React, { useContext, useEffect } from 'react' |
||||
import { SearchContext } from '../context/context' |
||||
|
||||
export const Exclude = props => { |
||||
const { setExclude } = useContext(SearchContext) |
||||
const change = e => { |
||||
const timeOutId = setTimeout(() => setExclude(e.target.value), 500) |
||||
return () => clearTimeout(timeOutId) |
||||
} |
||||
|
||||
useEffect(() => { |
||||
setExclude('.git/**') |
||||
}, []) |
||||
|
||||
return ( |
||||
<> |
||||
<div className="find-part"> |
||||
<input |
||||
placeholder="Exclude ie .git/**" |
||||
className="form-control" |
||||
onChange={change} |
||||
></input> |
||||
</div> |
||||
</> |
||||
) |
||||
} |
@ -1,17 +1,40 @@ |
||||
import React, { useContext, useReducer } from "react" |
||||
import { SearchContext } from "../context/context" |
||||
import { SearchingInitialState, SearchReducer } from "../reducers/Reducer" |
||||
|
||||
|
||||
import React, { useContext } from 'react' |
||||
import { SearchContext } from '../context/context' |
||||
|
||||
export const Find = props => { |
||||
const { setFind } = useContext(SearchContext) |
||||
const change = e => { |
||||
const timeOutId = setTimeout(() => setFind(e.target.value), 500) |
||||
return () => clearTimeout(timeOutId) |
||||
} |
||||
|
||||
const { setFind } = useContext(SearchContext) |
||||
const change = (e) => { |
||||
setFind(e.target.value) |
||||
} |
||||
|
||||
return(<> |
||||
<input placeholder="Search" className="form-control" onChange={change}></input> |
||||
</>) |
||||
} |
||||
return ( |
||||
<> |
||||
<div className="find-part"> |
||||
<input |
||||
placeholder="Search" |
||||
className="form-control" |
||||
onChange={change} |
||||
></input> |
||||
{/* <div className="controls"> |
||||
<div |
||||
title="Match Case (⌥⌘C)" |
||||
className="monaco-custom-checkbox codicon codicon-case-sensitive" |
||||
role="checkbox" |
||||
aria-checked="false" |
||||
aria-label="Match Case (⌥⌘C)" |
||||
aria-disabled="false" |
||||
></div> |
||||
<div |
||||
title="Match Whole Word" |
||||
className="monaco-custom-checkbox codicon codicon-whole-word" |
||||
role="checkbox" |
||||
aria-checked="false" |
||||
aria-label="Match Whole Word" |
||||
aria-disabled="false" |
||||
></div> |
||||
</div> */} |
||||
</div> |
||||
</> |
||||
) |
||||
} |
||||
|
@ -0,0 +1,22 @@ |
||||
import React, { useContext } from 'react' |
||||
import { SearchContext } from '../context/context' |
||||
|
||||
export const Include = props => { |
||||
const { setInclude } = useContext(SearchContext) |
||||
const change = e => { |
||||
const timeOutId = setTimeout(() => setInclude(e.target.value), 500) |
||||
return () => clearTimeout(timeOutId) |
||||
} |
||||
|
||||
return ( |
||||
<> |
||||
<div className="find-part"> |
||||
<input |
||||
placeholder="Include ie contracts/**/*.sol" |
||||
className="form-control" |
||||
onChange={change} |
||||
></input> |
||||
</div> |
||||
</> |
||||
) |
||||
} |
@ -1,16 +1,22 @@ |
||||
import React, { useContext, useEffect } from 'react' |
||||
import React, { useContext } from 'react' |
||||
import { SearchContext } from '../context/context' |
||||
|
||||
export const Replace = props => { |
||||
const { state, setReplace } = useContext(SearchContext) |
||||
|
||||
const { setReplace } = useContext(SearchContext) |
||||
const change = e => { |
||||
setReplace(e.target.value) |
||||
const timeOutId = setTimeout(() => setReplace(e.target.value), 500) |
||||
return () => clearTimeout(timeOutId) |
||||
} |
||||
|
||||
return ( |
||||
<> |
||||
<input placeholder='Replace' className="form-control" onChange={change}></input> |
||||
<div className="find-part"> |
||||
<input |
||||
placeholder="Replace" |
||||
className="form-control" |
||||
onChange={change} |
||||
></input> |
||||
</div> |
||||
</> |
||||
) |
||||
} |
||||
|
@ -0,0 +1,20 @@ |
||||
import { ViewPlugin } from '@remixproject/engine-web' |
||||
import React, { useContext, useEffect } from 'react' |
||||
import { SearchContext } from '../../context/context' |
||||
import { SearchResult } from '../../reducers/Reducer' |
||||
|
||||
interface ResultItemProps { |
||||
file: SearchResult |
||||
} |
||||
|
||||
export const ResultFileName = (props: ResultItemProps) => { |
||||
return ( |
||||
<div className="input-group udapp_nameNbuts"> |
||||
<div className="udapp_titleText input-group-prepend"> |
||||
<span className="input-group-text udapp_spanTitleText"> |
||||
{props.file.filename} |
||||
</span> |
||||
</div> |
||||
</div> |
||||
) |
||||
} |
@ -0,0 +1,64 @@ |
||||
import { ViewPlugin } from '@remixproject/engine-web' |
||||
import React, { useContext, useEffect, useState } from 'react' |
||||
import { SearchContext } from '../../context/context' |
||||
import { SearchResult, SearchResultLine } from '../../reducers/Reducer' |
||||
import { ResultFileName } from './ResultFileName' |
||||
import { ResultSummary } from './ResultSummary' |
||||
|
||||
interface ResultItemProps { |
||||
file: SearchResult |
||||
} |
||||
|
||||
export const ResultItem = (props: ResultItemProps) => { |
||||
const { state, findText } = useContext(SearchContext) |
||||
|
||||
const [lines, setLines] = useState<SearchResultLine[]>([]) |
||||
const [toggleExpander, setToggleExpander] = useState<boolean>(false) |
||||
|
||||
useEffect(() => { |
||||
if (!props.file.searchComplete) { |
||||
// console.log('searching for: ' + props.file.filename)
|
||||
} |
||||
}, [props.file.searchComplete]) |
||||
|
||||
const toggleClass = () => { |
||||
setToggleExpander(!toggleExpander) |
||||
} |
||||
|
||||
useEffect(() => { |
||||
if (!props.file.searchComplete) { |
||||
findText(props.file.filename).then(res => { |
||||
setLines(res) |
||||
}) |
||||
} |
||||
}, [state.find]) |
||||
|
||||
return ( |
||||
<> |
||||
{lines && lines.length ? ( |
||||
<> |
||||
<div onClick={toggleClass} className="search_result_item_title"> |
||||
<button className="btn" > |
||||
<i |
||||
className={`fas ${ |
||||
toggleExpander ? 'fa-angle-right' : 'fa-angle-down' |
||||
}`}
|
||||
aria-hidden="true" |
||||
></i> |
||||
</button>{' '} |
||||
<ResultFileName file={props.file} /> |
||||
</div> |
||||
{!toggleExpander ? ( |
||||
<ul> |
||||
{lines.map((line, index) => ( |
||||
<ResultSummary key={index} searchResult={props.file} line={line} /> |
||||
))} |
||||
</ul> |
||||
) : null} |
||||
</> |
||||
) : ( |
||||
<></> |
||||
)} |
||||
</> |
||||
) |
||||
} |
@ -0,0 +1,34 @@ |
||||
import { ViewPlugin } from '@remixproject/engine-web' |
||||
import React, { useContext, useEffect, useState } from 'react' |
||||
import { SearchContext } from '../../context/context' |
||||
import { SearchResult, SearchResultLine, SearchResultLineLine } from '../../reducers/Reducer' |
||||
import { ResultFileName } from './ResultFileName' |
||||
|
||||
interface ResultSummaryProps { |
||||
searchResult: SearchResult |
||||
line: SearchResultLine |
||||
} |
||||
|
||||
export const ResultSummary = (props: ResultSummaryProps) => { |
||||
const { hightLightInPath } = useContext(SearchContext) |
||||
const selectLine = async (line: SearchResultLineLine) => { |
||||
console.log(line, props.searchResult) |
||||
await hightLightInPath(props.searchResult, line) |
||||
} |
||||
return ( |
||||
<li className="p-1 wrap_summary"> |
||||
{props.line.lines.map((lineItem, index) => ( |
||||
<div |
||||
onClick={async () => { |
||||
selectLine(lineItem) |
||||
}} |
||||
key={index} |
||||
> |
||||
{lineItem.left.substring(lineItem.left.length - 20)} |
||||
<mark>{lineItem.center}</mark> |
||||
{lineItem.right.substring(0, 100)} |
||||
</div> |
||||
))} |
||||
</li> |
||||
) |
||||
} |
@ -1,81 +1,98 @@ |
||||
import { ViewPlugin } from "@remixproject/engine-web" |
||||
import React, { useContext, useEffect } from "react" |
||||
import { SearchContext } from "../../context/context" |
||||
import { SearchResult } from "../../reducers/Reducer" |
||||
import { ViewPlugin } from '@remixproject/engine-web' |
||||
import { matches } from 'lodash' |
||||
import React, { useContext, useEffect, useState } from 'react' |
||||
import { SearchContext } from '../../context/context' |
||||
import { SearchResult, SearchResultLine } from '../../reducers/Reducer' |
||||
import { ResultItem } from './ResultItem' |
||||
import { findLinesInStringWithMatch } from './SearchHelper' |
||||
const filePathFilter = require('@jsdevtools/file-path-filter') |
||||
|
||||
interface ResultsProps { |
||||
plugin: ViewPlugin |
||||
plugin: ViewPlugin |
||||
} |
||||
|
||||
export const Results = (props: ResultsProps) => { |
||||
const { state, setSearchResults, setFind } = useContext(SearchContext) |
||||
const [ alertText, setAlertText ] = useState('') |
||||
const { plugin } = props |
||||
|
||||
const { state, setSearchResults } = useContext(SearchContext) |
||||
|
||||
const { plugin } = props |
||||
|
||||
const getDirectory = async (dir) => { |
||||
let result = [] |
||||
const files = await plugin.call('fileManager', 'readdir', dir) |
||||
const fileArray = normalize(files) |
||||
for (const fi of fileArray) { |
||||
if (fi) { |
||||
const type = fi.data.isDirectory |
||||
if (type === true) { |
||||
result = [ |
||||
...result, |
||||
...(await getDirectory( |
||||
`${fi.filename}` |
||||
)) |
||||
] |
||||
} else { |
||||
result = [...result, fi.filename] |
||||
} |
||||
} |
||||
const getDirectory = async (dir: string) => { |
||||
let result = [] |
||||
const files = await plugin.call('fileManager', 'readdir', dir) |
||||
const fileArray = normalize(files) |
||||
for (const fi of fileArray) { |
||||
if (fi) { |
||||
const type = fi.data.isDirectory |
||||
if (type === true) { |
||||
result = [...result, ...(await getDirectory(`${fi.filename}`))] |
||||
} else { |
||||
result = [...result, fi.filename] |
||||
} |
||||
return result |
||||
} |
||||
} |
||||
return result |
||||
} |
||||
|
||||
const normalize = (filesList) => { |
||||
const folders = [] |
||||
const files = [] |
||||
Object.keys(filesList || {}).forEach(key => { |
||||
if (filesList[key].isDirectory) { |
||||
folders.push({ |
||||
filename: key, |
||||
data: filesList[key] |
||||
}) |
||||
} else { |
||||
files.push({ |
||||
filename: key, |
||||
data: filesList[key] |
||||
}) |
||||
} |
||||
const normalize = filesList => { |
||||
const folders = [] |
||||
const files = [] |
||||
Object.keys(filesList || {}).forEach(key => { |
||||
if (filesList[key].isDirectory) { |
||||
folders.push({ |
||||
filename: key, |
||||
data: filesList[key] |
||||
}) |
||||
} else { |
||||
files.push({ |
||||
filename: key, |
||||
data: filesList[key] |
||||
}) |
||||
return [...folders, ...files] |
||||
} |
||||
}) |
||||
return [...folders, ...files] |
||||
} |
||||
|
||||
useEffect(() => { |
||||
if (state.find) { |
||||
getDirectory('/').then(res => { |
||||
const ob = res.map(file => {
|
||||
const r:SearchResult = { |
||||
filename: file, |
||||
lines: [], |
||||
path: file, |
||||
searchComplete: false |
||||
} |
||||
return r |
||||
}) |
||||
console.log(ob) |
||||
setSearchResults(ob) |
||||
}) |
||||
} |
||||
},[state.find]) |
||||
useEffect(() => { |
||||
plugin.on('filePanel', 'setWorkspace', () => { |
||||
setSearchResults(null) |
||||
}) |
||||
return () => plugin.off('filePanel', 'setWorkspace') |
||||
}, []) |
||||
|
||||
useEffect(() => { |
||||
if (state.find) { |
||||
(async () => { |
||||
const res = await getDirectory('/') |
||||
const pathFilter: any = {} |
||||
if (state.include) |
||||
pathFilter.include = state.include.split(',').map(i => i.trim()) |
||||
if (state.exclude) |
||||
pathFilter.exclude = state.exclude.split(',').map(i => i.trim()) |
||||
const ob = res.filter(filePathFilter(pathFilter)).map(file => { |
||||
const r: SearchResult = { |
||||
filename: file, |
||||
lines: [], |
||||
path: file, |
||||
searchComplete: false |
||||
} |
||||
return r |
||||
}) |
||||
setSearchResults(ob) |
||||
})() |
||||
} |
||||
}, [state.find, state.replace, state.include, state.exclude]) |
||||
|
||||
useEffect(() => { |
||||
console.log(state.searchResults) |
||||
},[state.searchResults]) |
||||
|
||||
return(<></>) |
||||
} |
||||
return ( |
||||
<> |
||||
{alertText ? ( |
||||
<div className="alert alert-success mt-1" role="alert"> |
||||
{alertText} |
||||
</div> |
||||
) : null} |
||||
{state.searchResults && |
||||
state.searchResults.map((result, index) => { |
||||
return <ResultItem key={index} file={result} /> |
||||
})} |
||||
</> |
||||
) |
||||
} |
||||
|
@ -0,0 +1,45 @@ |
||||
import { SearchResultLineLine } from "../../reducers/Reducer" |
||||
|
||||
export const findLinesInStringWithMatch = (str: string, re: RegExp) => { |
||||
return str |
||||
.split(/\r?\n/) |
||||
.map(function (line, i) { |
||||
const matchResult = matchesInString(line, re) |
||||
if (matchResult.length) { |
||||
return { |
||||
lines: splitLines(matchResult, i), |
||||
} |
||||
} |
||||
}) |
||||
.filter(Boolean) |
||||
} |
||||
|
||||
const matchesInString = (str: string, re: RegExp) => { |
||||
let a: RegExpExecArray |
||||
let results:RegExpExecArray[] = []; |
||||
while ((a = re.exec(str || '')) !== null) { |
||||
results.push(a); |
||||
} |
||||
return results |
||||
} |
||||
|
||||
const splitLines = (matchResult: RegExpExecArray[], lineNumber: number) => { |
||||
return matchResult.map((matchResultPart, i) => { |
||||
const result:SearchResultLineLine = { |
||||
left: matchResultPart.input.substring(0, matchResultPart.index), |
||||
right: matchResultPart.input.substring(matchResultPart.index + matchResultPart[0].length), |
||||
center: matchResultPart[0], |
||||
position : { |
||||
start: { |
||||
line: lineNumber, |
||||
column: matchResultPart.index, |
||||
}, |
||||
end: { |
||||
line: lineNumber, |
||||
column: matchResultPart.index + matchResultPart[0].length, |
||||
}, |
||||
}, |
||||
} |
||||
return result |
||||
}) |
||||
} |
@ -0,0 +1,26 @@ |
||||
.search_result_item_title { |
||||
display: flex; |
||||
-webkit-user-select: none; /* Safari */ |
||||
-moz-user-select: none; /* Firefox */ |
||||
-ms-user-select: none; /* IE10+/Edge */ |
||||
user-select: none; /* Standard */ |
||||
cursor: pointer; |
||||
} |
||||
|
||||
.wrap_summary { |
||||
overflow: hidden; |
||||
white-space: nowrap; |
||||
-webkit-user-select: none; /* Safari */ |
||||
-moz-user-select: none; /* Firefox */ |
||||
-ms-user-select: none; /* IE10+/Edge */ |
||||
user-select: none; /* Standard */ |
||||
cursor: pointer; |
||||
} |
||||
|
||||
.find-part { |
||||
display: flex; |
||||
} |
||||
|
||||
.controls { |
||||
display: flex; |
||||
} |
Loading…
Reference in new issue