-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAnalyzeCommand.java
More file actions
113 lines (93 loc) · 4.42 KB
/
Copy pathAnalyzeCommand.java
File metadata and controls
113 lines (93 loc) · 4.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package io.github.randomcodespace.iq.cli;
import io.github.randomcodespace.iq.analyzer.AnalysisResult;
import io.github.randomcodespace.iq.analyzer.Analyzer;
import io.github.randomcodespace.iq.config.CodeIqConfig;
import org.springframework.stereotype.Component;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;
import java.nio.file.Path;
import java.text.NumberFormat;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.Callable;
/**
* Scan a codebase and build a knowledge graph.
* <p>
* This is the legacy command that uses in-memory graph building with Neo4j.
* For memory-efficient indexing, use {@code index} instead.
* Kept as backward-compatible alias.
*/
@Component
@Command(name = "analyze", mixinStandardHelpOptions = true,
description = "Scan codebase and build knowledge graph (legacy; prefer 'index' for large codebases)")
public class AnalyzeCommand implements Callable<Integer> {
@Parameters(index = "0", defaultValue = ".", description = "Path to codebase root")
private Path path;
@Option(names = {"--no-cache"}, description = "Skip incremental cache (full re-analysis)")
private boolean noCache;
@Option(names = {"--incremental"}, defaultValue = "true", negatable = true,
description = "Use incremental analysis (default: true)")
private boolean incremental;
@Option(names = {"--parallelism", "-p"},
description = "Max parallel threads (default: auto-detect from CPU)")
private Integer parallelism;
@Option(names = {"--graph"}, description = "Path to shared graph directory (for multi-repo)")
private Path graphDir;
@Option(names = {"--service-name"}, description = "Service name tag for nodes (for multi-repo)")
private String serviceName;
private final Analyzer analyzer;
private final CodeIqConfig config;
public AnalyzeCommand(Analyzer analyzer, CodeIqConfig config) {
this.analyzer = analyzer;
this.config = config;
}
@Override
public Integer call() {
Path root = path.toAbsolutePath().normalize();
CliOutput.configureFromOptions(config, graphDir, serviceName);
NumberFormat nf = NumberFormat.getIntegerInstance(Locale.US);
int cores = parallelism != null ? parallelism : Runtime.getRuntime().availableProcessors();
// --no-cache overrides --incremental
boolean useIncremental = incremental && !noCache;
CliOutput.step("[*]", "Scanning " + root + " ...");
if (useIncremental) {
CliOutput.info(" (incremental mode -- use --no-cache for full re-analysis)");
}
AnalysisResult result = analyzer.run(root, parallelism, useIncremental, msg -> {
if (msg.startsWith("Discovering")) {
CliOutput.step("[*]", msg);
} else if (msg.startsWith("Found")) {
CliOutput.step("[+]", "@|cyan " + msg + "|@");
} else if (msg.startsWith("Analyzing")) {
CliOutput.step("[~]", msg.replace("files...", "files using " + cores + " cores..."));
} else if (msg.startsWith("Building")) {
CliOutput.step("[^]", msg);
} else if (msg.startsWith("Linking")) {
CliOutput.step("[-]", msg);
} else if (msg.startsWith("Classifying")) {
CliOutput.step("[#]", msg);
} else if (msg.startsWith("Cache hits")) {
CliOutput.step("[!]", "@|green " + msg + "|@");
} else if (msg.startsWith("Incremental")) {
CliOutput.step("[!]", msg);
} else if (msg.startsWith("Analysis complete")) {
// handled below
} else {
CliOutput.info(msg);
}
});
CliOutput.printResultSummary(result, nf);
if (result.frameworkBreakdown() != null && !result.frameworkBreakdown().isEmpty()) {
StringBuilder fws = new StringBuilder(" Frameworks: ");
result.frameworkBreakdown().entrySet().stream()
.sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
.limit(15)
.forEach(e -> fws.append(e.getKey()).append(" (")
.append(nf.format(e.getValue())).append("), "));
if (fws.length() > 2) fws.setLength(fws.length() - 2);
CliOutput.info(fws.toString());
}
return 0;
}
}