lizard 1.18.0__py2.py3-none-any.whl → 1.20.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.
- {lizard-1.18.0.dist-info → lizard-1.20.0.dist-info}/METADATA +30 -20
- lizard-1.20.0.dist-info/RECORD +70 -0
- lizard_ext/__init__.py +0 -1
- lizard_ext/htmloutput.py +86 -17
- lizard_ext/lizardcomplextags.py +6 -0
- lizard_ext/lizardmccabe.py +6 -1
- lizard_ext/lizardmodified.py +7 -2
- lizard_ext/lizardnd.py +64 -1
- lizard_ext/lizardnonstrict.py +2 -1
- lizard_ext/lizardns.py +31 -8
- lizard_ext/version.py +1 -1
- lizard_languages/__init__.py +2 -0
- lizard_languages/clike.py +6 -3
- lizard_languages/code_reader.py +27 -3
- lizard_languages/csharp.py +21 -2
- lizard_languages/erlang.py +9 -1
- lizard_languages/fortran.py +5 -6
- lizard_languages/gdscript.py +6 -2
- lizard_languages/kotlin.py +6 -3
- lizard_languages/perl.py +8 -4
- lizard_languages/php.py +6 -2
- lizard_languages/plsql.py +422 -0
- lizard_languages/python.py +6 -4
- lizard_languages/r.py +9 -6
- lizard_languages/rubylike.py +6 -3
- lizard_languages/rust.py +7 -2
- lizard_languages/scala.py +6 -2
- lizard_languages/solidity.py +6 -1
- lizard_languages/st.py +9 -5
- lizard_languages/swift.py +6 -2
- lizard_languages/tnsdl.py +6 -1
- lizard_languages/tsx.py +2 -2
- lizard_languages/ttcn.py +5 -3
- lizard_languages/typescript.py +58 -4
- lizard_languages/zig.py +7 -1
- lizard-1.18.0.dist-info/RECORD +0 -69
- {lizard-1.18.0.dist-info → lizard-1.20.0.dist-info}/LICENSE.txt +0 -0
- {lizard-1.18.0.dist-info → lizard-1.20.0.dist-info}/WHEEL +0 -0
- {lizard-1.18.0.dist-info → lizard-1.20.0.dist-info}/entry_points.txt +0 -0
- {lizard-1.18.0.dist-info → lizard-1.20.0.dist-info}/top_level.txt +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: lizard
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.20.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
|
-
Download-URL: https://pypi.python.org/lizard/
|
|
7
6
|
Author: Terry Yin
|
|
8
7
|
Author-email: terry@odd-e.com
|
|
9
8
|
License: MIT
|
|
9
|
+
Download-URL: https://pypi.python.org/lizard/
|
|
10
10
|
Project-URL: Source, https://github.com/terryyin/lizard
|
|
11
11
|
Platform: any
|
|
12
12
|
Classifier: Development Status :: 5 - Production/Stable
|
|
@@ -50,31 +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
|
-
-
|
|
58
|
-
-
|
|
61
|
+
- Kotlin
|
|
62
|
+
- Lua
|
|
59
63
|
- Objective-C
|
|
60
|
-
-
|
|
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
|
-
-
|
|
71
|
-
- Kotlin
|
|
71
|
+
- Scala
|
|
72
72
|
- Solidity
|
|
73
|
-
- Erlang
|
|
74
|
-
- Zig
|
|
75
|
-
- Perl
|
|
76
73
|
- Structured Text (St)
|
|
77
|
-
-
|
|
74
|
+
- Swift
|
|
75
|
+
- TTCN-3
|
|
76
|
+
- TypeScript (With TSX)
|
|
77
|
+
- VueJS
|
|
78
|
+
- Zig
|
|
78
79
|
|
|
79
80
|
By default lizard will search for any source code that it knows and mix
|
|
80
81
|
all the results together. This might not be what you want. You can use
|
|
@@ -162,7 +163,7 @@ Options
|
|
|
162
163
|
search for all languages it knows. `lizard -l cpp -l java`searches for
|
|
163
164
|
C++ and Java code. The available languages are: cpp, java, csharp,
|
|
164
165
|
javascript, python, objectivec, ttcn, ruby, php, swift, scala, GDScript,
|
|
165
|
-
go, lua, rust, typescript
|
|
166
|
+
go, lua, rust, typescript, plsql
|
|
166
167
|
-V, --verbose Output in verbose mode (long function name)
|
|
167
168
|
-C CCN, --CCN CCN Threshold for cyclomatic complexity number warning. The default value is
|
|
168
169
|
15. Functions with CCN bigger than it will generate warning
|
|
@@ -197,7 +198,7 @@ Options
|
|
|
197
198
|
-X, --xml Generate XML in cppncss style instead of the tabular output. Useful to
|
|
198
199
|
generate report in Jenkins server
|
|
199
200
|
--csv Generate CSV output as a transform of the default output
|
|
200
|
-
-H, --html Output HTML report
|
|
201
|
+
-H, --html Output HTML report with interactive DataTables (sortable, searchable, filterable)
|
|
201
202
|
--checkstyle Generate Checkstyle XML output for integration with Jenkins and other tools
|
|
202
203
|
-m, --modified Calculate modified cyclomatic complexity number , which count a
|
|
203
204
|
switch/case with multiple cases as one CCN.
|
|
@@ -415,5 +416,14 @@ Lizard is also used as a plugin for fastlane to help check code complexity and s
|
|
|
415
416
|
- `European research project FASTEN (Fine-grained Analysis of SofTware Ecosystems as Networks, <http://fasten-project.eu/)>`_
|
|
416
417
|
- `for a quality analyzer <https://github.com/fasten-project/quality-analyzer>`_
|
|
417
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
|
+
|
|
418
428
|
|
|
419
429
|
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
lizard.py,sha256=B0g5lEp5me31bQpjfMKVayF0iUfH0PalVecOKeUX3-A,41365
|
|
2
|
+
lizard_ext/__init__.py,sha256=UQ2oZ4ej1CCekgiY2Qj8sGx8HheoYyxjcOxvrwF70kc,871
|
|
3
|
+
lizard_ext/auto_open.py,sha256=byD_RbeVhvSUhR2bJMRitvA3zcKEapFwv0-XaDJ6GFo,1096
|
|
4
|
+
lizard_ext/checkstyleoutput.py,sha256=UzDHg837ErEZepXkR8I8YCoz2r1lkmzGctMA7dpyB-M,1245
|
|
5
|
+
lizard_ext/csvoutput.py,sha256=43fhmo8kB85qcdujCwySGNuTC4FkKUPLqIApPeljPnA,2663
|
|
6
|
+
lizard_ext/default_ordered_dict.py,sha256=YbVz6nPlQ6DjWc_EOFBz6AJN2XLo9dpnUdeyejQvUDE,831
|
|
7
|
+
lizard_ext/extension_base.py,sha256=rnjUL2mqSGToUVYydju7fa8ZwynLPY8S1F17gIJP55I,346
|
|
8
|
+
lizard_ext/htmloutput.py,sha256=G7MdLD7AwuR0LLCAkEpyHx4N_ssCcFQdXd7Xg5jZWX0,6537
|
|
9
|
+
lizard_ext/keywords.py,sha256=VxsxoATtKV-8egMKd7I8sd2qbZMtEFEpsszk__6rmjQ,893
|
|
10
|
+
lizard_ext/lizardboolcount.py,sha256=abmMA9X3VFRO5mziicUxWKmHldHNC0jBEe7NKAKA5fs,1062
|
|
11
|
+
lizard_ext/lizardcomplextags.py,sha256=z4Jsz8HNxbZXhDRuUeVnNfgt5Zv4FYEW2kRPO4G3F3E,1027
|
|
12
|
+
lizard_ext/lizardcpre.py,sha256=bVrMXffGUZlydv_zwIVtp-Ij9XyXMFEcap2R4DmLXPU,1277
|
|
13
|
+
lizard_ext/lizarddependencycount.py,sha256=6Nt2z69cyG8P3pceTUCTU6yh4h73lezL2awRFh7K77Y,2423
|
|
14
|
+
lizard_ext/lizarddumpcomments.py,sha256=-c46U3CP_beDfBuB9QhObpiB6wK7xq1u9jeRjgAyU90,683
|
|
15
|
+
lizard_ext/lizardduplicate.py,sha256=tlU-UTB_d_qjekiO4MSaXPBT1FSX8uTaOcfeXnjBzU8,9762
|
|
16
|
+
lizard_ext/lizardduplicated_param_list.py,sha256=_ApeSbjA-NU_rmICsivyfGEEg0-O94wWgWpeeE8F3Zc,1811
|
|
17
|
+
lizard_ext/lizardexitcount.py,sha256=ziAVFi5RgjxJOwAQy6LzdZZflkvvxTiXOIp0FAL0wKQ,692
|
|
18
|
+
lizard_ext/lizardgotocount.py,sha256=w2GWWwqVh4j7Fum41Wvg172b70JvtCm5BCzZUmmTlcM,514
|
|
19
|
+
lizard_ext/lizardignoreassert.py,sha256=sqLwcnJQ06SYqIk901ib4NQ8ECwjIe_qL4T6z1wLXAk,644
|
|
20
|
+
lizard_ext/lizardio.py,sha256=xQN-AgLGLKJarJkgfaqX_TKyupbb7GTcwPxrL2B1J1w,3357
|
|
21
|
+
lizard_ext/lizardmccabe.py,sha256=nLwfYjnAWn1DSoDc-k3iPvltiSX_YmAEdAfM3rOdLSQ,1538
|
|
22
|
+
lizard_ext/lizardmodified.py,sha256=51EY-HM840k_-jFiJugp_fOPBKvovX2Wu0s4ghLM3us,1002
|
|
23
|
+
lizard_ext/lizardnd.py,sha256=h4MaSsFAY5rK8kOSr61LkWntwJWV1KnhaEOWZgp3-c0,7496
|
|
24
|
+
lizard_ext/lizardnonstrict.py,sha256=DWdE2kTn39Gf8UkaRwIO9ogWTDuqA4lXNlQIBT3uLRM,457
|
|
25
|
+
lizard_ext/lizardns.py,sha256=3PYJrXZeFjzFnbotINZBm-MHeS8MQxVNtkQJd_S5fYA,5161
|
|
26
|
+
lizard_ext/lizardoutside.py,sha256=FGm2tbBZ17-2OCgmQlD-vobUCfQKb0FAygf86eM3xuM,336
|
|
27
|
+
lizard_ext/lizardstatementcount.py,sha256=xYk6ixSIItSE1BWQXzrWmduFgGhA3VR817SNKLffyVQ,1182
|
|
28
|
+
lizard_ext/lizardwordcount.py,sha256=2QYXD7-AtkkgAbi9VSidunMbSsGQ7MKYb6IT-bS-cok,7575
|
|
29
|
+
lizard_ext/version.py,sha256=-BXaaeyLOLQJRgob-W_0qvVq5YJw_YJGMzo50rFVFAc,181
|
|
30
|
+
lizard_ext/xmloutput.py,sha256=-cbh0he4O_X-wX56gkv9AnSPNN0qvR7FACqlBeezUS4,5609
|
|
31
|
+
lizard_languages/__init__.py,sha256=DniggHM3SOFlwkP8Tm4csEcisbf7_i1EvfoAkG9Atg0,1611
|
|
32
|
+
lizard_languages/clike.py,sha256=msdvIm5WAlczPUEtl8tK7xobWxxwow1gxgxjr3qURPo,13834
|
|
33
|
+
lizard_languages/code_reader.py,sha256=a5JxBQ_4HAeXiWHfLb6o6aCGJCCIAESRLjJcl3Zx-SY,7875
|
|
34
|
+
lizard_languages/csharp.py,sha256=UDuiG20Ydbb4KMD_mgb82shD37URq3ZvqNLpI77VBPE,3015
|
|
35
|
+
lizard_languages/erlang.py,sha256=tbVPDqi90jV2X1-luDYS32H1zK_2KHwssbJ-tCCOvzg,4249
|
|
36
|
+
lizard_languages/fortran.py,sha256=vwKz1VdwyUtWoECURXuR6e8E2kTR71HtpdbOsmDoyDw,8993
|
|
37
|
+
lizard_languages/gdscript.py,sha256=3mHeCarDq_ilDK_OZ2LOvwfWJqLTzK2lquprLDUnX08,737
|
|
38
|
+
lizard_languages/go.py,sha256=sntz0jOEuj4klPipoTFd16UDK1fAUQfwK7YX_cLMZAc,1346
|
|
39
|
+
lizard_languages/golike.py,sha256=vRIfjTVvc0VmJf27lTOLht55ZF1AQ9wn0Fvu-9WabWk,2858
|
|
40
|
+
lizard_languages/java.py,sha256=HQBTZjUKbUJwgmtLYIzJrWtPpFP3ZdBP_NJK7YOXZC0,6424
|
|
41
|
+
lizard_languages/javascript.py,sha256=vniCNMW-ea9Jpv6c8qCcjLVDYjT8VztjXigp5XRWt0E,317
|
|
42
|
+
lizard_languages/js_style_regex_expression.py,sha256=Xgyogch4xElYtCG4EnBKvalHTl3tjRPcIIcIQRRd61I,1970
|
|
43
|
+
lizard_languages/kotlin.py,sha256=1ao-VOHUrrSluxgGjMcMPIDt_-dqVfT1JOz15PJLJH8,3024
|
|
44
|
+
lizard_languages/lua.py,sha256=3nqBcunBzJrhv4Iqaf8xvbyqxZy3aSxJ-IiHimHFlac,1573
|
|
45
|
+
lizard_languages/objc.py,sha256=2a1teLdaXZBtCeFiIZer1j_sVx9LZ1CbF2XfnqlvLmk,2319
|
|
46
|
+
lizard_languages/perl.py,sha256=lxxdC0KJsr3tmNLMATO7vx3O7yWGgICxHxVTfmu7db8,12111
|
|
47
|
+
lizard_languages/php.py,sha256=BIIxdKd-yeaqIjxw3yoq2ZFlhqQ-F1NNK26BOfRbO5U,10093
|
|
48
|
+
lizard_languages/plsql.py,sha256=Wtny6-YW4Jc6G4EhCgoSuw8D86x6JVREprzaUEyKffw,17261
|
|
49
|
+
lizard_languages/python.py,sha256=-sI7ZzJ1erdmqo1MnCcg5cqn1krUhCHlR_phlhnmd_M,5978
|
|
50
|
+
lizard_languages/r.py,sha256=NHNPDGnWXcl8hMi1VL8OM6nw8F9pP6xZX7EgUIocOec,12930
|
|
51
|
+
lizard_languages/ruby.py,sha256=HL1ZckeuUUJU3QSVAOPsG_Zsl0C6X2PX5_VaWqclzkM,2277
|
|
52
|
+
lizard_languages/rubylike.py,sha256=vnfY2lSeIYEgfjJWrfNL6fg1ISHGiwo32H7bf7Stwos,3604
|
|
53
|
+
lizard_languages/rust.py,sha256=7ioyo-kpy5zSQ6Fwua6GcYopkLUdfyExg6kLP0Br3p0,1027
|
|
54
|
+
lizard_languages/scala.py,sha256=onEuS8IsT7aCB-HV0arVDjmgpezXWndk1RCOgoySUes,1436
|
|
55
|
+
lizard_languages/script_language.py,sha256=SKe45AbO6Z-axbN8KW_g7jf9g7YTXZ6dWzJj4ubDsM8,1172
|
|
56
|
+
lizard_languages/solidity.py,sha256=eg7HQ7cuV1d0Awwj4lTySu9f63F1vrK8czsPt_W5Kqw,680
|
|
57
|
+
lizard_languages/st.py,sha256=QiiGNYE8CzmfmZAF_wLQKovXk1Fyqa8QFGTXM1meNNk,4385
|
|
58
|
+
lizard_languages/swift.py,sha256=JcHmBV-p916K2o2XrTKf98U6Bt9-jAEOS9JqA1W5tkA,2570
|
|
59
|
+
lizard_languages/tnsdl.py,sha256=y2dcZI_PmriNd7UTP1Db9QEpWBzKeoH8odRzkiN-P1k,3009
|
|
60
|
+
lizard_languages/tsx.py,sha256=fY9qzPcSvhHSs-62ZFh0mMGyKVaSCEfsYZxBS_uHzww,17161
|
|
61
|
+
lizard_languages/ttcn.py,sha256=tSkPmtifsuUI5aUKBG-uHvwcmZTWcbtMWoUMF3Cw4cI,2199
|
|
62
|
+
lizard_languages/typescript.py,sha256=SOef4ja2GE-Idt9fSF7d0gZTW6bwCNl8K2IT67nIMyA,14805
|
|
63
|
+
lizard_languages/vue.py,sha256=KXUBUo2R1zNF8Pffrz_KsQEN44m5XFRMoGXylxKUeT0,1038
|
|
64
|
+
lizard_languages/zig.py,sha256=RiDZyjnCn97jqFmcl5EQl7pbuH0G1oI2Qo72EXvdtDU,813
|
|
65
|
+
lizard-1.20.0.dist-info/LICENSE.txt,sha256=05ZjgQ8Cl1dD9p0BhW-Txzkc5rhCogGJVEuf1GT2Y_M,1303
|
|
66
|
+
lizard-1.20.0.dist-info/METADATA,sha256=36pI9t3KO-tvapXxKTbrX1Gc465zs1zQqubteXPS4fY,16697
|
|
67
|
+
lizard-1.20.0.dist-info/WHEEL,sha256=Kh9pAotZVRFj97E15yTA4iADqXdQfIVTHcNaZTjxeGM,110
|
|
68
|
+
lizard-1.20.0.dist-info/entry_points.txt,sha256=pPMMwoHAltzGHqR2WeJQItLeeyR7pbX5R2S_POC-xoo,40
|
|
69
|
+
lizard-1.20.0.dist-info/top_level.txt,sha256=5NTrTaOLhHuTzXaGcZPKfuaOgUv7WafNGe0Zl5aycpg,35
|
|
70
|
+
lizard-1.20.0.dist-info/RECORD,,
|
lizard_ext/__init__.py
CHANGED
|
@@ -21,7 +21,6 @@ def print_csv(results, options, _, total_factory):
|
|
|
21
21
|
|
|
22
22
|
def print_checkstyle(results, options, _, total_factory, file=None):
|
|
23
23
|
import sys
|
|
24
|
-
print("DEBUG: print_checkstyle called", file=sys.stderr)
|
|
25
24
|
output = checkstyle_output(total_factory(list(results)), options.verbose)
|
|
26
25
|
if file is None:
|
|
27
26
|
file = sys.stdout
|
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
|
|
117
|
-
<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/lizardcomplextags.py
CHANGED
|
@@ -5,10 +5,16 @@ that adding the complexity and the line numbers of the keywords appear.
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class LizardExtension(object): # pylint: disable=R0903
|
|
8
|
+
"""
|
|
9
|
+
Complex tags extension: records all complexity-adding keywords and their line numbers.
|
|
10
|
+
Uses reader.conditions (combined set of all condition types) to track all
|
|
11
|
+
complexity contributors: control flow, logical operators, case labels, and ternary.
|
|
12
|
+
"""
|
|
8
13
|
|
|
9
14
|
# pylint: disable=W0221
|
|
10
15
|
def __call__(self, tokens, reader):
|
|
11
16
|
context = reader.context
|
|
17
|
+
# Use combined conditions set - intentionally includes all types
|
|
12
18
|
conditions = reader.conditions
|
|
13
19
|
for token in tokens:
|
|
14
20
|
yield token
|
lizard_ext/lizardmccabe.py
CHANGED
|
@@ -14,9 +14,14 @@ from .extension_base import ExtensionBase
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class LizardExtension(ExtensionBase): # pylint: disable=R0903
|
|
17
|
+
"""
|
|
18
|
+
McCabe extension: only counts the first 'case' in a switch statement.
|
|
19
|
+
Consecutive cases without code between them don't add to complexity.
|
|
20
|
+
Works by detecting case tokens (conceptually from reader.case_keywords).
|
|
21
|
+
"""
|
|
17
22
|
|
|
18
23
|
def _state_global(self, token):
|
|
19
|
-
if token == "case":
|
|
24
|
+
if token == "case": # Detect case keywords
|
|
20
25
|
self._state = self._in_case
|
|
21
26
|
|
|
22
27
|
def _in_case(self, token):
|
lizard_ext/lizardmodified.py
CHANGED
|
@@ -6,14 +6,19 @@ where the whole switch/case will be counted as 1.
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class LizardExtension(object): # pylint: disable=R0903
|
|
9
|
+
"""
|
|
10
|
+
Modified CCN extension: counts entire switch/case as 1 complexity.
|
|
11
|
+
Adds +1 for 'switch', subtracts -1 for each 'case'.
|
|
12
|
+
Works with switch/case keywords (conceptually from reader.case_keywords).
|
|
13
|
+
"""
|
|
9
14
|
|
|
10
15
|
def __call__(self, tokens, reader):
|
|
11
16
|
for token in tokens:
|
|
12
|
-
if token == 'switch':
|
|
17
|
+
if token == 'switch': # Add complexity for switch statement
|
|
13
18
|
reader.context.add_condition()
|
|
14
19
|
if hasattr(reader.context, "add_nd_condition"):
|
|
15
20
|
reader.context.add_nd_condition()
|
|
16
|
-
elif token == 'case':
|
|
21
|
+
elif token == 'case': # Subtract complexity for each case
|
|
17
22
|
reader.context.add_condition(-1)
|
|
18
23
|
if hasattr(reader.context, "add_nd_condition"):
|
|
19
24
|
reader.context.add_nd_condition(-1)
|
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
|
-
|
|
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/lizardnonstrict.py
CHANGED
|
@@ -9,5 +9,6 @@ class LizardExtension(object): # pylint: disable=R0903
|
|
|
9
9
|
|
|
10
10
|
# pylint: disable=W0221
|
|
11
11
|
def __call__(self, tokens, reader):
|
|
12
|
-
|
|
12
|
+
# Remove logical operators from conditions (non-strict mode)
|
|
13
|
+
reader.conditions -= reader.logical_operators
|
|
13
14
|
return tokens
|
lizard_ext/lizardns.py
CHANGED
|
@@ -67,10 +67,35 @@ class LizardExtension(ExtensionBase): # pylint: disable=R0903
|
|
|
67
67
|
|
|
68
68
|
def __init__(self):
|
|
69
69
|
super(LizardExtension, self).__init__(None)
|
|
70
|
-
self.structure_piles = [0]
|
|
70
|
+
self.structure_piles = [0] # Invariant: must always have at least one element
|
|
71
71
|
|
|
72
|
-
def
|
|
72
|
+
def _push_scope(self):
|
|
73
|
+
"""Push a new scope level. Safe to call anytime."""
|
|
74
|
+
self.structure_piles.append(0)
|
|
75
|
+
|
|
76
|
+
def _pop_scope(self):
|
|
77
|
+
"""Pop a scope level. Maintains invariant of at least one element."""
|
|
78
|
+
if len(self.structure_piles) > 1:
|
|
79
|
+
self.structure_piles.pop()
|
|
80
|
+
|
|
81
|
+
def _increment_current_scope(self):
|
|
82
|
+
"""Increment structure count in current scope. Safe even if piles corrupted."""
|
|
83
|
+
if not self.structure_piles:
|
|
84
|
+
self.structure_piles = [0] # Restore invariant
|
|
73
85
|
self.structure_piles[-1] += 1
|
|
86
|
+
|
|
87
|
+
def _reset_or_decrement_current_scope(self, decrement=False):
|
|
88
|
+
"""Reset or decrement current scope counter. Safe even if piles corrupted."""
|
|
89
|
+
if not self.structure_piles:
|
|
90
|
+
self.structure_piles = [0] # Restore invariant
|
|
91
|
+
return
|
|
92
|
+
if decrement:
|
|
93
|
+
self.structure_piles[-1] -= 1
|
|
94
|
+
else:
|
|
95
|
+
self.structure_piles[-1] = 0
|
|
96
|
+
|
|
97
|
+
def pile_up_within_block(self):
|
|
98
|
+
self._increment_current_scope()
|
|
74
99
|
cur_level = sum(self.structure_piles)
|
|
75
100
|
# Is there a path around _state_global?
|
|
76
101
|
if not hasattr(self.context.current_function, "max_nested_structures"):
|
|
@@ -83,10 +108,10 @@ class LizardExtension(ExtensionBase): # pylint: disable=R0903
|
|
|
83
108
|
if not hasattr(self.context.current_function, "max_nested_structures"):
|
|
84
109
|
self.context.current_function.max_nested_structures = 0
|
|
85
110
|
if token == '{':
|
|
86
|
-
self.
|
|
111
|
+
self._push_scope()
|
|
87
112
|
elif token in ';}':
|
|
88
113
|
if token == '}':
|
|
89
|
-
self.
|
|
114
|
+
self._pop_scope()
|
|
90
115
|
self._state = self._block_ending
|
|
91
116
|
elif token in self.structures:
|
|
92
117
|
self._state = self._in_structure_head
|
|
@@ -98,10 +123,8 @@ class LizardExtension(ExtensionBase): # pylint: disable=R0903
|
|
|
98
123
|
self._state(token)
|
|
99
124
|
|
|
100
125
|
def _block_ending(self, token):
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
else:
|
|
104
|
-
self.structure_piles[-1] = 0
|
|
126
|
+
decrement = token in self.matching_structures
|
|
127
|
+
self._reset_or_decrement_current_scope(decrement=decrement)
|
|
105
128
|
self._state = self._state_global
|
|
106
129
|
self._state(token)
|
|
107
130
|
|
lizard_ext/version.py
CHANGED
lizard_languages/__init__.py
CHANGED
|
@@ -26,6 +26,7 @@ from .vue import VueReader
|
|
|
26
26
|
from .perl import PerlReader
|
|
27
27
|
from .st import StReader
|
|
28
28
|
from .r import RReader
|
|
29
|
+
from .plsql import PLSQLReader
|
|
29
30
|
|
|
30
31
|
|
|
31
32
|
def languages():
|
|
@@ -56,6 +57,7 @@ def languages():
|
|
|
56
57
|
PerlReader,
|
|
57
58
|
StReader,
|
|
58
59
|
RReader,
|
|
60
|
+
PLSQLReader,
|
|
59
61
|
]
|
|
60
62
|
|
|
61
63
|
|
lizard_languages/clike.py
CHANGED
|
@@ -32,8 +32,11 @@ class CLikeReader(CodeReader, CCppCommentsMixin):
|
|
|
32
32
|
|
|
33
33
|
@staticmethod
|
|
34
34
|
def generate_tokens(source_code, addition='', token_class=None):
|
|
35
|
-
# Add pattern for
|
|
36
|
-
|
|
35
|
+
# Add pattern for C++ raw string literals R"delimiter(content)delimiter"
|
|
36
|
+
# The delimiter can be empty or up to 16 chars (excluding parentheses, backslash, whitespace)
|
|
37
|
+
# Using a simplified pattern that handles most cases
|
|
38
|
+
addition = r"|R\"[^(\\]*\((?:[^)]|\)[^\"])*\)[^(\\]*\"" + \
|
|
39
|
+
r"|(?:\d*\.\d+(?:[eE][-+]?\d+)?)" + \
|
|
37
40
|
r"|(?:\d+\.(?:\d+)?(?:[eE][-+]?\d+)?)" + \
|
|
38
41
|
addition
|
|
39
42
|
return CodeReader.generate_tokens(source_code, addition, token_class)
|
|
@@ -54,7 +57,7 @@ class CLikeReader(CodeReader, CCppCommentsMixin):
|
|
|
54
57
|
elif macro.group(1) == 'include':
|
|
55
58
|
yield "#include"
|
|
56
59
|
yield macro.group(2) or "\"\""
|
|
57
|
-
for _ in macro.group(2).
|
|
60
|
+
for _ in macro.group(2).split('\n')[1:]:
|
|
58
61
|
yield '\n'
|
|
59
62
|
else:
|
|
60
63
|
yield token
|
lizard_languages/code_reader.py
CHANGED
|
@@ -94,12 +94,36 @@ class CodeReader:
|
|
|
94
94
|
ext = []
|
|
95
95
|
languages = None
|
|
96
96
|
extra_subclasses = set()
|
|
97
|
-
|
|
97
|
+
|
|
98
|
+
# Condition categories - separate types that contribute to cyclomatic complexity
|
|
99
|
+
_control_flow_keywords = {'if', 'for', 'while', 'catch'}
|
|
100
|
+
_logical_operators = {'&&', '||'}
|
|
101
|
+
_case_keywords = {'case'}
|
|
102
|
+
_ternary_operators = {'?'}
|
|
103
|
+
|
|
104
|
+
@classmethod
|
|
105
|
+
def _build_conditions(cls):
|
|
106
|
+
"""Build combined conditions set from separated categories.
|
|
107
|
+
|
|
108
|
+
Returns combined set of all condition types for CCN calculation.
|
|
109
|
+
"""
|
|
110
|
+
return (cls._control_flow_keywords |
|
|
111
|
+
cls._logical_operators |
|
|
112
|
+
cls._case_keywords |
|
|
113
|
+
cls._ternary_operators)
|
|
98
114
|
|
|
99
115
|
def __init__(self, context):
|
|
100
116
|
self.parallel_states = []
|
|
101
117
|
self.context = context
|
|
102
|
-
|
|
118
|
+
|
|
119
|
+
# Build combined conditions set from separated categories
|
|
120
|
+
self.conditions = copy(self.__class__._build_conditions())
|
|
121
|
+
|
|
122
|
+
# Expose individual categories for extensions
|
|
123
|
+
self.control_flow_keywords = copy(self.__class__._control_flow_keywords)
|
|
124
|
+
self.logical_operators = copy(self.__class__._logical_operators)
|
|
125
|
+
self.case_keywords = copy(self.__class__._case_keywords)
|
|
126
|
+
self.ternary_operators = copy(self.__class__._ternary_operators)
|
|
103
127
|
|
|
104
128
|
@classmethod
|
|
105
129
|
def match_filename(cls, filename):
|
|
@@ -138,7 +162,7 @@ class CodeReader:
|
|
|
138
162
|
r"|\/\/" + _until_end +
|
|
139
163
|
r"|\#" +
|
|
140
164
|
r"|:=|::|\*\*" +
|
|
141
|
-
r"
|
|
165
|
+
r"|\<(?=(?:[^<>]*\?)+[^<>]*\>)(?:[\w\s,.?]|(?:extends))+\>" +
|
|
142
166
|
r"|" + r"|".join(re.escape(s) for s in combined_symbols) +
|
|
143
167
|
r"|\\\n" +
|
|
144
168
|
r"|\n" +
|