branch: elpa/geiser-kawa commit 05e1eaa69ab06d916dfeb23dac87aca2bac11db0 Author: spellcard199 <spellcard...@protonmail.com> Commit: spellcard199 <spellcard...@protonmail.com>
Add initial support for completing java packages --- README.org | 12 ++- elisp/geiser-kawa-complete-java.el | 110 ++++++++++++++++++---------- pom.xml | 2 +- src/main/java/kawageiser/java/Complete.java | 77 +++++++++++-------- 4 files changed, 122 insertions(+), 79 deletions(-) diff --git a/README.org b/README.org index f919cf3..dc3cb66 100644 --- a/README.org +++ b/README.org @@ -31,7 +31,8 @@ Only versions of Kawa > 3.1 are supported, mostly due to the fact that before th - info: using emacs' Info-mode - epub: using emacs' eww browser - kawa- and java-specific: - - completion for java classes' fields and methods accessed through colon-notation: don't expect java-grade reliability + - completion for java's field and method names accessed through colon-notation (don't expect java-grade reliability) + - completion for java's package and class names ** Unsupported features @@ -46,9 +47,6 @@ geiser-related: - callees - generic-methods -kawa- and java-specific: -- completion for java package names and class names - ** Try geiser-kawa without modifying your emacs configuration 1. Get Emacs and [[https://github.com/cask/cask][Cask]] and make them available through your $PATH @@ -66,11 +64,11 @@ To try geiser-kawa you need neither Maven nor Kawa: - =mvnw= ([[https://github.com/takari/maven-wrapper][maven-wrapper]]) takes care of downloading a project-specific Maven version - kawa-geiser has [[https://gitlab.com/groups/kashell/][Kawa's master branch]] as one of its dependencies. When =quickstart.el= calls =./mvnw package= (wrapped by =geiser-kawa-compile-java-dependencies=), it produces a jar that includes kawa-geiser and all its dependencies, including Kawa itself. -** About completion for java classes' fields and methods +** About completion for java's fields, methods and packages (that's what =fmp= stands for in the function names) -The whole project is in a persistent "experimental" state, but this part even more so. +The whole project is in a persistent "experimental" state, but this part even more so because it's based on assumptions I'm not sure about. -The main interactive elisp function is =geiser-kawa-complete-java-fom-at-point=. It's not bound to a key by default. +The main interactive elisp function is =geiser-kawa-complete-java-fmp-at-point=. It's not bound to a key by default. For this to work you have to: - use Kawa's type annotations: rememver that the Kawa compiler mostly trusts you and can't actually check diff --git a/elisp/geiser-kawa-complete-java.el b/elisp/geiser-kawa-complete-java.el index 55eac45..2e4b2b8 100644 --- a/elisp/geiser-kawa-complete-java.el +++ b/elisp/geiser-kawa-complete-java.el @@ -43,51 +43,81 @@ (list (string-trim (car (split-string (geiser-eval--retort-output geiser-answer) - "\t"))))) + "\t"))))) (geiser-eval--retort-result geiser-answer)))) -(defun geiser-kawa-complete-java--user-choice - (compl-for-class modifiers - field-or-method completions - before-cursor) - "`for-class' is the class that owns the field or methods in -`completions'. -`field-or-method' should be either 'field or 'method, but it's not -checked. -`completions' is a list of names (strings) that the user can pick -from." - (completing-read - (concat "(" (string-join modifiers " ") " " field-or-method ") " - compl-for-class "." - ;; "- Exp : " compl-for-expr "\n" - ;; ": " - ) - completions - nil - nil - before-cursor)) - -(defun geiser-kawa-complete-java--user-choice-data - (compl-data) +(defun geiser-kawa-complete-java--user-choice--field-or-method + (fm-compl-data) + ;; fm stands for field or method. (let ((compl-for-class - (cadr (assoc "compl-for-class" compl-data))) + (cadr (assoc "compl-for-class" fm-compl-data))) (modifiers - (cadr (assoc "modifiers" compl-data))) + (cadr (assoc "modifiers" fm-compl-data))) (field-or-method - (cadr (assoc "field-or-method" compl-data))) - (completions - (cadr (assoc "completions" compl-data))) - ;; unused + (cadr (assoc "field-or-method-or-package" fm-compl-data))) + (names + (cadr (assoc "names" fm-compl-data))) (before-cursor - (cadr (assoc "before-cursor" compl-data))) + (cadr (assoc "before-cursor" fm-compl-data))) ;; unused (after-cursor - (cadr (assoc "after-cursor" compl-data)))) - (geiser-kawa-complete-java--user-choice - compl-for-class modifiers - field-or-method completions + (cadr (assoc "after-cursor" fm-compl-data)))) + + (completing-read + (concat "(" (string-join modifiers " ") " " field-or-method ") " + compl-for-class "." + ;; "- Exp : " compl-for-expr "\n" + ;; ": " + ) + names + nil + nil before-cursor))) +(defun geiser-kawa-complete-java--user-choice--package + (package-compl-data) + (let ((field-or-method-or-package + (cadr (assoc "field-or-method-or-package" package-compl-data))) + (package-name + (cadr (assoc "package-name" package-compl-data))) + (names + (cadr (assoc "names" package-compl-data))) + (before-cursor + (cadr (assoc "before-cursor" package-compl-data))) + ;; unused + (after-cursor + (cadr (assoc "after-cursor" package-compl-data)))) + (completing-read + (concat "(" field-or-method-or-package ") " + (if (string-equal "" package-name) + "(root.)" + (concat package-name "."))) + (mapcar (lambda (name) + (string-remove-prefix + "." (string-remove-prefix package-name name))) + names) + nil + nil + (string-remove-prefix + "." (string-remove-prefix package-name before-cursor)) + ))) + +(defun geiser-kawa-complete-java--user-choice-dispatch + (compl-data) + (let ((compl-for (cadr (assoc "field-or-method-or-package" + compl-data)))) + (cond ((equal compl-for "FIELD") + (geiser-kawa-complete-java--user-choice--field-or-method + compl-data)) + ((equal compl-for "METHOD") + (geiser-kawa-complete-java--user-choice--field-or-method + compl-data)) + ((equal compl-for "PACKAGE") + (geiser-kawa-complete-java--user-choice--package + compl-data)) + (t (error (format "[Unexpected condition] compl-for: %s" + (prin1-to-string compl-for))))))) + (defun geiser-kawa-complete-java--code-point-from-toplevel () (let* (reg-beg reg-end @@ -119,24 +149,24 @@ from." `("code-str" . ,code-str) `("cursor-index" . ,cursor-index)))) -(defun geiser-kawa-complete-java-fom-at-point () +(defun geiser-kawa-complete-java-fmp-at-point () (interactive) - "Complete java field or method at point" + "Complete java field or method or package (fmp) at point" (let* ((code-and-point-data - (geiser-kawa-complete-java--code-point-from-toplevel)) + (geiser-kawa-complete-java--code-point-from-toplevel)) (code-str (cdr (assoc "code-str" code-and-point-data))) (cursor-index (cdr (assoc "cursor-index" code-and-point-data))) (compl-data (geiser-kawa-complete-java--get-data code-str cursor-index)) - (user-choice (geiser-kawa-complete-java--user-choice-data + (user-choice (geiser-kawa-complete-java--user-choice-dispatch compl-data))) (when (word-at-point) (if (looking-back ":" (- (point) 2)) (kill-word 1) - (kill-word -1))) + (kill-word -1))) (insert user-choice) ;; (when (not (equal (word-at-point) user-choice)) ;; (kill-word 1) diff --git a/pom.xml b/pom.xml index 3d11883..c571396 100644 --- a/pom.xml +++ b/pom.xml @@ -63,7 +63,7 @@ <dependency> <groupId>com.gitlab.spellcard199</groupId> <artifactId>kawa-devutil</artifactId> - <version>0c12c104505e4965308b9214486f2051af152360</version> + <version>593d76dcb9a6b21e120f754b0b5f53dcfd4e17e0</version> </dependency> <!-- https://mvnrepository.com/artifact/org.testng/testng --> diff --git a/src/main/java/kawageiser/java/Complete.java b/src/main/java/kawageiser/java/Complete.java index 6be9291..deaec3b 100644 --- a/src/main/java/kawageiser/java/Complete.java +++ b/src/main/java/kawageiser/java/Complete.java @@ -8,22 +8,16 @@ package kawageiser.java; import gnu.expr.Language; import gnu.lists.IString; import gnu.lists.LList; -import gnu.lists.Pair; import gnu.mapping.Environment; -import gnu.mapping.Procedure1or2; -import gnu.mapping.Procedure3; import gnu.mapping.Procedure4; import gnu.math.IntNum; import kawadevutil.complete.*; -import java.io.File; -import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; -import java.util.stream.Stream; public class Complete extends Procedure4 { @@ -64,36 +58,57 @@ public class Complete extends Procedure4 { return LList.Empty; } else { CompletionDataForJava complData = complDataMaybe.get(); - if (complData.getClass().equals(CompletionDataForJavaField.class)) { - CompletionDataForJavaField complDataForField = (CompletionDataForJavaField) complData; - } else if (complData.getClass().equals(CompletionDataForJavaMethod.class)) { - CompletionDataForJavaMethod complDataForMethod = (CompletionDataForJavaMethod) complData; + LList res = null; + if (complData.getClass().equals(CompletionDataForJavaField.class) + || complData.getClass().equals(CompletionDataForJavaMethod.class)) { + res = toLList((CompletionDataForJavaFOM) complData); + } else if (complData.getClass().equals(CompletionDataForJavaPackage.class)) { + res = toLList((CompletionDataForJavaPackage) complData); } else { throw new Error("Bug spotted."); } + return gnu.kawa.functions.Format.format("~S", res); + } + } - String completionsForClass = complData.getForClass().getName(); - CompletionDataForJava.FieldOrMethod fieldOrMethod = complData.getFieldOrMethod(); - List<String> names = (List<String>) complData.getNames().stream().distinct().collect(Collectors.toList()); - String beforeCursor = complData.getCursorMatcher().getCursorMatch().getBeforeCursor(); - String afterCursor = complData.getCursorMatcher().getCursorMatch().getAfterCursor(); - // I don't know why it says "unchecked call" when using complData.getRequiredModifiers().stream() - ArrayList<String> modifiers = new ArrayList<>(); - for (Object modifier : complData.getRequiredModifiers()) { - modifiers.add(modifier.toString()); - } - - java.util.List<LList> res = Arrays.asList( - LList.list2("compl-for-class", completionsForClass), - LList.list2("modifiers", LList.makeList(modifiers)), - LList.list2("field-or-method", fieldOrMethod.toString()), - LList.list2("completions", LList.makeList(names)), - LList.list2("before-cursor", beforeCursor), - LList.list2("after-cursor", afterCursor) - ); - LList resLList = LList.makeList(res); - return gnu.kawa.functions.Format.format("~S", resLList); + private static LList toLList(CompletionDataForJavaFOM complData) { + String completionsForClass = complData.getForClass().getName(); + // I don't know why it says "unchecked call" when using complData.getRequiredModifiers().stream() + ArrayList<String> modifiers = new ArrayList<>(); + for (Object modifier : complData.getRequiredModifiers()) { + modifiers.add(modifier.toString()); } + + ArrayList<LList> res = new ArrayList<>(getCommonData(complData)); + res.addAll(Arrays.asList( + LList.list2("compl-for-class", completionsForClass), + LList.list2("modifiers", LList.makeList(modifiers)) + )); + return LList.makeList(res); + + } + + private static LList toLList(CompletionDataForJavaPackage complData) { + ArrayList<LList> res = new ArrayList<>(getCommonData(complData)); + res.addAll(Arrays.asList( + LList.list2("package-name", complData.getPinfo().getName()) + )); + return LList.makeList(res); + } + + private static List<LList> getCommonData(CompletionDataForJava complData) { + CompletionDataForJava.FieldOrMethodOrPackage fieldOrMethod = complData.getFieldOrMethodOrPackage(); + List<String> names = (List<String>) complData.getNames().stream().distinct().collect(Collectors.toList()); + String beforeCursor = complData.getCursorMatcher().getCursorMatch().getBeforeCursor(); + String afterCursor = complData.getCursorMatcher().getCursorMatch().getAfterCursor(); + + java.util.List<LList> res = Arrays.asList( + LList.list2("field-or-method-or-package", fieldOrMethod.toString()), + LList.list2("names", LList.makeList(names)), + LList.list2("before-cursor", beforeCursor), + LList.list2("after-cursor", afterCursor) + ); + return res; } }