lizard 1.17.29__tar.gz → 1.17.31__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.
- {lizard-1.17.29 → lizard-1.17.31}/PKG-INFO +2 -1
- {lizard-1.17.29 → lizard-1.17.31}/README.rst +1 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard.egg-info/PKG-INFO +2 -1
- {lizard-1.17.29 → lizard-1.17.31}/lizard.egg-info/SOURCES.txt +1 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard.py +36 -15
- {lizard-1.17.29 → lizard-1.17.31}/lizard_ext/__init__.py +14 -0
- lizard-1.17.31/lizard_ext/checkstyleoutput.py +31 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_ext/version.py +1 -1
- {lizard-1.17.29 → lizard-1.17.31}/lizard_languages/code_reader.py +19 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_languages/go.py +1 -1
- {lizard-1.17.29 → lizard-1.17.31}/lizard_languages/java.py +6 -3
- {lizard-1.17.29 → lizard-1.17.31}/lizard_languages/javascript.py +1 -1
- {lizard-1.17.29 → lizard-1.17.31}/lizard_languages/js_style_language_states.py +51 -5
- {lizard-1.17.29 → lizard-1.17.31}/lizard_languages/jsx.py +95 -23
- {lizard-1.17.29 → lizard-1.17.31}/lizard_languages/perl.py +20 -21
- {lizard-1.17.29 → lizard-1.17.31}/lizard_languages/php.py +14 -14
- {lizard-1.17.29 → lizard-1.17.31}/lizard_languages/python.py +1 -1
- {lizard-1.17.29 → lizard-1.17.31}/lizard_languages/rust.py +2 -2
- {lizard-1.17.29 → lizard-1.17.31}/lizard_languages/script_language.py +2 -2
- {lizard-1.17.29 → lizard-1.17.31}/lizard_languages/tsx.py +3 -2
- {lizard-1.17.29 → lizard-1.17.31}/lizard_languages/typescript.py +54 -11
- {lizard-1.17.29 → lizard-1.17.31}/test/testOutput.py +19 -0
- {lizard-1.17.29 → lizard-1.17.31}/test/testOutputFile.py +31 -0
- {lizard-1.17.29 → lizard-1.17.31}/LICENSE.txt +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard.egg-info/dependency_links.txt +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard.egg-info/entry_points.txt +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard.egg-info/requires.txt +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard.egg-info/top_level.txt +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_ext/auto_open.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_ext/csvoutput.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_ext/default_ordered_dict.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_ext/extension_base.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_ext/htmloutput.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_ext/keywords.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_ext/lizardboolcount.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_ext/lizardcomplextags.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_ext/lizardcpre.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_ext/lizarddependencycount.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_ext/lizarddumpcomments.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_ext/lizardduplicate.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_ext/lizardduplicated_param_list.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_ext/lizardexitcount.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_ext/lizardgotocount.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_ext/lizardignoreassert.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_ext/lizardio.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_ext/lizardmccabe.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_ext/lizardmodified.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_ext/lizardnd.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_ext/lizardnonstrict.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_ext/lizardns.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_ext/lizardoutside.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_ext/lizardstatementcount.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_ext/lizardwordcount.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_ext/xmloutput.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_languages/__init__.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_languages/clike.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_languages/csharp.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_languages/erlang.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_languages/fortran.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_languages/gdscript.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_languages/golike.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_languages/js_style_regex_expression.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_languages/kotlin.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_languages/lua.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_languages/objc.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_languages/ruby.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_languages/rubylike.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_languages/scala.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_languages/solidity.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_languages/swift.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_languages/tnsdl.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_languages/ttcn.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_languages/vue.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/lizard_languages/zig.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/setup.cfg +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/setup.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/test/testApplication.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/test/testAssertionExtension.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/test/testBasicFunctionInfo.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/test/testCOutsideComplexity.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/test/testCPreprocessorExtension.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/test/testCommentOptions.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/test/testCyclomaticComplexity.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/test/testExtension.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/test/testFilesFilter.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/test/testFunctionDependencyCount.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/test/testFunctionExitCount.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/test/testFunctionGotoCount.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/test/testFunctionStatementCount.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/test/testHelpers.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/test/testLanguages.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/test/testMcCabe.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/test/testNestedStructures.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/test/testNestingDepth.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/test/testOutputCSV.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/test/testOutputHTML.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/test/testTokenizer.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/test/test_analyzer.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/test/test_auto_open.py +0 -0
- {lizard-1.17.29 → lizard-1.17.31}/test/test_options.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: lizard
|
|
3
|
-
Version: 1.17.
|
|
3
|
+
Version: 1.17.31
|
|
4
4
|
Summary: A code analyzer without caring the C/C++ header files. It works with Java, C/C++, JavaScript, Python, Ruby, Swift, Objective C. Metrics includes cyclomatic complexity number etc.
|
|
5
5
|
Home-page: http://www.lizard.ws
|
|
6
6
|
Download-URL: https://pypi.python.org/lizard/
|
|
@@ -194,6 +194,7 @@ Options
|
|
|
194
194
|
generate report in Jenkins server
|
|
195
195
|
--csv Generate CSV output as a transform of the default output
|
|
196
196
|
-H, --html Output HTML report
|
|
197
|
+
--checkstyle Generate Checkstyle XML output for integration with Jenkins and other tools
|
|
197
198
|
-m, --modified Calculate modified cyclomatic complexity number , which count a
|
|
198
199
|
switch/case with multiple cases as one CCN.
|
|
199
200
|
-E EXTENSIONS, --extension EXTENSIONS
|
|
@@ -163,6 +163,7 @@ Options
|
|
|
163
163
|
generate report in Jenkins server
|
|
164
164
|
--csv Generate CSV output as a transform of the default output
|
|
165
165
|
-H, --html Output HTML report
|
|
166
|
+
--checkstyle Generate Checkstyle XML output for integration with Jenkins and other tools
|
|
166
167
|
-m, --modified Calculate modified cyclomatic complexity number , which count a
|
|
167
168
|
switch/case with multiple cases as one CCN.
|
|
168
169
|
-E EXTENSIONS, --extension EXTENSIONS
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: lizard
|
|
3
|
-
Version: 1.17.
|
|
3
|
+
Version: 1.17.31
|
|
4
4
|
Summary: A code analyzer without caring the C/C++ header files. It works with Java, C/C++, JavaScript, Python, Ruby, Swift, Objective C. Metrics includes cyclomatic complexity number etc.
|
|
5
5
|
Home-page: http://www.lizard.ws
|
|
6
6
|
Download-URL: https://pypi.python.org/lizard/
|
|
@@ -194,6 +194,7 @@ Options
|
|
|
194
194
|
generate report in Jenkins server
|
|
195
195
|
--csv Generate CSV output as a transform of the default output
|
|
196
196
|
-H, --html Output HTML report
|
|
197
|
+
--checkstyle Generate Checkstyle XML output for integration with Jenkins and other tools
|
|
197
198
|
-m, --modified Calculate modified cyclomatic complexity number , which count a
|
|
198
199
|
switch/case with multiple cases as one CCN.
|
|
199
200
|
-E EXTENSIONS, --extension EXTENSIONS
|
|
@@ -43,6 +43,7 @@ try:
|
|
|
43
43
|
from lizard_ext import print_csv
|
|
44
44
|
from lizard_ext import html_output
|
|
45
45
|
from lizard_ext import auto_open, auto_read
|
|
46
|
+
from lizard_ext import print_checkstyle
|
|
46
47
|
except ImportError:
|
|
47
48
|
sys.stderr.write("Cannot find the lizard_ext modules.")
|
|
48
49
|
|
|
@@ -217,6 +218,11 @@ def arg_parser(prog=None):
|
|
|
217
218
|
const="modified",
|
|
218
219
|
dest="extensions",
|
|
219
220
|
default=[])
|
|
221
|
+
parser.add_argument("--checkstyle",
|
|
222
|
+
help='''Generate Checkstyle XML output for integration with Jenkins and other tools.''',
|
|
223
|
+
action="store_const",
|
|
224
|
+
const=print_checkstyle,
|
|
225
|
+
dest="printer")
|
|
220
226
|
_extension_arg(parser)
|
|
221
227
|
parser.add_argument("-s", "--sort",
|
|
222
228
|
help='''Sort the warning with field. The field can be
|
|
@@ -902,7 +908,7 @@ def get_all_source_files(paths, exclude_patterns, lans):
|
|
|
902
908
|
'''
|
|
903
909
|
Function counts md5 hash for the given file and checks if it isn't a
|
|
904
910
|
duplicate using set of hashes for previous files.
|
|
905
|
-
|
|
911
|
+
|
|
906
912
|
If a .gitignore file is found in any of the given paths, it will be used
|
|
907
913
|
to filter out files that match the gitignore patterns.
|
|
908
914
|
'''
|
|
@@ -1000,7 +1006,10 @@ def parse_args(argv):
|
|
|
1000
1006
|
if opt.output_file:
|
|
1001
1007
|
inferred_printer = infer_printer_from_file_ext(opt.output_file)
|
|
1002
1008
|
if inferred_printer:
|
|
1003
|
-
|
|
1009
|
+
# Always use print_checkstyle for .checkstyle.xml
|
|
1010
|
+
if opt.output_file.lower().endswith('.checkstyle.xml'):
|
|
1011
|
+
opt.printer = inferred_printer
|
|
1012
|
+
elif not opt.printer:
|
|
1004
1013
|
opt.printer = inferred_printer
|
|
1005
1014
|
elif opt.printer != inferred_printer:
|
|
1006
1015
|
msg = "Warning: overriding output file extension.\n"
|
|
@@ -1009,15 +1018,16 @@ def parse_args(argv):
|
|
|
1009
1018
|
|
|
1010
1019
|
|
|
1011
1020
|
def infer_printer_from_file_ext(path):
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
+
lower_path = path.lower()
|
|
1022
|
+
if lower_path.endswith(".checkstyle.xml"):
|
|
1023
|
+
return print_checkstyle
|
|
1024
|
+
if lower_path.endswith(".html"):
|
|
1025
|
+
return html_output
|
|
1026
|
+
if lower_path.endswith(".xml"):
|
|
1027
|
+
return print_xml
|
|
1028
|
+
if lower_path.endswith(".csv"):
|
|
1029
|
+
return print_csv
|
|
1030
|
+
return None
|
|
1021
1031
|
|
|
1022
1032
|
|
|
1023
1033
|
def open_output_file(path):
|
|
@@ -1072,16 +1082,27 @@ def main(argv=None):
|
|
|
1072
1082
|
options.paths = auto_read(options.input_file).splitlines()
|
|
1073
1083
|
original_stdout = sys.stdout
|
|
1074
1084
|
output_file = None
|
|
1075
|
-
if options.output_file:
|
|
1076
|
-
output_file = open_output_file(options.output_file)
|
|
1077
|
-
sys.stdout = output_file
|
|
1078
1085
|
result = analyze(
|
|
1079
1086
|
options.paths,
|
|
1080
1087
|
options.exclude,
|
|
1081
1088
|
options.working_threads,
|
|
1082
1089
|
options.extensions,
|
|
1083
1090
|
options.languages)
|
|
1084
|
-
warning_count =
|
|
1091
|
+
warning_count = None
|
|
1092
|
+
if options.output_file:
|
|
1093
|
+
output_file = open_output_file(options.output_file)
|
|
1094
|
+
sys.stdout = output_file
|
|
1095
|
+
# Special handling for checkstyle output
|
|
1096
|
+
if getattr(printer, "__name__", "") == "print_checkstyle":
|
|
1097
|
+
warning_count = printer(result, options, schema, AllResult, file=output_file)
|
|
1098
|
+
else:
|
|
1099
|
+
warning_count = printer(result, options, schema, AllResult)
|
|
1100
|
+
else:
|
|
1101
|
+
# Special handling for checkstyle output
|
|
1102
|
+
if getattr(printer, "__name__", "") == "print_checkstyle":
|
|
1103
|
+
warning_count = printer(result, options, schema, AllResult, file=original_stdout)
|
|
1104
|
+
else:
|
|
1105
|
+
warning_count = printer(result, options, schema, AllResult)
|
|
1085
1106
|
print_extension_results(options.extensions)
|
|
1086
1107
|
list(result)
|
|
1087
1108
|
if output_file:
|
|
@@ -6,6 +6,7 @@ from .htmloutput import html_output
|
|
|
6
6
|
from .csvoutput import csv_output
|
|
7
7
|
from .xmloutput import xml_output
|
|
8
8
|
from .auto_open import auto_open, auto_read
|
|
9
|
+
from .checkstyleoutput import checkstyle_output
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
def print_xml(results, options, _, total_factory):
|
|
@@ -16,3 +17,16 @@ def print_xml(results, options, _, total_factory):
|
|
|
16
17
|
def print_csv(results, options, _, total_factory):
|
|
17
18
|
csv_output(total_factory(list(results)), options)
|
|
18
19
|
return 0
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def print_checkstyle(results, options, _, total_factory, file=None):
|
|
23
|
+
import sys
|
|
24
|
+
print("DEBUG: print_checkstyle called", file=sys.stderr)
|
|
25
|
+
output = checkstyle_output(total_factory(list(results)), options.verbose)
|
|
26
|
+
if file is None:
|
|
27
|
+
file = sys.stdout
|
|
28
|
+
file.write(output)
|
|
29
|
+
if not output.endswith("\n"):
|
|
30
|
+
file.write("\n")
|
|
31
|
+
file.flush()
|
|
32
|
+
return 0
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'''
|
|
2
|
+
Checkstyle XML output for Lizard
|
|
3
|
+
'''
|
|
4
|
+
|
|
5
|
+
def checkstyle_output(all_result, verbose):
|
|
6
|
+
result = all_result.result
|
|
7
|
+
import xml.etree.ElementTree as ET
|
|
8
|
+
|
|
9
|
+
checkstyle = ET.Element('checkstyle', version="4.3")
|
|
10
|
+
|
|
11
|
+
for source_file in result:
|
|
12
|
+
if source_file:
|
|
13
|
+
file_elem = ET.SubElement(checkstyle, 'file', name=source_file.filename)
|
|
14
|
+
for func in source_file.function_list:
|
|
15
|
+
# Each function with a warning (e.g., CCN > threshold) could be an <error>
|
|
16
|
+
# For now, output all functions as <error> for demonstration
|
|
17
|
+
ET.SubElement(
|
|
18
|
+
file_elem,
|
|
19
|
+
'error',
|
|
20
|
+
line=str(func.start_line),
|
|
21
|
+
column="0",
|
|
22
|
+
severity="info",
|
|
23
|
+
message=f"{func.name} has {func.nloc} NLOC, {func.cyclomatic_complexity} CCN, {func.token_count} token, {len(func.parameters)} PARAM, {func.length} length",
|
|
24
|
+
source="lizard"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
# Pretty print
|
|
28
|
+
import xml.dom.minidom
|
|
29
|
+
rough_string = ET.tostring(checkstyle, 'utf-8')
|
|
30
|
+
reparsed = xml.dom.minidom.parseString(rough_string)
|
|
31
|
+
return reparsed.toprettyxml(indent=" ")
|
|
@@ -184,6 +184,13 @@ class CodeReader:
|
|
|
184
184
|
def __call__(self, tokens, reader):
|
|
185
185
|
self.context = reader.context
|
|
186
186
|
for token in tokens:
|
|
187
|
+
# Allow language-specific token processing
|
|
188
|
+
if self.process_token(token):
|
|
189
|
+
for state in self.parallel_states:
|
|
190
|
+
state(token)
|
|
191
|
+
yield token
|
|
192
|
+
continue
|
|
193
|
+
|
|
187
194
|
for state in self.parallel_states:
|
|
188
195
|
state(token)
|
|
189
196
|
yield token
|
|
@@ -193,3 +200,15 @@ class CodeReader:
|
|
|
193
200
|
|
|
194
201
|
def eof(self):
|
|
195
202
|
pass
|
|
203
|
+
|
|
204
|
+
def process_token(self, token):
|
|
205
|
+
"""Process a token before normal handling.
|
|
206
|
+
Return True if the token has been handled specially and should skip normal processing.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
token: The token being processed
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
bool: True if the token should skip normal processing, False otherwise
|
|
213
|
+
"""
|
|
214
|
+
return False
|
|
@@ -34,6 +34,7 @@ class JavaStates(CLikeStates): # pylint: disable=R0903
|
|
|
34
34
|
def _state_imp(self, token):
|
|
35
35
|
# When entering a function implementation, set the flag
|
|
36
36
|
self.in_method_body = True
|
|
37
|
+
|
|
37
38
|
def callback():
|
|
38
39
|
# When exiting the function implementation, clear the flag
|
|
39
40
|
self.in_method_body = False
|
|
@@ -105,6 +106,7 @@ class JavaStates(CLikeStates): # pylint: disable=R0903
|
|
|
105
106
|
self.in_record_constructor = False
|
|
106
107
|
self._state = self._state_global
|
|
107
108
|
|
|
109
|
+
|
|
108
110
|
class JavaFunctionBodyStates(JavaStates):
|
|
109
111
|
def __init__(self, context):
|
|
110
112
|
super(JavaFunctionBodyStates, self).__init__(context)
|
|
@@ -132,12 +134,12 @@ class JavaFunctionBodyStates(JavaStates):
|
|
|
132
134
|
self.handling_dot_class = False
|
|
133
135
|
if token == "class":
|
|
134
136
|
return # Skip the 'class' token after a dot
|
|
135
|
-
|
|
137
|
+
|
|
136
138
|
# Special handling for tokens that could confuse the parser
|
|
137
139
|
if self.ignore_tokens:
|
|
138
140
|
self.ignore_tokens = False
|
|
139
141
|
return
|
|
140
|
-
|
|
142
|
+
|
|
141
143
|
if token == "new":
|
|
142
144
|
self.next(self._state_new)
|
|
143
145
|
else:
|
|
@@ -145,7 +147,7 @@ class JavaFunctionBodyStates(JavaStates):
|
|
|
145
147
|
# This ensures that local classes are properly detected
|
|
146
148
|
if self._try_start_a_class(token):
|
|
147
149
|
return
|
|
148
|
-
|
|
150
|
+
|
|
149
151
|
if self.br_count == 0:
|
|
150
152
|
self.statemachine_return()
|
|
151
153
|
|
|
@@ -166,6 +168,7 @@ class JavaFunctionBodyStates(JavaStates):
|
|
|
166
168
|
return
|
|
167
169
|
self.next(self._state_global, token)
|
|
168
170
|
|
|
171
|
+
|
|
169
172
|
class JavaClassBodyStates(JavaStates):
|
|
170
173
|
def __init__(self, class_name, is_record, context):
|
|
171
174
|
super(JavaClassBodyStates, self).__init__(context)
|
|
@@ -12,14 +12,30 @@ class JavaScriptStyleLanguageStates(CodeStateMachine): # pylint: disable=R0903
|
|
|
12
12
|
self.function_name = ''
|
|
13
13
|
self.started_function = None
|
|
14
14
|
self.as_object = False
|
|
15
|
+
self._getter_setter_prefix = None
|
|
16
|
+
self.arrow_function_pending = False
|
|
15
17
|
|
|
16
18
|
def _state_global(self, token):
|
|
17
19
|
if self.as_object:
|
|
20
|
+
# Support for getter/setter: look for 'get' or 'set' before method name
|
|
21
|
+
if token in ('get', 'set'):
|
|
22
|
+
self._getter_setter_prefix = token
|
|
23
|
+
return
|
|
24
|
+
if hasattr(self, '_getter_setter_prefix') and self._getter_setter_prefix:
|
|
25
|
+
# Next token is the property name
|
|
26
|
+
self.last_tokens = f"{self._getter_setter_prefix} {token}"
|
|
27
|
+
self._getter_setter_prefix = None
|
|
28
|
+
return
|
|
29
|
+
if token == '[':
|
|
30
|
+
self._collect_computed_name()
|
|
31
|
+
return
|
|
18
32
|
if token == ':':
|
|
19
33
|
self.function_name = self.last_tokens
|
|
20
34
|
return
|
|
21
35
|
elif token == '(':
|
|
22
|
-
self.
|
|
36
|
+
if not self.started_function:
|
|
37
|
+
self.arrow_function_pending = True
|
|
38
|
+
self._function(self.last_tokens)
|
|
23
39
|
self.next(self._function, token)
|
|
24
40
|
return
|
|
25
41
|
|
|
@@ -34,8 +50,7 @@ class JavaScriptStyleLanguageStates(CodeStateMachine): # pylint: disable=R0903
|
|
|
34
50
|
elif token in ('else', 'do', 'try', 'final'):
|
|
35
51
|
self.next(self._expecting_statement_or_block)
|
|
36
52
|
elif token in ('=>',):
|
|
37
|
-
|
|
38
|
-
self._push_function_to_stack()
|
|
53
|
+
# Only handle arrow function body, do not push function here
|
|
39
54
|
self._state = self._arrow_function
|
|
40
55
|
elif token == '=':
|
|
41
56
|
self.function_name = self.last_tokens
|
|
@@ -116,7 +131,9 @@ class JavaScriptStyleLanguageStates(CodeStateMachine): # pylint: disable=R0903
|
|
|
116
131
|
if token != '(':
|
|
117
132
|
self.function_name = token
|
|
118
133
|
else:
|
|
119
|
-
self.
|
|
134
|
+
if not self.started_function:
|
|
135
|
+
self._push_function_to_stack()
|
|
136
|
+
self.arrow_function_pending = False
|
|
120
137
|
self._state = self._dec
|
|
121
138
|
if token == '(':
|
|
122
139
|
self._dec(token)
|
|
@@ -134,6 +151,35 @@ class JavaScriptStyleLanguageStates(CodeStateMachine): # pylint: disable=R0903
|
|
|
134
151
|
self.context.add_to_long_function_name(" " + token)
|
|
135
152
|
|
|
136
153
|
def _expecting_func_opening_bracket(self, token):
|
|
137
|
-
|
|
154
|
+
# Do not reset started_function for arrow functions (=>)
|
|
155
|
+
if token != '{' and token != '=>':
|
|
138
156
|
self.started_function = None
|
|
139
157
|
self.next(self._state_global, token)
|
|
158
|
+
|
|
159
|
+
def _collect_computed_name(self):
|
|
160
|
+
# Collect tokens between [ and ]
|
|
161
|
+
tokens = []
|
|
162
|
+
|
|
163
|
+
def collect(token):
|
|
164
|
+
if token == ']':
|
|
165
|
+
# Try to join tokens and camelCase if possible
|
|
166
|
+
name = ''.join(tokens)
|
|
167
|
+
# Remove quotes and pluses for simple cases
|
|
168
|
+
name = name.replace("'", '').replace('"', '').replace('+', '').replace(' ', '')
|
|
169
|
+
# Lowercase first char, uppercase next word's first char
|
|
170
|
+
name = self._to_camel_case(name)
|
|
171
|
+
self.last_tokens = name
|
|
172
|
+
self.next(self._state_global)
|
|
173
|
+
return True
|
|
174
|
+
tokens.append(token)
|
|
175
|
+
return False
|
|
176
|
+
self.next(collect)
|
|
177
|
+
|
|
178
|
+
def _to_camel_case(self, s):
|
|
179
|
+
# Simple camelCase conversion for test case
|
|
180
|
+
if not s:
|
|
181
|
+
return s
|
|
182
|
+
parts = s.split()
|
|
183
|
+
if not parts:
|
|
184
|
+
return s
|
|
185
|
+
return parts[0][0].lower() + parts[0][1:] + ''.join(p.capitalize() for p in parts[1:])
|
|
@@ -3,12 +3,78 @@ Language parser for JSX
|
|
|
3
3
|
'''
|
|
4
4
|
|
|
5
5
|
from .javascript import JavaScriptReader
|
|
6
|
-
from .typescript import JSTokenizer, Tokenizer
|
|
6
|
+
from .typescript import JSTokenizer, Tokenizer, TypeScriptStates
|
|
7
7
|
from .code_reader import CodeReader
|
|
8
8
|
from .js_style_regex_expression import js_style_regex_expression
|
|
9
9
|
from .js_style_language_states import JavaScriptStyleLanguageStates
|
|
10
10
|
|
|
11
11
|
|
|
12
|
+
class JSXTypeScriptStates(TypeScriptStates):
|
|
13
|
+
"""State machine for JSX/TSX files extending TypeScriptStates"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, context):
|
|
16
|
+
super().__init__(context)
|
|
17
|
+
# Initialize attributes that might be accessed later
|
|
18
|
+
self._parent_function_name = None
|
|
19
|
+
self.in_variable_declaration = False
|
|
20
|
+
self.last_variable_name = None
|
|
21
|
+
|
|
22
|
+
def statemachine_before_return(self):
|
|
23
|
+
# Ensure the main function is closed at the end
|
|
24
|
+
if self.started_function:
|
|
25
|
+
self._pop_function_from_stack()
|
|
26
|
+
# After popping, if current_function is not *global*, pop again to add to function_list
|
|
27
|
+
if self.context.current_function and self.context.current_function.name != "*global*":
|
|
28
|
+
self.context.end_of_function()
|
|
29
|
+
|
|
30
|
+
def _state_global(self, token):
|
|
31
|
+
# Handle variable declarations
|
|
32
|
+
if token in ('const', 'let', 'var'):
|
|
33
|
+
self.in_variable_declaration = True
|
|
34
|
+
super()._state_global(token)
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
if self.in_variable_declaration:
|
|
38
|
+
if token == '=':
|
|
39
|
+
# Save the variable name when we see the assignment
|
|
40
|
+
self.last_variable_name = self.last_tokens.strip()
|
|
41
|
+
super()._state_global(token)
|
|
42
|
+
return
|
|
43
|
+
elif token == '=>':
|
|
44
|
+
# We're in an arrow function with a variable assignment
|
|
45
|
+
if self.last_variable_name and not self.started_function:
|
|
46
|
+
self.function_name = self.last_variable_name
|
|
47
|
+
self._push_function_to_stack()
|
|
48
|
+
self.in_variable_declaration = False
|
|
49
|
+
# Switch to arrow function state to handle the body
|
|
50
|
+
self._state = self._arrow_function
|
|
51
|
+
return
|
|
52
|
+
elif token == ';' or self.context.newline:
|
|
53
|
+
self.in_variable_declaration = False
|
|
54
|
+
|
|
55
|
+
# Handle arrow function in JSX/TSX prop context
|
|
56
|
+
if token == '=>' and not self.in_variable_declaration:
|
|
57
|
+
if not self.started_function:
|
|
58
|
+
self.function_name = '(anonymous)'
|
|
59
|
+
self._push_function_to_stack()
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
if not self.as_object:
|
|
63
|
+
if token == ':':
|
|
64
|
+
self._consume_type_annotation()
|
|
65
|
+
return
|
|
66
|
+
|
|
67
|
+
# Pop anonymous function after closing '}' in TSX/JSX prop
|
|
68
|
+
if token == '}' and self.started_function and self.function_name == '(anonymous)':
|
|
69
|
+
self._pop_function_from_stack()
|
|
70
|
+
|
|
71
|
+
# Continue with regular TypeScript state handling
|
|
72
|
+
super()._state_global(token)
|
|
73
|
+
|
|
74
|
+
def _arrow_function(self, token):
|
|
75
|
+
self.next(self._state_global, token)
|
|
76
|
+
|
|
77
|
+
|
|
12
78
|
class TSXTokenizer(JSTokenizer):
|
|
13
79
|
def __init__(self):
|
|
14
80
|
super().__init__()
|
|
@@ -18,12 +84,12 @@ class TSXTokenizer(JSTokenizer):
|
|
|
18
84
|
from .jsx import XMLTagWithAttrTokenizer # Import only when needed
|
|
19
85
|
self.sub_tokenizer = XMLTagWithAttrTokenizer()
|
|
20
86
|
return
|
|
21
|
-
|
|
87
|
+
|
|
22
88
|
if token == "=>":
|
|
23
89
|
# Special handling for arrow functions
|
|
24
90
|
yield token
|
|
25
91
|
return
|
|
26
|
-
|
|
92
|
+
|
|
27
93
|
for tok in super().process_token(token):
|
|
28
94
|
yield tok
|
|
29
95
|
|
|
@@ -57,18 +123,18 @@ class JSXMixin:
|
|
|
57
123
|
def _handle_arrow_function(self):
|
|
58
124
|
# Process arrow function in JSX context
|
|
59
125
|
self.context.add_to_long_function_name(" => ")
|
|
60
|
-
|
|
126
|
+
|
|
61
127
|
# Store the current function
|
|
62
128
|
current_function = self.context.current_function
|
|
63
|
-
|
|
129
|
+
|
|
64
130
|
# Create a new anonymous function
|
|
65
131
|
self.context.restart_new_function('(anonymous)')
|
|
66
|
-
|
|
132
|
+
|
|
67
133
|
# Set up for the arrow function body
|
|
68
134
|
def callback():
|
|
69
135
|
# Return to the original function when done
|
|
70
136
|
self.context.current_function = current_function
|
|
71
|
-
|
|
137
|
+
|
|
72
138
|
self.sub_state(self.__class__(self.context), callback)
|
|
73
139
|
|
|
74
140
|
def _expecting_arrow_function_body(self, token):
|
|
@@ -78,7 +144,7 @@ class JSXMixin:
|
|
|
78
144
|
else:
|
|
79
145
|
# Arrow function with expression body
|
|
80
146
|
self.next(self._expecting_func_opening_bracket)
|
|
81
|
-
|
|
147
|
+
|
|
82
148
|
def _function_body(self, token):
|
|
83
149
|
if token == '}':
|
|
84
150
|
# End of arrow function body
|
|
@@ -93,38 +159,40 @@ class JSXMixin:
|
|
|
93
159
|
class JSXJavaScriptStyleLanguageStates(JavaScriptStyleLanguageStates):
|
|
94
160
|
def __init__(self, context):
|
|
95
161
|
super(JSXJavaScriptStyleLanguageStates, self).__init__(context)
|
|
96
|
-
|
|
162
|
+
|
|
97
163
|
def _state_global(self, token):
|
|
98
|
-
|
|
164
|
+
# Handle variable declarations
|
|
165
|
+
if token in ('const', 'let', 'var'):
|
|
99
166
|
# Remember that we're in a variable declaration
|
|
100
167
|
self.in_variable_declaration = True
|
|
101
168
|
super()._state_global(token)
|
|
102
169
|
return
|
|
103
|
-
|
|
170
|
+
|
|
104
171
|
if hasattr(self, 'in_variable_declaration') and self.in_variable_declaration:
|
|
105
172
|
if token == '=':
|
|
106
173
|
# We're in a variable assignment
|
|
107
|
-
self.function_name = self.last_tokens
|
|
174
|
+
self.function_name = self.last_tokens.strip()
|
|
108
175
|
super()._state_global(token)
|
|
109
176
|
return
|
|
110
177
|
elif token == '=>':
|
|
111
178
|
# We're in an arrow function with a variable assignment
|
|
112
|
-
self.
|
|
179
|
+
if not self.started_function and self.function_name:
|
|
180
|
+
self._push_function_to_stack()
|
|
113
181
|
self._state = self._arrow_function
|
|
114
182
|
return
|
|
115
183
|
elif token == ';' or self.context.newline:
|
|
116
184
|
# End of variable declaration
|
|
117
185
|
self.in_variable_declaration = False
|
|
118
|
-
|
|
186
|
+
|
|
119
187
|
super()._state_global(token)
|
|
120
|
-
|
|
188
|
+
|
|
121
189
|
def _expecting_func_opening_bracket(self, token):
|
|
122
190
|
if token == ':':
|
|
123
191
|
# Handle type annotations like TypeScript does
|
|
124
192
|
self._consume_type_annotation()
|
|
125
193
|
return
|
|
126
194
|
super()._expecting_func_opening_bracket(token)
|
|
127
|
-
|
|
195
|
+
|
|
128
196
|
def _consume_type_annotation(self):
|
|
129
197
|
# Skip over type annotations (simplified version of TypeScript's behavior)
|
|
130
198
|
def skip_until_terminator(token):
|
|
@@ -132,7 +200,7 @@ class JSXJavaScriptStyleLanguageStates(JavaScriptStyleLanguageStates):
|
|
|
132
200
|
self.next(self._state_global, token)
|
|
133
201
|
return True
|
|
134
202
|
return False
|
|
135
|
-
|
|
203
|
+
|
|
136
204
|
self.next(skip_until_terminator)
|
|
137
205
|
|
|
138
206
|
|
|
@@ -153,8 +221,8 @@ class JSXReader(JavaScriptReader, JSXMixin):
|
|
|
153
221
|
|
|
154
222
|
def __init__(self, context):
|
|
155
223
|
super(JSXReader, self).__init__(context)
|
|
156
|
-
# Use our
|
|
157
|
-
self.parallel_states = [
|
|
224
|
+
# Use our JSXTypeScriptStates for better handling of JSX
|
|
225
|
+
self.parallel_states = [JSXTypeScriptStates(context)]
|
|
158
226
|
|
|
159
227
|
|
|
160
228
|
class XMLTagWithAttrTokenizer(Tokenizer):
|
|
@@ -171,7 +239,11 @@ class XMLTagWithAttrTokenizer(Tokenizer):
|
|
|
171
239
|
if not token.isspace():
|
|
172
240
|
result = self.state(token)
|
|
173
241
|
if result is not None:
|
|
174
|
-
|
|
242
|
+
if isinstance(result, list):
|
|
243
|
+
for tok in result:
|
|
244
|
+
yield tok
|
|
245
|
+
else:
|
|
246
|
+
return result
|
|
175
247
|
return ()
|
|
176
248
|
|
|
177
249
|
def abort(self):
|
|
@@ -219,7 +291,7 @@ class XMLTagWithAttrTokenizer(Tokenizer):
|
|
|
219
291
|
# Don't add the closing brace automatically
|
|
220
292
|
# self.cache.append("}")
|
|
221
293
|
self.sub_tokenizer = TSXTokenizer()
|
|
222
|
-
|
|
294
|
+
|
|
223
295
|
def _jsx_expression(self, token):
|
|
224
296
|
# Handle nested braces in expressions
|
|
225
297
|
if token == "{":
|
|
@@ -230,13 +302,13 @@ class XMLTagWithAttrTokenizer(Tokenizer):
|
|
|
230
302
|
# We've found the matching closing brace
|
|
231
303
|
self.state = self._after_tag
|
|
232
304
|
return
|
|
233
|
-
|
|
305
|
+
|
|
234
306
|
# Handle arrow functions in JSX attributes
|
|
235
307
|
if token == "=>":
|
|
236
308
|
self.arrow_function_detected = True
|
|
237
309
|
# Explicitly yield the arrow token to ensure it's processed
|
|
238
310
|
return ["=>"]
|
|
239
|
-
|
|
311
|
+
|
|
240
312
|
# Handle type annotations in JSX attributes
|
|
241
313
|
if token == "<":
|
|
242
314
|
# This might be a TypeScript generic type annotation
|