iflow-mcp_kandrwmrtn-cplusplus_mcp 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,92 @@
1
+ """File discovery and filtering for C++ projects."""
2
+
3
+ import os
4
+ import sys
5
+ from pathlib import Path
6
+ from typing import List, Set
7
+
8
+
9
+ class FileScanner:
10
+ """Handles file discovery and filtering for C++ projects."""
11
+
12
+ # C++ file extensions
13
+ CPP_EXTENSIONS = {'.cpp', '.cc', '.cxx', '.c++', '.h', '.hpp', '.hxx', '.h++'}
14
+
15
+ # Directories to exclude (set by configuration)
16
+ EXCLUDE_DIRS = set()
17
+
18
+ # Directories that contain dependencies (set by configuration)
19
+ DEPENDENCY_DIRS = set()
20
+
21
+ def __init__(self, project_root: Path, include_dependencies: bool = False):
22
+ self.project_root = project_root
23
+ self.include_dependencies = include_dependencies
24
+
25
+ def should_skip_directory(self, dir_path: str) -> bool:
26
+ """Check if a directory should be skipped"""
27
+ # Only skip if this directory is directly under the project root
28
+ try:
29
+ rel_path = Path(dir_path).relative_to(self.project_root)
30
+ # If the relative path has no parent, it's a top-level directory
31
+ if len(rel_path.parts) == 1:
32
+ return rel_path.parts[0] in self.EXCLUDE_DIRS
33
+ except ValueError:
34
+ # Directory is outside project root
35
+ pass
36
+ return False
37
+
38
+ def should_skip_file(self, file_path: str) -> bool:
39
+ """Check if a file should be skipped during indexing"""
40
+ # Skip files outside project root (shouldn't happen, but safety check)
41
+ try:
42
+ rel_path = Path(file_path).relative_to(self.project_root)
43
+ except ValueError:
44
+ # File is outside project root
45
+ if not self.include_dependencies:
46
+ return True
47
+ else:
48
+ return False
49
+
50
+ # Check if file is in a top-level excluded directory
51
+ if len(rel_path.parts) > 0 and rel_path.parts[0] in self.EXCLUDE_DIRS:
52
+ return True
53
+
54
+ return False
55
+
56
+ def find_cpp_files(self) -> List[str]:
57
+ """Find all C++ files in the project"""
58
+ files = []
59
+
60
+ try:
61
+ for root, dirs, filenames in os.walk(self.project_root):
62
+ # Filter directories in-place to prevent walking into them
63
+ dirs[:] = [d for d in dirs if not self.should_skip_directory(os.path.join(root, d))]
64
+
65
+ for filename in filenames:
66
+ if any(filename.endswith(ext) for ext in self.CPP_EXTENSIONS):
67
+ file_path = os.path.join(root, filename)
68
+ if not self.should_skip_file(file_path):
69
+ files.append(file_path)
70
+ except Exception as e:
71
+ print(f"Error scanning directory: {e}", file=sys.stderr)
72
+
73
+ return files
74
+
75
+ def is_project_file(self, file_path: str) -> bool:
76
+ """Check if a file is part of the project (not a dependency)"""
77
+ if not file_path:
78
+ return False
79
+
80
+ # Check if file is under project root
81
+ try:
82
+ rel_path = Path(file_path).relative_to(self.project_root)
83
+
84
+ # Check if file is in a dependency directory (at any level)
85
+ for part in rel_path.parts:
86
+ if part in self.DEPENDENCY_DIRS:
87
+ return False
88
+
89
+ return True
90
+ except ValueError:
91
+ # File is outside project root - it's a dependency
92
+ return False
@@ -0,0 +1,131 @@
1
+ """Search functionality for C++ symbols."""
2
+
3
+ import re
4
+ from typing import Dict, List, Optional, Any
5
+ from collections import defaultdict
6
+ from .symbol_info import SymbolInfo
7
+
8
+
9
+ class SearchEngine:
10
+ """Handles searching for C++ symbols."""
11
+
12
+ def __init__(self, class_index: Dict[str, List[SymbolInfo]],
13
+ function_index: Dict[str, List[SymbolInfo]],
14
+ file_index: Dict[str, List[SymbolInfo]],
15
+ usr_index: Dict[str, SymbolInfo]):
16
+ self.class_index = class_index
17
+ self.function_index = function_index
18
+ self.file_index = file_index
19
+ self.usr_index = usr_index
20
+
21
+ def search_classes(self, pattern: str, project_only: bool = True) -> List[Dict[str, Any]]:
22
+ """Search for classes matching a pattern"""
23
+ results = []
24
+ regex = re.compile(pattern, re.IGNORECASE)
25
+
26
+ for name, infos in self.class_index.items():
27
+ if regex.search(name):
28
+ for info in infos:
29
+ if not project_only or info.is_project:
30
+ results.append({
31
+ "name": info.name,
32
+ "kind": info.kind,
33
+ "file": info.file,
34
+ "line": info.line,
35
+ "is_project": info.is_project,
36
+ "base_classes": info.base_classes
37
+ })
38
+
39
+ return results
40
+
41
+ def search_functions(self, pattern: str, project_only: bool = True,
42
+ class_name: Optional[str] = None) -> List[Dict[str, Any]]:
43
+ """Search for functions matching a pattern"""
44
+ results = []
45
+ regex = re.compile(pattern, re.IGNORECASE)
46
+
47
+ for name, infos in self.function_index.items():
48
+ if regex.search(name):
49
+ for info in infos:
50
+ if not project_only or info.is_project:
51
+ # Filter by class name if specified
52
+ if class_name and info.parent_class != class_name:
53
+ continue
54
+
55
+ results.append({
56
+ "name": info.name,
57
+ "kind": info.kind,
58
+ "file": info.file,
59
+ "line": info.line,
60
+ "signature": info.signature,
61
+ "is_project": info.is_project,
62
+ "parent_class": info.parent_class
63
+ })
64
+
65
+ return results
66
+
67
+ def search_symbols(self, pattern: str, project_only: bool = True,
68
+ symbol_types: Optional[List[str]] = None) -> Dict[str, List[Dict[str, Any]]]:
69
+ """Search for any symbols matching a pattern"""
70
+ results = {"classes": [], "functions": []}
71
+
72
+ # Filter symbol types
73
+ search_classes = not symbol_types or any(t in ["class", "struct"] for t in symbol_types)
74
+ search_functions = not symbol_types or any(t in ["function", "method"] for t in symbol_types)
75
+
76
+ if search_classes:
77
+ results["classes"] = self.search_classes(pattern, project_only)
78
+
79
+ if search_functions:
80
+ results["functions"] = self.search_functions(pattern, project_only)
81
+
82
+ return results
83
+
84
+ def get_symbols_in_file(self, file_path: str) -> List[SymbolInfo]:
85
+ """Get all symbols in a specific file"""
86
+ return self.file_index.get(file_path, [])
87
+
88
+ def get_class_info(self, class_name: str) -> Optional[Dict[str, Any]]:
89
+ """Get detailed information about a class"""
90
+ infos = self.class_index.get(class_name, [])
91
+ if not infos:
92
+ return None
93
+
94
+ # Return the first match (could be enhanced to handle multiple matches)
95
+ info = infos[0]
96
+
97
+ # Find all methods of this class
98
+ methods = []
99
+ for name, func_infos in self.function_index.items():
100
+ for func_info in func_infos:
101
+ if func_info.parent_class == class_name:
102
+ methods.append({
103
+ "name": func_info.name,
104
+ "signature": func_info.signature,
105
+ "access": func_info.access,
106
+ "line": func_info.line
107
+ })
108
+
109
+ return {
110
+ "name": info.name,
111
+ "kind": info.kind,
112
+ "file": info.file,
113
+ "line": info.line,
114
+ "base_classes": info.base_classes,
115
+ "methods": sorted(methods, key=lambda x: x["line"]),
116
+ "is_project": info.is_project
117
+ }
118
+
119
+ def get_function_signature(self, function_name: str,
120
+ class_name: Optional[str] = None) -> List[str]:
121
+ """Get function signatures matching the name"""
122
+ signatures = []
123
+
124
+ for info in self.function_index.get(function_name, []):
125
+ if class_name is None or info.parent_class == class_name:
126
+ if info.parent_class:
127
+ signatures.append(f"{info.parent_class}::{info.name}{info.signature}")
128
+ else:
129
+ signatures.append(f"{info.name}{info.signature}")
130
+
131
+ return signatures
@@ -0,0 +1,42 @@
1
+ """Symbol information data structure for C++ analysis."""
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import List
5
+
6
+
7
+ @dataclass
8
+ class SymbolInfo:
9
+ """Information about a C++ symbol (class, function, etc.)"""
10
+ name: str
11
+ kind: str # "class", "function", "method", etc.
12
+ file: str
13
+ line: int
14
+ column: int
15
+ signature: str = ""
16
+ is_project: bool = True
17
+ namespace: str = ""
18
+ access: str = "public" # public, private, protected
19
+ parent_class: str = "" # For methods, the containing class
20
+ base_classes: List[str] = field(default_factory=list) # For classes
21
+ usr: str = "" # Unified Symbol Resolution - unique identifier
22
+ calls: List[str] = field(default_factory=list) # USRs of functions this function calls
23
+ called_by: List[str] = field(default_factory=list) # USRs of functions that call this
24
+
25
+ def to_dict(self):
26
+ """Convert to dictionary for JSON serialization"""
27
+ return {
28
+ "name": self.name,
29
+ "kind": self.kind,
30
+ "file": self.file,
31
+ "line": self.line,
32
+ "column": self.column,
33
+ "signature": self.signature,
34
+ "is_project": self.is_project,
35
+ "namespace": self.namespace,
36
+ "access": self.access,
37
+ "parent_class": self.parent_class,
38
+ "base_classes": self.base_classes,
39
+ "usr": self.usr,
40
+ "calls": self.calls,
41
+ "called_by": self.called_by
42
+ }