summaryrefslogtreecommitdiff
path: root/internal/io/fs
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2025-06-17 10:42:51 +0300
committerPaul Buetow <paul@buetow.org>2025-06-17 10:42:51 +0300
commit069dff93d8c6c8f0ba28d9f6123fa9b1430c2f92 (patch)
treef85d97709bef1d1737011874944cf580b3213cd3 /internal/io/fs
parent0a53b4e3352532e17522461b338d469d85210056 (diff)
Fix environment variable consistency and implement grep context lines support
- Changed DTAIL_USE_CHANNELLESS to use 'yes' instead of 'true' for consistency - Added support for --before, --after, and --max context options in channelless GrepProcessor - Implemented before context buffering and after context counting - Fixed consecutive match handling to avoid duplicate before context output - Context lines implementation matches original channel-based behavior structure - Still debugging after context line count issue in TestDGrepContext1 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Diffstat (limited to 'internal/io/fs')
-rw-r--r--internal/io/fs/directprocessor.go120
1 files changed, 104 insertions, 16 deletions
diff --git a/internal/io/fs/directprocessor.go b/internal/io/fs/directprocessor.go
index 72d58f0..006934f 100644
--- a/internal/io/fs/directprocessor.go
+++ b/internal/io/fs/directprocessor.go
@@ -310,16 +310,39 @@ type GrepProcessor struct {
plain bool
noColor bool
hostname string
+
+ // Context handling
+ beforeContext int
+ afterContext int
+ maxCount int
+
+ // State for context processing
+ matchCount int
+ afterRemaining int
+ beforeBuffer [][]byte
+ beforeLineNums []int
}
// NewGrepProcessor creates a new grep processor
-func NewGrepProcessor(re regex.Regex, plain, noColor bool, hostname string) *GrepProcessor {
- return &GrepProcessor{
- regex: re,
- plain: plain,
- noColor: noColor,
- hostname: hostname,
+func NewGrepProcessor(re regex.Regex, plain, noColor bool, hostname string, beforeContext, afterContext, maxCount int) *GrepProcessor {
+ gp := &GrepProcessor{
+ regex: re,
+ plain: plain,
+ noColor: noColor,
+ hostname: hostname,
+ beforeContext: beforeContext,
+ afterContext: afterContext,
+ maxCount: maxCount,
+ matchCount: 0,
+ afterRemaining: 0,
}
+
+ if beforeContext > 0 {
+ gp.beforeBuffer = make([][]byte, 0, beforeContext)
+ gp.beforeLineNums = make([]int, 0, beforeContext)
+ }
+
+ return gp
}
func (gp *GrepProcessor) Initialize(ctx context.Context) error {
@@ -331,7 +354,42 @@ func (gp *GrepProcessor) Cleanup() error {
}
func (gp *GrepProcessor) ProcessLine(line []byte, lineNum int, filePath string, stats *stats, sourceID string) ([]byte, bool) {
- if !gp.regex.Match(line) {
+ isMatch := gp.regex.Match(line)
+
+ // Handle after context lines
+ if gp.afterRemaining > 0 {
+ gp.afterRemaining--
+ // Send this line as context even if it doesn't match
+ if stats != nil {
+ stats.updateLineMatched() // Count context lines as transmitted
+ }
+ return gp.formatLine(line, lineNum, filePath, stats, sourceID), true
+ }
+
+ // Handle lines that don't match the regex
+ if !isMatch {
+ // If we have before context, buffer this line
+ if gp.beforeContext > 0 {
+ // Make a copy of the line for buffering
+ lineCopy := make([]byte, len(line))
+ copy(lineCopy, line)
+
+ // Add to buffer, removing oldest if at capacity
+ if len(gp.beforeBuffer) >= gp.beforeContext {
+ gp.beforeBuffer = gp.beforeBuffer[1:]
+ gp.beforeLineNums = gp.beforeLineNums[1:]
+ }
+ gp.beforeBuffer = append(gp.beforeBuffer, lineCopy)
+ gp.beforeLineNums = append(gp.beforeLineNums, lineNum)
+ }
+ return nil, false
+ }
+
+ // Line matches the regex
+ gp.matchCount++
+
+ // Check if we've reached maxCount
+ if gp.maxCount > 0 && gp.matchCount > gp.maxCount {
return nil, false
}
@@ -340,12 +398,45 @@ func (gp *GrepProcessor) ProcessLine(line []byte, lineNum int, filePath string,
stats.updateLineMatched()
}
+ // Build result with before context, current line, and set up after context
+ var result []byte
+
+ // First, output any before context lines
+ if gp.beforeContext > 0 {
+ for i, beforeLine := range gp.beforeBuffer {
+ beforeLineNum := gp.beforeLineNums[i]
+ formatted := gp.formatLine(beforeLine, beforeLineNum, filePath, stats, sourceID)
+ result = append(result, formatted...)
+ }
+ // Clear the buffer since we've used it
+ gp.beforeBuffer = gp.beforeBuffer[:0]
+ gp.beforeLineNums = gp.beforeLineNums[:0]
+ }
+
+ // Add the matching line
+ formatted := gp.formatLine(line, lineNum, filePath, stats, sourceID)
+ result = append(result, formatted...)
+
+ // Set up after context (only if we're not already in after context mode)
+ if gp.afterContext > 0 && gp.afterRemaining == 0 {
+ gp.afterRemaining = gp.afterContext
+ }
+
+ return result, true
+}
+
+func (gp *GrepProcessor) Flush() []byte {
+ return nil
+}
+
+// formatLine formats a line for output (shared by matching lines and context lines)
+func (gp *GrepProcessor) formatLine(line []byte, lineNum int, filePath string, stats *stats, sourceID string) []byte {
// Format output to match existing behavior
if gp.plain {
result := make([]byte, len(line)+1)
copy(result, line)
result[len(line)] = '\n'
- return result, true
+ return result
}
// Format exactly like original basehandler.go for non-plain mode
@@ -371,11 +462,7 @@ func (gp *GrepProcessor) ProcessLine(line []byte, lineNum int, filePath string,
result = append(result, line...)
result = append(result, '\n')
- return result, true
-}
-
-func (gp *GrepProcessor) Flush() []byte {
- return nil
+ return result
}
// CatProcessor handles cat-style output
@@ -435,8 +522,8 @@ func (cp *CatProcessor) ProcessLine(line []byte, lineNum int, filePath string, s
transmittedPerc, protocol.FieldDelimiter, count, protocol.FieldDelimiter,
sourceID, protocol.FieldDelimiter, string(line))
- // Apply ANSI color formatting if not in noColor mode
- if !cp.noColor {
+ // Apply ANSI color formatting if not in plain mode and not noColor mode
+ if !cp.plain && !cp.noColor {
colorized := brush.Colorfy(protocolLine)
// Add color reset prefix for all lines except the first
@@ -467,7 +554,8 @@ func (cp *CatProcessor) ProcessLine(line []byte, lineNum int, filePath string, s
func (cp *CatProcessor) Flush() []byte {
// Add final color reset line to match original behavior (no trailing newline)
- if !cp.noColor {
+ // Only in non-plain mode with colors enabled
+ if !cp.plain && !cp.noColor {
return []byte("\x1b[39m\x1b[49m\x1b[49m\x1b[39m")
}
return nil