qzx 0.2__tar.gz

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.
Files changed (98) hide show
  1. qzx-0.2/Commands/DevCommands/AnalyzeComplexity.py +797 -0
  2. qzx-0.2/Commands/DevCommands/CompareFiles.py +228 -0
  3. qzx-0.2/Commands/DevCommands/CreateDocTemplatePython.py +781 -0
  4. qzx-0.2/Commands/DevCommands/__init__.py +8 -0
  5. qzx-0.2/Commands/FileCommands/ChangePermissions.py +196 -0
  6. qzx-0.2/Commands/FileCommands/CopyFile.py +149 -0
  7. qzx-0.2/Commands/FileCommands/CountLinesInFile.py +327 -0
  8. qzx-0.2/Commands/FileCommands/CreateDirectory.py +64 -0
  9. qzx-0.2/Commands/FileCommands/DeleteFile.py +238 -0
  10. qzx-0.2/Commands/FileCommands/DownloadFile.py +206 -0
  11. qzx-0.2/Commands/FileCommands/FindFiles.py +948 -0
  12. qzx-0.2/Commands/FileCommands/FindLargeFiles.py +325 -0
  13. qzx-0.2/Commands/FileCommands/FindText.py +521 -0
  14. qzx-0.2/Commands/FileCommands/GetHumanLanguageStatsFromFile.py +733 -0
  15. qzx-0.2/Commands/FileCommands/GetProgrammingLanguageStatsFromFile.py +1056 -0
  16. qzx-0.2/Commands/FileCommands/ListFiles.py +295 -0
  17. qzx-0.2/Commands/FileCommands/MoveFile.py +122 -0
  18. qzx-0.2/Commands/FileCommands/ReadFile.py +137 -0
  19. qzx-0.2/Commands/FileCommands/TouchFile.py +141 -0
  20. qzx-0.2/Commands/FileCommands/WonderFileTypeMagic.py +297 -0
  21. qzx-0.2/Commands/FileCommands/WonderIfDirectoryEmpty.py +112 -0
  22. qzx-0.2/Commands/FileCommands/WonderIfFileEmpty.py +142 -0
  23. qzx-0.2/Commands/FileCommands/WonderIfFileIsBinary.py +320 -0
  24. qzx-0.2/Commands/FileCommands/__init__.py +8 -0
  25. qzx-0.2/Commands/SystemCommands/CommandsBridge.py +442 -0
  26. qzx-0.2/Commands/SystemCommands/CurrentDir.py +117 -0
  27. qzx-0.2/Commands/SystemCommands/GetCPULoad.py +179 -0
  28. qzx-0.2/Commands/SystemCommands/GetCurrentDate.py +71 -0
  29. qzx-0.2/Commands/SystemCommands/GetCurrentTime.py +73 -0
  30. qzx-0.2/Commands/SystemCommands/GetCurrentUser.py +147 -0
  31. qzx-0.2/Commands/SystemCommands/GetDiskInfo.py +183 -0
  32. qzx-0.2/Commands/SystemCommands/GetDiskName.py +305 -0
  33. qzx-0.2/Commands/SystemCommands/GetGPULoad.py +430 -0
  34. qzx-0.2/Commands/SystemCommands/GetRAMInfo.py +176 -0
  35. qzx-0.2/Commands/SystemCommands/GetSmartValues.py +153 -0
  36. qzx-0.2/Commands/SystemCommands/KillProcess.py +190 -0
  37. qzx-0.2/Commands/SystemCommands/ListProcesses.py +252 -0
  38. qzx-0.2/Commands/SystemCommands/QZXVersion.py +108 -0
  39. qzx-0.2/Commands/SystemCommands/RunScript.py +158 -0
  40. qzx-0.2/Commands/SystemCommands/Shell.py +50 -0
  41. qzx-0.2/Commands/SystemCommands/SystemInfo.py +158 -0
  42. qzx-0.2/Commands/SystemCommands/Term.py +58 -0
  43. qzx-0.2/Commands/SystemCommands/Terminal.py +414 -0
  44. qzx-0.2/Commands/SystemCommands/TerminalWelcome.py +321 -0
  45. qzx-0.2/Commands/SystemCommands/Welcome.py +103 -0
  46. qzx-0.2/Commands/SystemCommands/WonderCommandsAmount.py +116 -0
  47. qzx-0.2/Commands/SystemCommands/WonderIfAdmin.py +243 -0
  48. qzx-0.2/Commands/SystemCommands/__init__.py +8 -0
  49. qzx-0.2/Commands/__init__.py +8 -0
  50. qzx-0.2/Core/__init__.py +11 -0
  51. qzx-0.2/Core/command_base.py +144 -0
  52. qzx-0.2/Core/command_loader.py +258 -0
  53. qzx-0.2/Core/qzx_core.py +114 -0
  54. qzx-0.2/Documentation/Documentation-BuildingGreatCommands.md +506 -0
  55. qzx-0.2/Documentation/Documentation-CheckList-VerboseIsGold.csv +44 -0
  56. qzx-0.2/Documentation/Documentation-Commands.md +1253 -0
  57. qzx-0.2/Documentation/Documentation-JSON-Checklist.CSV +102 -0
  58. qzx-0.2/Documentation/Documentation-Philosophy.md +286 -0
  59. qzx-0.2/Documentation/Documentation-WildcardsAndRecursiveCommands-Support.csv +8 -0
  60. qzx-0.2/Documentation/PodmanTestChecklist.csv +10 -0
  61. qzx-0.2/MANIFEST.in +8 -0
  62. qzx-0.2/PKG-INFO +125 -0
  63. qzx-0.2/README-English.md +210 -0
  64. qzx-0.2/README.md +96 -0
  65. qzx-0.2/Resources/FunctionWords/cantonese.json +25 -0
  66. qzx-0.2/Resources/FunctionWords/english.json +18 -0
  67. qzx-0.2/Resources/FunctionWords/french.json +21 -0
  68. qzx-0.2/Resources/FunctionWords/german.json +21 -0
  69. qzx-0.2/Resources/FunctionWords/italian.json +24 -0
  70. qzx-0.2/Resources/FunctionWords/mandarin.json +25 -0
  71. qzx-0.2/Resources/FunctionWords/portuguese.json +20 -0
  72. qzx-0.2/Resources/FunctionWords/spanish.json +20 -0
  73. qzx-0.2/Resources/ProgrammingLanguages/c.json +53 -0
  74. qzx-0.2/Resources/ProgrammingLanguages/cpp.json +64 -0
  75. qzx-0.2/Resources/ProgrammingLanguages/csharp.json +72 -0
  76. qzx-0.2/Resources/ProgrammingLanguages/css.json +106 -0
  77. qzx-0.2/Resources/ProgrammingLanguages/html.json +79 -0
  78. qzx-0.2/Resources/ProgrammingLanguages/java.json +113 -0
  79. qzx-0.2/Resources/ProgrammingLanguages/javascript.json +62 -0
  80. qzx-0.2/Resources/ProgrammingLanguages/kotlin.json +108 -0
  81. qzx-0.2/Resources/ProgrammingLanguages/python.json +59 -0
  82. qzx-0.2/Resources/ProgrammingLanguages/rust.json +92 -0
  83. qzx-0.2/Resources/ProgrammingLanguages/typescript.json +89 -0
  84. qzx-0.2/pyproject.toml +12 -0
  85. qzx-0.2/qzx.bat +17 -0
  86. qzx-0.2/qzx.egg-info/PKG-INFO +125 -0
  87. qzx-0.2/qzx.egg-info/SOURCES.txt +96 -0
  88. qzx-0.2/qzx.egg-info/dependency_links.txt +1 -0
  89. qzx-0.2/qzx.egg-info/entry_points.txt +2 -0
  90. qzx-0.2/qzx.egg-info/requires.txt +8 -0
  91. qzx-0.2/qzx.egg-info/top_level.txt +3 -0
  92. qzx-0.2/qzx.py +333 -0
  93. qzx-0.2/qzx.sh +20 -0
  94. qzx-0.2/setup.cfg +4 -0
  95. qzx-0.2/setup.py +50 -0
  96. qzx-0.2/tests/test_code_with_comments.py +118 -0
  97. qzx-0.2/tests/test_terminal.py +20 -0
  98. qzx-0.2/tests/test_undocumented.py +163 -0
@@ -0,0 +1,797 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ AnalyzeComplexity Command - Analyzes code complexity for development environments
6
+ """
7
+
8
+ import os
9
+ import re
10
+ import math
11
+ import ast
12
+ from Core.command_base import CommandBase
13
+
14
+ class AnalyzeComplexityCommand(CommandBase):
15
+ """
16
+ Command to analyze code complexity metrics for development environments
17
+ """
18
+
19
+ name = "analyzeComplexity"
20
+ description = "Analyzes code complexity metrics for files or projects"
21
+ category = "dev"
22
+
23
+ parameters = [
24
+ {
25
+ 'name': 'file_path',
26
+ 'description': 'Path to the file or directory to analyze',
27
+ 'required': True
28
+ },
29
+ {
30
+ 'name': 'recursive',
31
+ 'description': 'Whether to recursively analyze directories',
32
+ 'required': False,
33
+ 'default': False
34
+ },
35
+ {
36
+ 'name': 'format',
37
+ 'description': 'Output format: "detailed" or "summary"',
38
+ 'required': False,
39
+ 'default': 'detailed'
40
+ }
41
+ ]
42
+
43
+ examples = [
44
+ {
45
+ 'command': 'qzx analyzeComplexity "main.py"',
46
+ 'description': 'Analyze complexity of a single Python file'
47
+ },
48
+ {
49
+ 'command': 'qzx analyzeComplexity "src/components" True',
50
+ 'description': 'Recursively analyze all code files in a directory'
51
+ },
52
+ {
53
+ 'command': 'qzx analyzeComplexity "project/" True "summary"',
54
+ 'description': 'Generate a summary report for an entire project'
55
+ }
56
+ ]
57
+
58
+ # File extensions to analyze
59
+ SUPPORTED_EXTENSIONS = {
60
+ '.py': 'python',
61
+ '.js': 'javascript',
62
+ '.jsx': 'javascript',
63
+ '.ts': 'typescript',
64
+ '.tsx': 'typescript',
65
+ '.java': 'java',
66
+ '.c': 'c',
67
+ '.cpp': 'cpp',
68
+ '.h': 'c',
69
+ '.hpp': 'cpp',
70
+ '.cs': 'csharp',
71
+ '.php': 'php',
72
+ '.rb': 'ruby',
73
+ '.go': 'go',
74
+ '.rs': 'rust'
75
+ }
76
+
77
+ def execute(self, file_path, recursive=False, format='detailed'):
78
+ """
79
+ Analyzes code complexity of files or directories
80
+
81
+ Args:
82
+ file_path: Path to file or directory to analyze
83
+ recursive: Whether to recursively analyze directories
84
+ format: Output format (detailed or summary)
85
+
86
+ Returns:
87
+ Analysis results as string
88
+ """
89
+ # Check if path exists
90
+ if not os.path.exists(file_path):
91
+ return f"Error: Path '{file_path}' does not exist"
92
+
93
+ results = []
94
+ total_files = 0
95
+ analyzed_files = 0
96
+
97
+ # Process a single file
98
+ if os.path.isfile(file_path):
99
+ analysis = self._analyze_file(file_path)
100
+ if analysis:
101
+ analyzed_files += 1
102
+ results.append(analysis)
103
+ total_files = 1
104
+
105
+ # Process a directory
106
+ elif os.path.isdir(file_path):
107
+ for root, dirs, files in os.walk(file_path):
108
+ if not recursive and root != file_path:
109
+ continue
110
+
111
+ for file in files:
112
+ total_files += 1
113
+ full_path = os.path.join(root, file)
114
+ analysis = self._analyze_file(full_path)
115
+ if analysis:
116
+ analyzed_files += 1
117
+ results.append(analysis)
118
+
119
+ # Format and return results
120
+ if not results:
121
+ return f"No analyzable files found in '{file_path}'"
122
+
123
+ return self._format_results(results, total_files, analyzed_files, format)
124
+
125
+ def _analyze_file(self, file_path):
126
+ """
127
+ Analyzes a single file for complexity metrics
128
+
129
+ Args:
130
+ file_path: Path to the file to analyze
131
+
132
+ Returns:
133
+ Dict with analysis results or None if file can't be analyzed
134
+ """
135
+ # Check if file type is supported
136
+ _, ext = os.path.splitext(file_path)
137
+ if ext.lower() not in self.SUPPORTED_EXTENSIONS:
138
+ return None
139
+
140
+ language = self.SUPPORTED_EXTENSIONS[ext.lower()]
141
+
142
+ try:
143
+ # Read file content
144
+ with open(file_path, 'r', encoding='utf-8', errors='replace') as f:
145
+ content = f.read()
146
+
147
+ # Get basic metrics
148
+ metrics = {
149
+ 'file_path': file_path,
150
+ 'language': language,
151
+ 'size_bytes': os.path.getsize(file_path),
152
+ 'line_count': content.count('\n') + 1,
153
+ 'character_count': len(content)
154
+ }
155
+
156
+ # Get language-specific metrics
157
+ metrics.update(self._get_language_metrics(content, language))
158
+
159
+ # Calculate complexity score
160
+ metrics['complexity_score'] = self._calculate_complexity_score(metrics)
161
+
162
+ return metrics
163
+ except Exception as e:
164
+ return {
165
+ 'file_path': file_path,
166
+ 'language': language,
167
+ 'error': str(e)
168
+ }
169
+
170
+ def _get_language_metrics(self, content, language):
171
+ """
172
+ Gets language-specific metrics for code
173
+
174
+ Args:
175
+ content: File content
176
+ language: Programming language
177
+
178
+ Returns:
179
+ Dict with language-specific metrics
180
+ """
181
+ metrics = {
182
+ 'comment_count': 0,
183
+ 'function_count': 0,
184
+ 'class_count': 0,
185
+ 'condition_count': 0,
186
+ 'loop_count': 0,
187
+ 'avg_line_length': 0,
188
+ 'max_line_length': 0,
189
+ 'cyclomatic_complexity': 1, # Base complexity
190
+ 'halstead_metrics': {},
191
+ 'maintainability_index': 0
192
+ }
193
+
194
+ # Count non-empty lines
195
+ lines = [line for line in content.split('\n') if line.strip()]
196
+ metrics['non_empty_lines'] = len(lines)
197
+
198
+ if not lines:
199
+ return metrics
200
+
201
+ # Calculate line length metrics
202
+ line_lengths = [len(line) for line in lines]
203
+ metrics['avg_line_length'] = sum(line_lengths) / len(line_lengths)
204
+ metrics['max_line_length'] = max(line_lengths)
205
+
206
+ # Python-specific analysis
207
+ if language == 'python':
208
+ return self._analyze_python(content, metrics)
209
+
210
+ # JavaScript/TypeScript analysis
211
+ elif language in ['javascript', 'typescript']:
212
+ return self._analyze_js_ts(content, metrics)
213
+
214
+ # Generic analysis for other languages
215
+ else:
216
+ return self._analyze_generic(content, metrics, language)
217
+
218
+ def _analyze_python(self, content, metrics):
219
+ """
220
+ Analyzes Python code metrics
221
+
222
+ Args:
223
+ content: File content
224
+ metrics: Metrics dictionary to update
225
+
226
+ Returns:
227
+ Updated metrics dictionary
228
+ """
229
+ try:
230
+ # Parse Python code
231
+ tree = ast.parse(content)
232
+
233
+ # Count functions and classes
234
+ for node in ast.walk(tree):
235
+ if isinstance(node, ast.FunctionDef) or isinstance(node, ast.AsyncFunctionDef):
236
+ metrics['function_count'] += 1
237
+
238
+ # Count conditions and loops within function
239
+ for child in ast.walk(node):
240
+ if isinstance(child, (ast.If, ast.IfExp)):
241
+ metrics['condition_count'] += 1
242
+ metrics['cyclomatic_complexity'] += 1
243
+ elif isinstance(child, (ast.For, ast.AsyncFor, ast.While)):
244
+ metrics['loop_count'] += 1
245
+ metrics['cyclomatic_complexity'] += 1
246
+ elif isinstance(child, ast.BoolOp):
247
+ # Each boolean operator (and, or) adds complexity
248
+ metrics['cyclomatic_complexity'] += len(child.values) - 1
249
+
250
+ elif isinstance(node, ast.ClassDef):
251
+ metrics['class_count'] += 1
252
+
253
+ # Count comments (approximate)
254
+ comment_pattern = r'^\s*#.*$'
255
+ metrics['comment_count'] = len(re.findall(comment_pattern, content, re.MULTILINE))
256
+
257
+ # Calculate Halstead metrics
258
+ metrics['halstead_metrics'] = self._calculate_halstead_metrics(content, 'python')
259
+
260
+ # Calculate maintainability index
261
+ metrics['maintainability_index'] = self._calculate_maintainability_index(
262
+ metrics['cyclomatic_complexity'],
263
+ metrics['halstead_metrics'].get('volume', 0),
264
+ metrics['line_count']
265
+ )
266
+
267
+ return metrics
268
+ except SyntaxError:
269
+ # If unable to parse, fall back to generic analysis
270
+ return self._analyze_generic(content, metrics, 'python')
271
+
272
+ def _analyze_js_ts(self, content, metrics):
273
+ """
274
+ Analyzes JavaScript/TypeScript code metrics
275
+
276
+ Args:
277
+ content: File content
278
+ metrics: Metrics dictionary to update
279
+
280
+ Returns:
281
+ Updated metrics dictionary
282
+ """
283
+ # Count functions
284
+ function_patterns = [
285
+ r'function\s+\w+\s*\(', # Named functions
286
+ r'const\s+\w+\s*=\s*function', # Function expressions
287
+ r'const\s+\w+\s*=\s*\([^)]*\)\s*=>', # Arrow functions
288
+ r'\w+\s*:\s*function', # Object methods
289
+ ]
290
+ for pattern in function_patterns:
291
+ metrics['function_count'] += len(re.findall(pattern, content))
292
+
293
+ # Count classes
294
+ class_pattern = r'class\s+\w+'
295
+ metrics['class_count'] = len(re.findall(class_pattern, content))
296
+
297
+ # Count conditionals
298
+ condition_patterns = [r'\bif\s*\(', r'\bswitch\s*\(', r'\?']
299
+ for pattern in condition_patterns:
300
+ count = len(re.findall(pattern, content))
301
+ metrics['condition_count'] += count
302
+ metrics['cyclomatic_complexity'] += count
303
+
304
+ # Count loops
305
+ loop_patterns = [r'\bfor\s*\(', r'\bwhile\s*\(', r'\bdo\s*\{']
306
+ for pattern in loop_patterns:
307
+ count = len(re.findall(pattern, content))
308
+ metrics['loop_count'] += count
309
+ metrics['cyclomatic_complexity'] += count
310
+
311
+ # Count boolean operators
312
+ bool_op_count = len(re.findall(r'&&|\|\|', content))
313
+ metrics['cyclomatic_complexity'] += bool_op_count
314
+
315
+ # Count comments
316
+ comment_patterns = [r'\/\/.*$', r'\/\*[\s\S]*?\*\/']
317
+ for pattern in comment_patterns:
318
+ metrics['comment_count'] += len(re.findall(pattern, content, re.MULTILINE))
319
+
320
+ # Calculate Halstead metrics
321
+ metrics['halstead_metrics'] = self._calculate_halstead_metrics(content, 'javascript')
322
+
323
+ # Calculate maintainability index
324
+ metrics['maintainability_index'] = self._calculate_maintainability_index(
325
+ metrics['cyclomatic_complexity'],
326
+ metrics['halstead_metrics'].get('volume', 0),
327
+ metrics['line_count']
328
+ )
329
+
330
+ return metrics
331
+
332
+ def _analyze_generic(self, content, metrics, language):
333
+ """
334
+ Performs generic code analysis for unsupported languages
335
+
336
+ Args:
337
+ content: File content
338
+ metrics: Metrics dictionary to update
339
+ language: Programming language
340
+
341
+ Returns:
342
+ Updated metrics dictionary
343
+ """
344
+ # Generic patterns for various languages
345
+ patterns = {
346
+ 'function': {
347
+ 'python': r'def\s+\w+\s*\(',
348
+ 'javascript': r'function\s+\w+|const\s+\w+\s*=\s*function|\w+\s*:\s*function|\([^)]*\)\s*=>',
349
+ 'typescript': r'function\s+\w+|const\s+\w+\s*=\s*function|\w+\s*:\s*function|\([^)]*\)\s*=>',
350
+ 'java': r'(public|private|protected|static|\s) +[\w\<\>\[\]]+\s+(\w+) *\([^\)]*\)',
351
+ 'c': r'\w+\s+\w+\s*\([^;]*\)\s*\{',
352
+ 'cpp': r'[\w\<\>\[\]]+\s+\w+\s*\([^;]*\)\s*\{',
353
+ 'csharp': r'(public|private|protected|static|\s) +[\w\<\>\[\]]+\s+(\w+) *\([^\)]*\)',
354
+ 'php': r'function\s+\w+\s*\(',
355
+ 'ruby': r'def\s+\w+',
356
+ 'go': r'func\s+\w+',
357
+ 'rust': r'fn\s+\w+'
358
+ },
359
+ 'class': {
360
+ 'python': r'class\s+\w+',
361
+ 'javascript': r'class\s+\w+',
362
+ 'typescript': r'class\s+\w+|interface\s+\w+',
363
+ 'java': r'class\s+\w+|interface\s+\w+',
364
+ 'cpp': r'class\s+\w+',
365
+ 'csharp': r'class\s+\w+|interface\s+\w+',
366
+ 'php': r'class\s+\w+',
367
+ 'ruby': r'class\s+\w+',
368
+ 'rust': r'struct\s+\w+|enum\s+\w+|trait\s+\w+'
369
+ },
370
+ 'condition': {
371
+ 'generic': r'\bif\s*\(|\bswitch\s*\(|\?|else\s+if'
372
+ },
373
+ 'loop': {
374
+ 'generic': r'\bfor\s*\(|\bwhile\s*\(|\bdo\s*\{|\bforeach'
375
+ },
376
+ 'comment': {
377
+ 'python': r'#.*$',
378
+ 'c_style': r'\/\/.*$|\/\*[\s\S]*?\*\/',
379
+ 'ruby': r'#.*$',
380
+ }
381
+ }
382
+
383
+ # Count functions
384
+ func_pattern = patterns['function'].get(language, r'\b\w+\s+\w+\s*\(')
385
+ metrics['function_count'] = len(re.findall(func_pattern, content, re.MULTILINE))
386
+
387
+ # Count classes
388
+ class_pattern = patterns['class'].get(language, r'class\s+\w+')
389
+ metrics['class_count'] = len(re.findall(class_pattern, content, re.MULTILINE))
390
+
391
+ # Count conditions and update cyclomatic complexity
392
+ condition_count = len(re.findall(patterns['condition']['generic'], content))
393
+ metrics['condition_count'] = condition_count
394
+ metrics['cyclomatic_complexity'] += condition_count
395
+
396
+ # Count loops and update cyclomatic complexity
397
+ loop_count = len(re.findall(patterns['loop']['generic'], content))
398
+ metrics['loop_count'] = loop_count
399
+ metrics['cyclomatic_complexity'] += loop_count
400
+
401
+ # Count comments
402
+ if language == 'python' or language == 'ruby':
403
+ comment_pattern = patterns['comment']['python']
404
+ else:
405
+ comment_pattern = patterns['comment']['c_style']
406
+
407
+ metrics['comment_count'] = len(re.findall(comment_pattern, content, re.MULTILINE))
408
+
409
+ # Approximate Halstead metrics
410
+ metrics['halstead_metrics'] = self._calculate_halstead_metrics(content, language)
411
+
412
+ # Calculate maintainability index
413
+ metrics['maintainability_index'] = self._calculate_maintainability_index(
414
+ metrics['cyclomatic_complexity'],
415
+ metrics['halstead_metrics'].get('volume', 0),
416
+ metrics['line_count']
417
+ )
418
+
419
+ return metrics
420
+
421
+ def _calculate_halstead_metrics(self, content, language):
422
+ """
423
+ Calculates Halstead complexity metrics
424
+
425
+ Args:
426
+ content: File content
427
+ language: Programming language
428
+
429
+ Returns:
430
+ Dict with Halstead metrics
431
+ """
432
+ # Define operators and operands based on language
433
+ operators = {
434
+ 'python': ['+', '-', '*', '/', '%', '**', '//', '=', '+=', '-=', '*=', '/=', '%=', '**=', '//=',
435
+ '==', '!=', '>', '<', '>=', '<=', 'and', 'or', 'not', 'in', 'is', 'lambda'],
436
+ 'javascript': ['+', '-', '*', '/', '%', '**', '=', '+=', '-=', '*=', '/=', '%=', '**=',
437
+ '==', '===', '!=', '!==', '>', '<', '>=', '<=', '&&', '||', '!', 'typeof', 'instanceof']
438
+ }
439
+
440
+ # Get appropriate operators or use a generic set
441
+ op_list = operators.get(language, operators['javascript'])
442
+
443
+ # Count unique operators
444
+ n1 = len(set(re.findall('|'.join(re.escape(op) for op in op_list), content)))
445
+ if n1 == 0:
446
+ n1 = 1 # Avoid division by zero
447
+
448
+ # Count total operators
449
+ N1 = len(re.findall('|'.join(re.escape(op) for op in op_list), content))
450
+
451
+ # Approximate unique operands (identifiers and literals)
452
+ identifier_pattern = r'\b[a-zA-Z_]\w*\b'
453
+ string_literal_pattern = r'"[^"]*"|\'[^\']*\''
454
+ number_pattern = r'\b\d+(\.\d+)?\b'
455
+
456
+ identifiers = set(re.findall(identifier_pattern, content))
457
+ strings = set(re.findall(string_literal_pattern, content))
458
+ numbers = set(re.findall(number_pattern, content))
459
+
460
+ # Remove keywords from identifiers based on language
461
+ keywords = {
462
+ 'python': ['def', 'class', 'if', 'else', 'elif', 'for', 'while', 'try', 'except', 'finally',
463
+ 'with', 'as', 'import', 'from', 'return', 'yield', 'break', 'continue', 'pass',
464
+ 'True', 'False', 'None'],
465
+ 'javascript': ['function', 'class', 'if', 'else', 'for', 'while', 'try', 'catch', 'finally',
466
+ 'with', 'switch', 'case', 'default', 'break', 'continue', 'return', 'throw',
467
+ 'typeof', 'instanceof', 'new', 'this', 'super', 'true', 'false', 'null', 'undefined']
468
+ }
469
+
470
+ lang_keywords = keywords.get(language, keywords['javascript'])
471
+ identifiers = identifiers - set(lang_keywords)
472
+
473
+ n2 = len(identifiers) + len(strings) + len(numbers)
474
+ if n2 == 0:
475
+ n2 = 1 # Avoid division by zero
476
+
477
+ # Count total operands (approximate)
478
+ N2 = len(re.findall(identifier_pattern, content)) + len(re.findall(string_literal_pattern, content)) + len(re.findall(number_pattern, content))
479
+
480
+ # Calculate metrics
481
+ N = N1 + N2
482
+ n = n1 + n2
483
+
484
+ # Calculate volume
485
+ volume = N * math.log2(n) if n > 0 else 0
486
+
487
+ # Calculate difficulty
488
+ difficulty = (n1 / 2) * (N2 / n2) if n2 > 0 else 0
489
+
490
+ # Calculate effort
491
+ effort = difficulty * volume
492
+
493
+ return {
494
+ 'unique_operators': n1,
495
+ 'unique_operands': n2,
496
+ 'total_operators': N1,
497
+ 'total_operands': N2,
498
+ 'vocabulary': n,
499
+ 'length': N,
500
+ 'volume': volume,
501
+ 'difficulty': difficulty,
502
+ 'effort': effort
503
+ }
504
+
505
+ def _calculate_maintainability_index(self, cyclomatic_complexity, halstead_volume, line_count):
506
+ """
507
+ Calculates the Maintainability Index
508
+
509
+ Args:
510
+ cyclomatic_complexity: Cyclomatic complexity
511
+ halstead_volume: Halstead volume
512
+ line_count: Number of lines of code
513
+
514
+ Returns:
515
+ Maintainability Index (0-100)
516
+ """
517
+ # Original formula: 171 - 5.2 * ln(V) - 0.23 * G - 16.2 * ln(LOC)
518
+ # Normalized to 0-100 scale
519
+
520
+ if halstead_volume <= 0 or line_count <= 0:
521
+ return 100 # Perfect score for empty files
522
+
523
+ mi = 171 - 5.2 * math.log(halstead_volume) - 0.23 * cyclomatic_complexity - 16.2 * math.log(line_count)
524
+
525
+ # Normalize to 0-100
526
+ normalized_mi = max(0, min(100, mi * 100 / 171))
527
+
528
+ return normalized_mi
529
+
530
+ def _calculate_complexity_score(self, metrics):
531
+ """
532
+ Calculates an overall complexity score based on multiple metrics
533
+
534
+ Args:
535
+ metrics: Dictionary of code metrics
536
+
537
+ Returns:
538
+ Complexity score from 0-10
539
+ """
540
+ # Define weights for each metric
541
+ weights = {
542
+ 'cyclomatic_complexity': 0.3,
543
+ 'halstead_effort': 0.2,
544
+ 'line_count': 0.1,
545
+ 'function_count': 0.1,
546
+ 'class_count': 0.1,
547
+ 'maintainability': 0.2
548
+ }
549
+
550
+ # Normalize metrics to 0-10 scale
551
+ normalized = {
552
+ 'cyclomatic_complexity': min(10, metrics['cyclomatic_complexity'] / 5),
553
+ 'halstead_effort': min(10, metrics['halstead_metrics'].get('effort', 0) / 10000),
554
+ 'line_count': min(10, metrics['line_count'] / 300),
555
+ 'function_count': min(10, metrics['function_count'] / 20),
556
+ 'class_count': min(10, metrics['class_count'] / 10),
557
+ 'maintainability': 10 - (metrics['maintainability_index'] / 10)
558
+ }
559
+
560
+ # Calculate weighted score
561
+ score = 0
562
+ for key, weight in weights.items():
563
+ score += normalized[key] * weight
564
+
565
+ return score
566
+
567
+ def _format_results(self, results, total_files, analyzed_files, format_type):
568
+ """
569
+ Formats the analysis results
570
+
571
+ Args:
572
+ results: List of analysis results
573
+ total_files: Total number of files found
574
+ analyzed_files: Number of files successfully analyzed
575
+ format_type: Output format type (detailed or summary)
576
+
577
+ Returns:
578
+ Formatted analysis results as string
579
+ """
580
+ # Sort results by complexity score (descending)
581
+ results.sort(key=lambda x: x.get('complexity_score', 0), reverse=True)
582
+
583
+ if format_type.lower() == 'summary':
584
+ return self._format_summary(results, total_files, analyzed_files)
585
+ else:
586
+ return self._format_detailed(results, total_files, analyzed_files)
587
+
588
+ def _format_detailed(self, results, total_files, analyzed_files):
589
+ """
590
+ Formats results in detailed format
591
+
592
+ Args:
593
+ results: List of analysis results
594
+ total_files: Total number of files found
595
+ analyzed_files: Number of files successfully analyzed
596
+
597
+ Returns:
598
+ Detailed formatted analysis results
599
+ """
600
+ output = [
601
+ "Code Complexity Analysis Report (Detailed)",
602
+ "=" * 50,
603
+ f"Files Analyzed: {analyzed_files}/{total_files}",
604
+ ""
605
+ ]
606
+
607
+ for i, result in enumerate(results, 1):
608
+ # Skip files with errors
609
+ if 'error' in result:
610
+ output.append(f"File {i}: {result['file_path']}")
611
+ output.append(f"Error: {result['error']}")
612
+ output.append("")
613
+ continue
614
+
615
+ # Add file info
616
+ output.append(f"File {i}: {result['file_path']}")
617
+ output.append(f"Language: {result['language']}")
618
+ output.append(f"Complexity Score: {result['complexity_score']:.2f}/10")
619
+
620
+ # Complexity classification
621
+ score = result['complexity_score']
622
+ if score < 2:
623
+ complexity = "Very Low"
624
+ elif score < 4:
625
+ complexity = "Low"
626
+ elif score < 6:
627
+ complexity = "Moderate"
628
+ elif score < 8:
629
+ complexity = "High"
630
+ else:
631
+ complexity = "Very High"
632
+
633
+ output.append(f"Complexity Rating: {complexity}")
634
+
635
+ # Add basic metrics
636
+ output.append("")
637
+ output.append("Basic Metrics:")
638
+ output.append(f"- Lines of Code: {result['line_count']}")
639
+ output.append(f"- Non-Empty Lines: {result.get('non_empty_lines', 'N/A')}")
640
+ output.append(f"- File Size: {result['size_bytes']} bytes")
641
+ output.append(f"- Average Line Length: {result['avg_line_length']:.2f} characters")
642
+ output.append(f"- Maximum Line Length: {result['max_line_length']} characters")
643
+
644
+ # Add structural metrics
645
+ output.append("")
646
+ output.append("Structural Metrics:")
647
+ output.append(f"- Functions/Methods: {result['function_count']}")
648
+ output.append(f"- Classes/Interfaces: {result['class_count']}")
649
+ output.append(f"- Conditional Statements: {result['condition_count']}")
650
+ output.append(f"- Loops: {result['loop_count']}")
651
+ output.append(f"- Comments: {result['comment_count']}")
652
+
653
+ # Add complexity metrics
654
+ output.append("")
655
+ output.append("Complexity Metrics:")
656
+ output.append(f"- Cyclomatic Complexity: {result['cyclomatic_complexity']}")
657
+ output.append(f"- Maintainability Index: {result['maintainability_index']:.2f}/100")
658
+
659
+ # Add Halstead metrics
660
+ halstead = result['halstead_metrics']
661
+ output.append("")
662
+ output.append("Halstead Metrics:")
663
+ output.append(f"- Vocabulary: {halstead.get('vocabulary', 'N/A')}")
664
+ output.append(f"- Length: {halstead.get('length', 'N/A')}")
665
+ output.append(f"- Volume: {halstead.get('volume', 'N/A'):.2f}")
666
+ output.append(f"- Difficulty: {halstead.get('difficulty', 'N/A'):.2f}")
667
+ output.append(f"- Effort: {halstead.get('effort', 'N/A'):.2f}")
668
+
669
+ # Add recommendations
670
+ output.append("")
671
+ output.append("Recommendations:")
672
+
673
+ if result['complexity_score'] >= 7:
674
+ output.append("- Consider refactoring complex sections into smaller functions")
675
+ output.append("- Reduce nesting levels in conditional statements")
676
+
677
+ if result['function_count'] > 0 and result['comment_count'] / result['function_count'] < 0.5:
678
+ output.append("- Add more comments to improve code documentation")
679
+
680
+ if result['avg_line_length'] > 80:
681
+ output.append("- Reduce average line length for better readability")
682
+
683
+ if result['max_line_length'] > 100:
684
+ output.append("- Break up long lines of code")
685
+
686
+ if result['maintainability_index'] < 65:
687
+ output.append("- Improve maintainability by simplifying complex methods")
688
+
689
+ # Separator between files
690
+ output.append("")
691
+ output.append("-" * 50)
692
+ output.append("")
693
+
694
+ # Add overall summary
695
+ output.append("")
696
+ output.append("Overall Summary:")
697
+
698
+ if results:
699
+ avg_complexity = sum(r.get('complexity_score', 0) for r in results) / len(results)
700
+ avg_maintainability = sum(r.get('maintainability_index', 0) for r in results if 'maintainability_index' in r) / len([r for r in results if 'maintainability_index' in r]) if any('maintainability_index' in r for r in results) else 0
701
+
702
+ output.append(f"- Average Complexity Score: {avg_complexity:.2f}/10")
703
+ output.append(f"- Average Maintainability Index: {avg_maintainability:.2f}/100")
704
+
705
+ # Most complex file
706
+ most_complex = max(results, key=lambda x: x.get('complexity_score', 0))
707
+ output.append(f"- Most Complex File: {most_complex['file_path']} (Score: {most_complex['complexity_score']:.2f})")
708
+
709
+ return "\n".join(output)
710
+
711
+ def _format_summary(self, results, total_files, analyzed_files):
712
+ """
713
+ Formats results in summary format
714
+
715
+ Args:
716
+ results: List of analysis results
717
+ total_files: Total number of files found
718
+ analyzed_files: Number of files successfully analyzed
719
+
720
+ Returns:
721
+ Summary formatted analysis results
722
+ """
723
+ output = [
724
+ "Code Complexity Analysis Report (Summary)",
725
+ "=" * 50,
726
+ f"Files Analyzed: {analyzed_files}/{total_files}",
727
+ ""
728
+ ]
729
+
730
+ # Calculate overall metrics
731
+ if results:
732
+ total_loc = sum(r.get('line_count', 0) for r in results)
733
+ total_functions = sum(r.get('function_count', 0) for r in results)
734
+ total_classes = sum(r.get('class_count', 0) for r in results)
735
+
736
+ avg_complexity = sum(r.get('complexity_score', 0) for r in results) / len(results)
737
+ avg_cyclomatic = sum(r.get('cyclomatic_complexity', 0) for r in results) / len(results)
738
+ avg_maintainability = sum(r.get('maintainability_index', 0) for r in results if 'maintainability_index' in r) / len([r for r in results if 'maintainability_index' in r]) if any('maintainability_index' in r for r in results) else 0
739
+
740
+ # Add overall metrics
741
+ output.append("Overall Metrics:")
742
+ output.append(f"- Total Lines of Code: {total_loc}")
743
+ output.append(f"- Total Functions/Methods: {total_functions}")
744
+ output.append(f"- Total Classes/Interfaces: {total_classes}")
745
+ output.append(f"- Average Complexity Score: {avg_complexity:.2f}/10")
746
+ output.append(f"- Average Cyclomatic Complexity: {avg_cyclomatic:.2f}")
747
+ output.append(f"- Average Maintainability Index: {avg_maintainability:.2f}/100")
748
+ output.append("")
749
+
750
+ # Distribution by complexity
751
+ low = len([r for r in results if r.get('complexity_score', 0) < 4])
752
+ medium = len([r for r in results if 4 <= r.get('complexity_score', 0) < 7])
753
+ high = len([r for r in results if r.get('complexity_score', 0) >= 7])
754
+
755
+ output.append("Complexity Distribution:")
756
+ output.append(f"- Low Complexity (0-3.99): {low} files ({low/len(results)*100:.1f}%)")
757
+ output.append(f"- Medium Complexity (4-6.99): {medium} files ({medium/len(results)*100:.1f}%)")
758
+ output.append(f"- High Complexity (7-10): {high} files ({high/len(results)*100:.1f}%)")
759
+ output.append("")
760
+
761
+ # Top 5 most complex files
762
+ output.append("Top 5 Most Complex Files:")
763
+
764
+ for i, result in enumerate(results[:5], 1):
765
+ score = result.get('complexity_score', 0)
766
+ cyclomatic = result.get('cyclomatic_complexity', 'N/A')
767
+ maintainability = result.get('maintainability_index', 'N/A')
768
+
769
+ if isinstance(maintainability, (int, float)):
770
+ maintainability = f"{maintainability:.2f}/100"
771
+
772
+ output.append(f"{i}. {result['file_path']}")
773
+ output.append(f" Score: {score:.2f}/10, Cyclomatic: {cyclomatic}, Maintainability: {maintainability}")
774
+
775
+ # Add specific recommendations for top files
776
+ if score >= 7:
777
+ output.append(" Recommendation: Consider refactoring into smaller components")
778
+ elif cyclomatic != 'N/A' and cyclomatic > 10:
779
+ output.append(" Recommendation: Reduce complexity by simplifying conditional logic")
780
+
781
+ # Add general recommendations
782
+ output.append("")
783
+ output.append("General Recommendations:")
784
+
785
+ if high > 0:
786
+ output.append("- Refactor highly complex files to improve maintainability")
787
+
788
+ if avg_maintainability < 65:
789
+ output.append("- Improve documentation and code structure to increase maintainability")
790
+
791
+ if avg_cyclomatic > 15:
792
+ output.append("- Reduce conditional logic complexity by extracting methods")
793
+
794
+ if high / len(results) > 0.3:
795
+ output.append("- Consider a code quality review process for complex components")
796
+
797
+ return "\n".join(output)