@@ -4,87 +4,143 @@ package reporting
44
55import core .*
66import Contexts .{Context , ctx }
7+ import Symbols .{Symbol , NoSymbol }
78import collection .mutable
89import util .{EqHashMap , NoSourcePosition }
10+ import util .Spans .{Span , NoSpan }
11+ import Decorators .i
12+ import parsing .Scanners .Scanner
13+ import io .AbstractFile
914
1015abstract class Profile :
1116 def unitProfile (unit : CompilationUnit ): Profile .Info
1217 def recordNewLine ()(using Context ): Unit
1318 def recordNewToken ()(using Context ): Unit
1419 def recordTasty (size : Int )(using Context ): Unit
20+ def recordMethodSize (meth : Symbol , size : Int , span : Span )(using Context ): Unit
1521 def printSummary ()(using Context ): Unit
1622
1723object Profile :
1824 def current (using Context ): Profile =
1925 val run = ctx.run
2026 if run == null then NoProfile else run.profile
2127
22- private val TastyFactor = 50
28+ inline val TastyChunkSize = 50
2329
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 ):
2534 var lineCount : Int = 0
2635 var tokenCount : Int = 0
2736 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
2948end Profile
3049
31- class ActiveProfile extends Profile :
50+ class ActiveProfile ( details : Int ) extends Profile :
3251
3352 private val pinfo = new EqHashMap [CompilationUnit , Profile .Info ]
3453
35- private val junkInfo = new Profile .Info
54+ private val junkInfo = new Profile .Info ( 0 )
3655
3756 private def curInfo (using Context ): Profile .Info =
3857 val unit : CompilationUnit | Null = ctx.compilationUnit
3958 if unit == null then junkInfo else unitProfile(unit)
4059
4160 def unitProfile (unit : CompilationUnit ): Profile .Info =
42- pinfo.getOrElseUpdate(unit, new Profile .Info )
61+ pinfo.getOrElseUpdate(unit, new Profile .Info (details) )
4362
4463 def recordNewLine ()(using Context ): Unit =
4564 curInfo.lineCount += 1
4665 def recordNewToken ()(using Context ): Unit =
4766 curInfo.tokenCount += 1
4867 def recordTasty (size : Int )(using Context ): Unit =
4968 curInfo.tastySize += size
69+ def recordMethodSize (meth : Symbol , size : Int , span : Span )(using Context ): Unit =
70+ curInfo.recordMethodSize(meth, size, span)
5071
5172 def printSummary ()(using Context ): Unit =
5273 val units =
5374 val rawUnits = pinfo.keysIterator.toArray
54- ctx.settings.YprofileSortedBy .value match
75+ ctx.settings.VprofileSortedBy .value match
5576 case " name" => rawUnits.sortBy(_.source.file.name)
5677 case " path" => rawUnits.sortBy(_.source.file.path)
5778 case " lines" => rawUnits.sortBy(unitProfile(_).lineCount)
5879 case " tokens" => rawUnits.sortBy(unitProfile(_).tokenCount)
5980 case " complexity" => rawUnits.sortBy(unitProfile(_).complexity)
6081 case _ => rawUnits.sortBy(unitProfile(_).tastySize)
6182
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
6591
66- def printInfo (name : String , info : Profile .Info , path : String ) =
92+ def printInfo (layout : String , name : String , info : Profile .Info , path : String ) =
6793 val complexity = info.complexity
6894 val explanation =
6995 if complexity < 1 then " low "
7096 else if complexity < 5 then " moderate"
7197 else if complexity < 25 then " high "
7298 else " extreme "
73- val complexityStr = s " ${" %6.2f" .format(info.complexity)} $explanation"
7499 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)
88144 end printSummary
89145end ActiveProfile
90146
@@ -93,4 +149,5 @@ object NoProfile extends Profile:
93149 def recordNewLine ()(using Context ): Unit = ()
94150 def recordNewToken ()(using Context ): Unit = ()
95151 def recordTasty (size : Int )(using Context ): Unit = ()
152+ def recordMethodSize (meth : Symbol , size : Int , span : Span )(using Context ): Unit = ()
96153 def printSummary ()(using Context ): Unit = ()
0 commit comments