@@ -4,87 +4,143 @@ package reporting
4
4
5
5
import core .*
6
6
import Contexts .{Context , ctx }
7
+ import Symbols .{Symbol , NoSymbol }
7
8
import collection .mutable
8
9
import util .{EqHashMap , NoSourcePosition }
10
+ import util .Spans .{Span , NoSpan }
11
+ import Decorators .i
12
+ import parsing .Scanners .Scanner
13
+ import io .AbstractFile
9
14
10
15
abstract class Profile :
11
16
def unitProfile (unit : CompilationUnit ): Profile .Info
12
17
def recordNewLine ()(using Context ): Unit
13
18
def recordNewToken ()(using Context ): Unit
14
19
def recordTasty (size : Int )(using Context ): Unit
20
+ def recordMethodSize (meth : Symbol , size : Int , span : Span )(using Context ): Unit
15
21
def printSummary ()(using Context ): Unit
16
22
17
23
object Profile :
18
24
def current (using Context ): Profile =
19
25
val run = ctx.run
20
26
if run == null then NoProfile else run.profile
21
27
22
- private val TastyFactor = 50
28
+ inline val TastyChunkSize = 50
23
29
24
- class Info :
30
+ case class MethodInfo (meth : Symbol , size : Int , span : Span )
31
+ object NoInfo extends MethodInfo (NoSymbol , 0 , NoSpan )
32
+
33
+ class Info (details : Int ):
25
34
var lineCount : Int = 0
26
35
var tokenCount : Int = 0
27
36
var tastySize : Int = 0
28
- def complexity : Float = tastySize.toFloat/ lineCount/ TastyFactor
37
+ def complexity : Float = (tastySize/ TastyChunkSize ).toFloat/ lineCount
38
+ val leading : Array [MethodInfo ] = Array .fill[MethodInfo ](details)(NoInfo )
39
+
40
+ def recordMethodSize (meth : Symbol , size : Int , span : Span ): Unit =
41
+ var i = leading.length
42
+ while i > 0 && leading(i - 1 ).size < size do
43
+ if i < leading.length then leading(i) = leading(i - 1 )
44
+ i -= 1
45
+ if i < leading.length then
46
+ leading(i) = MethodInfo (meth, size, span)
47
+ end Info
29
48
end Profile
30
49
31
- class ActiveProfile extends Profile :
50
+ class ActiveProfile ( details : Int ) extends Profile :
32
51
33
52
private val pinfo = new EqHashMap [CompilationUnit , Profile .Info ]
34
53
35
- private val junkInfo = new Profile .Info
54
+ private val junkInfo = new Profile .Info ( 0 )
36
55
37
56
private def curInfo (using Context ): Profile .Info =
38
57
val unit : CompilationUnit | Null = ctx.compilationUnit
39
58
if unit == null then junkInfo else unitProfile(unit)
40
59
41
60
def unitProfile (unit : CompilationUnit ): Profile .Info =
42
- pinfo.getOrElseUpdate(unit, new Profile .Info )
61
+ pinfo.getOrElseUpdate(unit, new Profile .Info (details) )
43
62
44
63
def recordNewLine ()(using Context ): Unit =
45
64
curInfo.lineCount += 1
46
65
def recordNewToken ()(using Context ): Unit =
47
66
curInfo.tokenCount += 1
48
67
def recordTasty (size : Int )(using Context ): Unit =
49
68
curInfo.tastySize += size
69
+ def recordMethodSize (meth : Symbol , size : Int , span : Span )(using Context ): Unit =
70
+ curInfo.recordMethodSize(meth, size, span)
50
71
51
72
def printSummary ()(using Context ): Unit =
52
73
val units =
53
74
val rawUnits = pinfo.keysIterator.toArray
54
- ctx.settings.YprofileSortedBy .value match
75
+ ctx.settings.VprofileSortedBy .value match
55
76
case " name" => rawUnits.sortBy(_.source.file.name)
56
77
case " path" => rawUnits.sortBy(_.source.file.path)
57
78
case " lines" => rawUnits.sortBy(unitProfile(_).lineCount)
58
79
case " tokens" => rawUnits.sortBy(unitProfile(_).tokenCount)
59
80
case " complexity" => rawUnits.sortBy(unitProfile(_).complexity)
60
81
case _ => rawUnits.sortBy(unitProfile(_).tastySize)
61
82
62
- val nameWidth = units.map(_.source.file.name.length).max.max(10 ).min(50 )
63
- val layout = s " %- ${nameWidth}s %6s %8s %8s %s %s "
64
- report.echo(layout.format(" Source file" , " Lines" , " Tokens" , " Tasty" , " Complexity/Line" , " Directory" ))
83
+ def printHeader (sourceNameWidth : Int , methNameWidth : Int = 0 ): String =
84
+ val prefix =
85
+ if methNameWidth > 0
86
+ then s " %- ${sourceNameWidth}s %- ${methNameWidth}s " .format(" Sourcefile" , " Method" )
87
+ else s " %- ${sourceNameWidth}s " .format(" Sourcefile" )
88
+ val layout = s " %- ${prefix.length}s %6s %8s %7s %s %s "
89
+ report.echo(layout.format(prefix, " Lines" , " Tokens" , " Tasty" , " Complexity/Line" , " Directory" ))
90
+ layout
65
91
66
- def printInfo (name : String , info : Profile .Info , path : String ) =
92
+ def printInfo (layout : String , name : String , info : Profile .Info , path : String ) =
67
93
val complexity = info.complexity
68
94
val explanation =
69
95
if complexity < 1 then " low "
70
96
else if complexity < 5 then " moderate"
71
97
else if complexity < 25 then " high "
72
98
else " extreme "
73
- val complexityStr = s " ${" %6.2f" .format(info.complexity)} $explanation"
74
99
report.echo(layout.format(
75
- name, info.lineCount, info.tokenCount, info.tastySize, complexityStr, path))
76
-
77
- val agg = new Profile .Info
78
- for unit <- units do
79
- val info = unitProfile(unit)
80
- val file = unit.source.file
81
- printInfo(file.name, info, file.container.path)
82
- agg.lineCount += info.lineCount
83
- agg.tokenCount += info.tokenCount
84
- agg.tastySize += info.tastySize
85
- if units.length > 1 then
86
- report.echo(s " ${" -" * nameWidth}------------------------------------------ " )
87
- printInfo(" Total" , agg, " " )
100
+ name, info.lineCount, info.tokenCount, info.tastySize/ Profile .TastyChunkSize ,
101
+ s " ${" %6.2f" .format(complexity)} $explanation" , path))
102
+
103
+ def safeMax (xs : Array [Int ]) = xs.max.max(10 ).min(50 )
104
+
105
+ def printAndAggregateSourceInfos (): Profile .Info =
106
+ val sourceNameWidth = safeMax(units.map(_.source.file.name.length))
107
+ val layout = printHeader(sourceNameWidth)
108
+ val agg = new Profile .Info (details)
109
+ for unit <- units do
110
+ val file = unit.source.file
111
+ val info = unitProfile(unit)
112
+ printInfo(layout, file.name, info, file.container.path)
113
+ agg.lineCount += info.lineCount
114
+ agg.tokenCount += info.tokenCount
115
+ agg.tastySize += info.tastySize
116
+ for Profile .MethodInfo (meth, size, span) <- info.leading do
117
+ agg.recordMethodSize(meth, size, span)
118
+ if units.length > 1 then
119
+ report.echo(s " ${" -" * sourceNameWidth}------------------------------------------ " )
120
+ printInfo(layout, " Total" , agg, " " )
121
+ agg
122
+
123
+ def printDetails (agg : Profile .Info ): Unit =
124
+ val sourceNameWidth = safeMax(agg.leading.map(_.meth.source.name.length))
125
+ val methNameWidth = safeMax(agg.leading.map(_.meth.name.toString.length))
126
+ report.echo(" \n Most complex methods:" )
127
+ val layout = printHeader(sourceNameWidth, methNameWidth)
128
+ for
129
+ Profile .MethodInfo (meth, size, span) <- agg.leading.reverse
130
+ unit <- units.find(_.source.eq(meth.source))
131
+ do
132
+ val methProfile = new ActiveProfile (0 )
133
+ val methCtx = ctx.fresh.setCompilationUnit(unit)
134
+ val s = Scanner (meth.source, span.start, methProfile)(using methCtx)
135
+ while s.offset < span.end do s.nextToken()
136
+ val info = methProfile.unitProfile(unit)
137
+ info.tastySize = size
138
+ val file = meth.source.file
139
+ val header = s " %- ${sourceNameWidth}s %- ${methNameWidth}s " .format(file.name, meth.name)
140
+ printInfo(layout, header, info, file.container.path)
141
+
142
+ val agg = printAndAggregateSourceInfos()
143
+ if details > 0 then printDetails(agg)
88
144
end printSummary
89
145
end ActiveProfile
90
146
@@ -93,4 +149,5 @@ object NoProfile extends Profile:
93
149
def recordNewLine ()(using Context ): Unit = ()
94
150
def recordNewToken ()(using Context ): Unit = ()
95
151
def recordTasty (size : Int )(using Context ): Unit = ()
152
+ def recordMethodSize (meth : Symbol , size : Int , span : Span )(using Context ): Unit = ()
96
153
def printSummary ()(using Context ): Unit = ()
0 commit comments