103103 TOption = enum
104104 optFind, optReplace, optPeg, optRegex, optRecursive, optConfirm, optStdin,
105105 optWord, optIgnoreCase, optIgnoreStyle, optVerbose, optFilenames,
106- optRex, optFollow, optLimitChars, optFit
106+ optRex, optFollow, optCount, optLimitChars, optFit
107107 TOptions = set [TOption ]
108108 TConfirmEnum = enum
109109 ceAbort, ceYes, ceAll, ceNo, ceNone
162162 options: TOptions = {optRegex}
163163 walkOpt {.threadvar .}: WalkOpt
164164 searchOpt {.threadvar .}: SearchOpt
165- justCount = false
166165 sortTime = false
167166 sortTimeOrder = SortOrder .Ascending
168167 useWriteStyled = true
171170 linesAfter = 0
172171 linesContext = 0
173172 newLine = false
174- gVar = (matches: 0 , errors: 0 , reallyReplace: false )
173+ gVar = (matches: 0 , errors: 0 , reallyReplace: true )
175174 # gVar - variables that can change during search/replace
176175 nWorkers = 0 # run in single thread by default
177176 searchRequestsChan: Channel [Trequest ]
@@ -474,7 +473,11 @@ proc printSubLinesBefore(filename: string, beforeMatch: string, lineBeg: int,
474473
475474proc getSubLinesAfter (buf: string , mi: MatchInfo ): string =
476475 let last = afterPattern (buf, mi.last+ 1 , 1 + linesAfter)
477- result = substr (buf, mi.last+ 1 , last)
476+ let skipByte = # workaround posix: suppress extra line at the end of file
477+ if (last == buf.len- 1 and buf.len >= 2 and
478+ buf[^ 1 ] == '\l ' and buf[^ 2 ] != '\c ' ): 1
479+ else : 0
480+ result = substr (buf, mi.last+ 1 , last - skipByte)
478481
479482proc printOverflow (filename: string , line: int , curCol: var Column ) =
480483 if curCol.overflowMatches > 0 :
@@ -494,11 +497,7 @@ proc printSubLinesAfter(filename: string, afterMatch: string, matchLineEnd: int,
494497 # complete the line after the match itself
495498 newLn (curCol)
496499 printOverflow (filename, matchLineEnd, curCol)
497- # let skipLine = # workaround posix line ending at the end of file
498- # if last == s.len-1 and s.len >= 2 and s[^1] == '\l' and s[^2] != '\c': 1
499- # else: 0 TODO:
500- let skipLine = 0
501- for i in 1 ..< sLines.len - skipLine:
500+ for i in 1 ..< sLines.len:
502501 lineHeader (filename, matchLineEnd + i, isMatch = false , curCol)
503502 sLines[i].printCropped (curCol, fromLeft = false )
504503 newLn (curCol)
@@ -552,6 +551,7 @@ proc printReplacement(filename: string, buf: string, mi: MatchInfo,
552551 printMatch (fileName, miFixLines, curCol)
553552 printSubLinesAfter (fileName, getSubLinesAfter (buf, miFixLines),
554553 miFixLines.lineEnd, curCol)
554+ if linesAfter + linesBefore >= 2 and not newLine: stdout.write (" \n " )
555555 stdout.flushFile ()
556556
557557proc replace1match (filename: string , buf: string , mi: MatchInfo , i: int ,
@@ -608,14 +608,14 @@ proc printOutput(filename: string, output: Output, curCol: var Column) =
608608 printSubLinesBefore (filename, output.pre, output.match.lineBeg,
609609 curCol, reserveChars (output.match))
610610 printMatch (filename, output.match, curCol)
611- # flush: TODO
612611 of BlockNextMatch :
613612 printBetweenMatches (filename, output.pre, output.match.lineBeg,
614613 curCol, reserveChars (output.match))
615614 printMatch (filename, output.match, curCol)
616615 of BlockEnd :
617616 printSubLinesAfter (filename, output.blockEnding, output.firstLine, curCol)
618- if linesAfter + linesBefore >= 2 and not newLine: stdout.write (" \n " )
617+ if linesAfter + linesBefore >= 2 and not newLine and
618+ optFilenames notin options: stdout.write (" \n " )
619619
620620iterator searchFile (pattern: Pattern ; filename: string ;
621621 buffer: string ): Output =
@@ -784,14 +784,14 @@ iterator processFile(searchOptC: SearchOptComp[Pattern], filename: string,
784784 var cnt = 0
785785 for output in searchFile (searchOptC.pattern, filename, buffer):
786786 found = true
787- if not justCount :
787+ if optCount notin options :
788788 yield output
789789 else :
790790 if output.kind in {BlockFirstMatch , BlockNextMatch }:
791791 inc (cnt)
792- if justCount and cnt > 0 :
792+ if optCount in options and cnt > 0 :
793793 yield Output (kind: JustCount , matches: cnt)
794- if yieldContents and found and not justCount :
794+ if yieldContents and found and optCount notin options :
795795 yield Output (kind: FileContents , buffer: buffer)
796796
797797proc hasRightFileName (path: string , walkOptC: WalkOptComp [Pattern ]): bool =
@@ -925,13 +925,18 @@ template processFileResult(pattern: Pattern; filename: string,
925925 showFilename
926926 if optReplace notin options:
927927 var curCol: Column
928+ var toFlush: bool
928929 for output in fileResult:
929930 updateCounters (output)
931+ toFlush = true
930932 if output.kind notin {Rejected , OpenError , JustCount } and not oneline:
931933 showFilename
932934 if output.kind == JustCount and oneline:
933935 printFile (filename & " :" )
934936 printOutput (filename, output, curCol)
937+ if nWorkers == 0 and output.kind in {BlockFirstMatch , BlockNextMatch }:
938+ stdout.flushFile () # flush immediately in single thread mode
939+ if toFlush: stdout.flushFile ()
935940 else :
936941 var buffer = " "
937942 var matches: FileResult
@@ -1116,7 +1121,7 @@ for kind, key, val in getopt():
11161121 of " only" : searchOpt.checkBin = biOnly
11171122 else : reportError (" unknown value for --bin" )
11181123 of " text" , " t" : searchOpt.checkBin = biNo
1119- of " count" : justCount = true
1124+ of " count" : incl (options, optCount)
11201125 of " sorttime" , " sort-time" , " s" :
11211126 sortTime = true
11221127 case normalize (val)
@@ -1172,6 +1177,7 @@ for kind, key, val in getopt():
11721177 of cmdEnd: assert (false ) # cannot happen
11731178
11741179checkOptions ({optFind, optReplace}, " find" , " replace" )
1180+ checkOptions ({optCount, optReplace}, " count" , " replace" )
11751181checkOptions ({optPeg, optRegex}, " peg" , " re" )
11761182checkOptions ({optIgnoreCase, optIgnoreStyle}, " ignore_case" , " ignore_style" )
11771183checkOptions ({optFilenames, optReplace}, " filenames" , " replace" )
0 commit comments