drccrd commented on code in PR #3965:
URL:
https://github.com/apache/incubator-kie-tools/pull/3965#discussion_r3842249468
##########
packages/drools-lsp/drools-completion/src/main/java/org/drools/completion/DRLCompletionHelper.java:
##########
@@ -208,6 +250,155 @@ private static List<CompletionItem>
getFieldCompletionItems(DRL10Parser.Compilat
return fieldItems(memberIndex.membersOf(fqcn));
}
+ /**
+ * The dot-separated segments preceding a caret that sits immediately
after a
+ * dot ({@code $ref.order.|} → {@code [$ref, order]}), or {@code null}
when the
+ * caret follows something else. A numeric head is a decimal literal being
+ * typed, not a path.
+ */
+ private static String[] dottedChainBeforeCaret(String text, Position
caret) {
+ if (text == null || caret == null) {
+ return null;
+ }
+ String[] lines = text.split("\n", -1);
+ int row = caret.getLine();
+ if (row < 0 || row >= lines.length) {
+ return null;
+ }
+ String line = lines[row];
+ int col = Math.min(caret.getCharacter(), line.length());
+ if (col == 0 || line.charAt(col - 1) != '.') {
+ return null;
+ }
+ int start = col - 1;
+ while (start > 0 && isChainChar(line.charAt(start - 1))) {
+ start--;
+ }
+ String path = line.substring(start, col - 1);
+ if (path.isEmpty() || path.endsWith(".") ||
Character.isDigit(path.charAt(0))) {
+ return null;
+ }
+ return path.split("\\.", -1);
+ }
+
+ private static boolean isChainChar(char c) {
+ return Character.isLetterOrDigit(c) || c == '_' || c == '$' || c ==
'.';
+ }
+
+ /**
+ * Member items for a dotted path the caret sits at the end of. The head
is a
+ * binding ({@code $p.}), a field of the enclosing pattern's type ({@code
ref.}
+ * inside {@code Fact(...)}), or a type name ({@code Status.}); the
remaining
+ * segments are fields. Every hop goes through the same walker the
bindings and
+ * hover use, so all three agree on what a path resolves to.
+ */
+ /**
+ * Completion items for the members of the type the chain resolves to, or
+ * {@code null} when the chain's head names no type the document knows — a
+ * qualified name rather than a member access. An empty list means the path
+ * did resolve and simply has no members to offer, which is an answer:
after
+ * a dot nothing but a member is legal.
+ */
+ private static List<CompletionItem> memberItemsForChain(String[] chain,
String text, Position caret,
+
DRL10Parser.CompilationUnitContext compilationUnit,
+ int
caretTokenIndex, ClassIndex classIndex,
+ ClassMemberIndex
memberIndex, Path documentPath,
+ Map<Path, String>
openFiles) {
+ if (compilationUnit == null) {
+ return null;
+ }
+ Map<String, DeclaredType> typeIndex = DRLWorkspaceTypeIndex.build(
+
DRLDeclaredTypeParser.extractFromCompilationUnit(compilationUnit),
documentPath, openFiles);
+
+ String head = chain[0];
+ String rootType;
+ int firstFieldSegment = 1;
+ if (head.startsWith("$")) {
+ rootType = LhsBindingResolver.resolveAt(text,
DRLHoverHelper.positionToOffset(text, caret), typeIndex)
+ .get(head.substring(1));
+ } else if (!head.isEmpty() && Character.isUpperCase(head.charAt(0))) {
+ rootType = head;
+ } else {
+ // A bare lower-case head is a field of the pattern the caret is
in.
+ rootType = enclosingPatternTypeFromText(text,
DRLHoverHelper.positionToOffset(text, caret));
+ firstFieldSegment = 0;
+ }
+ if (rootType == null || rootType.isEmpty()) {
+ // The head names no type the document knows, so this dot is not a
+ // member access at all — a qualified name, most likely.
+ return null;
+ }
+
+ String resolved = rootType.substring(rootType.lastIndexOf('.') + 1);
+ if (firstFieldSegment < chain.length) {
+ String path = String.join(".", Arrays.copyOfRange(chain,
firstFieldSegment, chain.length));
+ resolved = LhsBindingResolver.resolvePath(
+ LhsBindingResolver.typeOrClasspath(resolved, typeIndex),
path, typeIndex);
+ if (resolved == null) {
+ return List.of();
+ }
+ }
+ return memberItemsOfType(resolved, typeIndex, compilationUnit,
classIndex, memberIndex);
+ }
+
+ /**
+ * The type name of the pattern whose parentheses are still open at
+ * {@code offset}, read from the text rather than the parse tree: a caret
in
+ * mid-edit leaves the constraint incomplete, and error recovery then
parses
+ * the incomplete path itself as a pattern head, which
+ * {@link #findEnclosingPatternTypeName} would return in preference to the
+ * real pattern. Non-pattern parens ({@code accumulate(}, {@code eval(}, a
+ * method call) are stepped over — a DRL pattern type's simple name always
+ * begins upper case, which is also how {@code LhsBindingResolver} finds
+ * pattern heads. {@code null} when no pattern encloses the offset.
+ */
+ private static String enclosingPatternTypeFromText(String text, int
offset) {
+ int closed = 0;
Review Comment:
Fixed in a485288556d7d583095f5657d6f44eb29b672ce5 (the same fix as for
literal ""then" as was fixed elsewhere in this branch)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]