branch: elpa/geiser-kawa commit 95573c2fed46fceed1a5a1cb26ddf818c7cae1fa Author: spellcard199 <spellcard...@protonmail.com> Commit: spellcard199 <spellcard...@protonmail.com>
Add initial support for colon-notation completion --- README.org | 87 ++++++++++++++-------- elisp/geiser-kawa-complete-java.el | 145 +++++++++++++++++++++++++++++++++++++ elisp/geiser-kawa.el | 4 +- pom.xml | 2 +- 4 files changed, 206 insertions(+), 32 deletions(-) diff --git a/README.org b/README.org index 0e7574a..103b5ba 100644 --- a/README.org +++ b/README.org @@ -4,36 +4,6 @@ geiser-kawa-java is the emacs side of a geiser-kawa implementation that uses [[https://gitlab.com/spellcard199/kawa-geiser][kawa-geiser]] for its Kawa side. -** Supported features - -- eval -- load-file -- completions -- module-completions (very fragile atm) -- autodoc: - - for scheme procedures - - for java methods - -** Unsupported features - -geiser-related: -- macroexpand -- find-file -- symbol-location -- module-location -- symbol-documentation -- module-exports -- callers -- callees -- generic-methods -- (TODO) manual lookup: should work for both formats: - - info: using emacs' Info-mode - - epub: using emacs' eww browser - -kawa- and java-specific: -- completion for java methods -- completion for java classes - ** Supported Kawa versions :PROPERTIES: :CUSTOM_ID: 85b51f74-b1b2-4863-a888-0b11580321f3 @@ -53,6 +23,63 @@ Only versions of Kawa > 3.1 are supported, mostly due to the fact that before th The reason you don't need Kawa to try geiser-kawa is that =quickstart.el= uses the compiled [[https://gitlab.com/groups/kashell/][Kawa's master branch]] that is one of [[http://gitlab.com/spellcard199/kawa-geiser][kawa-geiser]]'s dependencies. To do so =quickstart.el= calls =mvn package= (wrapped by =geiser-kawa-compile-java-dependencies=), which produces a jar that includes all [[http://gitlab.com/spellcard199/kawa-geiser][kawa-geiser]]'s dependencies. +** Supported features + +- geiser-related: + - eval + - load-file + - completions + - module-completions (very fragile atm) + - autodoc: + - for scheme procedures + - for java methods + - manual lookup: + - remember to set variable =geiser-kawa-manual-path= + - should work for both formats: + - 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 + +*** About completion for java classes' fields and methods + +The main interactive elisp function is =geiser-kawa-complete-java-fom-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 +- avoid syntax errors (e.g. unbalanced parentheses, wrong number of children sexps inside a form, etc...) + +How it works: +1. Code sent is: + - from either: + - =(geiser-syntax--pop-to-top)=, if non-nil + - =(line-beginning-position)=, if =(geiser-syntax--pop-to-top)= is =nil= + - to either: + - end of sexp beginning at toplevel, if =(geiser-syntax--pop-to-top)= is non-nil + - =(line-end-position)=, if =(geiser-syntax--pop-to-top)= is =nil= +2. Kawa code and cursor point inside the code are sent to [[https://gitlab.com/spellcard199/kawa-geiser][kawa-geiser]], which uses [[https://gitlab.com/spellcard199/kawa-devutil][kawa-devutil]] for actual functionality +3. An arbitrary symbol is added at cursor point, so it can be found again in the AST +4. Code is compiled into Kawa's AST but not evaluated +5. Various AST "matchers" (functions that check if a certain pattern is found) are tried in order until the first matcher that matches + - "Matcher" functions also try to recognize the Class of the object of interest inside the AST + - If no matchers match, result is empty data + +** Unsupported features + +geiser-related: +- macroexpand +- find-file +- symbol-location +- module-location +- symbol-documentation +- module-exports +- callers +- callees +- generic-methods + +kawa- and java-specific: +- completion for java package names and class names + ** Cask issue with geiser If you use geiser as a dependency in a Cask project, Cask eagerly expands the =define-geiser-implementation= macro with =load-file-name= having the wrong value. The result is that geiser implementations in geiser do not work when geiser is managed as a dependency by Cask. diff --git a/elisp/geiser-kawa-complete-java.el b/elisp/geiser-kawa-complete-java.el new file mode 100644 index 0000000..55eac45 --- /dev/null +++ b/elisp/geiser-kawa-complete-java.el @@ -0,0 +1,145 @@ +;; Copyright (C) 2020 spellcard199 <spellcard...@protonmail.com> + +;; This program is free software; you can redistribute it and/or +;; modify it under the terms of the Modified BSD License. You should +;; have received a copy of the license along with this program. If +;; not, see <http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5>. + +(require 'subr-x) + +(defun geiser-kawa--repl--point-after-prompt () + (save-excursion + (and (string-prefix-p + (geiser-repl-buffer-name 'kawa) + (buffer-name)) + (re-search-backward geiser-kawa--prompt-regexp nil t) + (re-search-forward geiser-kawa--prompt-regexp nil t)))) + +(defun geiser-kawa--point-at-toplevel-p () + (equal (point) + (save-excursion + (geiser-syntax--pop-to-top) + (point)))) + +(defun geiser-kawa-complete-java--get-data (code-str cursor-index) + "`code' is a string containing the code. It must be syntatically + scheme, including balanced parentheses. +`cursor' is an integer representing where the cursor is in that code." + (let* ((geiser-question + ;; this formatting hell is caused by the fact geiser:eval + ;; takes a string instead of a form. + (format "(geiser:eval (interaction-environment) %S)" + (format "%S" + `(geiser:complete-java + ,code-str + ,cursor-index + (gnu.expr.Language:getDefaultLanguage) + (interaction-environment))))) + (geiser-answer (geiser-eval--send/wait + geiser-question))) + + (if (assoc 'error geiser-answer) + (signal 'peculiar-error + (list (string-trim + (car (split-string (geiser-eval--retort-output + geiser-answer) + "\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) + (let ((compl-for-class + (cadr (assoc "compl-for-class" compl-data))) + (modifiers + (cadr (assoc "modifiers" compl-data))) + (field-or-method + (cadr (assoc "field-or-method" compl-data))) + (completions + (cadr (assoc "completions" compl-data))) + ;; unused + (before-cursor + (cadr (assoc "before-cursor" 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 + before-cursor))) + +(defun geiser-kawa-complete-java--code-point-from-toplevel () + (let* (reg-beg + reg-end + code-str + cursor-index) + (if (geiser-kawa--point-at-toplevel-p) + (progn + (setq reg-beg (line-beginning-position)) + (setq reg-end (line-end-position)) + (setq cursor-index (current-column))) + (progn + (save-excursion + (setq reg-beg (progn (geiser-syntax--pop-to-top) + (point))) + (setq reg-end (condition-case data + (progn (forward-sexp) + (point)) + (scan-error data)))) + (when (and (listp reg-end) + (equal (car reg-end) 'scan-error)) + ;; For now, it's easier not to fix unbalanced parenses + (signal (car reg-end) (cdr reg-end))) + (setq cursor-index (- (point) reg-beg)))) + (setq code-str (buffer-substring-no-properties + reg-beg reg-end)) + (list + `("reg-beg" . ,reg-beg) + `("reg-end" . ,reg-end) + `("code-str" . ,code-str) + `("cursor-index" . ,cursor-index)))) + +(defun geiser-kawa-complete-java-fom-at-point () + (interactive) + "Complete java field or method at point" + + (let* ((code-and-point-data + (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 + compl-data))) + (when (word-at-point) + (if (looking-back ":" (- (point) 2)) + (kill-word 1) + (kill-word -1))) + (insert user-choice) + ;; (when (not (equal (word-at-point) user-choice)) + ;; (kill-word 1) + )) + +(provide 'geiser-kawa-complete-java) diff --git a/elisp/geiser-kawa.el b/elisp/geiser-kawa.el index 3e8e3a3..5bf5435 100644 --- a/elisp/geiser-kawa.el +++ b/elisp/geiser-kawa.el @@ -1,7 +1,7 @@ ;;; geiser-kawa.el --- geiser support for Kawa scheme -*- lexical-binding:t -*- ;; Copyright (C) 2018 Mathieu Lirzin <m...@gnu.org> -;; Copyright (C) 2019 spellcard199 <spellcard...@protonmail.com> +;; Copyright (C) 2019, 2020 spellcard199 <spellcard...@protonmail.com> ;; This program is free software; you can redistribute it and/or ;; modify it under the terms of the Modified BSD License. You should @@ -36,6 +36,8 @@ (require 'info-look) (require 'cl) +(require 'geiser-kawa-complete-java) + ;;; Code: diff --git a/pom.xml b/pom.xml index 4f63fd7..32e215e 100644 --- a/pom.xml +++ b/pom.xml @@ -55,7 +55,7 @@ <dependency> <groupId>com.gitlab.spellcard199</groupId> <artifactId>kawa-geiser</artifactId> - <version>f47df5ea22503b0eb4d83bc3be6066d9edb3cd18</version> + <version>3e7d104a58457e9710f57b1a9b79ea8b48467b5c</version> </dependency> </dependencies>