Skip to content

Latest commit

 

History

History
213 lines (166 loc) · 8.48 KB

File metadata and controls

213 lines (166 loc) · 8.48 KB

Technical Design Document: Fix Ignore Pattern Handling in Tree Command

Problem Statement

When executing node bin/cx.js ignore show, the command correctly shows .md files in the exclusion patterns. However, when running node bin/cx.js -t, the .md files still appear in the tree output. This suggests a disconnect between how ignore patterns are stored and how they are applied during tree visualization.

Root Cause Analysis

After analyzing the codebase, we identified the following issues:

  1. The tree command (handleTree function in bin/cx.js) is correctly passing a special purpose parameter called 'ignore-aware-tree' to the dirTree function:

    const tree = dirTree(absolutePath, 10, isVerbose, false, null, 'ignore-aware-tree');
  2. In the ExclusionManager class, there is special handling for the 'ignore-aware-tree' purpose, but the extension pattern matching logic in the matchesUserDefinedPatterns method has issues with how it handles patterns like *.md.

  3. Specific areas requiring fixes:

    • The matchesUserDefinedPatterns method in lib/exclusionManager.js does not consistently handle extension patterns for all file types
    • The pattern matching logic for extension-based exclusions like *.md is not accurately applied when the purpose is 'ignore-aware-tree'

Proposed Solution

  1. Enhance the matchesUserDefinedPatterns method to properly handle extension patterns:

    • Improve the extension pattern matching logic to be more reliable with patterns like *.md
    • Ensure consistent handling across all file types
    • Add more robust debugging for pattern matching to make issues easier to identify
  2. Add specific tests for the 'ignore-aware-tree' purpose to ensure ignore patterns are respected:

    • Ensure extension patterns like *.md are correctly matched and applied
    • Verify that config-based patterns are given highest priority
  3. Update the pattern matching logic for user-defined patterns to be more strict for the 'ignore-aware-tree' purpose

Implementation Plan

Step 1: Fix Extension Pattern Matching

Update the matchesUserDefinedPatterns method in lib/exclusionManager.js to improve extension pattern handling:

matchesUserDefinedPatterns(filePath, purpose = 'content') {
  const normalizedPath = path.normalize(filePath);
  const relativePath = path.relative(this.basePath, normalizedPath);
  const fileName = path.basename(filePath);
  const fileExt = path.extname(filePath);
  
  // Log current patterns for debugging
  this.log(`Checking file ${filePath} against user patterns: ${[...this.configPatterns].join(', ')}`);
  this.log(`File details: name=${fileName}, ext=${fileExt}, relativePath=${relativePath}`);
  this.log(`Purpose: ${purpose}`);
  
  // Check if we have any patterns at all
  if (this.configPatterns.size === 0) {
    this.log(`No user patterns to check against for ${filePath}`);
    return false;
  }
  
  // For 'ignore-aware-tree' purpose, apply more strict pattern matching
  if (purpose === 'ignore-aware-tree') {
    // Improved direct extension matching
    if (fileExt && fileExt.length > 0) {
      const extWithoutDot = fileExt.substring(1); // Remove leading dot
      const extensionPattern = `*.${extWithoutDot}`;
      
      // Check if this extension pattern exists in configPatterns
      if (this.configPatterns.has(extensionPattern)) {
        this.log(`[ignore-aware-tree] Excluding ${filePath} due to extension pattern: ${extensionPattern}`);
        return true;
      }
      
      // Also check by iterating through patterns for case-insensitive matching
      for (const pattern of this.configPatterns) {
        if (pattern.toLowerCase() === extensionPattern.toLowerCase()) {
          this.log(`[ignore-aware-tree] Excluding ${filePath} due to case-insensitive extension pattern: ${pattern}`);
          return true;
        }
      }
    }
  }
  
  // Regular pattern matching (improved version)
  for (const pattern of this.configPatterns) {
    this.log(`Checking pattern: ${pattern} against file: ${filePath}`);
    
    // For extension patterns like *.md, use more robust matching
    if (pattern.startsWith('*.') && fileExt) {
      const patternExt = pattern.substring(1); // *.md -> .md
      if (fileExt.toLowerCase() === patternExt.toLowerCase()) {
        this.log(`Extension match found: ${fileExt} matches pattern ${pattern}`);
        return true;
      }
    }
    
    // Use minimatch for other pattern types
    const matchesRelative = minimatch(relativePath, pattern, { dot: true, matchBase: true });
    const matchesFileName = minimatch(fileName, pattern, { dot: true, matchBase: true });
    
    this.log(`Pattern ${pattern} match results: relativePath=${matchesRelative}, fileName=${matchesFileName}`);
    
    if (matchesRelative || matchesFileName) {
      this.log(`Excluding ${filePath} due to user pattern: ${pattern}`);
      return true;
    }
  }
  
  this.log(`No matches found for ${filePath} against user patterns`);
  return false;
}

Step 2: Ensure Consistent Purpose Handling

Update the shouldExcludeFile method to ensure proper purpose handling:

shouldExcludeFile(filepath, purpose = 'content') {
  if (!filepath) return true;
  
  const cacheKey = this.getCacheKey(`file:${purpose}`, filepath);
  if (this.cache.has(cacheKey)) {
    return this.cache.get(cacheKey);
  }
  
  // Extract file information
  const fileName = path.basename(filepath);
  const fileExt = path.extname(filepath);
  
  // First, check if it matches user-defined patterns (highest priority)
  // Pass purpose parameter to ensure correct context
  if (this.matchesUserDefinedPatterns(filepath, purpose)) {
    this.log(`Excluding file by user-defined pattern: ${filepath}`);
    this.cache.set(cacheKey, true);
    return true;
  }
  
  // Special handling for 'ignore-aware-tree' purpose
  if (purpose === 'ignore-aware-tree') {
    // For ignore-aware tree visualization, strictly respect all ignore patterns
    if (this.matchesGitignoreOrConfig(filepath)) {
      this.log(`Excluding file by gitignore/config for ignore-aware-tree: ${filepath}`);
      this.cache.set(cacheKey, true);
      return true;
    }
  }
  
  // Rest of the method remains unchanged...
}

Step 3: Add Diagnostic Logging

Add additional diagnostic logging to help identify pattern matching issues:

// Add to ExclusionManager class
dumpPatterns() {
  console.log('\nExclusion Patterns:');
  console.log('Config patterns:', [...this.configPatterns].join(', '));
  console.log('Gitignore patterns:', [...this.gitignorePatterns].join(', '));
  console.log('System patterns:', [...this.systemPatterns].join(', '));
  console.log('Negation patterns:', [...this.negationPatterns].join(', '));
  console.log('All patterns:', [...this.patterns].join(', '));
}

Then update the dirTree function in lib/directoryTree.js to use this diagnostic logging when in verbose mode:

function dirTree(filepath, maxDepth, verbose = false, debug = false, exclusionManager, purpose = 'tree') {
  setVerbose(verbose);

  try {
    // Create exclusion manager if not provided
    if (!exclusionManager) {
      exclusionManager = new ExclusionManager(filepath, verbose);
      if (verbose) {
        console.log("Created new exclusion manager");
        exclusionManager.dumpPatterns(); // Add diagnostic output
      }
    }
    
    // Rest of the function remains unchanged...
  }
}

Testing Plan

  1. Test with a .md file in the current directory:

    node bin/cx.js ignore show    # Verify *.md is in ignore patterns
    node bin/cx.js -t             # Verify .md files do not appear in tree
    node bin/cx.js -t -v          # Run with verbose to see diagnostic output
  2. Test with different file extensions:

    node bin/cx.js ignore add "*.json"
    node bin/cx.js -t             # Verify .json files do not appear in tree
    node bin/cx.js ignore clear   # Clear all patterns
    node bin/cx.js -t             # Verify .json files now appear in tree
  3. Test with specific filenames:

    node bin/cx.js ignore add "README.md"
    node bin/cx.js -t             # Verify README.md does not appear, but other .md files do

Conclusion

The issue appears to be in how extension patterns like *.md are handled in the matchesUserDefinedPatterns method of the ExclusionManager class. By improving the pattern matching logic and adding more robust handling for the 'ignore-aware-tree' purpose, we can ensure that ignore patterns are correctly respected in the tree command output.