Package com.offbytwo.iclojure.repl

Source Code of com.offbytwo.iclojure.repl.IClojureRepl

package com.offbytwo.iclojure.repl;

import clojure.lang.Compiler;
import clojure.lang.*;
import com.offbytwo.class_finder.ClassFinder;
import com.offbytwo.iclojure.InputOutputCache;
import com.offbytwo.iclojure.completion.DelegatingCompleter;
import com.offbytwo.iclojure.exceptions.StopInputException;
import com.offbytwo.iclojure.handlers.DescribeJavaObjectHandler;
import com.offbytwo.iclojure.util.ConsoleOutputStreamWriter;
import jline.console.ConsoleReader;
import org.fusesource.jansi.AnsiString;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;

import static clojure.lang.RT.*;
import static com.offbytwo.iclojure.InputOutput.ColoredText.*;
import static java.lang.String.format;
import static java.lang.String.valueOf;
import static org.fusesource.jansi.Ansi.Color.BLUE;
import static org.fusesource.jansi.Ansi.Color.RED;


public class IClojureRepl {
    private ConsoleReader reader;
    private int inputNumber;
    private Var ns = var("clojure.core", "*ns*");

    private DescribeJavaObjectHandler describeHandler;
    private Var output1 = var("clojure.core", "*1");
    private Var output2 = var("clojure.core", "*2");
    private Var output3 = var("clojure.core", "*3");
    private Var lastError = var("clojure.core", "*e");
    private Var pst;
    private OutputStreamWriter writer;
    private InputOutputCache ioCache = new InputOutputCache(1000);
    private ClassFinder classFinder = new ClassFinder();
    private StringBuilder inputSoFar;
    private String lastPrompt;
    private boolean clojure1_2;


    public IClojureRepl(final ConsoleReader reader) throws ClassNotFoundException, IOException {
        String clojureVersion = (String) var("clojure.core", "clojure-version").invoke();
        this.clojure1_2 = clojureVersion.startsWith("1.2");

        this.reader = reader;
        this.inputNumber = 0;

        this.writer = new ConsoleOutputStreamWriter(reader);
        this.describeHandler = new DescribeJavaObjectHandler(reader);

        createNecessaryThreadBindings();
        createUserNamespace();

        this.pst = var("clj-stacktrace.repl", "pst");

        reader.addCompleter(new DelegatingCompleter(classFinder));
        inputSoFar = new StringBuilder();
    }

    private Object read() throws IOException, StopInputException {
        String line = readLine(true);

        if (line == null || line.equals("exit")) {
            throw new StopInputException();
        } else if (line.startsWith("%")) {
            return handleShortcuts(line);
        } else if (line.startsWith("?")) {
            return handleHelp(line);
        } else if (line.trim().equals("")) {
            return null;
        } else {
            return readPotentiallyMultilineForm(line);
        }
    }

    private Object readPotentiallyMultilineForm(String line) {
        inputSoFar.append(line).append("\n");

        try {
            for (; ; ) {
                try {
                    if (inputSoFar.toString().trim().length() > 0) {
                        Object input = readString(inputSoFar.toString());
                        return input;
                    } else {
                        return null;
                    }
                } catch (Throwable t) {
                    if (t.getMessage().contains("EOF while reading")) {
                        try {
                            String newLine = readLine(false);
                            inputSoFar.append(newLine).append("\n");
                        } catch (IOException e) {
                            e.printStackTrace();
                            return null;
                        }
                    } else {
                        printStackTrace(t);
                        return null;
                    }
                }
            }
        } finally {
            inputSoFar.setLength(0);
        }
    }

    private Object eval(Object line) throws StopInputException, IOException {
        try {
            Object ret = Compiler.eval(line);

            writer.flush();
            reader.getOutput().flush();

            return ret;
        } catch (Throwable t) {
            lastError.set(t);
            printStackTrace(t);
            return null;
        }
    }


    private void print(Object output) throws IOException {
        if (output != null) {
            reader.print(getOutputPrompt());
            try {
                reader.println(RT.printString(output));
            } catch (Throwable t) {
                lastError.set(t);
                printStackTrace(t);
            }
        }
        reader.println();
    }

    public void loop() {
        Object input;
        Object output;

        try {
            while (true) {
                inputNumber += 1;
                input = read();
                if (input == null) {
                    cacheInputOutput(null, null);
                    continue;
                }
                output = eval(input);
                cacheInputOutput(input, output);
                print(output);
            }
        } catch (IOException ioe) {
            ioe.printStackTrace();
        } catch (StopInputException e) {
            //
        }
    }

    private void cacheInputOutput(Object input, Object output) {
        captureLast3Outputs(output);
        ioCache.add(input, output);
    }

    private void createUserNamespace() throws ClassNotFoundException, IOException {
        Namespace userNs = Namespace.findOrCreate(Symbol.create(null, "user"));

        RT.load("clojure/repl");
        List<String> replFns = new ArrayList<String>(Arrays.asList("source", "apropos", "dir"));
        if (!clojure1_2) {
            replFns.add("doc");
            replFns.add("find-doc");
        }

        for (String name : replFns) {
            userNs.refer(Symbol.create(null, name), var("clojure.repl", name));
        }

        if (!clojure1_2) {
            RT.load("clojure/java/javadoc");
            userNs.refer(Symbol.create(null, "javadoc"), var("clojure.java.javadoc", "javadoc"));
        }

        RT.load("clojure/pprint");
        userNs.refer(Symbol.create(null, "pprint"), var("clojure.pprint", "pprint"));

        RT.load("clj_stacktrace/repl");
        userNs.refer(Symbol.create(null, "pst"), var("clj-stacktrace.repl", "pst"));

        RT.load("cd_client/core");
        userNs.refer(Symbol.create(null, "cdoc"), var("cd-client.core", "cdoc"));

        this.ns.set(userNs);
    }

    private void createNecessaryThreadBindings() {

        var("user", "input").bindRoot(ioCache.getInputLookupFn());
        var("user", "output").bindRoot(ioCache.getOutputLookupFn());

        Var.pushThreadBindings(map(
                var("clojure.core", "*out*"), writer,
                ns, ns.deref(),
                output1, null,
                output2, null,
                output3, null,
                lastError, null
        ));
    }

    public void abortCurrentRead() throws IOException {
        this.reader.println();
        this.reader.println("Keyboard Interrupt");
        this.reader.println();

        this.inputSoFar.setLength(0);

        this.reader.setCursorPosition(0);
        this.reader.killLine();

        this.reader.setPrompt(lastPrompt);

        this.reader.redrawLine();
        this.reader.flush();

    }

    private Object handleHelp(String line) throws IOException {
        String expanded;

        if (line.equals("?")) {
            help();
            return null;
        } else if (line.startsWith("???")) {
            expanded = "(cd-client.core/cdoc " + line.substring(3) + ")";
        } else if (line.startsWith("??")) {
            expanded = "(clojure.repl/source " + line.substring(2) + ")";
        } else {
            if (clojure1_2) {
                expanded = "(clojure.core/doc " + line.substring(1) + ")";
            } else {
                expanded = "(clojure.repl/doc " + line.substring(1) + ")";
            }
        }

        try {
            return readString(expanded);
        } catch (Throwable t) {
            reader.println("Error reading: " + expanded);
            return null;
        }
    }

    private Object handleShortcuts(String line) throws IOException {
        if (line.startsWith("%d")) {
            if (line.replace("%d", "").trim().length() == 0) {
                Object output = output1.deref();
                if (output instanceof Var) {
                    describeHandler.describe(((Var) output).deref());
                } else {
                    describeHandler.describe(output);
                }
                return null;
            }

            Object input;
            try {
                input = readString(line.replace("%d", "").trim());
            } catch (Throwable t) {
                reader.println("Error: unable to read form after %d");
                return null;
            }

            Object output;
            try {
                output = Compiler.eval(input);
            } catch (Throwable t) {
                t.printStackTrace();
                reader.println("Error: unable to evaluate form after %d");
                return null;
            }
            describeHandler.describe(output);
        } else if (line.startsWith("%f")) {
            String searchFor = line.replace("%f", "").trim();
            String className = "";
            String packageName = "";

            if (searchFor.contains(" ")) {
                String[] components = searchFor.split(" ");
                className = components[0];
                packageName = components[1];
            } else {
                className = searchFor;
            }

            Set<String> matches;

            if (searchFor.contains("*") || searchFor.contains("?")) {
                matches = classFinder.findClassesInPackageByGlob(packageName, className);
            } else {
                matches = classFinder.findClassesInPackageByName(packageName, className);
            }

            reader.printColumns(matches);

        } else {
            reader.println("Unknown command!");
        }

        return null;
    }

    private String readLine(boolean firstLine) throws IOException {
        String line = null;

        int promptLength = new AnsiString(getPrompt()).length();

        StringBuilder morePrompt = new StringBuilder();
        for (int i = 0; i < promptLength - 5; i++) {
            morePrompt.append(" ");
        }
        morePrompt.append(color(BLUE, "...: "));
        morePrompt.append(revertToDefaultColor());


        while (line == null) {
            if (firstLine) {
                line = reader.readLine(getPrompt());
            } else {
                line = reader.readLine(morePrompt.toString());
            }

            if (line == null) {
                System.out.print("\nDo you really want to exit ([y]/n)? ");
                char input = (char) reader.readCharacter('y', 'n', (char) 10);

                if (input != 'n') {
                    return null;
                } else {
                    System.out.println();
                    System.out.println();
                    reader.flush();
                }
            }
        }
        return line;
    }

    private String getPrompt() {
        StringBuilder sb = new StringBuilder();
        sb.append(color(BLUE, format("%s[", ns.deref())));
        sb.append(colorBright(BLUE, valueOf(inputNumber)));
        sb.append(color(BLUE, "]: "));
        sb.append(revertToDefaultColor());
        lastPrompt = sb.toString();
        return lastPrompt;
    }

    private String getOutputPrompt() {
        StringBuilder sb = new StringBuilder();
        sb.append(color(RED, format("%s[", ns.deref())));
        sb.append(colorBright(RED, valueOf(inputNumber)));
        sb.append(color(RED, "]= "));
        sb.append(revertToDefaultColor());
        return sb.toString();
    }

    private void captureLast3Outputs(Object ret) {
        output3.set(output2.deref());
        output2.set(output1.deref());
        output1.set(ret);
    }

    private void printStackTrace(Throwable t) {
        try {
            reader.println(color(RED, "---------------------------------------------------------------------------"));
            reader.print(revertToDefaultColor());
            reader.flush();
            pst.invoke(t);
            reader.println();
            StringBuffer sb = new StringBuffer();
            sb.append(color(RED, t.getClass().getSimpleName()));
            sb.append(revertToDefaultColor());
            sb.append(": ");
            sb.append(t.getLocalizedMessage());
            reader.println(sb.toString());
            reader.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void help() throws IOException {
        reader.println("(doc fn)         -> show documentation of the given function or macro");
        reader.println("?fn              -> show documentation of the given function or macro");
        reader.println("(source fn)      -> show source of the given function or macro");
        reader.println("??fn             -> show source of the given function or macro");
        reader.println();
        reader.println("(dir ns)         -> show all the names in the given namespace");
        reader.println("(apropos re)     -> all definitions in all loaded namespaces that match the given pattern");
        reader.println("(find-doc re     -> print doc for any var whose doc or name matches the given pattern");
        reader.println();
        reader.println("%d symbol        -> Describe Java class (show constructors, methods and fields)");
        reader.println("%f class         -> find all classes matching this name (supports globs)");
        reader.println("%f class package -> like the above, but restrict search to the given package");
        reader.println();
        reader.println("(pst e)          -> print stack trace of the given exception");
        reader.println("(pp)             -> pretty print the last value");
        reader.println("(pprint v)       -> pretty print the given value");
        reader.println();
        reader.println("(input i)        -> return the input from line i");
        reader.println("(output i)       -> return the output from line i");
    }

}
TOP

Related Classes of com.offbytwo.iclojure.repl.IClojureRepl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.