emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[nongnu] elpa/geiser-kawa 86ef157 020/119: Join projects: kawa-geiser is


From: Philip Kaludercic
Subject: [nongnu] elpa/geiser-kawa 86ef157 020/119: Join projects: kawa-geiser is now part of geiser-kawa
Date: Sun, 1 Aug 2021 18:30:31 -0400 (EDT)

branch: elpa/geiser-kawa
commit 86ef157ea976bce407e758850a7291aa0b574841
Author: spellcard199 <spellcard199@protonmail.com>
Commit: spellcard199 <spellcard199@protonmail.com>

    Join projects: kawa-geiser is now part of geiser-kawa
---
 elisp/geiser-kawa.el                               |   2 +-
 example-mvn-run-geiser.sh                          |  14 +
 pom.xml                                            |  21 +-
 src/main/java/kawageiser/Geiser.java               |  32 +++
 src/main/java/kawageiser/GeiserAutodoc.java        | 307 +++++++++++++++++++++
 src/main/java/kawageiser/GeiserCompleteModule.java |  86 ++++++
 src/main/java/kawageiser/GeiserCompleteSymbol.java |  70 +++++
 src/main/java/kawageiser/GeiserEval.java           | 109 ++++++++
 src/main/java/kawageiser/GeiserLoadFile.java       |  39 +++
 src/main/java/kawageiser/GeiserNoValues.java       |  24 ++
 .../kawageiser/StartKawaWithGeiserSupport.java     |  53 ++++
 .../geiserDoc/ManualEpubUnzipToTmpDir.java         |  42 +++
 src/main/java/kawageiser/java/Complete.java        |  99 +++++++
 src/test/java/kawageiser/GeiserAutodocTest.java    |  88 ++++++
 14 files changed, 982 insertions(+), 4 deletions(-)

diff --git a/elisp/geiser-kawa.el b/elisp/geiser-kawa.el
index 5bf5435..be5492f 100644
--- a/elisp/geiser-kawa.el
+++ b/elisp/geiser-kawa.el
@@ -86,7 +86,7 @@
 ;; jar containing all the java dependencies.
 (defcustom geiser-kawa-kawa-geiser-jar-path
   (expand-file-name
-   "./target/kawa-geiser-wrapper-0.1-SNAPSHOT-jar-with-dependencies.jar"
+   "./target/kawa-geiser-0.1-SNAPSHOT-jar-with-dependencies.jar"
    geiser-kawa-dir)
   "Path to the kawa-geiser fat jar."
   :type 'string
diff --git a/example-mvn-run-geiser.sh b/example-mvn-run-geiser.sh
new file mode 100644
index 0000000..f0cabc6
--- /dev/null
+++ b/example-mvn-run-geiser.sh
@@ -0,0 +1,14 @@
+#!/bin/env sh
+
+# This file is not used by geiser-kawa. It's just to show that you can
+# start a Kawa repl with geiser support using the Kawa version that is
+# included in the maven dependencies of kawa-geiser.
+
+# valid exec.args are either:
+# - a port number: starts a Kawa telnet server listening on that port
+# - --no-server: starts a Kawa repl in current terminal
+
+mvn compile &&
+    mvn exec:java \
+       -D"exec.mainClass"="kawageiser.StartKawaWithGeiserSupport" \
+       -D"exec.args"=$@
diff --git a/pom.xml b/pom.xml
index 32e215e..71bbc1a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,11 +1,16 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright (C) 2020 spellcard199 <spellcard199@protonmail.com>
+  ~ This is free software;  for terms and warranty disclaimer see ./COPYING.
+  -->
+
 <project xmlns="http://maven.apache.org/POM/4.0.0";
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
     <modelVersion>4.0.0</modelVersion>
 
     <groupId>com.gitlab.spellcard199</groupId>
-    <artifactId>kawa-geiser-wrapper</artifactId>
+    <artifactId>kawa-geiser</artifactId>
     <version>0.1-SNAPSHOT</version>
 
     <build>
@@ -52,11 +57,21 @@
     </repositories>
 
     <dependencies>
+
         <dependency>
             <groupId>com.gitlab.spellcard199</groupId>
-            <artifactId>kawa-geiser</artifactId>
-            <version>3e7d104a58457e9710f57b1a9b79ea8b48467b5c</version>
+            <artifactId>kawa-devutil</artifactId>
+            <version>0c12c104505e4965308b9214486f2051af152360</version>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/org.testng/testng -->
+        <dependency>
+            <groupId>org.testng</groupId>
+            <artifactId>testng</artifactId>
+            <version>7.0.0</version>
+            <scope>test</scope>
         </dependency>
+
     </dependencies>
 
 </project>
diff --git a/src/main/java/kawageiser/Geiser.java 
b/src/main/java/kawageiser/Geiser.java
new file mode 100644
index 0000000..40b7e18
--- /dev/null
+++ b/src/main/java/kawageiser/Geiser.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 spellcard199 <spellcard199@protonmail.com>
+ * This is free software;  for terms and warranty disclaimer see ./COPYING.
+ */
+
+package kawageiser;
+
+import gnu.expr.Language;
+import kawageiser.geiserDoc.ManualEpubUnzipToTmpDir;
+
+public class Geiser implements Runnable {
+    private static boolean prettyPrintResult = true;
+    public static boolean isPrettyPrintResult() { return prettyPrintResult; }
+    public static boolean isPrettyPrintOutput() { return 
evaluator.isPrettyPrintOutput(); }
+    public static void setPrettyPrintResult(boolean v) { prettyPrintResult = 
v; }
+    public static void setPrettyPrintOutput(boolean v) { 
evaluator.setPrettyPrintOutput(v); }
+
+    public static kawadevutil.eval.Eval evaluator = new 
kawadevutil.eval.Eval();
+
+    @Override
+    public void run() {
+        Language lang = Language.getDefaultLanguage();
+        lang.defineFunction(new GeiserEval("geiser:eval"));
+        lang.defineFunction(new GeiserNoValues("geiser:no-values"));
+        lang.defineFunction(new GeiserLoadFile("geiser:load-file"));
+        lang.defineFunction(new GeiserCompleteSymbol("geiser:completions"));
+        lang.defineFunction(new 
GeiserCompleteModule("geiser:module-completions"));
+        lang.defineFunction(new GeiserAutodoc("geiser:autodoc", lang));
+        lang.defineFunction(new 
kawageiser.java.Complete("geiser:complete-java"));
+        lang.defineFunction(new 
ManualEpubUnzipToTmpDir("geiser:manual-epub-unzip-to-tmp-dir"));
+    }
+}
diff --git a/src/main/java/kawageiser/GeiserAutodoc.java 
b/src/main/java/kawageiser/GeiserAutodoc.java
new file mode 100644
index 0000000..747bbd2
--- /dev/null
+++ b/src/main/java/kawageiser/GeiserAutodoc.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2020 spellcard199 <spellcard199@protonmail.com>
+ * This is free software;  for terms and warranty disclaimer see ./COPYING.
+ */
+
+package kawageiser;
+
+import gnu.expr.CompiledProc;
+import gnu.expr.Language;
+import gnu.kawa.functions.Format;
+import gnu.lists.LList;
+import gnu.mapping.*;
+import kawadevutil.data.ParamData;
+import kawadevutil.data.ProcDataGeneric;
+import kawadevutil.data.ProcDataNonGeneric;
+import kawadevutil.kawa.GnuMappingLocation;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+public class GeiserAutodoc extends Procedure1or2 {
+
+    public static boolean showTypes = true;
+    Language lang;
+
+    GeiserAutodoc(String name, Language lang) {
+        super(name);
+        this.lang = lang;
+    }
+
+    // TODO: find the "right" way to get modules for symbols.
+    // TODO: support for procedures defined in java, like `append'
+    // TODO: support for macros (possible?)
+    // TODO: support for getting parameter names for java instance
+    //       methods getMethods bytecode with ClassType (not so simple)
+    // TODO: support names with special characters, like |a[)|
+    //       Maybe we can:
+    //       1. keep a list of special chars
+    //       2. when `id' contains one: surround with || (e.g. |id|)
+    // TODO: consider multiple ids:
+    //  - Examples: to get more add (message (format "(geiser:%s %s)" proc 
form))
+    //              in the t clause of the geiser-kawa--geiser-procedure:
+    //   - (display [cursor] -> (geiser:autodoc ’(display))
+    //   - (display (symbol? (string->symbol [cursor] -> (geiser:autodoc 
’(string->symbol symbol? display))
+
+
+    // At the moment arguments are enclosed in double quotes. The
+    // reason is that geiser's output is `read' by elisp, but java types
+    // may contain characters that are not valid in elisp symbols
+    // (e.g. java arrays contain square brackets). So instead of symbols
+    // we use strings. When type annotations are disabled using
+    // (geiser:set-autodoc-show-types #f) parameter names displayed as
+    // symbols (without double quotes around them).
+
+    // List<Object> idsArr = List.getLocalVarsAttr(ids.toArray());
+    // idsArr.stream().map( (Object symId, Object env) -> {
+    // return (new SymToAutodoc()).apply2(symId, env);
+    // })
+
+    // @Override
+    public Object apply1(Object ids) {
+        return apply2(ids, Language.getDefaultLanguage().getEnvironment());
+    }
+
+    // @Override
+    public Object apply2(Object ids, Object env) {
+
+        if (!LList.class.isAssignableFrom(ids.getClass())) {
+            throw new IllegalArgumentException(String.format(
+                    "GeiserAutodoc's 1st arg should be a gnu.lists.LList"));
+        }
+        if (!Environment.class.isAssignableFrom(env.getClass())) {
+            throw new IllegalArgumentException(String.format(
+                    "GeiserAutodoc's 2nd arg should be a 
gnu.mapping.Environment"));
+        }
+        try {
+            ArrayList<Object> autodocList = new ArrayList<>();
+            for (Object symId : (LList) ids) {
+                AutodocDataForSymId autodocDataForSymId =
+                        new AutodocDataForSymId((Symbol) symId, (Environment) 
env, this.lang);
+                autodocList.add(autodocDataForSymId.toLList());
+            }
+            String formattedAutodoc =
+                    Format
+                            .format("~S", LList.makeList(autodocList))
+                            .toString();
+            return formattedAutodoc;
+        } catch (Throwable throwable) {
+            throwable.printStackTrace();
+            return throwable;
+        }
+    }
+
+    public static class OperatorArgListData {
+        ProcDataGeneric procDataGeneric;
+
+        public OperatorArgListData(ProcDataGeneric procDataGeneric) {
+            this.procDataGeneric = procDataGeneric;
+        }
+
+        private static String
+        formatParam(ParamData param, String formatting) {
+            // This method is just to reduce boilerplate in the other 
`formatParam'
+            String paramName = param.getName();
+            String formattedParamType =
+                    Format
+                            .format("::~a",
+                                    
param.getType().getReflectClass().getName())
+                            .toString();
+            return Format.format(
+                    formatting,
+                    paramName,
+                    formattedParamType).toString();
+        }
+
+        public static String
+        formatParam(ParamData param,
+                    boolean isOptionalParam) {
+            if (isOptionalParam && showTypes) {
+                return formatParam(param, "(~a~a)");
+            } else if (isOptionalParam && !showTypes) {
+                return formatParam(param, "(~a)");
+            } else if (!isOptionalParam && showTypes) {
+                return formatParam(param, "~a~a");
+            } else if (!isOptionalParam && !showTypes) {
+                return formatParam(param, "~a");
+            } else {
+                throw new Error("No worries, can't happen (2 booleans == 4 
possibilities)." +
+                        "Just silencing the \"Missing return statement\" 
error.");
+            }
+        }
+
+        public LList paramListToFormattedParamLList(List<ParamData> params, 
boolean areOptionalParams) {
+            List<String> formattedParamList = new ArrayList<>();
+            for (ParamData req : params) {
+                String s = formatParam(req, areOptionalParams);
+                formattedParamList.add(s);
+            }
+            return LList.makeList(formattedParamList);
+        }
+
+        public LList toLList() {
+            ArrayList<Object> genericProcArgList = new ArrayList<>();
+
+            for (ProcDataNonGeneric pd : 
this.procDataGeneric.getProcDataNonGenericList()) {
+                ArrayList<Object> nonGenericProcArgList = new ArrayList<>();
+                ArrayList<Object> requiredParamList = new ArrayList<>();
+                ArrayList<Object> optionalParamList = new ArrayList<>();
+
+                List<ParamData> requiredParams = pd.getRequiredParams();
+
+                if (!requiredParams.isEmpty()) {
+                    LList requiredParamLList = 
paramListToFormattedParamLList(requiredParams, false);
+                    // argList.add(LList.list2("required", 
requiredParamLList));
+                    requiredParamList.add("required");
+                    requiredParamList.addAll(requiredParamLList);
+                }
+
+                List<ParamData> optionalParams = pd.getOptionalParams();
+                Optional<ParamData> restParamMaybe = pd.getRestParam();
+                if (optionalParams.size() > 0 || restParamMaybe.isPresent()) {
+                    LList optionalOrRestParamLList =
+                            optionalParams.size() > 0
+                                    ? 
paramListToFormattedParamLList(optionalParams, true)
+                                    : 
LList.makeList(java.util.Collections.emptyList());
+                    if (restParamMaybe.isPresent()) {
+                        optionalOrRestParamLList.add(
+                                Format.format(
+                                        "(... ~a...)",
+                                        formatParam(restParamMaybe.get(), 
false)
+                                )
+                        );
+                    }
+                    optionalParamList.add("optional");
+                    optionalParamList.addAll(optionalOrRestParamLList);
+                }
+
+                if (!requiredParamList.isEmpty()) {
+                    
nonGenericProcArgList.add(LList.makeList(requiredParamList));
+                }
+                if (!optionalParamList.isEmpty()) {
+                    
nonGenericProcArgList.add(LList.makeList(optionalParamList));
+                }
+                genericProcArgList.add(LList.makeList(nonGenericProcArgList));
+            }
+
+            return LList.makeList(genericProcArgList);
+        }
+    }
+
+    public static class AutodocDataForSymId {
+        private boolean symExists;
+        private Symbol symId;
+        private Object operator;
+        private Environment environment;
+        private Optional<OperatorArgListData> operatorArgListMaybe;
+        // TODO: fix type, write way to get it
+        private Object module;
+
+
+        public AutodocDataForSymId(Symbol symId, Environment env, Language 
lang) {
+            this.symId = symId;
+            this.environment = env;
+
+            Optional<OperatorArgListData> operatorArgListMaybe = 
Optional.empty();
+            Object operator = null;
+            boolean symExists = false;
+            try {
+                operator = lang.eval(symId.toString());
+                symExists = true;  // If it didn't exist env.get(symId) would 
have raised UnboundLocationException
+                if (!Procedure.class.isAssignableFrom(operator.getClass())) {
+                    // Not a procedure
+                    // TODO : is it possible to implement autodoc for macros?
+                    //        If not: write a comment why.
+                    operatorArgListMaybe = Optional.empty();
+                } else {
+                    ProcDataGeneric procDataGeneric = 
ProcDataGeneric.makeForProcedure((Procedure) operator);
+                    operatorArgListMaybe = Optional.of(new 
OperatorArgListData(procDataGeneric));
+                }
+            } catch (Throwable throwable) {
+                throwable.printStackTrace();
+            }
+
+            this.operatorArgListMaybe = operatorArgListMaybe;
+            this.operator = operator;
+            this.symExists = symExists;
+        }
+
+        public LList toLList() {
+
+            ArrayList<Object> operatorArgListAsList = new ArrayList<>();
+            operatorArgListAsList.add("args");
+            if (operatorArgListMaybe.isPresent()) {
+                
operatorArgListAsList.addAll(operatorArgListMaybe.get().toLList());
+            } else {
+                operatorArgListAsList.add(false);
+            }
+            LList operatorArgListAsLList = 
LList.makeList(operatorArgListAsList);
+
+            // TODO: write a procedure that gets the module getMethods
+            //       which a symbol comes getMethods using "the right way" (is 
there one?)
+            // TODO: When we find the correct way to do it, refactor 
moduleValue inside
+            //       ProcDataNonGeneric or a generic wrapper for Procedure data
+            LList moduleValue = null;
+            if (operator.getClass() == CompiledProc.class) {
+                CompiledProc compProc = (CompiledProc) operator;
+                moduleValue = LList.makeList(
+                        java.util.Arrays
+                                .asList(compProc
+                                        .getModuleClass()
+                                        .getName()
+                                        .split("\\.")));
+            } else {
+                try {
+                    // If it's not a CompiledProc it does not have a
+                    // `getModule' method: fallback to trying to figure
+                    // out getMethods GnuMappingLocation in Environment.
+                    // TODO: generalize to arbitrary environment
+                    moduleValue = (LList) kawa.lib.ports.read(
+                            new gnu.kawa.io.CharArrayInPort(
+                                    
GnuMappingLocation.baseLocationToModuleName(
+                                            environment.lookup(symId).getBase()
+                                    )
+                            )
+                    );
+                } catch (NullPointerException e) {
+                    // If it is not even a sym in the environment, give up.
+                    // TODO: should we consider all java classes as modules?
+                    moduleValue = LList.makeList(new ArrayList());
+                }
+            }
+
+            // If you don't convert your symbol to String it may not match with
+            // the string seen from the emacs side and when that happens geiser
+            // does not considers that a valid autodoc response.
+            // Example: as a Symbol, java.lang.String:format is displayed by
+            // kawa as:
+            //   java.lang.String{$unknown$}:format
+            // which does not match:
+            //   java.lang.String:format
+            // so geiser ignores it.
+            String symIdAsStr = symId.toString();
+            LList returnMe;
+            if (moduleValue.size() > 0) {
+                ArrayList<Object> moduleList = new ArrayList<>();
+                moduleList.add("module");
+                for (Object m : moduleValue) {
+                    moduleList.add(Symbol.valueOf(m.toString()));
+                }
+                returnMe = LList.list3(
+                        symIdAsStr,
+                        operatorArgListAsLList,
+                        LList.makeList(moduleList)
+                );
+            } else {
+                returnMe = LList.list2(
+                        symIdAsStr,
+                        operatorArgListAsLList
+                );
+            }
+
+            return returnMe;
+        }
+    }
+}
+
diff --git a/src/main/java/kawageiser/GeiserCompleteModule.java 
b/src/main/java/kawageiser/GeiserCompleteModule.java
new file mode 100644
index 0000000..fe00508
--- /dev/null
+++ b/src/main/java/kawageiser/GeiserCompleteModule.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2020 spellcard199 <spellcard199@protonmail.com>
+ * This is free software;  for terms and warranty disclaimer see ./COPYING.
+ */
+
+package kawageiser;
+
+import gnu.expr.Language;
+import gnu.lists.IString;
+import gnu.lists.LList;
+import gnu.mapping.Environment;
+import gnu.mapping.Procedure1or2;
+import kawadevutil.kawa.GnuMappingLocation;
+
+import java.util.ArrayList;
+
+
+public class GeiserCompleteModule extends Procedure1or2 {
+
+    GeiserCompleteModule(String name) {
+        super(name);
+    }
+
+    @Override
+    public Object apply1(Object prefix) throws Throwable {
+        return apply2(
+                prefix,
+                Language.getDefaultLanguage().getEnvironment());
+    }
+
+    @Override
+    public Object apply2(Object prefix, Object env) throws Throwable {
+
+        String prefixStr = null;
+        if (prefix instanceof String) {
+            prefixStr = (String) prefix;
+        } else if (prefix instanceof IString) {
+            prefixStr = prefix.toString();
+        } else {
+            throw new IllegalArgumentException(
+                    "`prefix' arg should be either a String or an IString");
+        }
+
+        Environment castedEnv;
+        if (Environment.class.isAssignableFrom(env.getClass())) {
+            castedEnv = (Environment) env;
+        } else {
+            throw new IllegalArgumentException(
+                    "`env' arg should be an gnu.mapping.Environment");
+        }
+
+        ArrayList<String> moduleCompletions = getCompletions(prefixStr, 
castedEnv);
+        // Geiser protocol wants modules in the result to be printed
+        // between double quotes
+        // ("(... ... ...)" "(... ...)")
+        // Kawa repl doesn't show returned strings with surrounding
+        // quotes, so we have to manually surround completions.
+        return gnu.kawa.functions.Format.format("~S", 
LList.makeList(moduleCompletions));
+    }
+
+    private ArrayList<String> getCompletions(String prefix, Environment env) {
+
+        ArrayList<String> moduleCompletions = new ArrayList<>();
+
+        // Since this procedure works iterating over locations in the
+        // (interaction-environment), if a module does not export any
+        // symbol it won't appear in the result.
+        // TODO: this is an hack. If it exists, find a way to list
+        //       modules directly.
+        env.enumerateAllLocations().forEachRemaining(
+                loc ->
+                {
+                    String moduleStrRepr = GnuMappingLocation
+                            .baseLocationToModuleName(loc.getBase());
+                    if ((!moduleCompletions.contains(moduleStrRepr))
+                            && (!(moduleStrRepr.equals("")))
+                            && (moduleStrRepr.startsWith(prefix))
+                    ) {
+                        moduleCompletions.add(moduleStrRepr);
+                    }
+                }
+        );
+
+        return moduleCompletions;
+    }
+}
diff --git a/src/main/java/kawageiser/GeiserCompleteSymbol.java 
b/src/main/java/kawageiser/GeiserCompleteSymbol.java
new file mode 100644
index 0000000..5dda5c7
--- /dev/null
+++ b/src/main/java/kawageiser/GeiserCompleteSymbol.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 spellcard199 <spellcard199@protonmail.com>
+ * This is free software;  for terms and warranty disclaimer see ./COPYING.
+ */
+
+package kawageiser;
+
+import gnu.expr.Language;
+import gnu.lists.IString;
+import gnu.lists.LList;
+import gnu.mapping.Environment;
+import gnu.mapping.Procedure1or2;
+import gnu.mapping.Symbol;
+
+import java.util.ArrayList;
+
+public class GeiserCompleteSymbol extends Procedure1or2 {
+
+    GeiserCompleteSymbol(String name) {
+        super(name);
+    }
+
+    @Override
+    public Object apply1(Object prefix) {
+        return apply2(
+                prefix,
+                Language.getDefaultLanguage().getEnvironment());
+    }
+
+    @Override
+    public Object apply2(Object prefix, Object module) {
+
+        String prefixStr = null;
+        if (prefix instanceof String) {
+            prefixStr = (String) prefix;
+        } else if (prefix instanceof IString) {
+            prefixStr = prefix.toString();
+        } else {
+            throw new IllegalArgumentException(
+                    "prefix arg should be either String or IString");
+        }
+
+        Environment env = null;
+        if (Environment.class.isAssignableFrom(module.getClass())) {
+            // already an Environment
+            env = (Environment) module;
+        } else if (kawa.lib.lists.isList(module)) {
+            env = kawa.lib.scheme.eval.environment$V(((LList) module));
+        } else {
+            throw new IllegalArgumentException(
+                    "module argument should be either a proper list or an 
Environment.");
+        }
+
+        return getCompletions(prefixStr, env);
+    }
+
+    private LList getCompletions(String prefix, Environment env) {
+        ArrayList<Symbol> resultArrList = new ArrayList<>();
+        env.enumerateAllLocations().forEachRemaining(
+                loc -> {
+                    Symbol sym = loc.getKeySymbol();
+                    String symName = sym.getName();
+                    if (symName.contains(prefix)) {
+                        resultArrList.add(sym);
+                    }
+                }
+        );
+        return LList.makeList(resultArrList);
+    }
+}
diff --git a/src/main/java/kawageiser/GeiserEval.java 
b/src/main/java/kawageiser/GeiserEval.java
new file mode 100644
index 0000000..906b41a
--- /dev/null
+++ b/src/main/java/kawageiser/GeiserEval.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2020 spellcard199 <spellcard199@protonmail.com>
+ * This is free software;  for terms and warranty disclaimer see ./COPYING.
+ */
+
+package kawageiser;
+
+import gnu.kawa.functions.Format;
+import gnu.lists.IString;
+import gnu.lists.LList;
+import gnu.lists.Pair;
+import gnu.mapping.Environment;
+import gnu.mapping.Procedure2;
+import gnu.mapping.Symbol;
+import kawadevutil.eval.EvalResult;
+import kawadevutil.eval.EvalResultAndOutput;
+import kawadevutil.redirect.RedirectedOutErr;
+
+public class GeiserEval extends Procedure2 {
+    /*
+     *  Actual evaluation happens in kawadevtools.eval.Eval.
+     *  Here we are just sending arguments and converting our own
+     *  types into the geiser protocol.
+     */
+
+    GeiserEval(String procName) {
+        super(procName);
+    }
+
+    @Override
+    public String
+    apply2(Object module, Object codeStr) {
+        // Today (2019-12-9) Kawa has still that issue when
+        // quoting (this) followed by a double colon. So, to avoid
+        // it altogether, geiser:eval default is to accept Strings
+        // instead of sexprs.
+        // You can still evaluate expressions instead of strings using
+        // the other GeiserEval:eval method explicitly.
+        String code;
+        if (codeStr instanceof IString) {
+            code = ((IString) codeStr).toString();
+        } else if (codeStr instanceof String) {
+            code = (String) codeStr;
+        } else {
+            throw new IllegalArgumentException(
+                    "`codeStr' arg should be either a String or an IString");
+        }
+        return eval((Environment) module, code);
+    }
+
+    public static String
+    eval(Environment module, String codeStr) {
+        EvalResultAndOutput resOutErr = 
Geiser.evaluator.evalCatchingOutErr(module, codeStr);
+        return formatGeiserProtocol(evaluationDataToGeiserProtocol(resOutErr));
+    }
+
+    public static String
+    eval(Environment module, Object sexpr) {
+        EvalResultAndOutput resOutErr = 
Geiser.evaluator.evalCatchingOutErr(module, sexpr);
+        return formatGeiserProtocol(evaluationDataToGeiserProtocol(resOutErr));
+    }
+
+    public static LList
+    evaluationDataToGeiserProtocol(EvalResultAndOutput resOutErr) {
+        EvalResult evalRes = resOutErr.getResultOfSupplier();
+        RedirectedOutErr outErr = resOutErr.getOutErr();
+
+        // result
+        String geiserResult = evalRes.isSuccess()
+                ? evalRes.getResultAsString(Geiser.isPrettyPrintResult())
+                : "";
+
+        // output
+        String messages = (evalRes.getMessages() != null)
+                ? evalRes.getMessages().toString(100000)
+                : "";
+        messages = (messages != null) ? messages : "";
+        String stackTrace = (evalRes.getThrowed() != null)
+                ? Geiser.evaluator.formatStackTrace(evalRes.getThrowed())
+                : "";
+        String output = outErr.getOutAndErrInPrintOrder();
+        // If we wanted, we could include stack traces directly in
+        // the output using Eval.setPrintStackTrace(): that would
+        // display stack traces of exceptions after the output
+        // produced by the code instead of before.
+        // Since the Kawa repl prints in order: messages, stacktrace,
+        // output, we are doing the same.
+        String geiserOutput = messages + stackTrace + output;
+
+        return evaluationDataToGeiserProtocol(
+                evalRes.isSuccess(), geiserResult, geiserOutput);
+    }
+
+    private static LList
+    evaluationDataToGeiserProtocol
+            (boolean isSuccess, String geiserResult, String geiserOutput) {
+        LList geiserResOrErr =
+                isSuccess
+                        ? LList.list2(Symbol.valueOf("result"), geiserResult)
+                        : LList.list2(Symbol.valueOf("error"), "");
+        Pair geiserOut = Pair.make(Symbol.valueOf("output"), geiserOutput);
+        return LList.list2(geiserResOrErr, geiserOut);
+    }
+
+    public static String formatGeiserProtocol(LList geiserAnswer) {
+        return (String) Format.format("~S", geiserAnswer);
+    }
+
+}
diff --git a/src/main/java/kawageiser/GeiserLoadFile.java 
b/src/main/java/kawageiser/GeiserLoadFile.java
new file mode 100644
index 0000000..6fa7763
--- /dev/null
+++ b/src/main/java/kawageiser/GeiserLoadFile.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 spellcard199 <spellcard199@protonmail.com>
+ * This is free software;  for terms and warranty disclaimer see ./COPYING.
+ */
+
+package kawageiser;
+
+import gnu.expr.Language;
+import gnu.lists.IString;
+import gnu.lists.LList;
+import gnu.mapping.Procedure1;
+import kawa.standard.load;
+
+public class GeiserLoadFile extends Procedure1 {
+
+    GeiserLoadFile(String name) {
+        super(name);
+    }
+
+    @Override
+    public Object apply1(Object o) throws Throwable {
+        String filepath;
+        if (o instanceof String) {
+            filepath = (String) o;
+        } else if (o instanceof IString) {
+            filepath = ((IString) o).toString();
+        } else {
+            throw new IllegalArgumentException(
+                    "geiser:load should take a String or an IString as 
argument");
+        }
+        return load(filepath);
+    }
+
+    public Object load(String filepath) {
+        return GeiserEval.eval(
+                Language.getDefaultLanguage().getEnvironment(),
+                LList.list2(load.load, filepath));
+    }
+}
diff --git a/src/main/java/kawageiser/GeiserNoValues.java 
b/src/main/java/kawageiser/GeiserNoValues.java
new file mode 100644
index 0000000..b15b29c
--- /dev/null
+++ b/src/main/java/kawageiser/GeiserNoValues.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2020 spellcard199 <spellcard199@protonmail.com>
+ * This is free software;  for terms and warranty disclaimer see ./COPYING.
+ */
+
+package kawageiser;
+
+import gnu.mapping.Procedure0;
+import gnu.mapping.Values;
+
+public class GeiserNoValues extends Procedure0 {
+
+    GeiserNoValues(String name) {
+        super(name);
+    }
+
+    @Override
+    public Object apply0() throws Throwable {
+        gnu.kawa.io.InPort.inDefault().setLineNumber(
+                gnu.kawa.io.InPort.inDefault().getLineNumber() - 1);
+        // apply0 signature doesn't allow us to return void
+        return Values.FromArray.make();
+    }
+}
diff --git a/src/main/java/kawageiser/StartKawaWithGeiserSupport.java 
b/src/main/java/kawageiser/StartKawaWithGeiserSupport.java
new file mode 100644
index 0000000..cc558e8
--- /dev/null
+++ b/src/main/java/kawageiser/StartKawaWithGeiserSupport.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2020 spellcard199 <spellcard199@protonmail.com>
+ * This is free software;  for terms and warranty disclaimer see ./COPYING.
+ */
+
+package kawageiser;
+
+public class StartKawaWithGeiserSupport {
+
+    public static void main(String[] args) {
+        if (args.length == 0) {
+            int defaultPort = 37146;
+            System.out.println(
+                    String.format(
+                            "No port specified. Starting kawa server on 
default port (%d)...",
+                            defaultPort));
+            startKawaServerWithGeiserSupport(defaultPort);
+        } else if (args.length == 1 && args[0].matches("[0-9]+")) {
+            int port = Integer.parseInt(args[0]);
+            startKawaServerWithGeiserSupport(port);
+        } else if (args.length == 1 && args[0].equals("--no-server")) {
+            System.out.println("Starting kawa repl in current terminal...");
+            startKawaReplWithGeiserSupport();
+        } else {
+            System.out.println(
+                    "You must pass at most 1 argument and it can be only one 
of:\n"
+                            + "- a port number"
+                            + "- --no-server"
+            );
+        }
+    }
+
+    public static void startKawaReplWithGeiserSupport() {
+        String[] interpArgs = new String[]{
+                "-e", "(require <kawageiser.Geiser>)",
+                "--",
+        };
+        runSchemeAsApplication(interpArgs);
+    }
+
+    public static void startKawaServerWithGeiserSupport(int port) {
+        String[] interpArgs = new String[]{
+                "-e", "(require <kawageiser.Geiser>)",
+                "--server", String.valueOf(port)};
+        runSchemeAsApplication(interpArgs);
+    }
+
+    public static void runSchemeAsApplication(String[] args) {
+        kawa.standard.Scheme scheme = kawa.standard.Scheme.getInstance();
+        scheme.runAsApplication(args);
+    }
+
+}
diff --git a/src/main/java/kawageiser/geiserDoc/ManualEpubUnzipToTmpDir.java 
b/src/main/java/kawageiser/geiserDoc/ManualEpubUnzipToTmpDir.java
new file mode 100644
index 0000000..f24b035
--- /dev/null
+++ b/src/main/java/kawageiser/geiserDoc/ManualEpubUnzipToTmpDir.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 spellcard199 <spellcard199@protonmail.com>           
+ * This is free software;  for terms and warranty disclaimer see ./COPYING.
+ */
+
+package kawageiser.geiserDoc;
+
+import gnu.lists.IString;
+import gnu.mapping.Procedure1;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.List;
+
+public class ManualEpubUnzipToTmpDir extends Procedure1 {
+
+    public ManualEpubUnzipToTmpDir(String name) {
+        super(name);
+    }
+
+    @Override
+    public Object apply1(Object kawaEpubManualPath) throws Throwable {
+        if (! (kawaEpubManualPath.getClass() == String.class ||
+                kawaEpubManualPath.getClass() == IString.class)) {
+            throw new IllegalArgumentException(
+                    "`kawaEpubManualPath' arg must be either String or 
IString");
+        }
+        String systemTmpDir = System.getProperty("java.io.tmpdir");
+        String manualUnzippedTmpDir = String.join(
+                File.separator,
+                systemTmpDir,
+                "geiser-kawa",
+                "manual-epub-unzipped");
+
+        File zipArchiveFile = new File(kawaEpubManualPath.toString());
+        Path destDirPath = new File(manualUnzippedTmpDir).toPath();
+
+        kawadevutil.util.ZipExtractor.unzip(zipArchiveFile, destDirPath);
+        return gnu.kawa.functions.Format.format("~S", manualUnzippedTmpDir);
+    }
+}
diff --git a/src/main/java/kawageiser/java/Complete.java 
b/src/main/java/kawageiser/java/Complete.java
new file mode 100644
index 0000000..6be9291
--- /dev/null
+++ b/src/main/java/kawageiser/java/Complete.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2020 spellcard199 <spellcard199@protonmail.com>
+ * This is free software;  for terms and warranty disclaimer see ./COPYING.
+ */
+
+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 {
+
+    public static boolean showTypes = true;
+
+    public Complete(String name) {
+        super(name);
+    }
+
+    @Override
+    public Object
+    apply4(Object codeStr, Object cursorIndex, Object lang, Object env) throws 
Throwable {
+
+        String codeStrChecked = null;
+        if (codeStr.getClass().equals(IString.class) || 
codeStr.getClass().equals(String.class)) {
+            codeStrChecked = codeStr.toString();
+        } else {
+            throw new IllegalArgumentException(
+                    "`codeStr` must be either String or IString: " + 
codeStr.getClass().toString());
+        }
+
+        Integer cursorIndexChecked = null;
+        if (cursorIndex.getClass().equals(Integer.class)) {
+            cursorIndexChecked = (Integer) cursorIndex;
+        } else if (cursorIndex.getClass().equals(IntNum.class)) {
+            cursorIndexChecked = ((IntNum) cursorIndex).intValue();
+        } else {
+            throw new IllegalArgumentException(
+                    "`cursorIndex` must be either Integer or IntNum: " + 
cursorIndex.getClass().toString());
+        }
+
+        // Get Data
+        Optional<CompletionDataForJava> complDataMaybe = 
kawadevutil.complete.Complete.complete(
+                codeStrChecked, cursorIndexChecked, (Language) lang, 
(Environment) env, (String name) -> true);
+
+        // Wrap data of interest in Scheme's LList
+        if (!complDataMaybe.isPresent()) {
+            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;
+            } else {
+                throw new Error("Bug spotted.");
+            }
+
+            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);
+        }
+    }
+
+}
diff --git a/src/test/java/kawageiser/GeiserAutodocTest.java 
b/src/test/java/kawageiser/GeiserAutodocTest.java
new file mode 100644
index 0000000..a2c0253
--- /dev/null
+++ b/src/test/java/kawageiser/GeiserAutodocTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2020 spellcard199 <spellcard199@protonmail.com>
+ * This is free software;  for terms and warranty disclaimer see ./COPYING.
+ */
+
+package kawageiser;
+
+import gnu.lists.LList;
+import gnu.mapping.Environment;
+import gnu.mapping.Symbol;
+import kawa.lib.ports;
+import kawa.standard.Scheme;
+import kawadevutil.data.ProcDataGeneric;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.*;
+
+public class GeiserAutodocTest {
+
+    @Test
+    public void testApply2() {
+        Scheme scheme = new Scheme();
+        Environment env = scheme.getEnvironment();
+        Symbol displaySym = env.getSymbol("display");
+        Symbol cdddrSym = env.getSymbol("cdddr");
+        GeiserAutodoc geiserAutodoc = new 
GeiserAutodoc("geiser-testing-autodoc", scheme);
+        String autodocDisplay = (String) 
geiserAutodoc.apply2(LList.list1(displaySym), env);
+        String autodocCdddr = (String) 
geiserAutodoc.apply2(LList.list1(cdddrSym), env);
+
+        // System.out.println(autodocDisplay);
+
+        assertTrue(autodocDisplay.startsWith("((\"display\""));
+        assertTrue(autodocDisplay.contains(" (\"args\" "));
+        assertTrue(autodocDisplay.contains("((\"required\" "));
+        assertTrue(autodocDisplay.contains("(\"optional\" "));
+        assertTrue(autodocDisplay.contains("(\"module\" "));
+
+        assertTrue(autodocCdddr.startsWith("((\"cdddr\""));
+        assertTrue(autodocCdddr.contains(" (\"args\" "));
+        assertTrue(autodocCdddr.contains("((\"required\" "));
+        assertTrue(!autodocCdddr.contains("(\"optional\" "));
+        assertTrue(autodocCdddr.contains("(\"module\" "));
+    }
+
+    public static class OperatorArgListDataTest {
+
+        @Test
+        public void testToLList() {
+            ProcDataGeneric procDataNonGenericList = 
ProcDataGeneric.makeForProcedure(ports.display);
+            GeiserAutodoc.OperatorArgListData operatorArgListData = new 
GeiserAutodoc.OperatorArgListData(procDataNonGenericList);
+            LList llist = operatorArgListData.toLList();
+            // System.out.println(llist);
+            String required = (String) ((LList) ((LList) 
llist.get(0)).get(0)).get(0);
+            String optional = (String) ((LList) ((LList) 
llist.get(0)).get(1)).get(0);
+
+            assertEquals(required, "required");
+            assertEquals(optional, "optional");
+
+        }
+    }
+
+    public static class AutodocDataForSymIdTest {
+
+        @Test
+        public void testToLListForProc1() {
+            Scheme scheme = new Scheme();
+            Environment env = scheme.getEnvironment();
+            Symbol display = env.getSymbol("display");
+            GeiserAutodoc.AutodocDataForSymId autodocDataForDisplay = new 
GeiserAutodoc.AutodocDataForSymId(display, env, scheme);;
+            assertEquals(display.toString(), 
autodocDataForDisplay.toLList().get(0));
+        }
+
+        @Test
+        public void testToLListForProcN() {
+            Scheme scheme = new Scheme();
+            Environment env = scheme.getEnvironment();
+            Symbol strFormatSym = null;
+            try {
+                strFormatSym = (Symbol) 
scheme.eval("'java.lang.String:format");
+            } catch (Throwable throwable) {
+                throwable.printStackTrace();
+            }
+            GeiserAutodoc.AutodocDataForSymId autodocDataForDisplay = new 
GeiserAutodoc.AutodocDataForSymId(strFormatSym, env, scheme);;
+            LList llist = autodocDataForDisplay.toLList();
+            assertEquals("java.lang.String:format", llist.get(0));
+        }
+    }
+}
\ No newline at end of file



reply via email to

[Prev in Thread] Current Thread [Next in Thread]