diff options
| author | Paul Buetow <paul@buetow.org> | 2025-06-17 10:42:51 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2025-06-17 10:42:51 +0300 |
| commit | 069dff93d8c6c8f0ba28d9f6123fa9b1430c2f92 (patch) | |
| tree | f85d97709bef1d1737011874944cf580b3213cd3 /internal/io/fs | |
| parent | 0a53b4e3352532e17522461b338d469d85210056 (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.go | 120 |
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 |
