all repos — fae @ 65c00df97b6ece6c0c856db56382a9995ef93e96

A minuscule find and edit utility.

Implemented support for case-insensitive matching, refactoring.
h3rald h3rald@h3rald.com
Sat, 12 Mar 2016 13:25:16 +0100
commit

65c00df97b6ece6c0c856db56382a9995ef93e96

parent

f4401b58c34bde70e35ef8154d1df923b06df571

1 files changed, 72 insertions(+), 57 deletions(-)

jump to
M far.nimfar.nim

@@ -20,6 +20,7 @@ substitute: string

directory: string apply: bool test: bool + flags: int system.addQuitProc(resetAttributes)

@@ -41,6 +42,7 @@ without asking for confirmation.

-d, --directory Search in the specified directory (default: .) -f, --filter Specify a regular expression to filter file paths. -h, --help Display this message. + -i, --ignore-case Case-insensitive matching. -r, --recursive Search directories recursively. -t, --test Do not perform substitutions, just print results. -v, --version Display the program version.

@@ -67,46 +69,49 @@ quit("Regex Error: Too many brackets", match)

else: result = [-1, match] -proc matchBounds(str, regex: string, start = 0): StringBounds = +proc `$`(sb: StringBounds): string = + return "($1,$2)" % [$sb[0], $sb[1]] + +proc matchBounds(str, regex: string, start = 0, flags = 0): StringBounds = var c = cast[ptr array[0..9,Capture]](alloc0(sizeof(array[0..9, Capture]))) var s = str.substr(start).cstring - let match = slre_match(("(" & regex & ")").cstring, s, s.len.cint, c, 10, 0) + let match = slre_match(("(" & regex & ")").cstring, s, s.len.cint, c, 10, flags.cint) if match >= 0: result = [match-c[0].len+start, match-1+start] else: result = match.handleRegexErrors() -proc matchCaptures(str, regex: string, start = 0): StringMatches = +proc matchCaptures(str, regex: string, start = 0, flags = 0): StringMatches = var c = cast[ptr array[0..9,Capture]](alloc0(sizeof(array[0..9, Capture]))) var s = str.substr(start).cstring - let match = slre_match(("(" & regex & ")").cstring, s, s.len.cint, c, 10, 0) + let match = slre_match(("(" & regex & ")").cstring, s, s.len.cint, c, 10, flags.cint) if match >= 0: result = StringMatches(start: match-c[0].len+start, finish: match-1+start, captures: c) else: result = StringMatches(start: match, finish: match, captures: c) -proc matchBoundsRec(str, regex: string, start = 0, matches: var seq[StringBounds]) = - let match = str.matchBounds(regex, start) +proc matchBoundsRec(str, regex: string, start = 0, matches: var seq[StringBounds], flags = 0) = + let match = str.matchBounds(regex, start, flags = flags) if match[0] >= 0: matches.add(match) - matchBoundsRec(str, regex, match[1]+1, matches) + matchBoundsRec(str, regex, match[1]+1, matches, flags = flags) -proc match(str, regex: string, case_insensitive = 0): bool = +proc match(str, regex: string, flags = 0): bool = var c = cast[ptr array[0..9,Capture]](alloc0(sizeof(array[0..9, Capture]))) - return slre_match(regex.cstring, str.cstring, str.len.cint, c, 10, case_insensitive.cint) >= 0 + return slre_match(regex.cstring, str.cstring, str.len.cint, c, 10, flags.cint) >= 0 proc rawReplace(str: var string, sub: string, start, finish: int) = str.delete(start, finish) str.insert(sub, start) -proc replace(str, regex: string, substitute: var string, start = 0): string = +proc replace(str, regex: string, substitute: var string, start = 0, flags = 0): string = var newstr = str - let match = str.matchCaptures(regex, start) + let match = str.matchCaptures(regex, start, flags = flags) if match.finish >= 0: for i in 1..9: # Substitute captures var submatches = newSeq[StringBounds](0) - substitute.matchBoundsRec("\\\\" & $i, 0, submatches) + substitute.matchBoundsRec("\\\\" & $i, 0, submatches, flags = flags) for submatch in submatches: var capture = match.captures[i] if capture.len > 0:

@@ -160,9 +165,53 @@ return false

else: return confirm(msg) +proc processFile(f:string, options: FarOptions): int = + var matchesN = 0 + var contents = "" + var contentsLen = 0 + var lineN = 0 + var fileLines = newSeq[string]() + var file: File + if not file.open(f): + raise newException(IOError, "Unable to open file '$1'" % f) + while file.readline(contents): + lineN.inc + contentsLen = contents.len + fileLines.add contents + var matches = newSeq[StringBounds](0) + matchBoundsRec(contents, options.regex, 0, matches, flags = options.flags) + if matches.len > 0: + matchesN = matchesN + matches.len + var offset = 0 + var matchstart, matchend: int + for match in matches: + matchstart = match[0] + offset + matchend = match[1] + offset + displayFile(f) + if options.substitute != nil: + displayMatch(contents, matchstart, matchend, fgRed, lineN) + var substitute = options.substitute + var replacement = contents.replace(options.regex, substitute, matchstart) + var new_offset = substitute.len-(matchend-matchstart+1) + for i in 0..(f.len+1): + stdout.write(" ") + displayMatch(replacement, matchstart, matchend+new_offset, fgYellow, lineN) + fileLines[fileLines.high] = replacement + else: + displayMatch(contents, match[0], match[1], fgYellow, lineN) + if (not options.test) and (options.substitute != nil) and (options.apply or confirm("Confirm replacement? [Y/n] ")): + f.writefile(fileLines.join("\n")) + #contents = f.readFile() + #offset = offset + new_offset + return matchesN + +## MAIN + ## Processing Options -var options = FarOptions(regex: nil, recursive: false, filter: nil, substitute: nil, directory: ".", apply: false, test: false) +var duration = cpuTime() + +var options = FarOptions(regex: nil, recursive: false, filter: nil, substitute: nil, directory: ".", apply: false, test: false, flags: 0) for kind, key, val in getOpt(): case kind:

@@ -191,6 +240,8 @@ quit(0)

of "version", "v": echo version quit(0) + of "ignore-case", "i": + options.flags = 1 else: discard else:

@@ -200,56 +251,17 @@ if options.regex == nil:

echo usage quit(0) -## MAIN +## Processing -var contents = "" -var contentsLen = 0 -var matches = newSeq[StringBounds](0) var count = 0 var matchesN = 0 -var duration = cpuTime() - -proc processFile(f:string) = - count.inc - contents = "" - var lineN = 0 - var file: File - if not file.open(f): - raise newException(IOError, "Unable to open file '$1'" % f) - while file.readline(contents): - lineN.inc - contentsLen = contents.len - matchBoundsRec(contents, options.regex, 0, matches) - if matches.len > 0: - var offset = 0 - var matchstart, matchend: int - for match in matches: - matchesN.inc - matchstart = match[0] + offset - matchend = match[1] + offset - displayFile(f) - if options.substitute != nil: - displayMatch(contents, matchstart, matchend, fgRed, lineN) - var substitute = options.substitute - var replacement = contents.replace(options.regex, substitute, matchstart) - var new_offset = substitute.len-(matchend-matchstart+1) - for i in 0..(f.len+1): - stdout.write(" ") - displayMatch(replacement, matchstart, matchend+new_offset, fgYellow, lineN) - if (not options.test) and (options.apply or confirm("Confirm replacement? [Y/n] ")): - f.writefile(replacement) - contents = f.readFile() - offset = offset + new_offset - else: - displayMatch(contents, match[0], match[1], fgYellow, lineN) - matches = newSeq[StringBounds](0) - if options.recursive: for f in walkDirRec(options.directory): if options.filter == nil or f.match(options.filter): try: - processFile(f) + count.inc + matchesN = matchesN + processFile(f, options) except: stderr.writeLine getCurrentExceptionMsg() continue

@@ -257,9 +269,12 @@ else:

for kind, f in walkDir(options.directory): if kind == pcFile and (options.filter == nil or f.match(options.filter)): try: - processFile(f) + count.inc + matchesN = matchesN + processFile(f, options) except: stderr.writeLine getCurrentExceptionMsg() continue -echo "=== ", count, " files processed - ", matchesN, " matches found (", (cpuTime()-duration).int, "s)." +echo "=== ", count, " files processed - ", matchesN, " matches found (", (cpuTime()-duration), " seconds)." + +