@ -3,6 +3,7 @@ import { createContext, useReducer } from 'react'
import {
import {
findLinesInStringWithMatch ,
findLinesInStringWithMatch ,
getDirectory ,
getDirectory ,
replaceAllInFile ,
replaceTextInLine
replaceTextInLine
} from '../components/results/SearchHelper'
} from '../components/results/SearchHelper'
import { SearchReducer } from '../reducers/Reducer'
import { SearchReducer } from '../reducers/Reducer'
@ -11,7 +12,8 @@ import {
SearchResult ,
SearchResult ,
SearchResultLine ,
SearchResultLine ,
SearchResultLineLine ,
SearchResultLineLine ,
SearchingInitialState
SearchingInitialState ,
undoBufferRecord
} from '../types'
} from '../types'
import { filePathFilter } from '@jsdevtools/file-path-filter'
import { filePathFilter } from '@jsdevtools/file-path-filter'
import { escapeRegExp } from 'lodash'
import { escapeRegExp } from 'lodash'
@ -20,6 +22,7 @@ export interface SearchingStateInterface {
state : SearchState
state : SearchState
setFind : ( value : string ) = > void
setFind : ( value : string ) = > void
setReplace : ( value : string ) = > void
setReplace : ( value : string ) = > void
setReplaceEnabled : ( value : boolean ) = > void
setInclude : ( value : string ) = > void
setInclude : ( value : string ) = > void
setExclude : ( value : string ) = > void
setExclude : ( value : string ) = > void
setCaseSensitive : ( value : boolean ) = > void
setCaseSensitive : ( value : boolean ) = > void
@ -28,7 +31,7 @@ export interface SearchingStateInterface {
setSearchResults : ( value : SearchResult [ ] ) = > void
setSearchResults : ( value : SearchResult [ ] ) = > void
findText : ( path : string ) = > Promise < SearchResultLine [ ] >
findText : ( path : string ) = > Promise < SearchResultLine [ ] >
hightLightInPath : ( result : SearchResult , line : SearchResultLineLine ) = > void
hightLightInPath : ( result : SearchResult , line : SearchResultLineLine ) = > void
replaceText : ( result : SearchResult , line : SearchResultLineLine ) = > void
replaceText : ( result : SearchResult , line : SearchResultLineLine ) = > Promise < void >
reloadFile : ( file : string ) = > void
reloadFile : ( file : string ) = > void
toggleCaseSensitive : ( ) = > void
toggleCaseSensitive : ( ) = > void
toggleMatchWholeWord : ( ) = > void
toggleMatchWholeWord : ( ) = > void
@ -36,6 +39,9 @@ export interface SearchingStateInterface {
setReplaceWithoutConfirmation : ( value : boolean ) = > void
setReplaceWithoutConfirmation : ( value : boolean ) = > void
disableForceReload : ( file : string ) = > void
disableForceReload : ( file : string ) = > void
updateCount : ( count : number , file : string ) = > void
updateCount : ( count : number , file : string ) = > void
replaceAllInFile : ( result : SearchResult ) = > Promise < void >
undoReplace : ( buffer : undoBufferRecord ) = > Promise < void >
clearUndo : ( ) = > void
}
}
export const SearchContext = createContext < SearchingStateInterface > ( null )
export const SearchContext = createContext < SearchingStateInterface > ( null )
@ -63,6 +69,12 @@ export const SearchProvider = ({
payload : value
payload : value
} )
} )
} ,
} ,
setReplaceEnabled : ( value : boolean ) = > {
dispatch ( {
type : 'SET_REPLACE_ENABLED' ,
payload : value
} )
} ,
setInclude : ( value : string ) = > {
setInclude : ( value : string ) = > {
dispatch ( {
dispatch ( {
type : 'SET_INCLUDE' ,
type : 'SET_INCLUDE' ,
@ -135,6 +147,18 @@ export const SearchProvider = ({
payload : file
payload : file
} )
} )
} ,
} ,
setCurrentFile : ( file : string ) = > {
dispatch ( {
type : 'SET_CURRENT_FILE' ,
payload : file
} )
} ,
setCurrentWorkspace : ( workspace : any ) = > {
dispatch ( {
type : 'SET_CURRENT_WORKSPACE' ,
payload : workspace
} )
} ,
updateCount : ( count : number , file : string ) = > {
updateCount : ( count : number , file : string ) = > {
dispatch ( {
dispatch ( {
type : 'UPDATE_COUNT' ,
type : 'UPDATE_COUNT' ,
@ -144,15 +168,9 @@ export const SearchProvider = ({
findText : async ( path : string ) = > {
findText : async ( path : string ) = > {
if ( ! plugin ) return
if ( ! plugin ) return
try {
try {
if ( state . find . length < 3 ) return
if ( state . find . length < 1 ) return
const text = await plugin . call ( 'fileManager' , 'readFile' , path )
const text = await plugin . call ( 'fileManager' , 'readFile' , path )
let flags = 'g'
const result : SearchResultLine [ ] = findLinesInStringWithMatch ( text , createRegExFromFind ( ) )
let find = state . find
if ( ! state . casesensitive ) flags += 'i'
if ( ! state . useRegExp ) find = escapeRegExp ( find )
if ( state . matchWord ) find = ` \\ b ${ find } \\ b `
const re = new RegExp ( find , flags )
const result : SearchResultLine [ ] = findLinesInStringWithMatch ( text , re )
return result
return result
} catch ( e ) { }
} catch ( e ) { }
} ,
} ,
@ -162,6 +180,7 @@ export const SearchProvider = ({
) = > {
) = > {
await plugin . call ( 'editor' , 'discardHighlight' )
await plugin . call ( 'editor' , 'discardHighlight' )
await plugin . call ( 'editor' , 'highlight' , line . position , result . path )
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 )
} ,
} ,
replaceText : async ( result : SearchResult , line : SearchResultLineLine ) = > {
replaceText : async ( result : SearchResult , line : SearchResultLineLine ) = > {
try {
try {
@ -172,30 +191,99 @@ export const SearchProvider = ({
'readFile' ,
'readFile' ,
result . path
result . path
)
)
const replaced = replaceTextInLine ( content , line , state . replace )
await plugin . call (
await plugin . call (
'fileManager' ,
'fileManager' ,
'setFile' ,
'setFile' ,
result . path ,
result . path ,
replaceTextInLine ( content , line , state . replace )
replaced
)
)
setUndoState ( content , replaced , result . path )
} catch ( e ) {
} catch ( e ) {
throw new Error ( e )
throw new Error ( e )
}
}
} ,
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
)
setUndoState ( content , replaced , result . path )
} ,
setUndoEnabled : ( path :string , workspace : string , content : string ) = > {
dispatch ( {
type : 'SET_UNDO_ENABLED' ,
payload : {
path ,
workspace ,
content
}
} )
} ,
undoReplace : async ( buffer : undoBufferRecord ) = > {
const content = await plugin . call (
'fileManager' ,
'readFile' ,
buffer . path
)
if ( buffer . newContent !== content ) {
throw new Error ( 'Can not undo replace, file has been changed.' )
}
await plugin . call (
'fileManager' ,
'setFile' ,
buffer . path ,
buffer . oldContent
)
await plugin . call (
'fileManager' ,
'open' ,
buffer . path
)
} ,
clearUndo : ( ) = > {
dispatch ( {
type : 'CLEAR_UNDO' ,
payload : undefined
} )
}
}
}
}
const reloadStateForFile = async ( file : string ) = > {
const reloadStateForFile = async ( file : string ) = > {
await value . reloadFile ( file )
await value . reloadFile ( file )
}
}
useEffect ( ( ) = > {
useEffect ( ( ) = > {
plugin . on ( 'filePanel' , 'setWorkspace' , ( ) = > {
plugin . on ( 'filePanel' , 'setWorkspace' , async ( workspace ) = > {
value . setSearchResults ( null )
value . setSearchResults ( null )
value . clearUndo ( )
value . setCurrentWorkspace ( workspace . name )
} )
} )
plugin . on ( 'fileManager' , 'fileSaved' , async file = > {
plugin . on ( 'fileManager' , 'fileSaved' , async file = > {
await reloadStateForFile ( file )
await reloadStateForFile ( file )
await checkUndoState ( file )
} )
} )
plugin . on ( 'fileManager' , 'currentFileChanged' , async file = > {
value . setCurrentFile ( file )
await checkUndoState ( file )
} )
return ( ) = > {
return ( ) = > {
plugin . off ( 'fileManager' , 'fileChanged' )
plugin . off ( 'fileManager' , 'fileChanged' )
plugin . off ( 'filePanel' , 'setWorkspace' )
plugin . off ( 'filePanel' , 'setWorkspace' )
@ -214,6 +302,45 @@ export const SearchProvider = ({
return results
return results
}
}
const checkUndoState = async ( path : string ) = > {
if ( ! plugin ) return
try {
const content = await plugin . call (
'fileManager' ,
'readFile' ,
path
)
const workspace = await plugin . call ( 'filePanel' , 'getCurrentWorkspace' )
value . setUndoEnabled ( path , workspace . name , content )
} catch ( e ) {
console . log ( e )
}
}
const setUndoState = async ( oldContent : string , newContent : string , path : string ) = > {
const workspace = await plugin . call ( 'filePanel' , 'getCurrentWorkspace' )
const undo = {
oldContent ,
newContent ,
path ,
workspace : workspace.name
}
dispatch ( {
type : 'SET_UNDO' ,
payload : undo
} )
}
const createRegExFromFind = ( ) = > {
let flags = 'g'
let find = state . find
if ( ! state . casesensitive ) flags += 'i'
if ( ! state . useRegExp ) find = escapeRegExp ( find )
if ( state . matchWord ) find = ` \\ b ${ find } \\ b `
const re = new RegExp ( find , flags )
return re
}
useEffect ( ( ) = > {
useEffect ( ( ) = > {
if ( state . find ) {
if ( state . find ) {
( async ( ) = > {
( async ( ) = > {