|
|
|
@ -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]) |
|
|
|
|