Skip to content

Commit

Permalink
Merge pull request #8 from kasperjunge/change-ignore-parser-to-pathspec
Browse files Browse the repository at this point in the history
Switched to pathspec
  • Loading branch information
kasperjunge authored Aug 20, 2024
2 parents 728d5fa + 6daa87a commit 4399b83
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 68 deletions.
70 changes: 36 additions & 34 deletions copcon/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import mimetypes
import platform
import sys
import fnmatch
import pathspec

app = typer.Typer()

Expand All @@ -23,7 +23,7 @@
".vs",
"bin",
"obj",
"publish"
"publish",
}

# Default files to ignore
Expand All @@ -35,55 +35,56 @@
"yarn.lock",
}

def parse_copconignore(ignore_file: Path) -> List[str]:

def parse_copconignore(ignore_file: Path) -> pathspec.PathSpec:
"""
Parses the ignore patterns from a given .copconignore file.
Parses the ignore patterns from a given .copconignore file using pathspec.
Args:
ignore_file (Path): Path to the .copconignore file.
Returns:
List[str]: A list of ignore patterns.
pathspec.PathSpec: A PathSpec object for pattern matching.
"""
if not ignore_file.exists():
return []
return pathspec.PathSpec.from_lines("gitwildmatch", [])

try:
with ignore_file.open() as f:
return [line.strip() for line in f if line.strip() and not line.startswith('#')]
patterns = [
line.strip() for line in f if line.strip() and not line.startswith("#")
]
return pathspec.PathSpec.from_lines("gitwildmatch", patterns)
except Exception as e:
typer.echo(f"Error reading ignore file: {e}", err=True)
return []
return pathspec.PathSpec.from_lines("gitwildmatch", [])


def should_ignore(path: Path, ignore_patterns: List[str]) -> bool:
def should_ignore(path: Path, ignore_spec: pathspec.PathSpec) -> bool:
"""
Determines if a given path should be ignored based on the ignore patterns.
Args:
path (Path): The path to check.
ignore_patterns (List[str]): List of ignore patterns.
ignore_spec (pathspec.PathSpec): PathSpec object with ignore patterns.
Returns:
bool: True if the path should be ignored, False otherwise.
"""
# Convert Path to string and add a trailing slash for directories
path_str = str(path)
for pattern in ignore_patterns:
if pattern.endswith('/'):
if path_str.endswith('/'):
if fnmatch.fnmatch(path_str, pattern):
return True
elif fnmatch.fnmatch(path_str + '/', pattern):
return True
elif fnmatch.fnmatch(path_str, pattern):
return True
return False
if path.is_dir():
path_str += "/"
return ignore_spec.match_file(path_str)


def generate_tree(
directory: Path,
prefix: str = "",
depth: int = -1,
ignore_dirs: Set[str] = DEFAULT_IGNORE_DIRS,
ignore_files: Set[str] = DEFAULT_IGNORE_FILES,
ignore_patterns: List[str] = [],
ignore_spec: pathspec.PathSpec = pathspec.PathSpec([]),
root: Optional[Path] = None,
) -> str:
if depth == 0:
Expand All @@ -104,14 +105,14 @@ def generate_tree(
continue
if path.is_file() and path.name in ignore_files:
continue
if should_ignore(relative_path, ignore_patterns):
if should_ignore(relative_path, ignore_spec):
continue
visible_contents.append(path)

for i, path in enumerate(visible_contents):
is_last = i == len(visible_contents) - 1
current_prefix = "└── " if is_last else "├── "

if path.is_dir():
subtree_prefix = " " if is_last else "│ "
subtree = generate_tree(
Expand All @@ -120,7 +121,7 @@ def generate_tree(
depth - 1 if depth > 0 else -1,
ignore_dirs,
ignore_files,
ignore_patterns,
ignore_spec,
root,
)
if subtree: # Only include non-empty directories
Expand All @@ -131,6 +132,7 @@ def generate_tree(

return "\n".join(output)


def get_file_content(file_path: Path) -> str:
try:
mime_type, _ = mimetypes.guess_type(str(file_path))
Expand Down Expand Up @@ -202,13 +204,13 @@ def main(
if ignore_files:
files_to_ignore.update(ignore_files)

ignore_patterns = []
ignore_spec = pathspec.PathSpec([])
if copconignore:
ignore_patterns = parse_copconignore(copconignore)
ignore_spec = parse_copconignore(copconignore)
else:
default_copconignore = directory / ".copconignore"
if default_copconignore.exists():
ignore_patterns = parse_copconignore(default_copconignore)
ignore_spec = parse_copconignore(default_copconignore)

output = []
output.append("Directory Structure:")
Expand All @@ -219,7 +221,7 @@ def main(
depth=depth,
ignore_dirs=dirs_to_ignore,
ignore_files=files_to_ignore,
ignore_patterns=ignore_patterns,
ignore_spec=ignore_spec,
)
)
output.append("\nFile Contents:")
Expand All @@ -235,9 +237,9 @@ def main(
continue
if file_path.name in files_to_ignore:
continue
if should_ignore(file_path, ignore_patterns):
continue
relative_path = file_path.relative_to(directory)
if should_ignore(relative_path, ignore_spec):
continue
output.append(f"\nFile: {relative_path}")
output.append("-" * 40)
output.append(get_file_content(file_path))
Expand Down
19 changes: 15 additions & 4 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "copcon"
version = "0.2.4"
version = "0.2.5"
description = ""
authors = ["Kasper Junge <kasperjuunge@gmail.com>"]
readme = "README.md"
Expand All @@ -9,6 +9,7 @@ readme = "README.md"
python = "^3.11"
typer = "^0.12.3"
pywin32 = {version = "^300", platform = "win32"}
pathspec = "^0.12.1"

[tool.poetry.scripts]
copcon = "copcon:copcon_app"
Expand Down
Loading

0 comments on commit 4399b83

Please sign in to comment.