lizard 1.17.31__py2.py3-none-any.whl → 1.19.0__py2.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lizard
3
- Version: 1.17.31
3
+ Version: 1.19.0
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/
@@ -50,29 +50,32 @@ code analysis.
50
50
 
51
51
  A list of supported languages:
52
52
 
53
+ - C# (C Sharp)
53
54
  - C/C++ (works with C++14)
55
+ - Erlang
56
+ - Fortran
57
+ - GDScript
58
+ - Golang
54
59
  - Java
55
- - C# (C Sharp)
56
60
  - JavaScript (With ES6 and JSX)
57
- - TypeScript (With TSX)
58
- - VueJS
61
+ - Kotlin
62
+ - Lua
59
63
  - Objective-C
60
- - Swift
64
+ - Perl
65
+ - PHP
66
+ - PL/SQL
61
67
  - Python
68
+ - R
62
69
  - Ruby
63
- - TTCN-3
64
- - PHP
65
- - Scala
66
- - GDScript
67
- - Golang
68
- - Lua
69
70
  - Rust
70
- - Fortran
71
- - Kotlin
71
+ - Scala
72
72
  - Solidity
73
- - Erlang
73
+ - Structured Text (St)
74
+ - Swift
75
+ - TTCN-3
76
+ - TypeScript (With TSX)
77
+ - VueJS
74
78
  - Zig
75
- - Perl
76
79
 
77
80
  By default lizard will search for any source code that it knows and mix
78
81
  all the results together. This might not be what you want. You can use
@@ -160,7 +163,7 @@ Options
160
163
  search for all languages it knows. `lizard -l cpp -l java`searches for
161
164
  C++ and Java code. The available languages are: cpp, java, csharp,
162
165
  javascript, python, objectivec, ttcn, ruby, php, swift, scala, GDScript,
163
- go, lua, rust, typescript
166
+ go, lua, rust, typescript, plsql
164
167
  -V, --verbose Output in verbose mode (long function name)
165
168
  -C CCN, --CCN CCN Threshold for cyclomatic complexity number warning. The default value is
166
169
  15. Functions with CCN bigger than it will generate warning
@@ -195,7 +198,7 @@ Options
195
198
  -X, --xml Generate XML in cppncss style instead of the tabular output. Useful to
196
199
  generate report in Jenkins server
197
200
  --csv Generate CSV output as a transform of the default output
198
- -H, --html Output HTML report
201
+ -H, --html Output HTML report with interactive DataTables (sortable, searchable, filterable)
199
202
  --checkstyle Generate Checkstyle XML output for integration with Jenkins and other tools
200
203
  -m, --modified Calculate modified cyclomatic complexity number , which count a
201
204
  switch/case with multiple cases as one CCN.
@@ -413,3 +416,14 @@ Lizard is also used as a plugin for fastlane to help check code complexity and s
413
416
  - `European research project FASTEN (Fine-grained Analysis of SofTware Ecosystems as Networks, <http://fasten-project.eu/)>`_
414
417
  - `for a quality analyzer <https://github.com/fasten-project/quality-analyzer>`_
415
418
 
419
+ How To Contribute
420
+ -----------------
421
+
422
+ Contributions are welcome. Please refer to the rules and development workflow in:
423
+
424
+ - https://github.com/terryyin/lizard/tree/master/.cursor/rules
425
+
426
+ These guidelines are usable by both AI assistants and human contributors — what works for AI works for "I" as well — to keep changes cohesive, simple, and well-tested.
427
+
428
+
429
+
@@ -1,11 +1,11 @@
1
- lizard.py,sha256=H0EgGsU0ou4IlRY9AxIyY_O4_WO8trZda4GGM7a4kNQ,41403
1
+ lizard.py,sha256=B0g5lEp5me31bQpjfMKVayF0iUfH0PalVecOKeUX3-A,41365
2
2
  lizard_ext/__init__.py,sha256=AkZYVqCgd7XX1hMwLMyh_ABm9geIHmobEch-thYXZz8,932
3
3
  lizard_ext/auto_open.py,sha256=byD_RbeVhvSUhR2bJMRitvA3zcKEapFwv0-XaDJ6GFo,1096
4
- lizard_ext/checkstyleoutput.py,sha256=puuTq5IfM48NkDdds2FjF1BFq1QDGfsf5GahLTVfN6Y,1213
4
+ lizard_ext/checkstyleoutput.py,sha256=UzDHg837ErEZepXkR8I8YCoz2r1lkmzGctMA7dpyB-M,1245
5
5
  lizard_ext/csvoutput.py,sha256=43fhmo8kB85qcdujCwySGNuTC4FkKUPLqIApPeljPnA,2663
6
6
  lizard_ext/default_ordered_dict.py,sha256=YbVz6nPlQ6DjWc_EOFBz6AJN2XLo9dpnUdeyejQvUDE,831
7
7
  lizard_ext/extension_base.py,sha256=rnjUL2mqSGToUVYydju7fa8ZwynLPY8S1F17gIJP55I,346
8
- lizard_ext/htmloutput.py,sha256=oavhEzCIJtoJrFveiWIp0pMAOwg4pukJ6l07IpmPiag,4426
8
+ lizard_ext/htmloutput.py,sha256=G7MdLD7AwuR0LLCAkEpyHx4N_ssCcFQdXd7Xg5jZWX0,6537
9
9
  lizard_ext/keywords.py,sha256=VxsxoATtKV-8egMKd7I8sd2qbZMtEFEpsszk__6rmjQ,893
10
10
  lizard_ext/lizardboolcount.py,sha256=abmMA9X3VFRO5mziicUxWKmHldHNC0jBEe7NKAKA5fs,1062
11
11
  lizard_ext/lizardcomplextags.py,sha256=flrwYg24P5DoDsBO3gdcK9SxkugX_brhfjuu8zgPnOc,681
@@ -20,17 +20,17 @@ lizard_ext/lizardignoreassert.py,sha256=sqLwcnJQ06SYqIk901ib4NQ8ECwjIe_qL4T6z1wL
20
20
  lizard_ext/lizardio.py,sha256=xQN-AgLGLKJarJkgfaqX_TKyupbb7GTcwPxrL2B1J1w,3357
21
21
  lizard_ext/lizardmccabe.py,sha256=RiO8ASmQUah4udOH8SbE2OOMxwShIPByW93TlFxXlQU,1274
22
22
  lizard_ext/lizardmodified.py,sha256=4Ld7yy1D2m2biMtx-g0DtjXwLa-9mG2togS2IRDAF3k,705
23
- lizard_ext/lizardnd.py,sha256=2_uZkRlaM1mDLLW8yXtO7MtjMGklU8_Cy-6ocaILEuo,4391
23
+ lizard_ext/lizardnd.py,sha256=h4MaSsFAY5rK8kOSr61LkWntwJWV1KnhaEOWZgp3-c0,7496
24
24
  lizard_ext/lizardnonstrict.py,sha256=pPG22up2uh9rEkdRFtTWdiuOaiBNe0ZUjaZQpSTX5LE,394
25
25
  lizard_ext/lizardns.py,sha256=8pztUoRS_UWN24MawwxeHEJgYh49id5PWODUBb6O72U,4184
26
26
  lizard_ext/lizardoutside.py,sha256=FGm2tbBZ17-2OCgmQlD-vobUCfQKb0FAygf86eM3xuM,336
27
27
  lizard_ext/lizardstatementcount.py,sha256=xYk6ixSIItSE1BWQXzrWmduFgGhA3VR817SNKLffyVQ,1182
28
28
  lizard_ext/lizardwordcount.py,sha256=2QYXD7-AtkkgAbi9VSidunMbSsGQ7MKYb6IT-bS-cok,7575
29
- lizard_ext/version.py,sha256=N4oSGH5svpjDluQQOHRznK3TrM-OyaJq6FN_Yuc5pvw,182
29
+ lizard_ext/version.py,sha256=gScBFBQFiyhdj77Gb-x1hY6wABdKwZlb_aaNdiiADQ0,181
30
30
  lizard_ext/xmloutput.py,sha256=-cbh0he4O_X-wX56gkv9AnSPNN0qvR7FACqlBeezUS4,5609
31
- lizard_languages/__init__.py,sha256=ArNmUrVSU6HFxhDka1-vWMZpVIM39P-gqv6BoOLNMV8,1522
32
- lizard_languages/clike.py,sha256=INd5tkvwEVZm7dx2yHG2OIFHZn7JzQGmnT9WQNFZ2XU,11110
33
- lizard_languages/code_reader.py,sha256=IfEHg9lzKnyCipX9xscgyGEOovll5qr9dCe5cSX2sJM,6852
31
+ lizard_languages/__init__.py,sha256=DniggHM3SOFlwkP8Tm4csEcisbf7_i1EvfoAkG9Atg0,1611
32
+ lizard_languages/clike.py,sha256=_oDUbA1hEIPujKw3wyAtC1uFPvjmg4TBgKbHLa-S1wg,13597
33
+ lizard_languages/code_reader.py,sha256=O0nZkI4xEHIWGgFBDwczj8-ay7aKllPnzRsblQtL3Gc,6874
34
34
  lizard_languages/csharp.py,sha256=EfFAIOIcJXUUhXTlZApXGSlzG34NZvHM9OSe6m7hpv0,2141
35
35
  lizard_languages/erlang.py,sha256=7YJS2cMyXDKEV_kpH8DzBARxFCFcjKuTOPSQ3K52auU,3860
36
36
  lizard_languages/fortran.py,sha256=KATDsnfjob5W3579A_VxbwrbTkK7Rx3p0eXdBgjx25I,8973
@@ -39,31 +39,32 @@ lizard_languages/go.py,sha256=sntz0jOEuj4klPipoTFd16UDK1fAUQfwK7YX_cLMZAc,1346
39
39
  lizard_languages/golike.py,sha256=vRIfjTVvc0VmJf27lTOLht55ZF1AQ9wn0Fvu-9WabWk,2858
40
40
  lizard_languages/java.py,sha256=HQBTZjUKbUJwgmtLYIzJrWtPpFP3ZdBP_NJK7YOXZC0,6424
41
41
  lizard_languages/javascript.py,sha256=vniCNMW-ea9Jpv6c8qCcjLVDYjT8VztjXigp5XRWt0E,317
42
- lizard_languages/js_style_language_states.py,sha256=6mLrHfvDC4oHttKsSRGkE-ayG8uKSEm4E4rlhaUN5lA,6396
43
42
  lizard_languages/js_style_regex_expression.py,sha256=Xgyogch4xElYtCG4EnBKvalHTl3tjRPcIIcIQRRd61I,1970
44
- lizard_languages/jsx.py,sha256=TLH93qrZO2r_tiiv1XBIbw2_mbUMjwCZAuBz5vG5oBw,11696
45
43
  lizard_languages/kotlin.py,sha256=v_o2orEzA5gB9vM_0h-E4QXjrc5Yum-0K6W6_laOThc,2844
46
44
  lizard_languages/lua.py,sha256=3nqBcunBzJrhv4Iqaf8xvbyqxZy3aSxJ-IiHimHFlac,1573
47
45
  lizard_languages/objc.py,sha256=2a1teLdaXZBtCeFiIZer1j_sVx9LZ1CbF2XfnqlvLmk,2319
48
46
  lizard_languages/perl.py,sha256=136w620eECe_t-kmlRUGrsZSxQNo2JQ_PZTSQfCSmHY,11987
49
47
  lizard_languages/php.py,sha256=UV40p8WzNC64NQ5qElPKzcFTjVt5kenLMz-eKYlcnMY,9940
50
- lizard_languages/python.py,sha256=4AK0JAPBSM7eKrqDdSaHfewCGAYMzdhUhi5kZK4u23k,3847
48
+ lizard_languages/plsql.py,sha256=17zf0AkDLFW7mA3ngUNrU7z6U6DkmA96CKOcEhYNC5Q,17137
49
+ lizard_languages/python.py,sha256=AsL0SmQ73zhNS1iGi4Z8VtuUE0VjqBzo9W8W0mjqL0E,5790
50
+ lizard_languages/r.py,sha256=IoyMhmFtUmTji6rm6-fqss_j_kvIHu3JjABRh6RNys0,12583
51
51
  lizard_languages/ruby.py,sha256=HL1ZckeuUUJU3QSVAOPsG_Zsl0C6X2PX5_VaWqclzkM,2277
52
52
  lizard_languages/rubylike.py,sha256=dAGZ2wqW8nqaESMU8HkeR9gwQ-q9fmZqE6AANvVZD1Q,3426
53
- lizard_languages/rust.py,sha256=vfBBktNwIZL8AiaAJ5EW8o4rL7_TwjDo3QhpfxxecfE,596
53
+ lizard_languages/rust.py,sha256=WarDHnFZv99Yu3_C5DpZfLS8dVWz6AcOzo2dzLW94rA,817
54
54
  lizard_languages/scala.py,sha256=6Jr_TG945VYqB3o5weD7jN7S4beHt4aVj3r-fmKeMAM,1316
55
55
  lizard_languages/script_language.py,sha256=SKe45AbO6Z-axbN8KW_g7jf9g7YTXZ6dWzJj4ubDsM8,1172
56
56
  lizard_languages/solidity.py,sha256=Z0GD7U5bI5eUikdy7m_iKWeFD5yXRYq4r3zycscOhJQ,553
57
+ lizard_languages/st.py,sha256=7fpOfNAoUjNY8RCHSYLufnOzZTUkKwjVvcyRyM1xP2Y,4160
57
58
  lizard_languages/swift.py,sha256=p8S2OAkQOx9YQ02yhoVXFkr7pMqUH1Nb3RVXPHRU_9M,2450
58
59
  lizard_languages/tnsdl.py,sha256=pGcalA_lHY362v2wwPS86seYBOOBBjvmU6vd4Yy3A9g,2803
59
- lizard_languages/tsx.py,sha256=AgpULWV9VEF4wJGtNZeeOl6NuTiQOstM8P3jObz-_xY,1249
60
+ lizard_languages/tsx.py,sha256=1oOVCcz5yHkmYLYGhSarCMSXfGVasweklAqqapkuNR4,17160
60
61
  lizard_languages/ttcn.py,sha256=ygjw_raBmPF-4mgoM8m6CAdyEMpTI-n1kZJK1RL4Vxo,2131
61
- lizard_languages/typescript.py,sha256=fiEiGs0VO8uofsLcOD7t8UzKp0Un9SSbfQTU87mhNVo,5626
62
+ lizard_languages/typescript.py,sha256=unCDj040dY9fTOw9iIykqjt2j5tZWJ2Bm9fHYjOWY5I,14706
62
63
  lizard_languages/vue.py,sha256=KXUBUo2R1zNF8Pffrz_KsQEN44m5XFRMoGXylxKUeT0,1038
63
64
  lizard_languages/zig.py,sha256=NX1iyBstBuJFeAGBOAIaRfrmeBREne2HX6Pt4fXZZTQ,586
64
- lizard-1.17.31.dist-info/LICENSE.txt,sha256=05ZjgQ8Cl1dD9p0BhW-Txzkc5rhCogGJVEuf1GT2Y_M,1303
65
- lizard-1.17.31.dist-info/METADATA,sha256=8P82zQ9bK0enmWJ9bOSezN6Ly1oO5WvgfWO6i0jSEx4,16230
66
- lizard-1.17.31.dist-info/WHEEL,sha256=Kh9pAotZVRFj97E15yTA4iADqXdQfIVTHcNaZTjxeGM,110
67
- lizard-1.17.31.dist-info/entry_points.txt,sha256=ZBqPhu-J3NoGGW5vn2Gfyoo0vdVlgBgM-wlNm0SGYUQ,39
68
- lizard-1.17.31.dist-info/top_level.txt,sha256=5NTrTaOLhHuTzXaGcZPKfuaOgUv7WafNGe0Zl5aycpg,35
69
- lizard-1.17.31.dist-info/RECORD,,
65
+ lizard-1.19.0.dist-info/LICENSE.txt,sha256=05ZjgQ8Cl1dD9p0BhW-Txzkc5rhCogGJVEuf1GT2Y_M,1303
66
+ lizard-1.19.0.dist-info/METADATA,sha256=DNAVbhrtdVrI5tn2rWZoSBwCfZ5ArhMQ0XNmHvOqbh4,16697
67
+ lizard-1.19.0.dist-info/WHEEL,sha256=Kh9pAotZVRFj97E15yTA4iADqXdQfIVTHcNaZTjxeGM,110
68
+ lizard-1.19.0.dist-info/entry_points.txt,sha256=pPMMwoHAltzGHqR2WeJQItLeeyR7pbX5R2S_POC-xoo,40
69
+ lizard-1.19.0.dist-info/top_level.txt,sha256=5NTrTaOLhHuTzXaGcZPKfuaOgUv7WafNGe0Zl5aycpg,35
70
+ lizard-1.19.0.dist-info/RECORD,,
@@ -1,2 +1,3 @@
1
1
  [console_scripts]
2
2
  lizard = lizard:main
3
+
lizard.py CHANGED
@@ -623,7 +623,7 @@ def whitelist_filter(warnings, script=None, whitelist=None):
623
623
 
624
624
  def get_whitelist(whitelist):
625
625
  if os.path.isfile(whitelist):
626
- return open(whitelist, mode='r').read()
626
+ return auto_read(whitelist)
627
627
  if whitelist != DEFAULT_WHITELIST:
628
628
  print("WARNING: !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
629
629
  print("WARNING: the whitelist \""+whitelist+"\" doesn't exist.")
@@ -923,12 +923,12 @@ def get_all_source_files(paths, exclude_patterns, lans):
923
923
  for path in paths:
924
924
  gitignore_path = os.path.join(path, '.gitignore')
925
925
  if os.path.exists(gitignore_path):
926
- with open(gitignore_path, 'r') as gitignore_file:
927
- # Read lines and strip whitespace and empty lines
928
- patterns = [line.strip() for line in gitignore_file.readlines()]
929
- patterns = [p for p in patterns if p and not p.startswith('#')]
930
- gitignore_spec = pathspec.PathSpec.from_lines('gitwildmatch', patterns)
931
- base_path = path
926
+ gitignore_file = auto_read(gitignore_path)
927
+ # Read lines and strip whitespace and empty lines
928
+ patterns = [line.strip() for line in gitignore_file.splitlines()]
929
+ patterns = [p for p in patterns if p and not p.startswith('#')]
930
+ gitignore_spec = pathspec.PathSpec.from_lines('gitwildmatch', patterns)
931
+ base_path = path
932
932
  break
933
933
  except ImportError:
934
934
  pass
@@ -2,6 +2,7 @@
2
2
  Checkstyle XML output for Lizard
3
3
  '''
4
4
 
5
+
5
6
  def checkstyle_output(all_result, verbose):
6
7
  result = all_result.result
7
8
  import xml.etree.ElementTree as ET
@@ -20,7 +21,8 @@ def checkstyle_output(all_result, verbose):
20
21
  line=str(func.start_line),
21
22
  column="0",
22
23
  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
+ message=f"{func.name} has {func.nloc} NLOC, {func.cyclomatic_complexity} CCN, "
25
+ f"{func.token_count} token, {len(func.parameters)} PARAM, {func.length} length",
24
26
  source="lizard"
25
27
  )
26
28
 
@@ -28,4 +30,4 @@ def checkstyle_output(all_result, verbose):
28
30
  import xml.dom.minidom
29
31
  rough_string = ET.tostring(checkstyle, 'utf-8')
30
32
  reparsed = xml.dom.minidom.parseString(rough_string)
31
- return reparsed.toprettyxml(indent=" ")
33
+ return reparsed.toprettyxml(indent=" ")
lizard_ext/htmloutput.py CHANGED
@@ -48,6 +48,12 @@ TEMPLATE = '''<!DOCTYPE HTML PUBLIC
48
48
  <head>
49
49
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
50
50
  <title>Code complexity report</title>
51
+ <!-- DataTables CSS -->
52
+ <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css">
53
+ <!-- jQuery -->
54
+ <script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
55
+ <!-- DataTables JS -->
56
+ <script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
51
57
  <style>
52
58
  h2
53
59
  {
@@ -88,6 +94,42 @@ TEMPLATE = '''<!DOCTYPE HTML PUBLIC
88
94
  font-family: sans-serif;
89
95
  white-space: nowrap;
90
96
  }
97
+ td.file-header
98
+ {
99
+ background-color: LightBlue;
100
+ font-weight: bold;
101
+ }
102
+ td.function-name
103
+ {
104
+ background-color: LightSteelBlue;
105
+ }
106
+ /* DataTables wrapper styling */
107
+ .dataTables_wrapper {
108
+ margin: 0 auto;
109
+ width: 95%;
110
+ }
111
+ /* Fallback styling if DataTables CSS doesn't load */
112
+ table#complexityTable {
113
+ border-collapse: collapse;
114
+ width: 95%;
115
+ margin: 0 auto;
116
+ }
117
+ table#complexityTable th {
118
+ background-color: #4CAF50;
119
+ color: white;
120
+ padding: 10px;
121
+ text-align: left;
122
+ border: 1px solid #ddd;
123
+ }
124
+ table#complexityTable td {
125
+ border: 1px solid #ddd;
126
+ }
127
+ table#complexityTable tr:nth-child(even) {
128
+ background-color: #f2f2f2;
129
+ }
130
+ table#complexityTable tr:hover {
131
+ background-color: #ddd;
132
+ }
91
133
  </style>
92
134
  </head>
93
135
  <body>
@@ -95,26 +137,29 @@ TEMPLATE = '''<!DOCTYPE HTML PUBLIC
95
137
 
96
138
  <center>
97
139
 
98
- <table>
140
+ <table id="complexityTable" class="display" style="width:100%">
141
+ <thead>
142
+ <tr>
143
+ <th>File</th>
144
+ <th>Function name</th>
145
+ <th>Cyclomatic complexity ({{ thresholds["cyclomatic_complexity"] }})</th>
146
+ <th>LOC ({{ thresholds["nloc"] }})</th>
147
+ <th>
148
+ {% if thresholds["token_count"] %}
149
+ Token count ({{ thresholds["token_count"] }})
150
+ {% else %}
151
+ Token count
152
+ {% endif %}
153
+ </th>
154
+ <th>Parameter count ({{ thresholds["parameter_count"] }})</th>
155
+ </tr>
156
+ </thead>
157
+ <tbody>
99
158
  {% for file in files %}
100
- <tr><td colspan="7" style="background-color:LightBlue;">
101
- Source file: <b>{{ file.filename }}</b></td></tr>
102
- {% if file.functions|length > 0 %}
103
- <tr><th>Function name</th><th></th><th>
104
- Cyclomatic complexity
105
- ({{ thresholds["cyclomatic_complexity"] }})
106
- </th><th>LOC ({{ thresholds["nloc"] }})</th><th>
107
- {% if thresholds["token_count"] %}
108
- Token count ({{ thresholds["token_count"] }})
109
- {% else %}
110
- Token count
111
- {% endif %}
112
- </th><th>Parameter count ({{ thresholds["parameter_count"] }})</th></tr>
113
- {% endif %}
114
159
  {% for func in file.functions %}
115
160
  <tr>
116
- <td style="background-color:LightSteelBlue">{{ func.name }}</td>
117
- <td></td>
161
+ <td>{{ file.filename }}</td>
162
+ <td class="function-name">{{ func.name }}</td>
118
163
  {% if func.cyclomatic_complexity > thresholds["cyclomatic_complexity"] %}
119
164
  <td class="greater-value">{{ func.cyclomatic_complexity }}</td>
120
165
  {% else %}
@@ -145,6 +190,7 @@ Cyclomatic complexity
145
190
  </tr>
146
191
  {% endfor %}
147
192
  {% endfor %}
193
+ </tbody>
148
194
  </table>
149
195
  <center>
150
196
 
@@ -154,6 +200,29 @@ Cyclomatic complexity
154
200
  <a href="http://www.lizard.ws/">Lizard</a> on {{ date }}
155
201
  </td></tr>
156
202
  </table>
203
+
204
+ <script>
205
+ // Gracefully degrade if CDN is unavailable
206
+ if (typeof jQuery !== 'undefined' && typeof jQuery.fn.dataTable !== 'undefined') {
207
+ $(document).ready(function() {
208
+ try {
209
+ $('#complexityTable').DataTable({
210
+ "pageLength": 25,
211
+ "order": [[2, "desc"]], // Sort by cyclomatic complexity descending by default
212
+ "columnDefs": [
213
+ { "type": "num", "targets": [2, 3, 4, 5] } // Ensure numeric sorting for metric columns
214
+ ]
215
+ });
216
+ } catch (e) {
217
+ console.warn('DataTables initialization failed. Displaying static table.', e);
218
+ }
219
+ });
220
+ } else {
221
+ // Fallback: Table will display as static HTML
222
+ console.info('DataTables not available. Displaying static HTML table.');
223
+ }
224
+ </script>
225
+
157
226
  </body>
158
227
  </html>
159
228
 
lizard_ext/lizardnd.py CHANGED
@@ -45,11 +45,43 @@ class LizardExtension(object): # pylint: disable=R0903
45
45
  else:
46
46
  indent_indicator = ';'
47
47
  for token in tokens:
48
- if token in loops:
48
+ # Handle opening parenthesis - start of condition
49
+ if token == '(':
50
+ reader.context.set_in_condition(True)
51
+ reader.context.increment_condition_depth()
52
+ reader.context.set_logical_operator_added(False) # Reset for new condition
53
+ # Handle closing parenthesis - end of condition
54
+ elif token == ')':
55
+ reader.context.decrement_condition_depth()
56
+ if reader.context.get_condition_depth() == 0:
57
+ reader.context.set_in_condition(False)
58
+ reader.context.set_logical_operator_added(False)
59
+
60
+ # Handle logical operators && and || within conditions
61
+ elif token in ('&&', '||'):
62
+ if reader.context.get_in_condition():
63
+ # Only add nesting depth for the first logical operator in a condition
64
+ # Subsequent && or || operators in the same condition don't add depth
65
+ if not reader.context.get_logical_operator_added():
66
+ l_depth = reader.context.add_nd_condition()
67
+ if not reader.context.get_loop_status():
68
+ reader.context.add_hidden_bracket_condition()
69
+ reader.context.loop_bracket_status()
70
+ reader.context.set_logical_operator_added(True)
71
+ else:
72
+ # Not in a condition, treat as regular nesting
73
+ l_depth = reader.context.add_nd_condition()
74
+ if not reader.context.get_loop_status():
75
+ reader.context.add_hidden_bracket_condition()
76
+ reader.context.loop_bracket_status()
77
+
78
+ # Handle other loop keywords (if, for, while, etc.)
79
+ elif token in loops and token not in ('&&', '||'):
49
80
  l_depth = reader.context.add_nd_condition()
50
81
  if not reader.context.get_loop_status():
51
82
  reader.context.add_hidden_bracket_condition()
52
83
  reader.context.loop_bracket_status()
84
+
53
85
  if token == loop_indicator:
54
86
  reader.context.loop_bracket_status()
55
87
  if token == bracket:
@@ -82,6 +114,7 @@ class NDFileInfoAddition(FileInfoBuilder):
82
114
  self.current_function.nesting_depth = 0
83
115
  self.current_function.hidden_bracket = 0
84
116
  self.current_function.bracket_loop = False
117
+ self.reset_condition_tracking()
85
118
 
86
119
  def add_hidden_bracket_condition(self, inc=1):
87
120
  self.current_function.hidden_bracket += inc
@@ -96,6 +129,33 @@ class NDFileInfoAddition(FileInfoBuilder):
96
129
  def get_loop_status(self):
97
130
  return self.current_function.bracket_loop
98
131
 
132
+ def set_in_condition(self, in_condition):
133
+ self.current_function.in_condition = in_condition
134
+
135
+ def get_in_condition(self):
136
+ return self.current_function.in_condition
137
+
138
+ def increment_condition_depth(self):
139
+ self.current_function.condition_depth += 1
140
+
141
+ def decrement_condition_depth(self):
142
+ if self.current_function.condition_depth > 0:
143
+ self.current_function.condition_depth -= 1
144
+
145
+ def get_condition_depth(self):
146
+ return self.current_function.condition_depth
147
+
148
+ def reset_condition_tracking(self):
149
+ self.current_function.in_condition = False
150
+ self.current_function.condition_depth = 0
151
+ self.current_function.logical_operator_added = False
152
+
153
+ def set_logical_operator_added(self, added):
154
+ self.current_function.logical_operator_added = added
155
+
156
+ def get_logical_operator_added(self):
157
+ return self.current_function.logical_operator_added
158
+
99
159
 
100
160
  def get_method(cls, name):
101
161
  """ python3 doesn't need the __func__ to get the func of the
@@ -127,6 +187,9 @@ def _init_nesting_depth_data(self, *_):
127
187
  self.max_nesting_depth = 0
128
188
  self.hidden_bracket = 0
129
189
  self.bracket_loop = False
190
+ self.in_condition = False # Track if we're inside a condition
191
+ self.condition_depth = 0 # Track nesting depth within conditions
192
+ self.logical_operator_added = False # Track if we've added nesting for logical operators in current condition
130
193
 
131
194
 
132
195
  patch(NDFileInfoAddition, FileInfoBuilder)
lizard_ext/version.py CHANGED
@@ -3,4 +3,4 @@
3
3
  #
4
4
  # pylint: disable=missing-docstring,invalid-name
5
5
 
6
- version = "1.17.31"
6
+ version = "1.19.0"
@@ -21,10 +21,12 @@ from .rust import RustReader
21
21
  from .typescript import TypeScriptReader
22
22
  from .fortran import FortranReader
23
23
  from .solidity import SolidityReader
24
- from .jsx import JSXReader
25
24
  from .tsx import TSXReader
26
25
  from .vue import VueReader
27
26
  from .perl import PerlReader
27
+ from .st import StReader
28
+ from .r import RReader
29
+ from .plsql import PLSQLReader
28
30
 
29
31
 
30
32
  def languages():
@@ -50,10 +52,12 @@ def languages():
50
52
  SolidityReader,
51
53
  ErlangReader,
52
54
  ZigReader,
53
- JSXReader,
54
55
  TSXReader,
55
56
  VueReader,
56
57
  PerlReader,
58
+ StReader,
59
+ RReader,
60
+ PLSQLReader,
57
61
  ]
58
62
 
59
63
 
lizard_languages/clike.py CHANGED
@@ -19,7 +19,7 @@ class CCppCommentsMixin(object): # pylint: disable=R0903
19
19
  class CLikeReader(CodeReader, CCppCommentsMixin):
20
20
  ''' This is the reader for C, C++ and Java. '''
21
21
 
22
- ext = ["c", "cpp", "cc", "mm", "cxx", "h", "hpp"]
22
+ ext = ["c", "cpp", "cc", "cxx", "h", "hpp"]
23
23
  language_names = ['cpp', 'c']
24
24
  macro_pattern = re.compile(r"#\s*(\w+)\s*(.*)", re.M | re.S)
25
25
 
@@ -54,7 +54,7 @@ class CLikeReader(CodeReader, CCppCommentsMixin):
54
54
  elif macro.group(1) == 'include':
55
55
  yield "#include"
56
56
  yield macro.group(2) or "\"\""
57
- for _ in macro.group(2).splitlines()[1:]:
57
+ for _ in macro.group(2).split('\n')[1:]:
58
58
  yield '\n'
59
59
  else:
60
60
  yield token
@@ -162,6 +162,11 @@ class CLikeStates(CodeStateMachine):
162
162
  def _state_global(self, token):
163
163
  if token[0].isalpha() or token[0] in '_~':
164
164
  self.try_new_function(token)
165
+ elif token == '[':
166
+ # Check if this might be a lambda expression (C++ only)
167
+ # Java doesn't have lambda expressions, so skip lambda detection for Java
168
+ if not hasattr(self, 'class_name'): # JavaStates has class_name attribute
169
+ self._state = self._state_lambda_check
165
170
 
166
171
  def _state_function(self, token):
167
172
  if token == '(':
@@ -310,3 +315,57 @@ class CLikeStates(CodeStateMachine):
310
315
  def _state_attribute(self, _):
311
316
  "Ignores function attributes with C++11 syntax, i.e., [[ attribute ]]."
312
317
  pass
318
+
319
+ def _state_lambda_check(self, token):
320
+ """Check if this is a lambda expression or a function attribute."""
321
+ if token == ']':
322
+ # This is a lambda expression [](params) or [capture](params)
323
+ # Skip the lambda and continue parsing normally
324
+ self._state = self._state_lambda_params
325
+ elif token == '[':
326
+ # This is a function attribute [[attribute]]
327
+ self._state = self._state_attribute
328
+ else:
329
+ # This is a lambda with capture list [capture](params)
330
+ # Skip until we find the closing bracket
331
+ self._state = self._state_lambda_capture
332
+
333
+ def _state_lambda_params(self, token):
334
+ """Handle lambda parameters and body."""
335
+ if token == '(':
336
+ # Start of parameter list, skip until closing parenthesis
337
+ self._state = self._state_lambda_param_list
338
+ else:
339
+ # No parameters, check for body
340
+ self._state = self._state_lambda_body
341
+
342
+ def _state_lambda_param_list(self, token):
343
+ """Handle lambda parameter list."""
344
+ if token == ')':
345
+ # End of parameter list, check for body
346
+ self._state = self._state_lambda_body
347
+ # Otherwise, continue in parameter list
348
+
349
+ def _state_lambda_body(self, token):
350
+ """Handle lambda body."""
351
+ if token == '{':
352
+ # Start of lambda body, skip until closing brace
353
+ self._state = self._state_lambda_body_skip
354
+ elif token == ';':
355
+ # Lambda without body, just a semicolon
356
+ self._state = self._state_global
357
+ # Otherwise, continue
358
+
359
+ def _state_lambda_body_skip(self, token):
360
+ """Skip lambda body until closing brace."""
361
+ if token == '}':
362
+ # End of lambda body, continue parsing normally
363
+ self._state = self._state_global
364
+ # Otherwise, continue skipping
365
+
366
+ def _state_lambda_capture(self, token):
367
+ """Handle lambda capture list."""
368
+ if token == ']':
369
+ # End of capture list, continue parsing normally
370
+ self._state = self._state_global
371
+ # Otherwise, continue in capture list
@@ -138,7 +138,7 @@ class CodeReader:
138
138
  r"|\/\/" + _until_end +
139
139
  r"|\#" +
140
140
  r"|:=|::|\*\*" +
141
- r"|\<\s*\?(?:\s*extends\s+\w+)?\s*\>" +
141
+ r"|\<(?=(?:[^<>]*\?)+[^<>]*\>)(?:[\w\s,.?]|(?:extends))+\>" +
142
142
  r"|" + r"|".join(re.escape(s) for s in combined_symbols) +
143
143
  r"|\\\n" +
144
144
  r"|\n" +