Skip to content

Commit 1fbdd3a

Browse files
authored
Merge pull request #541 from devoxx/issue-534
ProjectScannerService refactored into smaller classes + unit tests
2 parents 8450cad + 9ae0183 commit 1fbdd3a

20 files changed

+1762
-359
lines changed

build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ dependencies {
7575
implementation("com.knuddels:jtokkit:1.0.0")
7676
implementation("org.commonmark:commonmark:0.22.0")
7777

78+
// GitIgnore Reader
79+
implementation("nl.basjes.gitignore:gitignore-reader:1.6.0")
80+
7881
// TDG : Add Log4j dependencies
7982
implementation("org.apache.logging.log4j:log4j-api:2.22.1")
8083
implementation("org.apache.logging.log4j:log4j-core:2.22.1")

src/main/java/com/devoxx/genie/service/ProjectContentService.java

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,18 @@ public static ProjectContentService getInstance() {
4040
public CompletableFuture<ScanContentResult> getProjectContent(Project project,
4141
int windowContextMaxTokens,
4242
boolean isTokenCalculation) {
43-
return ProjectScannerService.getInstance()
44-
.scanProject(project, null, windowContextMaxTokens, isTokenCalculation)
45-
.thenApply(content -> {
46-
if (!isTokenCalculation) {
47-
copyToClipboard(content);
48-
}
49-
return content;
50-
});
43+
ProjectScannerService instance = ProjectScannerService.getInstance();
44+
ScanContentResult scanContentResult = instance.scanProject(project, null, windowContextMaxTokens, isTokenCalculation);
45+
return CompletableFuture.completedFuture(scanContentResult)
46+
.thenApply(content ->
47+
getScanContentResult(isTokenCalculation, content));
48+
}
49+
50+
private ScanContentResult getScanContentResult(boolean isTokenCalculation, ScanContentResult content) {
51+
if (!isTokenCalculation) {
52+
copyToClipboard(content);
53+
}
54+
return content;
5155
}
5256

5357
/**
@@ -65,16 +69,18 @@ public CompletableFuture<ScanContentResult> getDirectoryContent(Project project,
6569
VirtualFile directory,
6670
int tokenLimit,
6771
boolean isTokenCalculation) {
68-
return ProjectScannerService.getInstance()
69-
.scanProject(project, directory, tokenLimit, isTokenCalculation)
70-
.thenApply(content -> {
71-
if (!isTokenCalculation) {
72-
copyToClipboard(content);
73-
}
74-
return content;
75-
});
72+
ProjectScannerService instance = ProjectScannerService.getInstance();
73+
ScanContentResult scanContentResult = instance.scanProject(project, directory, tokenLimit, isTokenCalculation);
74+
return CompletableFuture.completedFuture(scanContentResult)
75+
.thenApply(content ->
76+
getScanContentResult(isTokenCalculation, content));
7677
}
7778

79+
/**
80+
* Retrieves and processes the content of a specified file within a Project.
81+
* @param provider ModelProvider enum value representing the model provider to use
82+
* @return Encoding object representing the encoding to use for the specified provider
83+
*/
7884
public static Encoding getEncodingForProvider(@NotNull ModelProvider provider) {
7985
return switch (provider) {
8086
case OpenAI, Anthropic, Google, AzureOpenAI ->
@@ -88,6 +94,10 @@ public static Encoding getEncodingForProvider(@NotNull ModelProvider provider) {
8894
};
8995
}
9096

97+
/**
98+
* Copies the content of a ScanContentResult object to the system clipboard.
99+
* @param contentResult ScanContentResult object containing the content to copy
100+
*/
91101
private void copyToClipboard(@NotNull ScanContentResult contentResult) {
92102
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
93103
clipboard.setContents(new StringSelection(contentResult.getContent()), null);

src/main/java/com/devoxx/genie/service/TokenCalculationService.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,11 @@ public void calculateTokensAndCost(@NotNull Project project,
4040
if (showCost) {
4141
showCostAndScanInfo(project, selectedProvider, selectedLanguageModel, contentFuture, listener);
4242
} else {
43-
showOnlyScanInfo(project, directory, selectedProvider, contentFuture, listener);
43+
showOnlyScanInfo(directory, selectedProvider, contentFuture, listener);
4444
}
4545
}
4646

47-
private static void showOnlyScanInfo(@NotNull Project project,
48-
VirtualFile directory,
47+
private static void showOnlyScanInfo(VirtualFile directory,
4948
@NotNull ModelProvider selectedProvider,
5049
@NotNull CompletableFuture<ScanContentResult> contentFuture,
5150
TokenCalculationListener listener) {
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package com.devoxx.genie.service.projectscanner;
2+
3+
import com.devoxx.genie.ui.settings.DevoxxGenieStateService;
4+
import com.intellij.openapi.application.ApplicationManager;
5+
import com.intellij.openapi.application.ReadAction;
6+
import com.intellij.openapi.diagnostic.Logger;
7+
import com.intellij.openapi.vfs.VirtualFile;
8+
import org.jetbrains.annotations.NotNull;
9+
10+
import java.io.BufferedReader;
11+
import java.io.IOException;
12+
import java.io.InputStream;
13+
import java.io.InputStreamReader;
14+
import java.nio.charset.StandardCharsets;
15+
16+
public class ContentExtractor {
17+
private static final Logger LOG = Logger.getInstance(ContentExtractor.class.getName());
18+
19+
/**
20+
* Extracts the content of a file and formats it for inclusion in the project scan.
21+
*/
22+
public String extractFileContent(@NotNull VirtualFile file) {
23+
StringBuilder content = new StringBuilder();
24+
String header = "\n--- " + file.getPath() + " ---\n";
25+
content.append(header);
26+
27+
// Simplified approach - skip the ReadAction complexity for now
28+
try {
29+
extractFileContentInternal(file, content);
30+
} catch (Exception e) {
31+
// Just add the error message to the content
32+
content.append("Error reading file content: ").append(e.getMessage());
33+
}
34+
35+
return content.toString();
36+
}
37+
38+
/**
39+
* Internal implementation to extract file content - separated for easier testing
40+
*/
41+
protected void extractFileContentInternal(VirtualFile file, StringBuilder content) throws IOException {
42+
String fileContent;
43+
44+
try (InputStream is = file.getInputStream();
45+
BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
46+
47+
StringBuilder sb = new StringBuilder();
48+
String line;
49+
while ((line = reader.readLine()) != null) {
50+
sb.append(line).append('\n');
51+
}
52+
fileContent = sb.toString();
53+
}
54+
55+
// Process and append the file content
56+
content.append(processFileContent(fileContent));
57+
}
58+
59+
public String combineContent(String directoryStructure, String fileContents) {
60+
return "Directory Structure:\n" +
61+
directoryStructure +
62+
"\n\nFile Contents:\n" +
63+
fileContents;
64+
}
65+
66+
private String processFileContent(String content) {
67+
if (Boolean.TRUE.equals(DevoxxGenieStateService.getInstance().getExcludeJavaDoc())) {
68+
return removeJavadoc(content);
69+
}
70+
return content;
71+
}
72+
73+
private @NotNull String removeJavadoc(String content) {
74+
// Remove block comments (which include Javadoc)
75+
content = content.replaceAll("/\\*{1,2}[\\s\\S]*?\\*/", "");
76+
// Remove single-line comments that start with '///'
77+
content = content.replaceAll("^\\s*///.*$", "");
78+
return content;
79+
}
80+
}

src/main/java/com/devoxx/genie/service/projectscanner/UniqueDirectoryScannerService.java renamed to src/main/java/com/devoxx/genie/service/projectscanner/DirectoryScannerService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import java.util.*;
1010

1111
@Getter
12-
public class UniqueDirectoryScannerService {
12+
public class DirectoryScannerService {
1313

1414
private final Map<String, VirtualFile> uniqueDirectories = new HashMap<>();
1515

0 commit comments

Comments
 (0)