Skip to content

Commit 4ce0797

Browse files
committed
feat: update the method
1 parent 6838ba3 commit 4ce0797

File tree

1 file changed

+111
-29
lines changed

1 file changed

+111
-29
lines changed

jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/parser/ContextResolver.java

Lines changed: 111 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
package com.microsoft.jdtls.ext.core.parser;
1313

1414
import java.util.ArrayList;
15+
import java.util.HashSet;
1516
import java.util.List;
1617
import java.util.Set;
1718
import java.util.regex.Matcher;
@@ -31,6 +32,17 @@
3132
*/
3233
public class ContextResolver {
3334

35+
// Pre-compiled regex patterns for performance
36+
private static final Pattern MARKDOWN_CODE_PATTERN = Pattern.compile("(?s)```(?:java)?\\n?(.*?)```");
37+
private static final Pattern HTML_PRE_PATTERN = Pattern.compile("(?is)<pre[^>]*>(.*?)</pre>");
38+
private static final Pattern HTML_CODE_PATTERN = Pattern.compile("(?is)<code[^>]*>(.*?)</code>");
39+
40+
// Constants for limiting displayed members
41+
private static final int MAX_METHODS_TO_DISPLAY = 10;
42+
private static final int MAX_FIELDS_TO_DISPLAY = 10;
43+
private static final int MAX_STATIC_METHODS_TO_DISPLAY = 10;
44+
private static final int MAX_STATIC_FIELDS_TO_DISPLAY = 10;
45+
3446
/**
3547
* ImportClassInfo - Conforms to Copilot CodeSnippet format
3648
* Used to provide Java class context information and JavaDoc to Copilot
@@ -170,7 +182,7 @@ public static void resolveStaticMembersFromClass(IJavaProject javaProject, Strin
170182
for (IMethod method : methods) {
171183
int flags = method.getFlags();
172184
if (org.eclipse.jdt.core.Flags.isStatic(flags) && org.eclipse.jdt.core.Flags.isPublic(flags)) {
173-
if (staticMethodSigs.size() < 10) {
185+
if (staticMethodSigs.size() < MAX_STATIC_METHODS_TO_DISPLAY) {
174186
staticMethodSigs.add(generateMethodSignature(method));
175187
}
176188
}
@@ -182,7 +194,7 @@ public static void resolveStaticMembersFromClass(IJavaProject javaProject, Strin
182194
for (org.eclipse.jdt.core.IField field : fields) {
183195
int flags = field.getFlags();
184196
if (org.eclipse.jdt.core.Flags.isStatic(flags) && org.eclipse.jdt.core.Flags.isPublic(flags)) {
185-
if (staticFieldSigs.size() < 10) {
197+
if (staticFieldSigs.size() < MAX_STATIC_FIELDS_TO_DISPLAY) {
186198
staticFieldSigs.add(generateFieldSignature(field));
187199
}
188200
}
@@ -321,15 +333,12 @@ public static void extractTypeInfo(org.eclipse.jdt.core.IType type, List<ImportC
321333
return;
322334
}
323335

324-
// Generate human-readable class description
325-
String description = generateClassDescription(type);
326-
327-
// Extract relevant JavaDoc content (code snippets with fallback strategy)
336+
// Extract relevant JavaDoc content first (code snippets with fallback strategy)
328337
// This uses a hybrid approach: AST extraction -> HTML extraction -> Markdown extraction -> fallback
329338
String relevantJavadoc = extractRelevantJavaDocContent(type, monitor);
330-
if (isNotEmpty(relevantJavadoc)) {
331-
description = description + "\n" + relevantJavadoc;
332-
}
339+
340+
// Generate human-readable class description with JavaDoc inserted after signature
341+
String description = generateClassDescription(type, relevantJavadoc);
333342

334343
// Create ImportClassInfo (conforms to Copilot CodeSnippet format)
335344
ImportClassInfo info = new ImportClassInfo(uri, description);
@@ -385,8 +394,10 @@ public static String getTypeUri(org.eclipse.jdt.core.IType type) {
385394

386395
/**
387396
* Generate complete class description (natural language format, similar to JavaDoc)
397+
* @param type the Java type to describe
398+
* @param javadoc optional JavaDoc content to insert after signature (can be null or empty)
388399
*/
389-
public static String generateClassDescription(org.eclipse.jdt.core.IType type) {
400+
public static String generateClassDescription(org.eclipse.jdt.core.IType type, String javadoc) {
390401
StringBuilder description = new StringBuilder();
391402

392403
try {
@@ -449,7 +460,12 @@ public static String generateClassDescription(org.eclipse.jdt.core.IType type) {
449460

450461
description.append("Signature: ").append(signature).append("\n\n");
451462

452-
// === 2. Constructors ===
463+
// === 2. JavaDoc (inserted after signature) ===
464+
if (isNotEmpty(javadoc)) {
465+
description.append("JavaDoc:\n").append(javadoc).append("\n\n");
466+
}
467+
468+
// === 3. Constructors ===
453469
IMethod[] methods = type.getMethods();
454470
List<String> constructorSigs = new ArrayList<>();
455471

@@ -467,13 +483,13 @@ public static String generateClassDescription(org.eclipse.jdt.core.IType type) {
467483
description.append("\n");
468484
}
469485

470-
// === 3. Public methods (limited to first 10) ===
486+
// === 4. Public methods (limited to first 10) ===
471487
List<String> methodSigs = new ArrayList<>();
472488
int methodCount = 0;
473489

474490
for (IMethod method : methods) {
475491
if (!method.isConstructor() && org.eclipse.jdt.core.Flags.isPublic(method.getFlags())) {
476-
if (methodCount < 10) {
492+
if (methodCount < MAX_METHODS_TO_DISPLAY) {
477493
methodSigs.add(generateMethodSignature(method));
478494
methodCount++;
479495
} else {
@@ -487,19 +503,19 @@ public static String generateClassDescription(org.eclipse.jdt.core.IType type) {
487503
for (String sig : methodSigs) {
488504
description.append(" - ").append(sig).append("\n");
489505
}
490-
if (methodCount == 10 && methods.length > methodCount) {
506+
if (methodCount == MAX_METHODS_TO_DISPLAY && methods.length > methodCount) {
491507
description.append(" - ... (more methods available)\n");
492508
}
493509
description.append("\n");
494510
}
495511

496-
// === 4. Public fields (limited to first 10) ===
512+
// === 5. Public fields (limited to first 10) ===
497513
org.eclipse.jdt.core.IField[] fields = type.getFields();
498514
List<String> fieldSigs = new ArrayList<>();
499515
int fieldCount = 0;
500516

501517
for (org.eclipse.jdt.core.IField field : fields) {
502-
if (org.eclipse.jdt.core.Flags.isPublic(field.getFlags()) && fieldCount < 10) {
518+
if (org.eclipse.jdt.core.Flags.isPublic(field.getFlags()) && fieldCount < MAX_FIELDS_TO_DISPLAY) {
503519
fieldSigs.add(generateFieldSignature(field));
504520
fieldCount++;
505521
}
@@ -552,13 +568,13 @@ private static String extractRelevantJavaDocContent(org.eclipse.jdt.core.IType t
552568
}
553569

554570
StringBuilder allCodeSnippets = new StringBuilder();
571+
Set<String> seenCodeSnippets = new HashSet<>();
555572

556573
// 1. Extract markdown code blocks (```...```)
557-
Pattern markdownPattern = Pattern.compile("(?s)```(?:java)?\\n?(.*?)```");
558-
Matcher markdownMatcher = markdownPattern.matcher(rawJavadoc);
574+
Matcher markdownMatcher = MARKDOWN_CODE_PATTERN.matcher(rawJavadoc);
559575
while (markdownMatcher.find()) {
560576
String code = markdownMatcher.group(1).trim();
561-
if (isNotEmpty(code)) {
577+
if (isNotEmpty(code) && seenCodeSnippets.add(code)) {
562578
allCodeSnippets.append("```java\n").append(code).append("\n```\n\n");
563579
}
564580
}
@@ -569,22 +585,20 @@ private static String extractRelevantJavaDocContent(org.eclipse.jdt.core.IType t
569585
cleanedForHtml = convertHtmlEntities(cleanedForHtml);
570586

571587
// Priority 1: <pre> blocks (often contain well-formatted code)
572-
Pattern prePattern = Pattern.compile("(?is)<pre[^>]*>(.*?)</pre>");
573-
Matcher preMatcher = prePattern.matcher(cleanedForHtml);
588+
Matcher preMatcher = HTML_PRE_PATTERN.matcher(cleanedForHtml);
574589
while (preMatcher.find()) {
575590
String code = preMatcher.group(1).replaceAll("(?i)<code[^>]*>", "").replaceAll("(?i)</code>", "").trim();
576-
if (isNotEmpty(code)) {
591+
if (isNotEmpty(code) && seenCodeSnippets.add(code)) {
577592
allCodeSnippets.append("```java\n").append(code).append("\n```\n\n");
578593
}
579594
}
580595

581596
// Priority 2: <code> blocks (for inline snippets)
582-
Pattern codePattern = Pattern.compile("(?is)<code[^>]*>(.*?)</code>");
583-
Matcher codeMatcher = codePattern.matcher(cleanedForHtml);
597+
Matcher codeMatcher = HTML_CODE_PATTERN.matcher(cleanedForHtml);
584598
while (codeMatcher.find()) {
585599
String code = codeMatcher.group(1).trim();
586-
// Avoid adding duplicates that might be inside <pre><code>
587-
if (isNotEmpty(code) && allCodeSnippets.indexOf(code) == -1) {
600+
// Use HashSet for O(1) duplicate checking
601+
if (isNotEmpty(code) && seenCodeSnippets.add(code)) {
588602
allCodeSnippets.append("```java\n").append(code).append("\n```\n\n");
589603
}
590604
}
@@ -804,12 +818,18 @@ public static String convertTypeSignature(String jdtSignature) {
804818
if (jdtSignature.startsWith("Q") && jdtSignature.endsWith(";")) {
805819
baseType = jdtSignature.substring(1, jdtSignature.length() - 1);
806820
baseType = baseType.replace('/', '.');
821+
822+
// Handle generic type parameters (e.g., "QResult<QUser;>;")
823+
baseType = processGenericTypes(baseType);
807824
baseType = simplifyTypeName(baseType);
808825
}
809826
// Handle fully qualified types (starts with L)
810827
else if (jdtSignature.startsWith("L") && jdtSignature.endsWith(";")) {
811828
baseType = jdtSignature.substring(1, jdtSignature.length() - 1);
812829
baseType = baseType.replace('/', '.');
830+
831+
// Handle generic type parameters
832+
baseType = processGenericTypes(baseType);
813833
baseType = simplifyTypeName(baseType);
814834
}
815835
// Handle primitive types
@@ -835,16 +855,78 @@ public static String convertTypeSignature(String jdtSignature) {
835855

836856
return baseType;
837857
}
858+
859+
/**
860+
* Process generic type parameters in a type name
861+
* Example: "Result<QUser;>" -> "Result<User>"
862+
*/
863+
private static String processGenericTypes(String typeName) {
864+
if (typeName == null || !typeName.contains("<")) {
865+
return typeName;
866+
}
867+
868+
StringBuilder result = new StringBuilder();
869+
int i = 0;
870+
871+
while (i < typeName.length()) {
872+
char c = typeName.charAt(i);
873+
874+
if (c == '<' || c == ',' || c == ' ') {
875+
// Keep angle brackets, commas, and spaces
876+
result.append(c);
877+
i++;
878+
879+
// Skip whitespace after comma or opening bracket
880+
while (i < typeName.length() && typeName.charAt(i) == ' ') {
881+
result.append(' ');
882+
i++;
883+
}
884+
885+
// Check if next is a type parameter (Q or L prefix)
886+
if (i < typeName.length()) {
887+
char next = typeName.charAt(i);
888+
889+
if (next == 'Q' || next == 'L') {
890+
// Find the end of this type parameter (marked by ;)
891+
int endIndex = typeName.indexOf(';', i);
892+
if (endIndex != -1) {
893+
// Extract the type parameter and convert it
894+
String typeParam = typeName.substring(i + 1, endIndex);
895+
896+
// Recursively process nested generics
897+
typeParam = processGenericTypes(typeParam);
898+
typeParam = simplifyTypeName(typeParam);
899+
900+
result.append(typeParam);
901+
i = endIndex + 1; // Skip past the semicolon
902+
} else {
903+
result.append(next);
904+
i++;
905+
}
906+
} else {
907+
// Not a type parameter, just append
908+
result.append(next);
909+
i++;
910+
}
911+
}
912+
} else {
913+
result.append(c);
914+
i++;
915+
}
916+
}
917+
918+
return result.toString();
919+
}
838920

839921
/**
840922
* Simplify fully qualified type name to just the simple name
841923
*/
842924
private static String simplifyTypeName(String qualifiedName) {
843-
if (qualifiedName == null || !qualifiedName.contains(".")) {
925+
if (qualifiedName == null) {
844926
return qualifiedName;
845927
}
846-
String[] parts = qualifiedName.split("\\.");
847-
return parts[parts.length - 1];
928+
int lastDot = qualifiedName.lastIndexOf('.');
929+
return lastDot == -1 ? qualifiedName : qualifiedName.substring(lastDot + 1);
848930
}
849931

850932
/**

0 commit comments

Comments
 (0)