forked from CodeBoarding/CodeBoarding
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscanner.py
More file actions
106 lines (83 loc) · 3.6 KB
/
Copy pathscanner.py
File metadata and controls
106 lines (83 loc) · 3.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import json
import logging
import subprocess
from pathlib import Path
from typing import Set
from static_analyzer.programming_language import ProgrammingLanguage, ProgrammingLanguageBuilder
from telemetry.events import track_tech_stack
from utils import get_config
logger = logging.getLogger(__name__)
class ProjectScanner:
def __init__(self, repo_location: Path):
self.repo_location = repo_location
self.all_text_files: list[str] = []
def scan(self) -> list[ProgrammingLanguage]:
"""
Scan the repository using Tokei and return parsed results.
Also populates self.all_text_files with all text file paths found by Tokei.
Returns:
list[ProgrammingLanguage]: technologies with their sizes, percentages, and suffixes
"""
commands = get_config("tools")["tokei"]["command"]
result = subprocess.run(commands, cwd=self.repo_location, capture_output=True, text=True, check=True)
if not result.stdout:
stderr_msg = result.stderr.strip() if result.stderr else "no stderr output"
raise RuntimeError(
f"Tokei produced no output for repository '{self.repo_location}'. "
f"stderr: {stderr_msg}. "
f"This may indicate a tokei installation issue (e.g. Windows binary invoked inside WSL). "
f"Verify that 'tokei -o json' works in your terminal."
)
server_config = get_config("lsp_servers")
builder = ProgrammingLanguageBuilder(server_config)
# Parse Tokei JSON output
tokei_data = json.loads(result.stdout)
# Compute total code count
total_code = tokei_data.get("Total", {}).get("code", 0)
if not total_code:
logger.warning("No total code count found in Tokei output")
return []
programming_languages: list[ProgrammingLanguage] = []
all_files: list[str] = []
for technology, stats in tokei_data.items():
if technology == "Total":
continue
# Collect ALL text file paths from Tokei for file coverage,
# including languages with code_count == 0 (e.g. Markdown is 100% comments)
for report in stats.get("reports", []):
all_files.append(report["name"])
code_count = stats.get("code", 0)
if code_count == 0:
continue
percentage = code_count / total_code * 100
# Extract suffixes from reports
suffixes = set()
for report in stats.get("reports", []):
suffixes |= self._extract_suffixes([report["name"]])
pl = builder.build(
tokei_language=technology,
code_count=code_count,
percentage=percentage,
file_suffixes=suffixes,
)
logger.debug(f"Found: {pl}")
if pl.percentage >= 1:
programming_languages.append(pl)
self.all_text_files = all_files
track_tech_stack(self.repo_location, total_code, programming_languages)
return programming_languages
@staticmethod
def _extract_suffixes(files: list[str]) -> Set[str]:
"""
Extract unique file suffixes from a list of files.
Args:
files (list[str]): list of file paths
Returns:
Set[str]: Unique file extensions/suffixes
"""
suffixes = set()
for file_path in files:
suffix = Path(file_path).suffix
if suffix: # Only add non-empty suffixes
suffixes.add(suffix)
return suffixes