Skip to content
This repository has been archived by the owner on Oct 19, 2022. It is now read-only.

Commit

Permalink
Introduce local threshold
Browse files Browse the repository at this point in the history
  • Loading branch information
Eugen Wissner authored and Eugen Wissner committed Apr 5, 2022
1 parent 5f3cd34 commit af95b3e
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 63 deletions.
4 changes: 2 additions & 2 deletions make.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,15 @@ def clean
dub 'clean'
end

(ARGV.empty? ? ['d'] : ARGV).each do |argument|
(ARGV.empty? ? ['run'] : ARGV).each do |argument|
case argument
when 'clean'
clean
when 'release', 'debug'
build argument
when 'run'
build 'debug'
system 'build/cogito', 'sample/sample.d'
system 'build/cogito', 'tools/sample.d'
when 'test'
build 'unittest'
system 'build/test', '-s'
Expand Down
6 changes: 0 additions & 6 deletions sample/sample.d

This file was deleted.

55 changes: 51 additions & 4 deletions src/cogito/arguments.d
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import std.typecons;
/// Help message.
enum string help = q"HELP
Usage: cogito [OPTION…] SOURCE…
--threshold NUMBER fail if the source score exceeds this threshold
--module-threshold NUMBER fail if the source score exceeds this threshold
--threshold NUMBER fail if a function score exceeds this threshold
--format ENUMERATION "silent", "flat" or "verbose"
Return codes:
0 Success
Expand All @@ -18,15 +20,29 @@ Usage: cogito [OPTION…] SOURCE…
3 Excess of threshold
HELP";

/**
* Possible output formats.
*/
enum OutputFormat
{
silent,
flat,
verbose
}

/**
* Arguments supported by the CLI.
*/
struct Arguments
{
/// Input files.
string[] files = [];
/// Threshold.
/// Module threshold.
Nullable!uint moduleThreshold;
/// Function threshold.
Nullable!uint threshold;
/// Output format.
Nullable!OutputFormat format;
/// Display help message.
bool help = false;
}
Expand Down Expand Up @@ -122,12 +138,20 @@ SumType!(ArgumentError, Arguments) parseArguments(string[] args)
{
return ArgumentResult(ArgumentError(ArgumentError.Type.unknown, args.front));
}
else if (args.front == "--threshold")
else if (args.front == "--module-threshold" || args.front == "--threshold")
{
const next = args.front;
args.popFront;
try
{
arguments.threshold = nullable(args.front.to!uint);
if (next == "--module-threshold")
{
arguments.moduleThreshold = nullable(args.front.to!uint);
}
else
{
arguments.threshold = nullable(args.front.to!uint);
}
}
catch (ConvException e)
{
Expand All @@ -137,6 +161,29 @@ SumType!(ArgumentError, Arguments) parseArguments(string[] args)
return ArgumentResult(error);
}
}
else if (args.front == "--format")
{
args.popFront;
if (args.front == "flat")
{
arguments.format = nullable(OutputFormat.flat);
}
else if (args.front == "silent")
{
arguments.format = nullable(OutputFormat.silent);
}
else if (args.front == "verbose")
{
arguments.format = nullable(OutputFormat.verbose);
}
else
{
auto error = ArgumentError(ArgumentError.Type.wrongType, args.front);
error.expected = "silent|flat|verbose";

return ArgumentResult(error);
}
}
else if (args.front.startsWith("--"))
{
return ArgumentResult(ArgumentError(ArgumentError.Type.unknown, args.front));
Expand Down
166 changes: 116 additions & 50 deletions src/cogito/meter.d
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import dmd.globals;
import dmd.console;
import dmd.root.outbuffer;

import cogito.arguments;
import cogito.list;
import std.algorithm;
import std.conv;
Expand Down Expand Up @@ -98,50 +99,95 @@ struct Meter
this.type = type;
}

private void toString(void delegate(const(char)[]) sink, const uint indentation)
/**
* Returns: $(D_KEYWORD true) if any function inside the current node
* excceds the threshold, otherwise $(D_KEYWORD false).
*/
bool isAbove(Nullable!uint threshold)
{
const indentBytes = ' '.repeat(indentation * 2).array;
const nextIndentation = indentation + 1;
const nextIndentBytes = ' '.repeat(nextIndentation * 2).array;
const identifierName = this.name.toString();

sink(indentBytes);
sink(identifierName.empty ? "(λ)" : identifierName);
debug
if (threshold.isNull)
{
return false;
}
if (this.type == Type.callable)
{
sink(":\n");
sink(nextIndentBytes);
sink("Location: ");
sink(to!string(this.location.linnum));
sink(":");
sink(to!string(this.location.charnum));
sink("\n");
sink(nextIndentBytes);
sink("Score: ");
return this.score > threshold.get;
}
else
{
sink(": ");
return reduce!((accum, x) => accum || x.isAbove(threshold))(false, this.inner[]);
}
sink(to!string(this.score));
sink("\n");
}

mixin Ruler!();
}

/**
* Prints the information about the given identifier.
*
* Params:
* sink = Function used to print the information.
* indentation = Indentation.
*/
void verbose(ref Meter meter, void delegate(const(char)[]) sink,
const uint indentation = 1)
{
const indentBytes = ' '.repeat(indentation * 2).array;
const nextIndentation = indentation + 1;
const nextIndentBytes = ' '.repeat(nextIndentation * 2).array;
const identifierName = meter.name.toString();

sink(indentBytes);
sink(identifierName.empty ? "(λ)" : identifierName);

sink(":\n");
sink(nextIndentBytes);
sink("Location: ");
sink(to!string(meter.location.linnum));
sink(":");
sink(to!string(meter.location.charnum));
sink("\n");
sink(nextIndentBytes);
sink("Score: ");

sink(meter.score.to!string);
sink("\n");

meter.inner[].each!(meter => verbose(meter, sink, nextIndentation));
}

this.inner[].each!(meter => meter.toString(sink, nextIndentation));
/**
* Prints the information about the given identifier.
*
* Params:
* sink = Function used to print the information.
* path = Identifier path.
* always = Produce output not looking at threshold.
* threshold = Function score limit.
*/
void flat(ref Meter meter, void delegate(const(char)[]) sink,
bool always, Nullable!uint threshold,
const string[] path = [])
{
if (!always && !meter.isAbove(threshold))
{
return;
}
const identifierName = meter.name.toString();
const anonymousName = identifierName.empty? "(λ)" : identifierName;
const nameParts = path ~ [anonymousName.idup];

/**
* Prints the information about the given identifier. Debug build provides
* more details.
*
* Params:
* sink = Function used to print the information.
*/
void toString(void delegate(const(char)[]) sink)
if (meter.type == Meter.Type.callable)
{
toString(sink, 1);
sink(" ");
sink(nameParts.join("."));
sink(": ");
sink(meter.score.to!string);
sink("\n");
}

mixin Ruler!();
meter.inner[].each!(meter => flat(meter, sink,
always, threshold, nameParts));
}

/**
Expand All @@ -166,6 +212,19 @@ struct Source
this.filename = filename;
}

/**
* Returns: $(D_KEYWORD true) if any function inside the current node
* excceds the threshold, otherwise $(D_KEYWORD false).
*/
bool isAbove(Nullable!uint threshold)
{
if (threshold.isNull)
{
return false;
}
return reduce!((accum, x) => accum || x.isAbove(threshold))(false, this.inner[]);
}

mixin Ruler!();
}

Expand All @@ -174,36 +233,43 @@ struct Source
*
* Params:
* source = Collected metrics and scores.
* threshold = Maximum acceptable score.
* threshold = Maximum acceptable function score.
* moduleThreshold = Maximum acceptable module score.
* format = Output format.
*
* Returns: true if the score exceeds the threshold, otherwise returns false.
* Returns: $(D_KEYWORD true) if the score exceeds the threshold, otherwise
* returns $(D_KEYWORD false).
*/
bool printMeter(Source source, Nullable!uint threshold)
bool printMeter(Source source, Nullable!uint threshold,
Nullable!uint moduleThreshold, Nullable!OutputFormat format)
{
const sourceScore = source.score;
debug
{
enum bool isDebug = true;
}
else
{
enum bool isDebug = false;
}
const bool aboveThreshold = !threshold.isNull && sourceScore > threshold.get;
const bool aboveModuleThreshold = !moduleThreshold.isNull
&& sourceScore > moduleThreshold.get;
const bool aboveThreshold = aboveModuleThreshold || source.isAbove(threshold);

if (aboveThreshold || isDebug)
if ((aboveThreshold || format == nullable(OutputFormat.verbose))
&& format != nullable(OutputFormat.silent))
{
writefln("\x1b[36m%s:\x1b[0m", source.filename);
writefln("\x1b[36m%s: %s\x1b[0m", source.filename, sourceScore);

if (!source.inner.empty)
foreach (m; source.inner[])
{
foreach (m; source.inner[])
if (format == nullable(OutputFormat.verbose))
{
m.toString(input => write(input));
verbose(m, input => write(input));
}
else if (aboveModuleThreshold || format == nullable(OutputFormat.flat))
{
flat(m, input => write(input), true, threshold);
}
else
{
flat(m, input => write(input), false, threshold);
}
}
writefln(" \x1b[36mScore: %s\x1b[0m", sourceScore);
}

return aboveThreshold;
}

Expand Down
6 changes: 5 additions & 1 deletion src/main.d
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ int accumulateResult(Arguments arguments, int accumulator, Result result)
printErrors(errors);
return 1;
},
(Source source) => printMeter(source, arguments.threshold) ? 3 : 0
(Source source) {
const result = printMeter(source, arguments.threshold,
arguments.moduleThreshold, arguments.format);
return result ? 3 : 0;
}
)(result);
if (accumulator == 1 || nextResult == 1)
{
Expand Down

0 comments on commit af95b3e

Please sign in to comment.