From c17417242f6b0a7e3f18b4eba3acf229dcbbba56 Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Sun, 29 Jan 2023 20:20:39 +0100 Subject: [PATCH 01/40] debugger breakpoints and such but it's buggy mess in sbcl - probably won't use it at all and just archive it --- .../slt/plugin/lisp/LispParserUtil.java | 3 +- .../plugin/lisp/LispSymbolPresentation.java | 2 +- .../slt/plugin/lisp/lisp/LispUtils.java | 12 + .../services/lisp/LispEnvironmentService.java | 13 + .../lisp/LispEnvironmentServiceImpl.java | 34 ++- .../lisp/components/SltBreakpoint.java | 123 +++++++++ .../components/SltBreakpointContainer.java | 200 ++++++++++++++ .../slt/plugin/swank/SlimeListener.java | 4 + .../slt/plugin/swank/SwankPacket.java | 32 +++ .../plugin/swank/requests/StepperAction.java | 69 +++++ .../slt/plugin/ui/SltCoreWindow.java | 15 + .../ui/debug/SltBreakpointListener.java | 33 +++ .../ui/debug/SltBreakpointProperties.java | 31 +++ .../slt/plugin/ui/debug/SltDebuggerImpl.java | 186 ++++++++++++- .../slt/plugin/ui/debug/SltInspector.java | 15 + .../ui/debug/SltSymbolBreakpointType.java | 259 ++++++++++++++++++ src/main/resources/META-INF/plugin.xml | 9 + .../resources/messages/SltBundle.properties | 5 + .../resources/templates/en_US/initscript.cl | 2 + src/main/resources/templates/en_US/slt.cl | 18 +- 20 files changed, 1054 insertions(+), 11 deletions(-) create mode 100644 src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltBreakpoint.java create mode 100644 src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltBreakpointContainer.java create mode 100644 src/main/java/com/en_circle/slt/plugin/swank/requests/StepperAction.java create mode 100644 src/main/java/com/en_circle/slt/plugin/ui/debug/SltBreakpointListener.java create mode 100644 src/main/java/com/en_circle/slt/plugin/ui/debug/SltBreakpointProperties.java create mode 100644 src/main/java/com/en_circle/slt/plugin/ui/debug/SltSymbolBreakpointType.java diff --git a/src/main/java/com/en_circle/slt/plugin/lisp/LispParserUtil.java b/src/main/java/com/en_circle/slt/plugin/lisp/LispParserUtil.java index 8dd83f3..2f9f42c 100644 --- a/src/main/java/com/en_circle/slt/plugin/lisp/LispParserUtil.java +++ b/src/main/java/com/en_circle/slt/plugin/lisp/LispParserUtil.java @@ -39,7 +39,7 @@ public static String getPackage(PsiFile psiFile, int offset, Function notFoundSupplier) { @@ -139,4 +139,5 @@ public static LispList getIfHead(PsiElement element) { } return null; } + } diff --git a/src/main/java/com/en_circle/slt/plugin/lisp/LispSymbolPresentation.java b/src/main/java/com/en_circle/slt/plugin/lisp/LispSymbolPresentation.java index 68de30b..2a2db42 100644 --- a/src/main/java/com/en_circle/slt/plugin/lisp/LispSymbolPresentation.java +++ b/src/main/java/com/en_circle/slt/plugin/lisp/LispSymbolPresentation.java @@ -54,7 +54,7 @@ private SymbolState refreshState() { return switch (state.binding) { case NONE, SPECIAL_FORM -> null; case FUNCTION -> Nodes.Function; - case MACRO -> Nodes.Template; + case MACRO -> Nodes.Annotationtype; case CONSTANT, KEYWORD -> Nodes.Constant; case SPECIAL_VARIABLE -> Nodes.Gvariable; case CLASS -> Nodes.Class; diff --git a/src/main/java/com/en_circle/slt/plugin/lisp/lisp/LispUtils.java b/src/main/java/com/en_circle/slt/plugin/lisp/lisp/LispUtils.java index ec6cd6e..eb9c311 100644 --- a/src/main/java/com/en_circle/slt/plugin/lisp/lisp/LispUtils.java +++ b/src/main/java/com/en_circle/slt/plugin/lisp/lisp/LispUtils.java @@ -37,6 +37,18 @@ public static List convertAst(PsiFile source, Map convertAst(LispToplevel source) { + return convertAst(source, null, null); + } + + public static List convertAst(LispToplevel source, Map lineOffsets, String offsetText) { + List elements = new ArrayList<>(); + + source.accept(new LispVisitorImpl(elements, lineOffsets, offsetText)); + + return elements; + } + public static String unescape(String text) { text = StringUtils.replace(text, "\\\"", "\""); return text; diff --git a/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentService.java b/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentService.java index 56f7f80..717828b 100644 --- a/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentService.java +++ b/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentService.java @@ -5,13 +5,18 @@ import com.en_circle.slt.plugin.environment.SltLispEnvironment.SltLispOutputChangedListener; import com.en_circle.slt.plugin.lisp.lisp.LispElement; import com.en_circle.slt.plugin.lisp.psi.LispList; +import com.en_circle.slt.plugin.services.lisp.components.SltBreakpoint; import com.en_circle.slt.plugin.swank.SlimeListener.DebugInterface; import com.en_circle.slt.plugin.swank.SlimeListener.RequestResponseLogger; import com.en_circle.slt.plugin.swank.SlimeRequest; +import com.en_circle.slt.plugin.ui.debug.SltBreakpointProperties; import com.intellij.openapi.Disposable; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; +import com.intellij.xdebugger.breakpoints.XBreakpoint; + +import java.util.Collection; public interface LispEnvironmentService extends Disposable { @@ -50,6 +55,14 @@ static LispEnvironmentService getInstance(Project project) { Integer calculateOffset(PsiElement element, PsiFile file, boolean wasAfter, String text, int offset, String packageOverride); + void addBreakpoint(XBreakpoint nativeBreakpoint); + + void removeBreakpoint(XBreakpoint nativeBreakpoint); + + void nativeBreakpointUpdated(XBreakpoint nativeBreakpoint); + + Collection getAllBreakpoints(); + enum LispEnvironmentState { STOPPED, READY, INITIALIZING } diff --git a/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentServiceImpl.java b/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentServiceImpl.java index 1d7653f..20139c8 100644 --- a/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentServiceImpl.java +++ b/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentServiceImpl.java @@ -10,14 +10,13 @@ import com.en_circle.slt.plugin.sdk.LispProjectSdk; import com.en_circle.slt.plugin.sdk.LispSdk; import com.en_circle.slt.plugin.sdk.SdkList; -import com.en_circle.slt.plugin.services.lisp.components.SltIndentationContainer; -import com.en_circle.slt.plugin.services.lisp.components.SltLispEnvironmentMacroExpandCache; -import com.en_circle.slt.plugin.services.lisp.components.SltLispEnvironmentSymbolCache; +import com.en_circle.slt.plugin.services.lisp.components.*; import com.en_circle.slt.plugin.swank.SlimeListener; import com.en_circle.slt.plugin.swank.SlimeListener.DebugInterface; import com.en_circle.slt.plugin.swank.SlimeListener.RequestResponseLogger; import com.en_circle.slt.plugin.swank.SlimeRequest; import com.en_circle.slt.plugin.swank.SwankClient; +import com.en_circle.slt.plugin.ui.debug.SltBreakpointProperties; import com.en_circle.slt.tools.ProjectUtils; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; @@ -26,10 +25,12 @@ import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; +import com.intellij.xdebugger.breakpoints.XBreakpoint; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.function.Supplier; @@ -52,6 +53,7 @@ public class LispEnvironmentServiceImpl implements LispEnvironmentService { private final SltIndentationContainer indentationContainer; private final SltLispEnvironmentSymbolCache symbolCache; private final SltLispEnvironmentMacroExpandCache macroExpandCache; + private final SltBreakpointContainer breakpointContainer; public LispEnvironmentServiceImpl(Project project) { this.project = project; @@ -59,7 +61,10 @@ public LispEnvironmentServiceImpl(Project project) { indentationContainer.init(project); symbolCache = new SltLispEnvironmentSymbolCache(project); macroExpandCache = new SltLispEnvironmentMacroExpandCache(); + breakpointContainer = new SltBreakpointContainer(project); symbolCache.start(); + + addServerListener(breakpointContainer); } @Override @@ -204,6 +209,9 @@ public void sendToLisp(SlimeRequest request) throws Exception { @Override public void sendToLisp(SlimeRequest request, boolean startServer) throws Exception { + if (request == null) + return; + if (startServer && environment == null || !environment.isActive()) { ApplicationManager.getApplication().invokeLater(() -> { starting = true; @@ -289,6 +297,26 @@ public Integer calculateOffset(PsiElement element, PsiFile file, boolean wasAfte return indentationContainer.calculateIndent(element, file, wasAfter, text, offset, packageOverride); } + @Override + public void addBreakpoint(XBreakpoint nativeBreakpoint) { + breakpointContainer.addBreakpoint(nativeBreakpoint); + } + + @Override + public void removeBreakpoint(XBreakpoint nativeBreakpoint) { + breakpointContainer.removeBreakpoint(nativeBreakpoint); + } + + @Override + public void nativeBreakpointUpdated(XBreakpoint nativeBreakpoint) { + breakpointContainer.onUpdate(nativeBreakpoint); + } + + @Override + public Collection getAllBreakpoints() { + return breakpointContainer.getAllBreakpoints(); + } + @Override public void dispose() { try { diff --git a/src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltBreakpoint.java b/src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltBreakpoint.java new file mode 100644 index 0000000..563d49b --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltBreakpoint.java @@ -0,0 +1,123 @@ +package com.en_circle.slt.plugin.services.lisp.components; + +import com.en_circle.slt.plugin.ui.debug.SltBreakpointProperties; +import com.intellij.xdebugger.breakpoints.SuspendPolicy; +import com.intellij.xdebugger.breakpoints.XBreakpoint; +import org.jetbrains.annotations.NotNull; + +import java.util.Comparator; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +public class SltBreakpoint implements Comparable { + private static final Comparator COMPARATOR = + Comparator.comparing(SltBreakpoint::getSymbol) + .thenComparing(SltBreakpoint::getParentSymbol, Comparator.nullsFirst(String::compareTo)) + .thenComparing(SltBreakpoint::getParentBindType, Comparator.nullsFirst(String::compareTo)); + + private String symbol; + private String parentSymbol; + private String parentBindType; + private SltBreakpointType type = SltBreakpointType.STANDARD; + + private boolean installed; + + private final Set> nativeBreakpoints = new HashSet<>(); + + public SltBreakpoint(String breakpointSymbol) { + this.symbol = breakpointSymbol; + } + + public SltBreakpointType getType() { + return type; + } + + public void setType(SltBreakpointType type) { + this.type = type; + } + + public Set> getNativeBreakpoints() { + return nativeBreakpoints; + } + + public boolean isInstalled() { + return installed; + } + + public void setInstalled(boolean installed) { + this.installed = installed; + } + + public String getSymbol() { + return symbol; + } + + public void setSymbol(String symbol) { + this.symbol = symbol; + } + + public String getParentSymbol() { + return parentSymbol; + } + + public void setParentSymbol(String parentSymbol) { + this.parentSymbol = parentSymbol; + } + + public String getParentBindType() { + return parentBindType; + } + + public void setParentBindType(String parentBindType) { + this.parentBindType = parentBindType; + } + + @Override + public String toString() { + return "Breakpoint(" + symbol + ")"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + SltBreakpoint that = (SltBreakpoint) o; + + if (!Objects.equals(symbol, that.symbol)) return false; + if (!Objects.equals(parentSymbol, that.parentSymbol)) return false; + if (!Objects.equals(parentBindType, that.parentBindType)) + return false; + return type == that.type; + } + + @Override + public int hashCode() { + int result = symbol != null ? symbol.hashCode() : 0; + result = 31 * result + (parentSymbol != null ? parentSymbol.hashCode() : 0); + result = 31 * result + (parentBindType != null ? parentBindType.hashCode() : 0); + result = 31 * result + (type != null ? type.hashCode() : 0); + return result; + } + + public boolean shouldBeInstalled() { + for (XBreakpoint breakpoint : nativeBreakpoints) { + if (breakpoint.isEnabled() && breakpoint.getSourcePosition() != null && breakpoint.getSuspendPolicy() != SuspendPolicy.NONE) { + return true; + } + } + return false; + } + + @Override + public int compareTo(@NotNull SltBreakpoint o) { + return COMPARATOR.compare(this, o); + } + + public enum SltBreakpointType { + + STANDARD, INNER, METHOD + + } +} diff --git a/src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltBreakpointContainer.java b/src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltBreakpointContainer.java new file mode 100644 index 0000000..c8a0365 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltBreakpointContainer.java @@ -0,0 +1,200 @@ +package com.en_circle.slt.plugin.services.lisp.components; + +import com.en_circle.slt.plugin.environment.SltLispEnvironment.SltOutput; +import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService; +import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService.LispEnvironmentListener; +import com.en_circle.slt.plugin.services.lisp.components.SltBreakpoint.SltBreakpointType; +import com.en_circle.slt.plugin.swank.SlimeRequest; +import com.en_circle.slt.plugin.swank.requests.Eval; +import com.en_circle.slt.plugin.ui.debug.SltBreakpointProperties; +import com.intellij.openapi.project.Project; +import com.intellij.xdebugger.breakpoints.XBreakpoint; +import org.apache.commons.lang3.StringUtils; + +import java.util.Collection; +import java.util.TreeSet; + +public class SltBreakpointContainer implements LispEnvironmentListener { + + private final TreeSet breakpoints = new TreeSet<>(); + private final Project project; + + public SltBreakpointContainer(Project project) { + this.project = project; + } + + + public void addBreakpoint(XBreakpoint nativeBreakpoint) { + SltBreakpoint breakpoint = getBreakpoint(nativeBreakpoint); + + SltBreakpoint existing = search(breakpoint); + if (existing == null) { + breakpoints.add(breakpoint); + existing = breakpoint; + } + + existing.getNativeBreakpoints().add(nativeBreakpoint); + updateBreakpoint(existing); + } + + public void removeBreakpoint(XBreakpoint nativeBreakpoint) { + SltBreakpoint breakpoint = getBreakpoint(nativeBreakpoint); + breakpoint.getNativeBreakpoints().remove(nativeBreakpoint); + + if (breakpoint.getNativeBreakpoints().isEmpty()) { + if (breakpoint.isInstalled()) + uninstallBreakpoint(breakpoint); + breakpoints.remove(breakpoint); + } else { + uninstallBreakpoint(breakpoint); + } + } + + private SltBreakpoint getBreakpoint(XBreakpoint nativeBreakpoint) { + SltBreakpointProperties p = nativeBreakpoint.getProperties(); + SltBreakpoint breakpoint = new SltBreakpoint(getBreakpointSymbol(p.packageName, p.symbolName)); + if (p.fletType != null) { + breakpoint.setParentBindType(p.fletType); + breakpoint.setParentSymbol(getBreakpointSymbol(p.ppackageName, p.psymbolName)); + breakpoint.setType(SltBreakpointType.INNER); + } + return breakpoint; + } + + public void onUpdate(XBreakpoint nativeBreakpoint) { + for (SltBreakpoint breakpoint : breakpoints) { + if (breakpoint.getNativeBreakpoints().contains(nativeBreakpoint)) { + updateBreakpoint(breakpoint); + return; + } + } + } + + private String getBreakpointSymbol(String packageName, String symbolName) { + if (StringUtils.isBlank(packageName)) { + return symbolName; + } + if (symbolName.startsWith(":")) { + return symbolName; + } + return packageName + "::" + symbolName; + } + + private SltBreakpoint search(SltBreakpoint key) { + SltBreakpoint ceil = breakpoints.ceiling(key); + SltBreakpoint floor = breakpoints.floor(key); + return ceil == floor? ceil : null; + } + + private void updateBreakpoint(SltBreakpoint breakpoint) { + if (breakpoint.isInstalled()) { + if (!breakpoint.shouldBeInstalled()) { + uninstallBreakpoint(breakpoint); + } + } else { + if (breakpoint.shouldBeInstalled()) { + installBreakpoint(breakpoint); + } + } + } + + private void installBreakpoint(SltBreakpoint breakpoint) { + try { + LispEnvironmentService.getInstance(project) + .sendToLisp(installRequest(breakpoint), false); + } catch (Exception ignore) { + + } + } + + private SlimeRequest installRequest(SltBreakpoint breakpoint) { + switch (breakpoint.getType()) { + case STANDARD -> { + return Eval.eval("(slt-core:install-breakpoint '" + breakpoint.getSymbol() + ")", r -> installResult(r, breakpoint)); + } + case INNER -> { + return Eval.eval("(slt-core:install-inner-breakpoint '" + breakpoint.getSymbol() + + " '" + breakpoint.getParentSymbol() + " '" + breakpoint.getParentBindType() + + ")", r -> installResult(r, breakpoint)); + } + case METHOD -> { + // TODO + return null; + } + } + return null; + } + + private void installResult(String s, SltBreakpoint breakpoint) { + if ("T".equalsIgnoreCase(s)) { + breakpoint.setInstalled(true); + } + } + + private void uninstallBreakpoint(SltBreakpoint breakpoint) { + try { + LispEnvironmentService.getInstance(project) + .sendToLisp(uninstallRequest(breakpoint), false); + } catch (Exception ignore) { + + } + } + + private SlimeRequest uninstallRequest(SltBreakpoint breakpoint) { + switch (breakpoint.getType()) { + case STANDARD -> { + return Eval.eval("(slt-core:uninstall-breakpoint '" + breakpoint.getSymbol() + ")", r -> uninstallResult(r, breakpoint)); + } + case INNER -> { + return Eval.eval("(slt-core:uninstall-inner-breakpoint '" + breakpoint.getSymbol() + + " '" + breakpoint.getParentSymbol() + " '" + breakpoint.getParentBindType() + + ")", r -> uninstallResult(r, breakpoint)); + } + case METHOD -> { + // TODO + return null; + } + } + return null; + } + + private void uninstallResult(String s, SltBreakpoint breakpoint) { + if ("T".equalsIgnoreCase(s)) { + breakpoint.setInstalled(false); + } + } + + public Collection getAllBreakpoints() { + return breakpoints; + } + + @Override + public void onOutputChanged(SltOutput output, String newData) { + + } + + @Override + public void onPreStart() { + + } + + @Override + public void onPostStart() { + for (SltBreakpoint breakpoint : breakpoints) { + updateBreakpoint(breakpoint); + } + } + + @Override + public void onPreStop() { + for (SltBreakpoint breakpoint : breakpoints) { + breakpoint.setInstalled(false); + } + } + + @Override + public void onPostStop() { + + } + +} diff --git a/src/main/java/com/en_circle/slt/plugin/swank/SlimeListener.java b/src/main/java/com/en_circle/slt/plugin/swank/SlimeListener.java index 4edbbc5..c686fdd 100644 --- a/src/main/java/com/en_circle/slt/plugin/swank/SlimeListener.java +++ b/src/main/java/com/en_circle/slt/plugin/swank/SlimeListener.java @@ -138,6 +138,10 @@ private void processReturn(LispContainer reply) { action.processReply((LispContainer) reply.getItems().get(1)); } + if (request instanceof StepperAction action) { + action.processReply((LispContainer) reply.getItems().get(1)); + } + if (request instanceof MacroexpandAll macroexpandAll) { macroexpandAll.processReply((LispContainer) reply.getItems().get(1)); } diff --git a/src/main/java/com/en_circle/slt/plugin/swank/SwankPacket.java b/src/main/java/com/en_circle/slt/plugin/swank/SwankPacket.java index 849bd4e..b6790fe 100644 --- a/src/main/java/com/en_circle/slt/plugin/swank/SwankPacket.java +++ b/src/main/java/com/en_circle/slt/plugin/swank/SwankPacket.java @@ -188,6 +188,38 @@ public static SwankPacket inspectorRefresh(BigInteger threadId, String packageNa return new SwankPacket(formatted); } + public static SwankPacket activateStepping(BigInteger threadId, String packageName, BigInteger continuation) { + packageName = StringUtils.replace(packageName, "\\", "\\\\"); + packageName = StringUtils.replace(packageName, "\"", "\\\""); + String formatted = String.format("(:emacs-rex (swank::activate-stepping 0) \":%s\" %s %s)", + packageName, threadId, continuation); + return new SwankPacket(formatted); + } + + public static SwankPacket stepperIn(BigInteger threadId, String packageName, BigInteger continuation) { + packageName = StringUtils.replace(packageName, "\\", "\\\\"); + packageName = StringUtils.replace(packageName, "\"", "\\\""); + String formatted = String.format("(:emacs-rex (swank:sldb-step 0) \":%s\" %s %s)", + packageName, threadId, continuation); + return new SwankPacket(formatted); + } + + public static SwankPacket stepperOut(BigInteger threadId, String packageName, BigInteger continuation) { + packageName = StringUtils.replace(packageName, "\\", "\\\\"); + packageName = StringUtils.replace(packageName, "\"", "\\\""); + String formatted = String.format("(:emacs-rex (swank:sldb-out 0) \":%s\" %s %s)", + packageName, threadId, continuation); + return new SwankPacket(formatted); + } + + public static SwankPacket stepperNext(BigInteger threadId, String packageName, BigInteger continuation) { + packageName = StringUtils.replace(packageName, "\\", "\\\\"); + packageName = StringUtils.replace(packageName, "\"", "\\\""); + String formatted = String.format("(:emacs-rex (swank:sldb-next 0) \":%s\" %s %s)", + packageName, threadId, continuation); + return new SwankPacket(formatted); + } + public static SwankPacket loadFile(String file, BigInteger continuation) { return loadFile(file, "CL-USER", continuation); } diff --git a/src/main/java/com/en_circle/slt/plugin/swank/requests/StepperAction.java b/src/main/java/com/en_circle/slt/plugin/swank/requests/StepperAction.java new file mode 100644 index 0000000..edd028c --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/swank/requests/StepperAction.java @@ -0,0 +1,69 @@ +package com.en_circle.slt.plugin.swank.requests; + +import com.en_circle.slt.plugin.lisp.lisp.LispContainer; +import com.en_circle.slt.plugin.lisp.lisp.LispElement; +import com.en_circle.slt.plugin.lisp.lisp.LispSymbol; +import com.en_circle.slt.plugin.swank.SlimeRequest; +import com.en_circle.slt.plugin.swank.SwankPacket; + +import java.math.BigInteger; + +public class StepperAction extends SlimeRequest { + + public static SlimeRequest action(ActionType actionType, BigInteger threadId, String module, Callback callback) { + return new StepperAction(actionType, threadId, module, callback); + } + + public static SlimeRequest action(ActionType actionType, BigInteger threadId, Callback callback) { + return new StepperAction(actionType, threadId, "CL-USER", callback); + } + + protected final Callback callback; + protected final String module; + protected final ActionType actionType; + protected final BigInteger threadId; + + protected StepperAction(ActionType actionType, BigInteger threadId, String module, Callback callback) { + this.callback = callback; + this.module = module; + this.actionType = actionType; + this.threadId = threadId; + } + + public void processReply(LispContainer data) { + if (isOk(data)) { + callback.onResult(data.getItems().get(1)); + } + } + + private boolean isOk(LispContainer data) { + return data.getItems().size() > 0 && + data.getItems().get(0) instanceof LispSymbol && + ":ok".equals(((LispSymbol) data.getItems().get(0)).getValue()); + } + + @Override + public SwankPacket createPacket(BigInteger requestId) { + switch (actionType) { + case ENABLE: + return SwankPacket.activateStepping(threadId, module, requestId); + case IN: + return SwankPacket.stepperIn(threadId, module, requestId); + case NEXT: + return SwankPacket.stepperNext(threadId, module, requestId); + case OUT: + return SwankPacket.stepperOut(threadId, module, requestId); + } + throw new IllegalStateException(); + } + + public interface Callback { + void onResult(LispElement result); + } + + public enum ActionType { + + ENABLE, IN, NEXT, OUT + + } +} diff --git a/src/main/java/com/en_circle/slt/plugin/ui/SltCoreWindow.java b/src/main/java/com/en_circle/slt/plugin/ui/SltCoreWindow.java index 13aa498..c736a8b 100644 --- a/src/main/java/com/en_circle/slt/plugin/ui/SltCoreWindow.java +++ b/src/main/java/com/en_circle/slt/plugin/ui/SltCoreWindow.java @@ -181,6 +181,11 @@ public void actionPerformed(@NotNull AnActionEvent e) { ApplicationManager.getApplication().invokeLater(SltCoreWindow.this::start); } + @Override + public @NotNull ActionUpdateThread getActionUpdateThread() { + return ActionUpdateThread.EDT; + } + @Override public void update(@NotNull AnActionEvent e) { super.update(e); @@ -200,6 +205,11 @@ public void actionPerformed(@NotNull AnActionEvent e) { stop(); } + @Override + public @NotNull ActionUpdateThread getActionUpdateThread() { + return ActionUpdateThread.EDT; + } + @Override public void update(@NotNull AnActionEvent e) { super.update(e); @@ -219,6 +229,11 @@ public void actionPerformed(@NotNull AnActionEvent e) { addRepl(); } + @Override + public @NotNull ActionUpdateThread getActionUpdateThread() { + return ActionUpdateThread.EDT; + } + @Override public void update(@NotNull AnActionEvent e) { super.update(e); diff --git a/src/main/java/com/en_circle/slt/plugin/ui/debug/SltBreakpointListener.java b/src/main/java/com/en_circle/slt/plugin/ui/debug/SltBreakpointListener.java new file mode 100644 index 0000000..a64bf62 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/ui/debug/SltBreakpointListener.java @@ -0,0 +1,33 @@ +package com.en_circle.slt.plugin.ui.debug; + +import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService; +import com.intellij.openapi.project.Project; +import com.intellij.xdebugger.breakpoints.XBreakpointListener; +import com.intellij.xdebugger.breakpoints.XLineBreakpoint; +import org.jetbrains.annotations.NotNull; + +public class SltBreakpointListener implements XBreakpointListener> { + + private final Project project; + + public SltBreakpointListener(Project project) { + this.project = project; + } + + @Override + public void breakpointAdded(@NotNull XLineBreakpoint breakpoint) { + LispEnvironmentService.getInstance(project) + .addBreakpoint(breakpoint); + } + + @Override + public void breakpointRemoved(@NotNull XLineBreakpoint breakpoint) { + LispEnvironmentService.getInstance(project) + .removeBreakpoint(breakpoint); + } + + @Override + public void breakpointChanged(@NotNull XLineBreakpoint breakpoint) { + LispEnvironmentService.getInstance(project).nativeBreakpointUpdated(breakpoint); + } +} diff --git a/src/main/java/com/en_circle/slt/plugin/ui/debug/SltBreakpointProperties.java b/src/main/java/com/en_circle/slt/plugin/ui/debug/SltBreakpointProperties.java new file mode 100644 index 0000000..dda7b48 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/ui/debug/SltBreakpointProperties.java @@ -0,0 +1,31 @@ +package com.en_circle.slt.plugin.ui.debug; + +import com.en_circle.slt.plugin.ui.debug.SltSymbolBreakpointType.SymbolType; +import com.intellij.util.xmlb.XmlSerializerUtil; +import com.intellij.xdebugger.breakpoints.XBreakpointProperties; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class SltBreakpointProperties extends XBreakpointProperties { + + public String symbolName; + public String packageName; + public SymbolType symbolType; + public String psymbolName; + public String ppackageName; + public String fletType; + + public int offset; + public String file; + + @Override + public @Nullable SltBreakpointProperties getState() { + return this; + } + + @Override + public void loadState(@NotNull SltBreakpointProperties state) { + XmlSerializerUtil.copyBean(state, this); + } + +} diff --git a/src/main/java/com/en_circle/slt/plugin/ui/debug/SltDebuggerImpl.java b/src/main/java/com/en_circle/slt/plugin/ui/debug/SltDebuggerImpl.java index 786bea4..0569244 100644 --- a/src/main/java/com/en_circle/slt/plugin/ui/debug/SltDebuggerImpl.java +++ b/src/main/java/com/en_circle/slt/plugin/ui/debug/SltDebuggerImpl.java @@ -2,20 +2,22 @@ import com.en_circle.slt.plugin.SltBundle; import com.en_circle.slt.plugin.SltUIConstants; +import com.en_circle.slt.plugin.lisp.lisp.LispElement; import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService; +import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService.LispEnvironmentState; import com.en_circle.slt.plugin.swank.debug.SltDebugAction; import com.en_circle.slt.plugin.swank.debug.SltDebugArgument; import com.en_circle.slt.plugin.swank.debug.SltDebugInfo; import com.en_circle.slt.plugin.swank.debug.SltDebugStackTraceElement; import com.en_circle.slt.plugin.swank.requests.FrameLocalsAndCatchTags; import com.en_circle.slt.plugin.swank.requests.InvokeNthRestart; +import com.en_circle.slt.plugin.swank.requests.StepperAction; +import com.en_circle.slt.plugin.swank.requests.StepperAction.ActionType; import com.en_circle.slt.plugin.swank.requests.ThrowToToplevel; import com.intellij.icons.AllIcons.Actions; +import com.intellij.icons.AllIcons.Debugger; import com.intellij.openapi.Disposable; -import com.intellij.openapi.actionSystem.ActionPlaces; -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.OpenFileDescriptor; @@ -55,6 +57,7 @@ public class SltDebuggerImpl implements SltDebugger, Disposable { private final TabInfo tabInfo; private final SltDebuggers parent; private BigInteger lastDebugId; + private SltDebugInfo lastDebug; private JPanel singleFrameComponent; private final List stackframes = new ArrayList<>(); private BigInteger debugContext; @@ -87,10 +90,23 @@ public void redraw(SltDebugInfo debugInfo) { this.content.removeAll(); this.lastDebugId = debugInfo.getThreadId(); + this.lastDebug = debugInfo; + + JPanel content = new JPanel(new BorderLayout()); + this.content.add(content, BorderLayout.CENTER); + + DefaultActionGroup controlGroup = new DefaultActionGroup(); + controlGroup.add(new StepActivate()); +// controlGroup.add(new StepIntoAction()); +// controlGroup.add(new StepNextAction()); +// controlGroup.add(new StepOutAction()); + ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar("SltProcessWindowWrapEvent", controlGroup, false); + toolbar.setTargetComponent(this.content); + this.content.add(toolbar.getComponent(), BorderLayout.WEST); JBSplitter splitter = new JBSplitter(false); splitter.setProportion(0.5f); - this.content.add(splitter, BorderLayout.CENTER); + content.add(splitter, BorderLayout.CENTER); JBTextField errorName = new JBTextField(); errorName.setEditable(false); @@ -183,6 +199,65 @@ public void mouseClicked(MouseEvent e) { splitter.setSecondComponent(singleFrameParent); } + private void enableStepping() { + try { + LispEnvironmentService.getInstance(parent.getProject()) + .sendToLisp(StepperAction.action(ActionType.ENABLE, lastDebug.getThreadId(), result -> + ApplicationManager.getApplication().invokeLater(() -> {}))); + } catch (Exception e) { + log.warn(SltBundle.message("slt.error.sbclstart"), e); + Messages.showErrorDialog(parent.getProject(), e.getMessage(), SltBundle.message("slt.ui.errors.sbcl.start")); + } + } + + private void enableStepping(Runnable onEnable) { + try { + LispEnvironmentService.getInstance(parent.getProject()) + .sendToLisp(StepperAction.action(ActionType.ENABLE, lastDebug.getThreadId(), result -> + ApplicationManager.getApplication().invokeLater(onEnable::run))); + } catch (Exception e) { + log.warn(SltBundle.message("slt.error.sbclstart"), e); + Messages.showErrorDialog(parent.getProject(), e.getMessage(), SltBundle.message("slt.ui.errors.sbcl.start")); + } + } + + private void stepIn() { + try { + LispEnvironmentService.getInstance(parent.getProject()) + .sendToLisp(StepperAction.action(ActionType.IN, lastDebug.getThreadId(), result -> + ApplicationManager.getApplication().invokeLater(() -> processResult(result)))); + } catch (Exception e) { + log.warn(SltBundle.message("slt.error.sbclstart"), e); + Messages.showErrorDialog(parent.getProject(), e.getMessage(), SltBundle.message("slt.ui.errors.sbcl.start")); + } + } + + private void stepOut() { + try { + LispEnvironmentService.getInstance(parent.getProject()) + .sendToLisp(StepperAction.action(ActionType.OUT, lastDebug.getThreadId(), result -> + ApplicationManager.getApplication().invokeLater(() -> processResult(result)))); + } catch (Exception e) { + log.warn(SltBundle.message("slt.error.sbclstart"), e); + Messages.showErrorDialog(parent.getProject(), e.getMessage(), SltBundle.message("slt.ui.errors.sbcl.start")); + } + } + + private void stepNext() { + try { + LispEnvironmentService.getInstance(parent.getProject()) + .sendToLisp(StepperAction.action(ActionType.NEXT, lastDebug.getThreadId(), result -> + ApplicationManager.getApplication().invokeLater(() -> processResult(result)))); + } catch (Exception e) { + log.warn(SltBundle.message("slt.error.sbclstart"), e); + Messages.showErrorDialog(parent.getProject(), e.getMessage(), SltBundle.message("slt.ui.errors.sbcl.start")); + } + } + + private void processResult(LispElement ignored) { + // ignored + } + private String getText(String text) { return StringUtils.replace(text, "\n", " "); } @@ -278,10 +353,111 @@ private void close() { @Override public void activate() { this.tabInfo.fireAlert(); + if (lastDebug != null) { + if (lastDebug.getStacktrace() != null && lastDebug.getStacktrace().size() > 0) { + stackframeClicked(lastDebug.getStacktrace().get(0), lastDebug); + } + } } @Override public void dispose() { } + + private class StepActivate extends AnAction { + + private StepActivate() { + super(SltBundle.message("slt.ui.debugger.stepper.activate"), "", Debugger.ShowCurrentFrame); + } + + @Override + public void actionPerformed(@NotNull AnActionEvent e) { + enableStepping(); + } + + @Override + public @NotNull ActionUpdateThread getActionUpdateThread() { + return ActionUpdateThread.EDT; + } + + @Override + public void update(@NotNull AnActionEvent e) { + super.update(e); + + e.getPresentation().setEnabled(LispEnvironmentService.getInstance(parent.getProject()).getState() == LispEnvironmentState.READY); + } + } + + private class StepIntoAction extends AnAction { + + private StepIntoAction() { + super(SltBundle.message("slt.ui.debugger.stepper.into"), "", Actions.TraceInto); + } + + @Override + public void actionPerformed(@NotNull AnActionEvent e) { + enableStepping(SltDebuggerImpl.this::stepIn); + } + + @Override + public @NotNull ActionUpdateThread getActionUpdateThread() { + return ActionUpdateThread.EDT; + } + + @Override + public void update(@NotNull AnActionEvent e) { + super.update(e); + + e.getPresentation().setEnabled(LispEnvironmentService.getInstance(parent.getProject()).getState() == LispEnvironmentState.READY); + } + } + + private class StepOutAction extends AnAction { + + private StepOutAction() { + super(SltBundle.message("slt.ui.debugger.stepper.out"), "", Actions.StepOut); + } + + @Override + public void actionPerformed(@NotNull AnActionEvent e) { + enableStepping(SltDebuggerImpl.this::stepOut); + } + + @Override + public @NotNull ActionUpdateThread getActionUpdateThread() { + return ActionUpdateThread.EDT; + } + + @Override + public void update(@NotNull AnActionEvent e) { + super.update(e); + + e.getPresentation().setEnabled(LispEnvironmentService.getInstance(parent.getProject()).getState() == LispEnvironmentState.READY); + } + } + + private class StepNextAction extends AnAction { + + private StepNextAction() { + super(SltBundle.message("slt.ui.debugger.stepper.next"), "", Actions.TraceOver); + } + + @Override + public void actionPerformed(@NotNull AnActionEvent e) { + enableStepping(SltDebuggerImpl.this::stepNext); + } + + @Override + public @NotNull ActionUpdateThread getActionUpdateThread() { + return ActionUpdateThread.EDT; + } + + @Override + public void update(@NotNull AnActionEvent e) { + super.update(e); + + e.getPresentation().setEnabled(LispEnvironmentService.getInstance(parent.getProject()).getState() == LispEnvironmentState.READY); + } + } } diff --git a/src/main/java/com/en_circle/slt/plugin/ui/debug/SltInspector.java b/src/main/java/com/en_circle/slt/plugin/ui/debug/SltInspector.java index ecbc4fa..21b7af2 100644 --- a/src/main/java/com/en_circle/slt/plugin/ui/debug/SltInspector.java +++ b/src/main/java/com/en_circle/slt/plugin/ui/debug/SltInspector.java @@ -160,6 +160,11 @@ public void actionPerformed(@NotNull AnActionEvent event) { } } + @Override + public @NotNull ActionUpdateThread getActionUpdateThread() { + return ActionUpdateThread.EDT; + } + @Override public void update(@NotNull AnActionEvent e) { super.update(e); @@ -186,6 +191,11 @@ public void actionPerformed(@NotNull AnActionEvent event) { } } + @Override + public @NotNull ActionUpdateThread getActionUpdateThread() { + return ActionUpdateThread.EDT; + } + @Override public void update(@NotNull AnActionEvent e) { super.update(e); @@ -212,6 +222,11 @@ public void actionPerformed(@NotNull AnActionEvent event) { } } + @Override + public @NotNull ActionUpdateThread getActionUpdateThread() { + return ActionUpdateThread.EDT; + } + @Override public void update(@NotNull AnActionEvent e) { super.update(e); diff --git a/src/main/java/com/en_circle/slt/plugin/ui/debug/SltSymbolBreakpointType.java b/src/main/java/com/en_circle/slt/plugin/ui/debug/SltSymbolBreakpointType.java new file mode 100644 index 0000000..17c0dd3 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/ui/debug/SltSymbolBreakpointType.java @@ -0,0 +1,259 @@ +package com.en_circle.slt.plugin.ui.debug; + +import com.en_circle.slt.plugin.SltBundle; +import com.en_circle.slt.plugin.SltCommonLispFileType; +import com.en_circle.slt.plugin.lisp.LispParserUtil; +import com.en_circle.slt.plugin.lisp.psi.LispList; +import com.en_circle.slt.plugin.lisp.psi.LispSexpr; +import com.en_circle.slt.plugin.lisp.psi.LispSymbol; +import com.en_circle.slt.plugin.lisp.psi.LispToplevel; +import com.intellij.icons.AllIcons.Nodes; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileManager; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.xdebugger.XSourcePosition; +import com.intellij.xdebugger.breakpoints.XBreakpoint; +import com.intellij.xdebugger.breakpoints.XLineBreakpointType; +import com.intellij.xdebugger.impl.XSourcePositionImpl; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.*; + +public class SltSymbolBreakpointType extends XLineBreakpointType { + + protected SltSymbolBreakpointType() { + super(SltSymbolBreakpointType.class.getName(), SltBundle.message("slt.ui.debugger.breakpoint")); + } + + @Override + public boolean canPutAt(@NotNull VirtualFile file, int line, @NotNull Project project) { + if (file.getFileType() == SltCommonLispFileType.INSTANCE) + return getPossiblePsiDebugSymbols(file, line, project).size() > 0; + return false; + } + + @Override + public @Nullable SltBreakpointProperties createBreakpointProperties(@NotNull VirtualFile file, int line) { + return null; + } + + private List getPossiblePsiDebugSymbols(VirtualFile file, int line, Project project) { + List infoList = new ArrayList<>(); + + PsiFile f = PsiManager.getInstance(project).findFile(file); + if (f != null) { + Document document = PsiDocumentManager.getInstance(project).getDocument(f); + if (document != null) { + int offset = document.getLineStartOffset(line); + int endoffset = document.getLineEndOffset(line); + Set checkedToplevels = new HashSet<>(); + Set checkedElements = new HashSet<>(); + + while (offset < endoffset) { + PsiElement lineElement = getValidElement(offset++, f); + if (lineElement != null) { + if (checkedElements.contains(lineElement)) + continue; + checkedElements.add(lineElement); + + LispToplevel toplevel = PsiTreeUtil.getTopmostParentOfType(lineElement, LispToplevel.class); + if (toplevel != null) { + if (checkedToplevels.contains(toplevel)) + continue; + checkedToplevels.add(toplevel); + + LispList list = PsiTreeUtil.getParentOfType(lineElement, LispList.class); + do { + if (list != null) { + List sexprs = list.getSexprList(); + if (sexprs.size() > 0) { + if (sexprs.get(0).getDatum() != null && + Objects.requireNonNull(sexprs.get(0).getDatum()).getCompoundSymbol() != null) { + LispSymbol head = Objects.requireNonNull(Objects.requireNonNull(sexprs.get(0).getDatum()).getCompoundSymbol()).getSymbol(); + + if ("defun".equalsIgnoreCase(head.getName())) { + if (sexprs.size() > 1) { + addIfSymbol(sexprs.get(1), infoList, offset, endoffset, SymbolType.FUNCTION, head.getName()); + } + } else if ("defgeneric".equalsIgnoreCase(head.getName())) { + if (sexprs.size() > 1) { + addIfSymbol(sexprs.get(1), infoList, offset, endoffset, SymbolType.METHOD, head.getName()); + } + } else if ("defmacro".equalsIgnoreCase(head.getName())) { + if (sexprs.size() > 1) { + addIfSymbol(sexprs.get(1), infoList, offset, endoffset, SymbolType.MACRO, head.getName()); + } + } else if ("defmethod".equalsIgnoreCase(head.getName())) { + // TODO: handle method special stuff to get tracing working correctly and not just on defgeneric level + if (sexprs.size() > 1) { + addIfSymbol(sexprs.get(1), infoList, offset, endoffset, SymbolType.METHOD, head.getName()); + if (sexprs.size() > 2) { + addIfSymbol(sexprs.get(2), infoList, offset, endoffset, SymbolType.METHOD, head.getName()); + } + } + } else if ("flet".equalsIgnoreCase(head.getName()) || + "labels".equalsIgnoreCase(head.getName())) { + // macrolet not supported in SBCL :( + if (sexprs.size() > 1) { + if (sexprs.get(1).getDatum() != null && Objects.requireNonNull(sexprs.get(1).getDatum()).getList() != null) { + LispList funcdefList = Objects.requireNonNull(Objects.requireNonNull(sexprs.get(1).getDatum()).getList()); + for (LispSexpr sexpr : funcdefList.getSexprList()) { + if (sexpr.getDatum() != null && sexpr.getDatum().getList() != null) { + LispList definition = sexpr.getDatum().getList(); + if (definition.getSexprList().size() > 0) { + addIfSymbol(definition.getSexprList().get(0), infoList, offset, endoffset, SymbolType.FUNCTION, head.getName()); + } + } + } + } + } + } + + // TODO: add defclass accessor/setter/getter into the mix! + } + } + list = PsiTreeUtil.getParentOfType(list, LispList.class); + } + } while (list != null); + } + } + } + } + } + + return infoList; + } + + private void addIfSymbol(LispSexpr sexpr, List infoList, int offset, int endoffset, SymbolType symbolType, + String headName) { + if (sexpr.getDatum() != null) { + if (sexpr.getDatum().getCompoundSymbol() != null) { + LispSymbol symbol = sexpr.getDatum().getCompoundSymbol().getSymbol(); + int soffset = symbol.getTextOffset(); + if (soffset >= offset && soffset <= endoffset) { + String packageName = LispParserUtil.getPackage(symbol); + SltBreakpointInfo info = new SltBreakpointInfo(symbol, packageName, symbolType); + if ("flet".equalsIgnoreCase(headName) || "labels".equalsIgnoreCase(headName)) { + info.fletType = headName.toLowerCase(); + LispList list = PsiTreeUtil.getParentOfType(sexpr, LispList.class); + do { + if (list != null) { + List sexprs = list.getSexprList(); + if (sexprs.size() > 0) { + if (sexprs.get(0).getDatum() != null && + Objects.requireNonNull(sexprs.get(0).getDatum()).getCompoundSymbol() != null) { + LispSymbol head = Objects.requireNonNull(Objects.requireNonNull(sexprs.get(0).getDatum()).getCompoundSymbol()).getSymbol(); + + if ("defun".equalsIgnoreCase(head.getName())) { + if (sexprs.size() > 1) { + LispSexpr psym = sexprs.get(1); + if (psym.getDatum() != null) { + if (psym.getDatum().getCompoundSymbol() != null) { + LispSymbol parentSymbol = psym.getDatum().getCompoundSymbol().getSymbol(); + info.ppackageName = info.packageName; + info.pelement = parentSymbol; + infoList.add(info); + } + } + } + } + } + } + } + list = PsiTreeUtil.getParentOfType(list, LispList.class); + } while (list != null); + } else { + infoList.add(info); + } + } + } + } + } + + private PsiElement getValidElement(int offset, PsiFile f) { + PsiElement element = f.findElementAt(offset); + while (element != null) { + if (element instanceof PsiWhiteSpace) { + element = f.findElementAt(++offset); + continue; + } + return element; + } + return null; + } + + @Override + public XSourcePosition getSourcePosition(@NotNull XBreakpoint breakpoint) { + SltBreakpointProperties properties = breakpoint.getProperties(); + VirtualFile virtualFile = VirtualFileManager.getInstance().findFileByUrl(properties.file); + return XSourcePositionImpl.createByOffset(virtualFile, properties.offset); + } + + @Override + public @NotNull List.XLineBreakpointVariant> + computeVariants(@NotNull Project project, @NotNull XSourcePosition position) { + return getPossiblePsiDebugSymbols(position.getFile(), position.getLine(), project); + } + + private class SltBreakpointInfo extends XLineBreakpointVariant { + + private final LispSymbol element; + private final String packageName; + private final SymbolType symbolType; + private String fletType; + private LispSymbol pelement; + private String ppackageName; + + private SltBreakpointInfo(@NotNull LispSymbol element, String packageName, SymbolType symbolType) { + this.element = element; + this.packageName = packageName; + this.symbolType = symbolType; + } + + @Override + public @NotNull @Nls String getText() { + return Objects.requireNonNull(element.getName()); + } + + @Override + public @Nullable Icon getIcon() { + return switch (symbolType) { + case FUNCTION -> Nodes.Function; + case MACRO -> Nodes.Annotationtype; + case METHOD -> Nodes.Method; + }; + } + + @Override + public @Nullable TextRange getHighlightRange() { + return element.getTextRange(); + } + + @Override + public @Nullable SltBreakpointProperties createProperties() { + SltBreakpointProperties properties = new SltBreakpointProperties(); + + properties.symbolName = element.getName(); + properties.packageName = packageName; + properties.symbolType = symbolType; + properties.psymbolName = pelement == null ? null : pelement.getName(); + properties.ppackageName = ppackageName; + properties.fletType = fletType; + properties.file = element.getContainingFile().getVirtualFile().getUrl(); + properties.offset = element.getTextOffset(); + + return properties; + } + } + + public enum SymbolType { + FUNCTION, MACRO, METHOD + } +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 2ea73a7..21c6110 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -92,6 +92,8 @@ + + @@ -101,6 +103,8 @@ + + @@ -152,6 +156,11 @@ + + + + 0.3.0 New features:

diff --git a/src/main/resources/messages/SltBundle.properties b/src/main/resources/messages/SltBundle.properties index d187d88..7656f3c 100644 --- a/src/main/resources/messages/SltBundle.properties +++ b/src/main/resources/messages/SltBundle.properties @@ -79,6 +79,11 @@ slt.ui.debugger.frame.inspector=Inspector slt.ui.debugger.frame.failedtoparse=Failed to parse slt.ui.debugger.frame.arg=Name slt.ui.debugger.frame.value=Value +slt.ui.debugger.breakpoint=Symbol Breakpoint +slt.ui.debugger.stepper.activate=Activate Stepping +slt.ui.debugger.stepper.into=Step Into +slt.ui.debugger.stepper.out=Step Out +slt.ui.debugger.stepper.next=Step Next # inspector slt.ui.inspector.navigation.back=Go Back in History diff --git a/src/main/resources/templates/en_US/initscript.cl b/src/main/resources/templates/en_US/initscript.cl index 2489a39..3038130 100644 --- a/src/main/resources/templates/en_US/initscript.cl +++ b/src/main/resources/templates/en_US/initscript.cl @@ -1,3 +1,5 @@ +(proclaim '(optimize (debug 3))) + (load "~qlpath~") (ql:quickload :swank) (ql:quickload :eclector) diff --git a/src/main/resources/templates/en_US/slt.cl b/src/main/resources/templates/en_US/slt.cl index 1dfd843..62aa3ac 100644 --- a/src/main/resources/templates/en_US/slt.cl +++ b/src/main/resources/templates/en_US/slt.cl @@ -4,6 +4,8 @@ (:use :cl :swank) (:export analyze-symbol analyze-symbols read-fix-packages list-package-names initialize-or-get-debug-context debug-context debug-frame-variable register-variable + install-breakpoint uninstall-breakpoint ; TODO install-method-breakpoint uninstall-method-breakpoint + install-inner-breakpoint uninstall-inner-breakpoint )) ; swank/slime overrides @@ -142,7 +144,7 @@ format suitable for Emacs." (defun analyze-symbol (test-sym) (cons test-sym (let ((*standard-output* (make-string-output-stream)) - (*source-snippet-size* 0)) + (swank::*source-snippet-size* 0)) (cond ((not test-sym) (list NIL NIL NIL)) ((and (fboundp test-sym) @@ -219,4 +221,18 @@ format suitable for Emacs." (loop for package in packages collect (package-name package)))) +(defun install-breakpoint (symbol) + (ignore-errors (eval `(trace ,symbol :break T)) T)) + +(defun uninstall-breakpoint (symbol) + (ignore-errors (eval `(untrace ,symbol)) T)) + +; for methods (method fname qualifiers* (specializers*)) denoting a method. + +(defun install-inner-breakpoint (symbol parent-symbol type) + (ignore-errors (eval `(trace (,type ,symbol :in ,parent-symbol) :break T)) T)) + +(defun uninstall-inner-breakpoint (symbol parent-symbol type) + (ignore-errors (eval `(untrace (,type ,symbol :in ,parent-symbol))) T)) + (in-package :cl-user) \ No newline at end of file From e34dfa1a40b7ff70d9cc636b05bcfeae27975829 Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Thu, 2 Feb 2023 20:48:24 +0100 Subject: [PATCH 02/40] changelog --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d8abae..d943186 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## 0.4.0 + +### Added + +### Fixes + +### Changes + ## 0.3.1 ### Added From cd5bc8ae72248024a1402277982031b57b03a56c Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Fri, 3 Feb 2023 21:16:09 +0100 Subject: [PATCH 03/40] version --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index e045788..8d8c4ef 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,7 +4,7 @@ plugins { } group = "com.en_circle.slt" -version = "0.3.1" +version = "0.4.0" repositories { mavenCentral() From 3b90fdf39bc5acf4c7ae2e44afa30b2f61e31cef Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Sat, 4 Feb 2023 16:41:06 +0100 Subject: [PATCH 04/40] abcl support --- build.gradle.kts | 1 + .../slt/plugin/environment/Environment.java | 34 +++ .../slt/plugin/environment/LispInterpret.java | 1 + .../environment/SltABCLEnvironment.java | 167 ++++++++++++ .../SltABCLEnvironmentConfiguration.java | 106 ++++++++ .../environment/SltProcessStreamGobbler.java | 2 +- .../environment/SltSBCLEnvironment.java | 2 +- .../com/en_circle/slt/plugin/sdk/LispSdk.java | 7 +- .../lisp/LispEnvironmentServiceImpl.java | 41 ++- .../slt/plugin/swank/SlimeListener.java | 10 +- .../slt/plugin/swank/SwankClient.java | 6 +- .../swank/components/SourceLocation.java | 3 +- .../debug/SltDebugStackTraceElement.java | 2 +- .../slt/plugin/ui/SltCoreWindow.java | 3 +- .../slt/plugin/ui/SltGlobalUIService.java | 16 ++ .../slt/plugin/ui/debug/SltDebuggerImpl.java | 28 +- .../ui/sdk/SdkConfigurationABCLProcess.java | 240 ++++++++++++++++++ .../ui/sdk/SdkConfigurationSBCLProcess.java | 8 +- .../slt/plugin/ui/sdk/SdkConfigurer.java | 44 +++- .../slt/templates/VerifyABCLTemplate.java | 20 ++ ...erifyTemplate.java => VerifyTemplate.java} | 6 +- .../com/en_circle/slt/tools/ABCLUtils.java | 79 ++++++ .../com/en_circle/slt/tools/SBCLUtils.java | 4 +- .../tools/platform/DownloadSBCLMsiAction.java | 2 +- src/main/lisp/load.lisp | 4 + src/main/lisp/slt/slt-abcl.lisp | 4 + src/main/lisp/slt/slt.lisp | 4 +- src/main/lisp/swank/swank-abcl.lisp | 25 ++ src/main/lisp/swank/swank-sbcl.lisp | 22 +- src/main/lisp/swank/swank.lisp | 22 +- src/main/resources/META-INF/plugin.xml | 8 +- .../resources/messages/SltBundle.properties | 19 +- .../resources/templates/en_US/initscript.cl | 4 +- .../resources/templates/en_US/verify.abcl.cl | 6 + .../en_US/{sbcl-init.cl => verify.cl} | 2 +- src/test/java/SlimeTest.java | 17 +- 36 files changed, 881 insertions(+), 88 deletions(-) create mode 100644 src/main/java/com/en_circle/slt/plugin/environment/SltABCLEnvironment.java create mode 100644 src/main/java/com/en_circle/slt/plugin/environment/SltABCLEnvironmentConfiguration.java create mode 100644 src/main/java/com/en_circle/slt/plugin/ui/SltGlobalUIService.java create mode 100644 src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationABCLProcess.java create mode 100644 src/main/java/com/en_circle/slt/templates/VerifyABCLTemplate.java rename src/main/java/com/en_circle/slt/templates/{SbclVerifyTemplate.java => VerifyTemplate.java} (72%) create mode 100644 src/main/java/com/en_circle/slt/tools/ABCLUtils.java create mode 100644 src/main/lisp/slt/slt-abcl.lisp create mode 100644 src/main/lisp/swank/swank-abcl.lisp create mode 100644 src/main/resources/templates/en_US/verify.abcl.cl rename src/main/resources/templates/en_US/{sbcl-init.cl => verify.cl} (62%) diff --git a/build.gradle.kts b/build.gradle.kts index 8d8c4ef..ec362d0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,6 +15,7 @@ dependencies { implementation("org.watertemplate:watertemplate-engine:1.2.2") implementation("com.google.guava:guava:31.1-jre") implementation("org.rauschig:jarchivelib:1.2.0") + implementation("org.abcl:abcl:1.8.0") testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.2") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.2") diff --git a/src/main/java/com/en_circle/slt/plugin/environment/Environment.java b/src/main/java/com/en_circle/slt/plugin/environment/Environment.java index 85d6595..9223591 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/Environment.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/Environment.java @@ -3,6 +3,7 @@ import com.en_circle.slt.plugin.SltBundle; import com.en_circle.slt.plugin.environment.SltLispEnvironmentConfiguration.Builder; import com.en_circle.slt.plugin.sdk.LispSdk; +import com.en_circle.slt.plugin.ui.sdk.SdkConfigurationABCLProcess; import com.en_circle.slt.plugin.ui.sdk.SdkConfigurationSBCLProcess; import com.en_circle.slt.plugin.ui.sdk.SdkDialogProvider; import com.en_circle.slt.tools.platform.DownloadLispAction; @@ -13,6 +14,39 @@ public enum Environment { + ABCL_PROCESS(new EnvironmentDefinition() { + @Override + public String getName() { + return SltBundle.message("slt.environment.abcl"); + } + + @Override + public Class getDownloadActionDef() { + return null; + } + + @Override + public Supplier getEnvironmentCreator() { + return SltABCLEnvironment::new; + } + + @SuppressWarnings("unchecked") + @Override + public , R extends SltLispEnvironmentConfiguration> Builder + buildConfiguration(LispSdk sdk, Project project) { + return (Builder) new SltABCLEnvironmentConfiguration.Builder() + .setJvm(sdk.abclJvm) + .setJvmArgs(sdk.abclJvmArgs) + .setAbclJar(sdk.abclJar) + .setQuicklispStartScriptPath(sdk.quickLispPath) + .setProjectDirectory(project.getBasePath()); + } + + @Override + public SdkDialogProvider getDialogProvider() { + return SdkConfigurationABCLProcess::new; + } + }), SBCL_PROCESS(new EnvironmentDefinition() { @Override public String getName() { diff --git a/src/main/java/com/en_circle/slt/plugin/environment/LispInterpret.java b/src/main/java/com/en_circle/slt/plugin/environment/LispInterpret.java index 73d63dc..6bc4939 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/LispInterpret.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/LispInterpret.java @@ -2,6 +2,7 @@ public enum LispInterpret { + ABCL(":abcl"), SBCL(":sbcl") ; diff --git a/src/main/java/com/en_circle/slt/plugin/environment/SltABCLEnvironment.java b/src/main/java/com/en_circle/slt/plugin/environment/SltABCLEnvironment.java new file mode 100644 index 0000000..a068654 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/environment/SltABCLEnvironment.java @@ -0,0 +1,167 @@ +package com.en_circle.slt.plugin.environment; + +import com.en_circle.slt.plugin.SltLibrary; +import com.en_circle.slt.plugin.environment.SltProcessStreamGobbler.ProcessInitializationWaiter; +import com.en_circle.slt.plugin.environment.SltProcessStreamGobbler.WaitForOccurrence; +import com.en_circle.slt.tools.PluginPath; +import com.intellij.openapi.util.io.FileUtil; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; +import org.watertemplate.Template; + +import java.io.File; +import java.io.IOException; +import java.net.ServerSocket; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +public class SltABCLEnvironment extends SltLispEnvironmentProcess { + + private int port; + + @Override + public int getSwankPort() { + return port; + } + + @Override + public SltLispProcessInformation getInformation() { + return new SltABCLLispProcessInformation(); + } + + @Override + protected Object prepareProcessEnvironment(SltLispEnvironmentProcessConfiguration configuration) throws SltProcessException { + SltABCLEnvironmentConfiguration c = getConfiguration(configuration); + ABCLEnvironment e = new ABCLEnvironment(); + try { + e.port = getFreePort(); + if (e.port == 0) { + throw new IOException("no free port available"); + } + + File tempDir = FileUtil.createTempDirectory(PluginPath.getPluginFolder(), + "SLTinit", ""); + + e.sltCore = SltLibrary.getLibraryInitFile(); + e.serverStartSetup = new File(tempDir, "startServer.cl"); + e.serverStartSetup.deleteOnExit(); + String sltCorePath = e.sltCore.getAbsolutePath(); + String startScriptTemplate = new ABCLInitScriptTemplate(c, sltCorePath, e.port).render(); + FileUtils.write(e.serverStartSetup, startScriptTemplate, StandardCharsets.UTF_8); + + tempDir.deleteOnExit(); + } catch (Exception ex) { + throw new SltProcessException(ex); + } + return e; + } + + @Override + protected File getProcessWorkDirectory(SltLispEnvironmentProcessConfiguration configuration, Object environment) throws SltProcessException { + SltABCLEnvironmentConfiguration c = getConfiguration(configuration); + ABCLEnvironment e = getEnvironment(environment); + + return e.serverStartSetup.getParentFile(); + } + + @Override + protected String[] getProcessCommand(SltLispEnvironmentProcessConfiguration configuration, Object environment) throws SltProcessException { + SltABCLEnvironmentConfiguration c = getConfiguration(configuration); + ABCLEnvironment e = getEnvironment(environment); + this.port = e.port; + + List parameters = new ArrayList<>(); + parameters.add(c.getJvmPath()); + if (StringUtils.isNotBlank(c.getJvmArgs())) { + StringTokenizer st = new StringTokenizer(c.getJvmArgs()); + while (st.hasMoreTokens()) + parameters.add(st.nextToken()); + } + parameters.add("-jar"); + parameters.add(c.getAbclJar()); + parameters.add("--load"); + parameters.add(e.serverStartSetup.getName()); + + return parameters.toArray(new String[0]); + } + + @Override + protected ProcessInitializationWaiter waitForFullInitialization(SltLispEnvironmentProcessConfiguration configuration, Object environment) throws SltProcessException { + SltABCLEnvironmentConfiguration c = getConfiguration(configuration); + ABCLEnvironment e = getEnvironment(environment); + + WaitForOccurrence wait = new WaitForOccurrence("Swank started at port"); + outputController.addUpdateListener(wait); + + return wait; + } + + private SltABCLEnvironmentConfiguration getConfiguration(SltLispEnvironmentProcessConfiguration configuration) throws SltProcessException { + if (!(configuration instanceof SltABCLEnvironmentConfiguration)) + throw new SltProcessException("Configuration must be SltABCLEnvironmentConfiguration"); + return (SltABCLEnvironmentConfiguration) configuration; + } + + private ABCLEnvironment getEnvironment(Object environment) { + assert (environment instanceof ABCLEnvironment); + + return (ABCLEnvironment) environment; + } + + private int getFreePort() { + var freePort = 0; + try (ServerSocket s = new ServerSocket(0)) { + freePort = s.getLocalPort(); + } catch (Exception ignored) { + + } + + return freePort; + } + + private static class ABCLEnvironment { + + File sltCore; + File serverStartSetup; + int port; + + } + + private class SltABCLLispProcessInformation implements SltLispProcessInformation { + + @Override + public String getPid() { + return "" + process.pid(); + } + } + + private static class ABCLInitScriptTemplate extends Template { + + public ABCLInitScriptTemplate(SltABCLEnvironmentConfiguration configuration, String sltCoreScript, int port) { + String quicklispPath = configuration.getQuicklispStartScript(); + if (quicklispPath.contains("\\")) { + quicklispPath = StringUtils.replace(quicklispPath, "\\", "\\\\"); + } + String cwd = configuration.getProjectDirectory(); + if (cwd.contains("\\")) { + cwd = StringUtils.replace(cwd, "\\", "\\\\"); + } + if (sltCoreScript.contains("\\")) { + sltCoreScript = StringUtils.replace(sltCoreScript, "\\", "\\\\"); + } + add("qlpath", quicklispPath); + add("port", "" + port); + add("cwd", cwd); + add("corefile", sltCoreScript); + add("interpret", LispInterpret.ABCL.symbolName); + } + + @Override + protected String getFilePath() { + return "initscript.cl"; + } + } + +} diff --git a/src/main/java/com/en_circle/slt/plugin/environment/SltABCLEnvironmentConfiguration.java b/src/main/java/com/en_circle/slt/plugin/environment/SltABCLEnvironmentConfiguration.java new file mode 100644 index 0000000..277a270 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/environment/SltABCLEnvironmentConfiguration.java @@ -0,0 +1,106 @@ +package com.en_circle.slt.plugin.environment; + + +import com.en_circle.slt.plugin.environment.SltLispEnvironment.SltLispOutputChangedListener; +import com.en_circle.slt.plugin.environment.SltLispEnvironmentProcess.SltLispEnvironmentProcessConfiguration; + +public class SltABCLEnvironmentConfiguration implements SltLispEnvironmentProcessConfiguration { + + private String jvmPath = "java"; + private String jvmArgs = ""; + private String abclJar = ""; + private String quicklispStartScript = "~/quicklisp/setup.lisp"; + private String projectDirectory = "/tmp"; + private SltLispOutputChangedListener listener = null; + + private SltABCLEnvironmentConfiguration() { + + } + + public String getJvmPath() { + return jvmPath; + } + + public String getJvmArgs() { + return jvmArgs; + } + + public String getAbclJar() { + return abclJar; + } + + public String getQuicklispStartScript() { + return quicklispStartScript; + } + + public String getProjectDirectory() { + return projectDirectory; + } + + @Override + public SltLispOutputChangedListener getListener() { + return listener; + } + + public static class Builder implements SltLispEnvironmentConfiguration.Builder { + + private final SltABCLEnvironmentConfiguration c = new SltABCLEnvironmentConfiguration(); + private boolean built = false; + + public Builder setJvm(String executable) { + checkNotBuilt(); + + c.jvmPath = executable; + return this; + } + + public Builder setJvmArgs(String args) { + checkNotBuilt(); + + c.jvmArgs = args; + return this; + } + + public Builder setAbclJar(String jar) { + checkNotBuilt(); + + c.abclJar = jar; + return this; + } + + public Builder setQuicklispStartScriptPath(String quicklispStartScript) { + checkNotBuilt(); + + c.quicklispStartScript = quicklispStartScript; + return this; + } + + public Builder setProjectDirectory(String projectDirectory) { + checkNotBuilt(); + + c.projectDirectory = projectDirectory; + return this; + } + + @Override + public Builder setListener(SltLispOutputChangedListener listener) { + checkNotBuilt(); + + c.listener = listener; + return this; + } + + @Override + public SltABCLEnvironmentConfiguration build() { + checkNotBuilt(); + built = true; + return c; + } + + private void checkNotBuilt() { + if (built) throw new IllegalStateException("already built"); + } + + } + +} diff --git a/src/main/java/com/en_circle/slt/plugin/environment/SltProcessStreamGobbler.java b/src/main/java/com/en_circle/slt/plugin/environment/SltProcessStreamGobbler.java index c67d1e4..25cc926 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/SltProcessStreamGobbler.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/SltProcessStreamGobbler.java @@ -90,7 +90,7 @@ public WaitForOccurrence(String occurrence) { } public boolean awaitFor(Process process) { - return awaitFor(process, null, 10L, TimeUnit.SECONDS); + return awaitFor(process, null, 60L, TimeUnit.SECONDS); } public boolean awaitFor(Process process, SltProcessStreamGobbler gobbler, long time, TimeUnit unit) { diff --git a/src/main/java/com/en_circle/slt/plugin/environment/SltSBCLEnvironment.java b/src/main/java/com/en_circle/slt/plugin/environment/SltSBCLEnvironment.java index 3b1c8f5..11f47eb 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/SltSBCLEnvironment.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/SltSBCLEnvironment.java @@ -152,7 +152,7 @@ public SBCLInitScriptTemplate(SltSBCLEnvironmentConfiguration configuration, Str add("qlpath", quicklispPath); add("port", "" + port); add("cwd", cwd); - add("sbclcorefile", sltCoreScript); + add("corefile", sltCoreScript); add("interpret", LispInterpret.SBCL.symbolName); } diff --git a/src/main/java/com/en_circle/slt/plugin/sdk/LispSdk.java b/src/main/java/com/en_circle/slt/plugin/sdk/LispSdk.java index 2ecb57f..5d8b0d8 100644 --- a/src/main/java/com/en_circle/slt/plugin/sdk/LispSdk.java +++ b/src/main/java/com/en_circle/slt/plugin/sdk/LispSdk.java @@ -13,11 +13,16 @@ public class LispSdk implements PersistentStateComponent { // use getter! public Environment environment; + public String quickLispPath; // SBCL Process used public String sbclExecutable; public String sbclCorePath; - public String quickLispPath; + + // ABCL Process used + public String abclJvm; + public String abclJvmArgs; + public String abclJar; public Environment getEnvironment() { if (environment == null) { diff --git a/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentServiceImpl.java b/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentServiceImpl.java index 643495f..e999799 100644 --- a/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentServiceImpl.java +++ b/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentServiceImpl.java @@ -29,12 +29,14 @@ import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; +import com.intellij.util.ExceptionUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; public class LispEnvironmentServiceImpl implements LispEnvironmentService { @@ -107,13 +109,14 @@ public void setDebugInterface(DebugInterface debugInterface) { @Override public void start() { starting = true; - ApplicationManager.getApplication().invokeLater(() -> { + ApplicationManager.getApplication().invokeAndWait(this::ensureToolWindowIsOpen); + ApplicationManager.getApplication().executeOnPooledThread(() -> { try { - ensureToolWindowIsOpen(); doStart(); } catch (Exception e) { log.warn(SltBundle.message("slt.error.start"), e); - Messages.showErrorDialog(project, e.getMessage(), SltBundle.message("slt.ui.errors.lisp.start")); + ApplicationManager.getApplication().invokeLater(() -> + Messages.showErrorDialog(project, e.getMessage(), SltBundle.message("slt.ui.errors.lisp.start"))); } }); } @@ -138,12 +141,19 @@ public void stop() { private boolean doStart() throws Exception { try { if (configurationBuilder == null) { - if (!configured()) { - log.warn(SltBundle.message("slt.error.start")); - Messages.showErrorDialog(project, - SltBundle.message("slt.ui.errors.lisp.start.noconf"), - SltBundle.message("slt.ui.errors.lisp.start")); - return false; + AtomicBoolean state = new AtomicBoolean(false); + ApplicationManager.getApplication().invokeAndWait(() -> { + if (!configured()) { + log.warn(SltBundle.message("slt.error.start")); + Messages.showErrorDialog(project, + SltBundle.message("slt.ui.errors.lisp.start.noconf"), + SltBundle.message("slt.ui.errors.lisp.start")); + return; + } + state.set(true); + }); + if (!state.get()) { + return state.get(); } } @@ -159,15 +169,22 @@ private boolean doStart() throws Exception { environment = environmentProvider.get(); environment.start(configuration); - slimeListener = new SlimeListener(project, true, logger, debugInterface); + slimeListener = new SlimeListener(project, true, e -> { + for (LispEnvironmentListener listener : serverListeners) { + String text = ExceptionUtil.getThrowableText(e); + listener.onOutputChanged(SltOutput.STDERR, text); + } + }, logger, debugInterface); client = new SwankClient("127.0.0.1", environment.getSwankPort(), slimeListener); for (LispEnvironmentListener listener : serverListeners) { listener.onPostStart(); } - ParameterHintsPassFactory.forceHintsUpdateOnNextPass(); - DaemonCodeAnalyzer.getInstance(project).restart(); + ApplicationManager.getApplication().invokeLaterOnWriteThread(() -> { + ParameterHintsPassFactory.forceHintsUpdateOnNextPass(); + DaemonCodeAnalyzer.getInstance(project).restart(); + }); } finally { starting = false; } diff --git a/src/main/java/com/en_circle/slt/plugin/swank/SlimeListener.java b/src/main/java/com/en_circle/slt/plugin/swank/SlimeListener.java index 17da9df..af87694 100644 --- a/src/main/java/com/en_circle/slt/plugin/swank/SlimeListener.java +++ b/src/main/java/com/en_circle/slt/plugin/swank/SlimeListener.java @@ -15,6 +15,7 @@ import java.math.BigInteger; import java.util.*; +import java.util.function.Consumer; public class SlimeListener implements SwankClient.SwankReply { @@ -29,12 +30,14 @@ public static synchronized BigInteger nextRpc() { private final Project project; private final boolean fromUi; private final Map requests = Collections.synchronizedMap(new HashMap<>()); + private final Consumer onReadFailure; private final RequestResponseLogger logger; private final DebugInterface debugInterface; - public SlimeListener(Project project, boolean fromUi, RequestResponseLogger logger, DebugInterface debugInterface) { + public SlimeListener(Project project, boolean fromUi, Consumer onReadFailure, RequestResponseLogger logger, DebugInterface debugInterface) { this.project = project; this.fromUi = fromUi; + this.onReadFailure = onReadFailure; this.logger = logger; this.debugInterface = debugInterface; } @@ -61,6 +64,11 @@ public void onSwankMessage(SwankPacket packet) { } } + @Override + public void onReadError(Exception e) { + + } + private void resolve(String data) { if (logger != null) { logger.logResponse(data); diff --git a/src/main/java/com/en_circle/slt/plugin/swank/SwankClient.java b/src/main/java/com/en_circle/slt/plugin/swank/SwankClient.java index 18ea5a2..12abc25 100644 --- a/src/main/java/com/en_circle/slt/plugin/swank/SwankClient.java +++ b/src/main/java/com/en_circle/slt/plugin/swank/SwankClient.java @@ -86,20 +86,22 @@ public void run() { SwankPacket packet = SwankPacket.fromInput(is); callback.onSwankMessage(packet); } catch (ConnectException | InterruptedException e) { - e.printStackTrace(); + callback.onReadError(e); return; } catch (IOException e) { if (e instanceof EOFException) { return; } } catch (Exception e) { - e.printStackTrace(); + callback.onReadError(e); } } } public interface SwankReply { void onSwankMessage(SwankPacket packet); + + void onReadError(Exception e); } } diff --git a/src/main/java/com/en_circle/slt/plugin/swank/components/SourceLocation.java b/src/main/java/com/en_circle/slt/plugin/swank/components/SourceLocation.java index ae1a490..c364ddd 100644 --- a/src/main/java/com/en_circle/slt/plugin/swank/components/SourceLocation.java +++ b/src/main/java/com/en_circle/slt/plugin/swank/components/SourceLocation.java @@ -13,8 +13,9 @@ public SourceLocation() { } - public SourceLocation(LispContainer src) { + public SourceLocation(LispElement srcElement) { try { + LispContainer src = (LispContainer) srcElement; if (src.getItems().get(0).equals(new LispSymbol(":location"))) { for (int i=1; i= 0) { + if (element.isFile()) { Project project = parent.getProject(); ProjectFileIndex index = ProjectFileIndex.getInstance(project); VirtualFile vf = LocalFileSystem.getInstance().findFileByIoFile(new File(element.getLocation())); if (vf != null) { if (index.isInSource(vf)) { FileEditorManager.getInstance(project) - .openTextEditor(new OpenFileDescriptor(project, vf, element.getPosition()), true); + .openTextEditor(new OpenFileDescriptor(project, vf, + element.getPosition() >= 0 ? element.getPosition() : -1), true); } else { FileEditorManager.getInstance(project) - .openEditor(new OpenFileDescriptor(project, vf, element.getPosition()), true); + .openEditor(new OpenFileDescriptor(project, vf, + element.getPosition() >= 0 ? element.getPosition() : -1), true); } } } diff --git a/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationABCLProcess.java b/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationABCLProcess.java new file mode 100644 index 0000000..451f815 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationABCLProcess.java @@ -0,0 +1,240 @@ +package com.en_circle.slt.plugin.ui.sdk; + +import com.en_circle.slt.plugin.SltBundle; +import com.en_circle.slt.plugin.sdk.LispSdk; +import com.en_circle.slt.plugin.ui.SltGlobalUIService; +import com.en_circle.slt.plugin.ui.sdk.SdkDialogProvider.OnSave; +import com.en_circle.slt.tools.ABCLUtils; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.fileChooser.FileChooserDescriptor; +import com.intellij.openapi.fileChooser.FileChooserFactory; +import com.intellij.openapi.fileChooser.FileTextField; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.impl.ProgressResult; +import com.intellij.openapi.progress.impl.ProgressRunner; +import com.intellij.openapi.progress.impl.ProgressRunner.ThreadToUse; +import com.intellij.openapi.progress.util.ProgressWindow; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.ui.TextFieldWithBrowseButton; +import com.intellij.openapi.util.Disposer; +import com.intellij.ui.components.JBTextField; +import com.intellij.util.ui.FormBuilder; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.event.CaretListener; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.io.File; + +public class SdkConfigurationABCLProcess extends DialogWrapper { + + private final Disposable parentDisposable; + private final LispSdk instance; + private final OnSave onSave; + private boolean isVerified = false; + + private JBTextField name; + private FileTextField jvmExecutable; + private JBTextField jvmArguments; + private FileTextField jar; + private FileTextField quicklispPath; + + public SdkConfigurationABCLProcess(@NotNull Component parent, LispSdk instance, String title, OnSave onSave) { + super(parent, true); + + this.parentDisposable = SltGlobalUIService.getInstance(); + this.instance = instance; + this.onSave = onSave; + + setTitle(title); + setSize(700, 0); + init(); + } + + @Override + protected @Nullable JComponent createCenterPanel() { + name = new JBTextField(); + name.addCaretListener(createChangeListener()); + name.setText(instance.userName); + if (StringUtils.isBlank(instance.userName)) { + name.setText(SltBundle.message("slt.ui.settings.sdk.editor.name.abcl.default")); + } + + FileChooserDescriptor descriptor = new FileChooserDescriptor(true, false, false, + false, false, false); + FileChooserDescriptor descriptorJar = new FileChooserDescriptor(true, false, true, + true, false, false); + + jvmExecutable = FileChooserFactory.getInstance() + .createFileTextField(descriptor, true, parentDisposable); + jvmExecutable.getField().addCaretListener(createChangeListener()); + jvmExecutable.getField().setText(instance.abclJvm); + jvmArguments = new JBTextField(); + jvmArguments.addCaretListener(createChangeListener()); + jvmArguments.setText(instance.abclJvmArgs); + jar = FileChooserFactory.getInstance() + .createFileTextField(descriptorJar, true, parentDisposable); + jar.getField().addCaretListener(createChangeListener()); + jar.getField().setText(instance.abclJar); + quicklispPath = FileChooserFactory.getInstance() + .createFileTextField(descriptor, true, parentDisposable); + quicklispPath.getField().addCaretListener(createChangeListener()); + quicklispPath.getField().setText(instance.quickLispPath); + + TextFieldWithBrowseButton abclExecutablePicker = new TextFieldWithBrowseButton(jvmExecutable.getField()); + abclExecutablePicker.addBrowseFolderListener( + SltBundle.message("slt.ui.settings.sdk.editor.jvm.select"), "", null, descriptor); + TextFieldWithBrowseButton abclJarPicker = new TextFieldWithBrowseButton(jar.getField()); + abclJarPicker.addBrowseFolderListener( + SltBundle.message("slt.ui.settings.sdk.editor.abcl.select"), "", null, descriptorJar); + TextFieldWithBrowseButton quicklispPathPicker = new TextFieldWithBrowseButton(quicklispPath.getField()); + //noinspection DialogTitleCapitalization + quicklispPathPicker.addBrowseFolderListener( + SltBundle.message("slt.ui.settings.sdk.editor.quicklisp.select"), "", null, descriptor); + + return new FormBuilder() + .addLabeledComponent(SltBundle.message("slt.ui.settings.sdk.editor.name"), name, 1, false) + .addLabeledComponent(SltBundle.message("slt.ui.settings.sdk.editor.abcl.jvm.executable"), + abclExecutablePicker, 1, false) + .addLabeledComponent(SltBundle.message("slt.ui.settings.sdk.editor.abcl.jvm.args"), + jvmArguments, 1, false) + .addLabeledComponent(SltBundle.message("slt.ui.settings.sdk.editor.abcl.jar"), + abclJarPicker, 1, false) + .addLabeledComponent(SltBundle.message("slt.ui.settings.sdk.editor.quicklisp"), + quicklispPathPicker, 1, false) + .addComponentFillVertically(new JPanel(), 0) + .getPanel(); + } + + private CaretListener createChangeListener() { + return e -> isVerified = false; + } + + private void verifySdk() { + name.putClientProperty("JComponent.outline", null); + jvmExecutable.getField().putClientProperty("JComponent.outline", null); + jar.getField().putClientProperty("JComponent.outline", null); + quicklispPath.getField().putClientProperty("JComponent.outline", null); + + boolean verified = true; + + if (StringUtils.isBlank(name.getText())) { + verified = false; + name.putClientProperty("JComponent.outline", "error"); + } + + String jvm = jvmExecutable.getField().getText(); + if (StringUtils.isBlank(jvm)) { + verified = false; + jvmExecutable.getField().putClientProperty("JComponent.outline", "error"); + } + + String jvmArgs = jvmArguments.getText(); + + String jar = this.jar.getField().getText(); + if (StringUtils.isNotBlank(jar)) { + File file = new File(jar); + if (!file.exists()) { + verified = false; + this.jar.getField().putClientProperty("JComponent.outline", "error"); + } + } + + String quicklisp = quicklispPath.getField().getText(); + if (StringUtils.isBlank(quicklisp)) { + verified = false; + quicklispPath.getField().putClientProperty("JComponent.outline", "error"); + } else { + File qlFile = new File(quicklisp); + if (!qlFile.exists() || !qlFile.isFile()) { + verified = false; + quicklispPath.getField().putClientProperty("JComponent.outline", "error"); + } + } + + name.repaint(); + jvmExecutable.getField().repaint(); + this.jar.getField().repaint(); + quicklispPath.getField().repaint(); + + if (verified) + verified = checkAndLoadAbclCore(jvm, jvmArgs, jar, quicklisp); + if (!verified) { + Messages.showErrorDialog(SltBundle.message("slt.ui.settings.sdk.editor.abcl.process.verifying.error"), + SltBundle.message("slt.ui.settings.sdk.editor.verifying.error.title")); + } + + isVerified = verified; + } + + private boolean checkAndLoadAbclCore(String jvm, String jvmArgs, String jar, String quicklisp) { + ProgressWindow verifyWindow = new ProgressWindow(true, false, null, + getRootPane(), SltBundle.message("slt.ui.settings.sdk.editor.verifying.cancel")); + verifyWindow.setTitle(SltBundle.message("slt.ui.settings.sdk.editor.verifying.abcl")); + Disposer.register(parentDisposable, verifyWindow); + + ProgressResult result = new ProgressRunner<>(pi -> verifyAbcl(pi, jvm, jvmArgs, jar, quicklisp)) + .sync() + .onThread(ThreadToUse.POOLED) + .withProgress(verifyWindow) + .modal() + .submitAndGet(); + return Boolean.TRUE.equals(result.getResult()); + } + + private boolean verifyAbcl(ProgressIndicator pi, String jvm, String jvmArgs, String jar, String quicklisp) { + return ABCLUtils.verifyAndInstallDependencies(jvm, jvmArgs, jar, quicklisp, pi); + } + + private void save() { + instance.userName = name.getText(); + instance.abclJvm = jvmExecutable.getField().getText(); + instance.abclJvmArgs = jvmArguments.getText(); + instance.abclJar = jar.getField().getText(); + instance.quickLispPath = quicklispPath.getField().getText(); + onSave.saveAction(instance); + close(0); + } + + @Override + protected Action @NotNull [] createActions() { + return new Action[] { + new VerifyAction(), new SaveAction() + }; + } + + public class VerifyAction extends AbstractAction { + + public VerifyAction() { + super(SltBundle.message("slt.ui.settings.sdk.editor.abcl.process.verify")); + } + + @Override + public void actionPerformed(ActionEvent e) { + verifySdk(); + } + } + + public class SaveAction extends AbstractAction { + + public SaveAction() { + super(SltBundle.message("slt.ui.settings.sdk.editor.save")); + } + + @Override + public void actionPerformed(ActionEvent e) { + if (!isVerified) { + Messages.showInfoMessage(SltBundle.message("slt.ui.settings.sdk.editor.notverified.title"), + SltBundle.message("slt.ui.settings.sdk.editor.abcl.process.notverified.message")); + return; + } + + save(); + } + } + +} diff --git a/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationSBCLProcess.java b/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationSBCLProcess.java index 8e09ef1..844f43e 100644 --- a/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationSBCLProcess.java +++ b/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationSBCLProcess.java @@ -2,6 +2,7 @@ import com.en_circle.slt.plugin.SltBundle; import com.en_circle.slt.plugin.sdk.LispSdk; +import com.en_circle.slt.plugin.ui.SltGlobalUIService; import com.en_circle.slt.plugin.ui.sdk.SdkDialogProvider.OnSave; import com.en_circle.slt.tools.SBCLUtils; import com.intellij.openapi.Disposable; @@ -28,7 +29,6 @@ import java.awt.*; import java.awt.event.ActionEvent; import java.io.File; -import java.util.Objects; public class SdkConfigurationSBCLProcess extends DialogWrapper { @@ -45,7 +45,7 @@ public class SdkConfigurationSBCLProcess extends DialogWrapper { public SdkConfigurationSBCLProcess(@NotNull Component parent, LispSdk instance, String title, OnSave onSave) { super(parent, true); - this.parentDisposable = Objects.requireNonNull(DialogWrapper.findInstanceFromFocus()).getDisposable(); + this.parentDisposable = SltGlobalUIService.getInstance(); this.instance = instance; this.onSave = onSave; @@ -60,7 +60,7 @@ public SdkConfigurationSBCLProcess(@NotNull Component parent, LispSdk instance, name.addCaretListener(createChangeListener()); name.setText(instance.userName); if (StringUtils.isBlank(instance.userName)) { - name.setText(SltBundle.message("slt.ui.settings.sdk.editor.name.default")); + name.setText(SltBundle.message("slt.ui.settings.sdk.editor.name.sbcl.default")); } FileChooserDescriptor descriptor = new FileChooserDescriptor(true, false, false, @@ -164,7 +164,7 @@ private void verifySdk() { private boolean checkAndLoadSbclCore(String executable, String core, String quicklisp) { ProgressWindow verifyWindow = new ProgressWindow(true, false, null, getRootPane(), SltBundle.message("slt.ui.settings.sdk.editor.verifying.cancel")); - verifyWindow.setTitle(SltBundle.message("slt.ui.settings.sdk.editor.verifying")); + verifyWindow.setTitle(SltBundle.message("slt.ui.settings.sdk.editor.verifying.sbcl")); Disposer.register(parentDisposable, verifyWindow); ProgressResult result = new ProgressRunner<>(pi -> verifySbcl(pi, executable, core, quicklisp)) diff --git a/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurer.java b/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurer.java index b5fd712..cff217c 100644 --- a/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurer.java +++ b/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurer.java @@ -14,6 +14,7 @@ import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.ui.SelectFromListDialog; import com.intellij.openapi.util.NlsContexts.ConfigurableName; import com.intellij.ui.components.JBScrollPane; import com.intellij.ui.table.JBTable; @@ -102,8 +103,11 @@ private void addSdk() { newSdk.uuid = UUID.randomUUID().toString(); - // TODO: Environment selector - Environment environment = Environment.SBCL_PROCESS; + Environment environment = selectEnvironment(); + if (environment == null) { + return; + } + DialogWrapper configuration = environment.getDefinition().getDialogProvider().createSdkConfiguration( root, newSdk, SltBundle.message("slt.ui.settings.sdk.editor.title.new"), sdk -> { @@ -120,8 +124,7 @@ private void editSdk() { LispSdk copy = new LispSdk(); copy.loadState(sdk); - // TODO: Environment selector - Environment environment = Environment.SBCL_PROCESS; + Environment environment = copy.environment; DialogWrapper configuration = environment.getDefinition().getDialogProvider().createSdkConfiguration( root, copy, SltBundle.message("slt.ui.settings.sdk.editor.title.edit"), sdkModified -> { @@ -145,8 +148,17 @@ private void removeSdk() { @SuppressWarnings("IncorrectParentDisposable") private void downloadSdk() { - // TODO: Use Environment - Environment environment = Environment.SBCL_PROCESS; + List downloadable = new ArrayList<>(); + for (Environment environment : Environment.values()) { + if (environment.getDefinition().getDownloadActionDef() != null) { + downloadable.add(environment); + } + } + + Environment environment = selectEnvironment(downloadable.toArray(new Environment[0])); + if (environment == null) { + return; + } Objects.requireNonNull(PlatformActionsContainer.getAction(environment.getDefinition().getDownloadActionDef())) .downloadSdk(ApplicationManager.getApplication(), root, @@ -163,6 +175,26 @@ private void downloadSdk() { }); } + private Environment selectEnvironment() { + return selectEnvironment(Environment.values()); + } + + private Environment selectEnvironment(Environment[] environments) { + + SelectFromListDialog selectFromListDialog = new SelectFromListDialog(null, + environments, e -> ((Environment)e).getDefinition().getName(), + SltBundle.message("slt.ui.settings.sdk.select"), + ListSelectionModel.SINGLE_SELECTION); + selectFromListDialog.setSize(450, 250); + if (selectFromListDialog.showAndGet()) { + Object[] selection = selectFromListDialog.getSelection(); + if (selection.length > 0) { + return (Environment) selection[0]; + } + } + return null; + } + private class EditSdkAction extends AnAction { public EditSdkAction() { diff --git a/src/main/java/com/en_circle/slt/templates/VerifyABCLTemplate.java b/src/main/java/com/en_circle/slt/templates/VerifyABCLTemplate.java new file mode 100644 index 0000000..0b40ac3 --- /dev/null +++ b/src/main/java/com/en_circle/slt/templates/VerifyABCLTemplate.java @@ -0,0 +1,20 @@ +package com.en_circle.slt.templates; + +import org.apache.commons.lang3.StringUtils; +import org.watertemplate.Template; + +public class VerifyABCLTemplate extends Template { + + public VerifyABCLTemplate(String quicklispPath) { + if (quicklispPath.contains("\\")) { + quicklispPath = StringUtils.replace(quicklispPath, "\\", "\\\\"); + } + add("qlpath", quicklispPath); + } + + @Override + protected String getFilePath() { + return "verify.abcl.cl"; + } + +} diff --git a/src/main/java/com/en_circle/slt/templates/SbclVerifyTemplate.java b/src/main/java/com/en_circle/slt/templates/VerifyTemplate.java similarity index 72% rename from src/main/java/com/en_circle/slt/templates/SbclVerifyTemplate.java rename to src/main/java/com/en_circle/slt/templates/VerifyTemplate.java index 2b019ec..c3601a3 100644 --- a/src/main/java/com/en_circle/slt/templates/SbclVerifyTemplate.java +++ b/src/main/java/com/en_circle/slt/templates/VerifyTemplate.java @@ -3,9 +3,9 @@ import org.apache.commons.lang3.StringUtils; import org.watertemplate.Template; -public class SbclVerifyTemplate extends Template { +public class VerifyTemplate extends Template { - public SbclVerifyTemplate(String quicklispPath) { + public VerifyTemplate(String quicklispPath) { if (quicklispPath.contains("\\")) { quicklispPath = StringUtils.replace(quicklispPath, "\\", "\\\\"); } @@ -14,7 +14,7 @@ public SbclVerifyTemplate(String quicklispPath) { @Override protected String getFilePath() { - return "sbcl-init.cl"; + return "verify.cl"; } } diff --git a/src/main/java/com/en_circle/slt/tools/ABCLUtils.java b/src/main/java/com/en_circle/slt/tools/ABCLUtils.java new file mode 100644 index 0000000..6f827f8 --- /dev/null +++ b/src/main/java/com/en_circle/slt/tools/ABCLUtils.java @@ -0,0 +1,79 @@ +package com.en_circle.slt.tools; + +import com.en_circle.slt.plugin.environment.SltProcessStreamGobbler; +import com.en_circle.slt.plugin.environment.SltProcessStreamGobbler.WaitForOccurrence; +import com.en_circle.slt.templates.VerifyABCLTemplate; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.util.io.FileUtil; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; +import java.util.concurrent.TimeUnit; + +public class ABCLUtils { + + @SuppressWarnings("ResultOfMethodCallIgnored") + public static boolean verifyAndInstallDependencies(String jvm, String jvmArgs, String jar, String quicklisp, ProgressIndicator pi) { + try { + List args = new ArrayList<>(); + args.add(jvm); + if (StringUtils.isNotBlank(jvmArgs)) { + StringTokenizer st = new StringTokenizer(jvmArgs); + while (st.hasMoreTokens()) + args.add(st.nextToken()); + } + args.add("-jar"); + args.add(jar); + + File tempTestFile = FileUtil.createTempFile("testABCL", ".cl"); + if (tempTestFile.exists()) + tempTestFile.delete(); + FileUtils.writeStringToFile(tempTestFile, new VerifyABCLTemplate(quicklisp).render(), StandardCharsets.UTF_8); + tempTestFile.deleteOnExit(); + + args.add("--load"); + args.add(tempTestFile.getAbsolutePath()); + + try { + ProcessBuilder processBuilder = new ProcessBuilder(args.toArray(new String[0])); + Process process = processBuilder.start(); + + StringBuilder returnValue = new StringBuilder(); + StringBuilder textValue = new StringBuilder(); + SltProcessStreamGobbler errorController = new SltProcessStreamGobbler(process.getErrorStream()); + SltProcessStreamGobbler outputController = new SltProcessStreamGobbler(process.getInputStream()); + errorController.addUpdateListener(returnValue::append); + outputController.addUpdateListener(textValue::append); + WaitForOccurrence waiter = new WaitForOccurrence("SltVerified"); + outputController.addUpdateListener(waiter); + errorController.start(); + outputController.start(); + if (!waiter.awaitFor(null, outputController, 10, TimeUnit.MINUTES, pi::isCanceled)) { + if (process.isAlive()) + process.destroy(); + + errorController.join(); + outputController.join(); + return false; + } + + if (process.isAlive()) + process.destroy(); + + errorController.join(); + outputController.join(); + return textValue.toString().contains("SltVerified"); + } finally { + tempTestFile.delete(); + } + } catch (Exception ignored) { + return false; + } + } + +} diff --git a/src/main/java/com/en_circle/slt/tools/SBCLUtils.java b/src/main/java/com/en_circle/slt/tools/SBCLUtils.java index dd4bdac..87c2647 100644 --- a/src/main/java/com/en_circle/slt/tools/SBCLUtils.java +++ b/src/main/java/com/en_circle/slt/tools/SBCLUtils.java @@ -2,7 +2,7 @@ import com.en_circle.slt.plugin.environment.SltProcessStreamGobbler; import com.en_circle.slt.plugin.environment.SltProcessStreamGobbler.WaitForOccurrence; -import com.en_circle.slt.templates.SbclVerifyTemplate; +import com.en_circle.slt.templates.VerifyTemplate; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.util.io.FileUtil; import org.apache.commons.io.FileUtils; @@ -30,7 +30,7 @@ public static boolean verifyAndInstallDependencies(String executable, String cor File tempTestFile = FileUtil.createTempFile("testSBCL", ".cl"); if (tempTestFile.exists()) tempTestFile.delete(); - FileUtils.writeStringToFile(tempTestFile, new SbclVerifyTemplate(quicklisp).render(), StandardCharsets.UTF_8); + FileUtils.writeStringToFile(tempTestFile, new VerifyTemplate(quicklisp).render(), StandardCharsets.UTF_8); tempTestFile.deleteOnExit(); args.add("--load"); diff --git a/src/main/java/com/en_circle/slt/tools/platform/DownloadSBCLMsiAction.java b/src/main/java/com/en_circle/slt/tools/platform/DownloadSBCLMsiAction.java index 0d956d4..233aefc 100644 --- a/src/main/java/com/en_circle/slt/tools/platform/DownloadSBCLMsiAction.java +++ b/src/main/java/com/en_circle/slt/tools/platform/DownloadSBCLMsiAction.java @@ -214,7 +214,7 @@ private String installQuicklisp(String uuidInstall, SBCLInstallation installatio private void verifySBCL(String executable, String core, String quicklisp, String installUuid) { ProgressWindow verifyWindow = new ProgressWindow(true, false, null, rootPane, SltBundle.message("slt.ui.settings.sdk.editor.verifying.cancel")); - verifyWindow.setTitle(SltBundle.message("slt.ui.settings.sdk.editor.verifying")); + verifyWindow.setTitle(SltBundle.message("slt.ui.settings.sdk.editor.verifying.sbcl")); Disposer.register(parentDisposable, verifyWindow); ProgressResult result = new ProgressRunner<>(pi -> verifySbcl(pi, executable, core, quicklisp)) diff --git a/src/main/lisp/load.lisp b/src/main/lisp/load.lisp index 610a99c..be3d598 100644 --- a/src/main/lisp/load.lisp +++ b/src/main/lisp/load.lisp @@ -28,6 +28,10 @@ (lisp-implementation-type)) (format *error-output* "Invalid lisp instance. Maybe a configuration error? This is not SBCL!~%") (portable-quit 1))) + (:abcl (unless (string= "Armed Bear Common Lisp" + (lisp-implementation-type)) + (format *error-output* "Invalid lisp instance. Maybe a configuration error? This is not ABCL!~%") + (portable-quit 1))) (otherwise (format *error-output* "Unsupported lisp instance. Maybe a configuration error?~%") (portable-quit 1))) diff --git a/src/main/lisp/slt/slt-abcl.lisp b/src/main/lisp/slt/slt-abcl.lisp new file mode 100644 index 0000000..82daa99 --- /dev/null +++ b/src/main/lisp/slt/slt-abcl.lisp @@ -0,0 +1,4 @@ +(in-package :slt-core) + +(defun specialp (test-sym) + NIL) \ No newline at end of file diff --git a/src/main/lisp/slt/slt.lisp b/src/main/lisp/slt/slt.lisp index a4a9f10..060c00d 100644 --- a/src/main/lisp/slt/slt.lisp +++ b/src/main/lisp/slt/slt.lisp @@ -6,6 +6,8 @@ (when (eq slt:+slt-interpret+ :sbcl) (load (merge-pathnames "slt-sbcl.lisp" *load-truename*))) +(when (eq slt:+slt-interpret+ :abcl) + (load (merge-pathnames "slt-abcl.lisp" *load-truename*))) (in-package :slt-core) @@ -82,7 +84,7 @@ (ECLECTOR.READER:TWO-PACKAGE-MARKERS-MUST-BE-ADJACENT #'reader-recover) (error (lambda (c) - (format *error-output* "general error: ~S ~%" c)))) + (format *error-output* "general error: ~A ~%" c)))) (eclector.reader:read-from-string str))) (defun list-package-names () diff --git a/src/main/lisp/swank/swank-abcl.lisp b/src/main/lisp/swank/swank-abcl.lisp new file mode 100644 index 0000000..cf049ee --- /dev/null +++ b/src/main/lisp/swank/swank-abcl.lisp @@ -0,0 +1,25 @@ +(in-package :swank/abcl) + +(defmethod source-location ((func (eql :ANONYMOUS-INTERPRETED-FUNCTION))) + NIL) + +(in-package :swank) + +(defslimefun backtrace (start end) + (loop for frame in (compute-backtrace start end) + for i from start collect + (list i (frame-to-string frame) + (format NIL "~A" (print-frame-call-place frame)) + (handler-case + (frame-source-location i) + (error (c) (format t "Error: ~A~%" c) + NIL)) + NIL))) + +(defun print-frame-call-place (frame) + (handler-case + (if (typep frame 'sys::lisp-stack-frame) + (first (sys::frame-to-list frame)) + (sys::frame-to-string frame)) + (error (c) + (format t "Error: ~A~%" c)))) \ No newline at end of file diff --git a/src/main/lisp/swank/swank-sbcl.lisp b/src/main/lisp/swank/swank-sbcl.lisp index 6379d61..8d5c30e 100644 --- a/src/main/lisp/swank/swank-sbcl.lisp +++ b/src/main/lisp/swank/swank-sbcl.lisp @@ -1,10 +1,3 @@ -(in-package sb-debug) - -(export 'frame-code-location) -(export 'frame-call) -(export 'ensure-printable-object) -(export 'code-location-source-form) - (in-package :swank/sbcl) (defun form-number-position (definition-source stream) @@ -34,9 +27,20 @@ (in-package :swank) +(defslimefun backtrace (start end) + (loop for frame in (compute-backtrace start end) + for i from start collect + (list i (frame-to-string frame) + (format NIL "~A" (print-frame-call-place frame)) + (frame-source-location i) + (let ((pkg (frame-package i))) + (cond + (pkg (package-name pkg)) + (T NIL)))))) + (defun print-frame-call-place (frame) (multiple-value-bind (name args info) - (SB-DEBUG:frame-call frame) + (sb-debug::frame-call frame) (declare (ignore args info)) - (let ((name (SB-DEBUG:ensure-printable-object name))) + (let ((name (sb-debug::ensure-printable-object name))) name))) \ No newline at end of file diff --git a/src/main/lisp/swank/swank.lisp b/src/main/lisp/swank/swank.lisp index d0d0b70..a0b947e 100644 --- a/src/main/lisp/swank/swank.lisp +++ b/src/main/lisp/swank/swank.lisp @@ -1,10 +1,11 @@ (load (merge-pathnames "swank-backend.lisp" *load-truename*)) -(when (eq +slt-interpret+ :sbcl) - (load (merge-pathnames "swank-sbcl.lisp" *load-truename*))) - -(in-package swank/source-file-cache) -(setf *SOURCE-SNIPPET-SIZE* 0) +(when (eq +slt-interpret+ :sbcl) + (load (merge-pathnames "swank-sbcl.lisp" *load-truename*)) + (in-package swank/source-file-cache) + (setf *source-snippet-size* 0)) +(when (eq +slt-interpret+ :abcl) + (load (merge-pathnames "swank-abcl.lisp" *load-truename*))) (in-package :swank) @@ -43,17 +44,6 @@ format suitable for Emacs." (princ restart stream))) (swank-backend:arglist (slot-value restart 'function)))))) -(defslimefun backtrace (start end) - (loop for frame in (compute-backtrace start end) - for i from start collect - (list i (frame-to-string frame) - (format NIL "~S" (print-frame-call-place frame)) - (frame-source-location i) - (let ((pkg (frame-package i))) - (cond - (pkg (package-name pkg)) - (T NIL)))))) - (defslimefun compile-string-region-slt (string buffer offset filename package) (with-buffer-syntax () (collect-notes diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 9ff64ce..aa9e7a2 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -43,6 +43,8 @@ + + @@ -150,12 +152,6 @@ class="com.en_circle.slt.plugin.actions.EvalFile"> - - - - - - { - listener.onSwankMessage(packet); - expected.addAndGet(1); + try (SwankClient client = new SwankClient("127.0.0.1", 4005, new SwankReply() { + @Override + public void onSwankMessage(SwankPacket packet) { + listener.onSwankMessage(packet); + expected.addAndGet(1); + } + + @Override + public void onReadError(Exception e) { + e.printStackTrace(); + } })) { sent.addAndGet(1); client.swankSend(SwankPacket.sltEval("(+ + 5)", new BigInteger("3"))); From 8aa6b38c6fff0e7aedf62e93062a4e725be09c0c Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Sat, 4 Feb 2023 17:57:16 +0100 Subject: [PATCH 05/40] abcl support - overrides and fixes swank source --- .../slt/plugin/environment/Environment.java | 14 +++++ .../environment/LispSltOverridesBase.java | 53 +++++++++++++++++ .../environment/SltABCLEnvironment.java | 5 ++ .../environment/SltLispEnvironment.java | 1 + .../environment/SltSBCLEnvironment.java | 5 ++ .../environment/abcl/ABCLOverrides.java | 11 ++++ .../environment/sbcl/SBCLOverrides.java | 10 ++++ .../services/lisp/LispEnvironmentService.java | 2 + .../lisp/LispEnvironmentServiceImpl.java | 52 ++++++++++------- .../services/lisp/LispSltOverrides.java | 10 ++++ .../swank/debug/SltInspectedObject.java | 57 ++++++++++--------- .../slt/plugin/ui/SltCoreWindow.java | 3 +- .../slt/plugin/ui/debug/SltInspector.java | 10 +++- src/main/lisp/swank/swank-abcl.lisp | 45 +++++++++++++++ 14 files changed, 225 insertions(+), 53 deletions(-) create mode 100644 src/main/java/com/en_circle/slt/plugin/environment/LispSltOverridesBase.java create mode 100644 src/main/java/com/en_circle/slt/plugin/environment/abcl/ABCLOverrides.java create mode 100644 src/main/java/com/en_circle/slt/plugin/environment/sbcl/SBCLOverrides.java create mode 100644 src/main/java/com/en_circle/slt/plugin/services/lisp/LispSltOverrides.java diff --git a/src/main/java/com/en_circle/slt/plugin/environment/Environment.java b/src/main/java/com/en_circle/slt/plugin/environment/Environment.java index 9223591..3f760a1 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/Environment.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/Environment.java @@ -2,7 +2,10 @@ import com.en_circle.slt.plugin.SltBundle; import com.en_circle.slt.plugin.environment.SltLispEnvironmentConfiguration.Builder; +import com.en_circle.slt.plugin.environment.abcl.ABCLOverrides; +import com.en_circle.slt.plugin.environment.sbcl.SBCLOverrides; import com.en_circle.slt.plugin.sdk.LispSdk; +import com.en_circle.slt.plugin.services.lisp.LispSltOverrides; import com.en_circle.slt.plugin.ui.sdk.SdkConfigurationABCLProcess; import com.en_circle.slt.plugin.ui.sdk.SdkConfigurationSBCLProcess; import com.en_circle.slt.plugin.ui.sdk.SdkDialogProvider; @@ -46,6 +49,11 @@ public Supplier getEnvironmentCreator() { public SdkDialogProvider getDialogProvider() { return SdkConfigurationABCLProcess::new; } + + @Override + public LispSltOverrides getOverrides() { + return new ABCLOverrides(); + } }), SBCL_PROCESS(new EnvironmentDefinition() { @Override @@ -78,6 +86,11 @@ SltLispEnvironmentConfiguration.Builder buildConfiguration(LispSdk sdk, Pr public SdkDialogProvider getDialogProvider() { return SdkConfigurationSBCLProcess::new; } + + @Override + public LispSltOverrides getOverrides() { + return new SBCLOverrides(); + } }) ; @@ -100,6 +113,7 @@ public static abstract class EnvironmentDefinition { public abstract , R extends SltLispEnvironmentConfiguration> SltLispEnvironmentConfiguration.Builder buildConfiguration(LispSdk sdk, Project project); public abstract SdkDialogProvider getDialogProvider(); + public abstract LispSltOverrides getOverrides(); } diff --git a/src/main/java/com/en_circle/slt/plugin/environment/LispSltOverridesBase.java b/src/main/java/com/en_circle/slt/plugin/environment/LispSltOverridesBase.java new file mode 100644 index 0000000..9402351 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/environment/LispSltOverridesBase.java @@ -0,0 +1,53 @@ +package com.en_circle.slt.plugin.environment; + +import com.en_circle.slt.plugin.environment.abcl.ABCLOverrides; +import com.en_circle.slt.plugin.lisp.lisp.*; +import com.en_circle.slt.plugin.services.lisp.LispSltOverrides; +import com.en_circle.slt.plugin.swank.debug.SltInspectedObject; +import com.en_circle.slt.plugin.swank.debug.SltInspectedObject.SltInspectionElement; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public abstract class LispSltOverridesBase implements LispSltOverrides { + private static final Logger log = LoggerFactory.getLogger(ABCLOverrides.class); + + @Override + public SltInspectedObject parseInspectedObject(LispContainer container) throws Exception { + SltInspectedObject object = new SltInspectedObject(); + object.setTitle(((LispString) LispUtils.pvalue(container, new LispSymbol(":TITLE"))).getValue()); + object.setId(((LispInteger) LispUtils.pvalue(container, new LispSymbol(":ID"))).getValue()); + + LispContainer contents = (LispContainer) LispUtils.pvalue(container, new LispSymbol(":CONTENT")); + LispContainer content = (LispContainer) contents.getItems().get(0); + for (LispElement element : content.getItems()) { + if (element instanceof LispString) { + if (object.getElements().isEmpty() || object.getElements().get(object.getElements().size() - 1).getId() != null) { + SltInspectionElement e = new SltInspectionElement(); + e.setText(LispUtils.unescape(((LispString) element).getValue())); + object.getElements().add(e); + } else { + String text = object.getElements().get(object.getElements().size() - 1).getText(); + text += LispUtils.unescape(((LispString) element).getValue()); + object.getElements().get(object.getElements().size() - 1).setText(text); + } + } else if (element instanceof LispContainer subelement) { + if (LispUtils.hasPValue(subelement, new LispSymbol(":LABEL"))) { + SltInspectionElement sub = new SltInspectionElement(); + sub.setText(LispUtils.unescape(((LispString) LispUtils.pvalue(subelement, new LispSymbol(":LABEL"))).getValue())); + sub.setTitled(true); + object.getElements().add(sub); + } else if (LispUtils.hasPValue(subelement, new LispSymbol(":VALUE"))){ + SltInspectionElement sub = new SltInspectionElement(); + sub.setText(LispUtils.unescape(((LispString) subelement.getItems().get(1)).getValue())); + sub.setId(((LispInteger) subelement.getItems().get(2)).getValue()); + object.getElements().add(sub); + } + } else { + log.warn("Unknown element in inspector!"); + } + } + + return object; + } + +} diff --git a/src/main/java/com/en_circle/slt/plugin/environment/SltABCLEnvironment.java b/src/main/java/com/en_circle/slt/plugin/environment/SltABCLEnvironment.java index a068654..875a0f7 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/SltABCLEnvironment.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/SltABCLEnvironment.java @@ -31,6 +31,11 @@ public SltLispProcessInformation getInformation() { return new SltABCLLispProcessInformation(); } + @Override + public Environment getType() { + return Environment.ABCL_PROCESS; + } + @Override protected Object prepareProcessEnvironment(SltLispEnvironmentProcessConfiguration configuration) throws SltProcessException { SltABCLEnvironmentConfiguration c = getConfiguration(configuration); diff --git a/src/main/java/com/en_circle/slt/plugin/environment/SltLispEnvironment.java b/src/main/java/com/en_circle/slt/plugin/environment/SltLispEnvironment.java index ee5d259..84685ec 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/SltLispEnvironment.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/SltLispEnvironment.java @@ -8,6 +8,7 @@ public interface SltLispEnvironment { boolean isActive(); SltLispProcessInformation getInformation(); + Environment getType(); interface SltLispOutputChangedListener { diff --git a/src/main/java/com/en_circle/slt/plugin/environment/SltSBCLEnvironment.java b/src/main/java/com/en_circle/slt/plugin/environment/SltSBCLEnvironment.java index 11f47eb..16bb5b7 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/SltSBCLEnvironment.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/SltSBCLEnvironment.java @@ -30,6 +30,11 @@ public SltLispProcessInformation getInformation() { return new SltSBCLLispProcessInformation(); } + @Override + public Environment getType() { + return Environment.SBCL_PROCESS; + } + @Override protected Object prepareProcessEnvironment(SltLispEnvironmentProcessConfiguration configuration) throws SltProcessException { SltSBCLEnvironmentConfiguration c = getConfiguration(configuration); diff --git a/src/main/java/com/en_circle/slt/plugin/environment/abcl/ABCLOverrides.java b/src/main/java/com/en_circle/slt/plugin/environment/abcl/ABCLOverrides.java new file mode 100644 index 0000000..d87062a --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/environment/abcl/ABCLOverrides.java @@ -0,0 +1,11 @@ +package com.en_circle.slt.plugin.environment.abcl; + +import com.en_circle.slt.plugin.environment.LispSltOverridesBase; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ABCLOverrides extends LispSltOverridesBase { + private static final Logger log = LoggerFactory.getLogger(ABCLOverrides.class); + + +} diff --git a/src/main/java/com/en_circle/slt/plugin/environment/sbcl/SBCLOverrides.java b/src/main/java/com/en_circle/slt/plugin/environment/sbcl/SBCLOverrides.java new file mode 100644 index 0000000..ec64813 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/environment/sbcl/SBCLOverrides.java @@ -0,0 +1,10 @@ +package com.en_circle.slt.plugin.environment.sbcl; + +import com.en_circle.slt.plugin.environment.LispSltOverridesBase; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SBCLOverrides extends LispSltOverridesBase { + private static final Logger log = LoggerFactory.getLogger(SBCLOverrides.class); + +} diff --git a/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentService.java b/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentService.java index a39c316..5642764 100644 --- a/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentService.java +++ b/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentService.java @@ -55,6 +55,8 @@ static LispEnvironmentService getInstance(Project project) { Integer calculateOffset(PsiElement element, PsiFile file, boolean wasAfter, String text, int offset, String packageOverride); + LispSltOverrides getOverrides(); + enum LispEnvironmentState { STOPPED, READY, INITIALIZING } diff --git a/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentServiceImpl.java b/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentServiceImpl.java index e999799..fd5893d 100644 --- a/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentServiceImpl.java +++ b/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentServiceImpl.java @@ -51,6 +51,7 @@ public class LispEnvironmentServiceImpl implements LispEnvironmentService { private RequestResponseLogger logger; private DebugInterface debugInterface; private final List serverListeners = Collections.synchronizedList(new ArrayList<>()); + private LispSltOverrides overrides; private volatile boolean starting = false; private final Project project; @@ -108,16 +109,18 @@ public void setDebugInterface(DebugInterface debugInterface) { @Override public void start() { - starting = true; - ApplicationManager.getApplication().invokeAndWait(this::ensureToolWindowIsOpen); ApplicationManager.getApplication().executeOnPooledThread(() -> { - try { - doStart(); - } catch (Exception e) { - log.warn(SltBundle.message("slt.error.start"), e); - ApplicationManager.getApplication().invokeLater(() -> - Messages.showErrorDialog(project, e.getMessage(), SltBundle.message("slt.ui.errors.lisp.start"))); - } + starting = true; + ApplicationManager.getApplication().invokeAndWait(this::ensureToolWindowIsOpen); + ApplicationManager.getApplication().executeOnPooledThread(() -> { + try { + doStart(); + } catch (Exception e) { + log.warn(SltBundle.message("slt.error.start"), e); + ApplicationManager.getApplication().invokeLater(() -> + Messages.showErrorDialog(project, e.getMessage(), SltBundle.message("slt.ui.errors.lisp.start"))); + } + }); }); } @@ -168,6 +171,7 @@ private boolean doStart() throws Exception { } environment = environmentProvider.get(); environment.start(configuration); + overrides = environment.getType().getDefinition().getOverrides(); slimeListener = new SlimeListener(project, true, e -> { for (LispEnvironmentListener listener : serverListeners) { @@ -230,13 +234,13 @@ public void sendToLisp(SlimeRequest request, boolean startServer) throws Excepti @Override public void sendToLisp(SlimeRequest request, boolean startServer, Runnable onFailureServerState) throws Exception { - if (environment == null || !environment.isActive()) { - if (startServer) { - ApplicationManager.getApplication().invokeLater(() -> { + ApplicationManager.getApplication().executeOnPooledThread(() -> { + if (environment == null || !environment.isActive()) { + if (startServer) { starting = true; - ApplicationManager.getApplication().invokeLater(() -> { + ApplicationManager.getApplication().invokeAndWait(this::ensureToolWindowIsOpen); + ApplicationManager.getApplication().executeOnPooledThread(() -> { try { - ensureToolWindowIsOpen(); if (!doStart()) { if (onFailureServerState != null) onFailureServerState.run(); @@ -246,17 +250,18 @@ public void sendToLisp(SlimeRequest request, boolean startServer, Runnable onFai doSend(request); } catch (Exception e) { log.warn(SltBundle.message("slt.error.start"), e); - Messages.showErrorDialog(project, e.getMessage(), SltBundle.message("slt.ui.errors.lisp.start")); + ApplicationManager.getApplication().invokeLater(() -> + Messages.showErrorDialog(project, e.getMessage(), SltBundle.message("slt.ui.errors.lisp.start"))); } }); - }); + } else { + if (onFailureServerState != null) + onFailureServerState.run(); + } } else { - if (onFailureServerState != null) - onFailureServerState.run(); + doSend(request); } - } else { - doSend(request); - } + }); } private void doSend(SlimeRequest request) { @@ -320,6 +325,11 @@ public Integer calculateOffset(PsiElement element, PsiFile file, boolean wasAfte return indentationContainer.calculateIndent(element, file, wasAfter, text, offset, packageOverride); } + @Override + public LispSltOverrides getOverrides() { + return overrides; + } + @Override public void dispose() { try { diff --git a/src/main/java/com/en_circle/slt/plugin/services/lisp/LispSltOverrides.java b/src/main/java/com/en_circle/slt/plugin/services/lisp/LispSltOverrides.java new file mode 100644 index 0000000..a25e0f1 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/services/lisp/LispSltOverrides.java @@ -0,0 +1,10 @@ +package com.en_circle.slt.plugin.services.lisp; + +import com.en_circle.slt.plugin.lisp.lisp.LispContainer; +import com.en_circle.slt.plugin.swank.debug.SltInspectedObject; + +public interface LispSltOverrides { + + SltInspectedObject parseInspectedObject(LispContainer container) throws Exception; + +} diff --git a/src/main/java/com/en_circle/slt/plugin/swank/debug/SltInspectedObject.java b/src/main/java/com/en_circle/slt/plugin/swank/debug/SltInspectedObject.java index 2e018bd..0281d5a 100644 --- a/src/main/java/com/en_circle/slt/plugin/swank/debug/SltInspectedObject.java +++ b/src/main/java/com/en_circle/slt/plugin/swank/debug/SltInspectedObject.java @@ -1,6 +1,5 @@ package com.en_circle.slt.plugin.swank.debug; -import com.en_circle.slt.plugin.lisp.lisp.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -11,35 +10,12 @@ public class SltInspectedObject { private static final Logger log = LoggerFactory.getLogger(SltInspectedObject.class); - private final String title; - private final BigInteger id; + private String title; + private BigInteger id; private final List elements = new ArrayList<>(); - public SltInspectedObject(LispContainer container) throws Exception { - this.title = ((LispString) LispUtils.pvalue(container, new LispSymbol(":TITLE"))).getValue(); - this.id = ((LispInteger) LispUtils.pvalue(container, new LispSymbol(":ID"))).getValue(); - - LispContainer contents = (LispContainer) LispUtils.pvalue(container, new LispSymbol(":CONTENT")); - LispContainer content = (LispContainer) contents.getItems().get(0); - for (LispElement element : content.getItems()) { - if (element instanceof LispString) { - if (elements.isEmpty() || elements.get(elements.size() - 1).id != null) { - SltInspectionElement e = new SltInspectionElement(); - e.text = LispUtils.unescape(((LispString) element).getValue()); - elements.add(e); - } else { - elements.get(elements.size() - 1).text += LispUtils.unescape(((LispString) element).getValue()); - } - } else if (element instanceof LispContainer) { - LispContainer subelement = (LispContainer) element; - SltInspectionElement e = new SltInspectionElement(); - e.text = LispUtils.unescape(((LispString) LispUtils.pvalue(subelement, new LispSymbol(":VALUE"))).getValue()); - e.id = ((LispInteger) subelement.getItems().get(2)).getValue(); - elements.add(e); - } else { - log.warn("Unknown element in inspector!"); - } - } + public SltInspectedObject() { + } public String getTitle() { @@ -54,10 +30,19 @@ public List getElements() { return elements; } + public void setTitle(String title) { + this.title = title; + } + + public void setId(BigInteger id) { + this.id = id; + } + public static class SltInspectionElement { private String text; private BigInteger id = null; + private boolean titled = false; public String getText() { return text; @@ -66,5 +51,21 @@ public String getText() { public BigInteger getId() { return id; } + + public void setText(String text) { + this.text = text; + } + + public void setId(BigInteger id) { + this.id = id; + } + + public boolean isTitled() { + return titled; + } + + public void setTitled(boolean titled) { + this.titled = titled; + } } } diff --git a/src/main/java/com/en_circle/slt/plugin/ui/SltCoreWindow.java b/src/main/java/com/en_circle/slt/plugin/ui/SltCoreWindow.java index b1859df..d0d8c06 100644 --- a/src/main/java/com/en_circle/slt/plugin/ui/SltCoreWindow.java +++ b/src/main/java/com/en_circle/slt/plugin/ui/SltCoreWindow.java @@ -12,7 +12,6 @@ import com.intellij.icons.AllIcons.General; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.wm.ToolWindow; @@ -177,7 +176,7 @@ private StartLispAction() { @Override public void actionPerformed(@NotNull AnActionEvent e) { - ApplicationManager.getApplication().invokeLater(SltCoreWindow.this::start); + start(); } @Override diff --git a/src/main/java/com/en_circle/slt/plugin/ui/debug/SltInspector.java b/src/main/java/com/en_circle/slt/plugin/ui/debug/SltInspector.java index 31ba1ae..f3bad4f 100644 --- a/src/main/java/com/en_circle/slt/plugin/ui/debug/SltInspector.java +++ b/src/main/java/com/en_circle/slt/plugin/ui/debug/SltInspector.java @@ -79,7 +79,8 @@ public void loadLocal(Local local, BigInteger frame) { private void processResult(LispElement result) { SltInspectedObject parsedResult = null; try { - parsedResult = new SltInspectedObject((LispContainer) result); + parsedResult = LispEnvironmentService.getInstance(project).getOverrides() + .parseInspectedObject((LispContainer) result); } catch (Exception ignored) { // in case we get garbage we show error } @@ -104,7 +105,12 @@ private void loadInspectedObject(SltInspectedObject inspectedObject) { if (element.getId() == null) { String[] parts = text.split(Pattern.quote("\n")); for (int i=0; i (cond (isfunction (list :function-name (princ-to-string (second what)) :position (1+ (or pos 0)))) + (ismethod (stringify-method-specs what) :position (1+ (or pos 0))) + (t (list :position (1+ (or pos 0)))))) + (path2 (if (eq path :top-level) + ;; this is bogus - figure out some way to guess which is the repl associated with :toplevel + ;; or get rid of this + "emacs-buffer:*slime-repl*" + (maybe-redirect-to-jar path)))) + (when (atom what) + (setq what (list what sym))) + (list (definition-specifier what) + (if (ext:pathname-jar-p (pathname path2)) + `(:location + (:zip ,@(split-string (subseq path2 (length "jar:file:")) "!/")) + ;; pos never seems right. Use function name. + , + (:align t)) + ;; conspire with swank-compile-string to keep the + ;; buffer name in a pathname whose device is + ;; "emacs-buffer". + (if (eql 0 (search "emacs-buffer:" path2)) + `(:location + (:buffer ,(subseq path2 (load-time-value (length "emacs-buffer:")))) + , + (:align t)) + `(:location + (:file ,path2) + , + (:align t)))))))) + (in-package :swank) (defslimefun backtrace (start end) From 33b3ca3bc03abba95ebda3b53d93ec4f6380e441 Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Sat, 4 Feb 2023 23:50:35 +0100 Subject: [PATCH 06/40] fixed debuggers --- .../slt/plugin/ui/debug/SltDebuggerImpl.java | 31 +++++++++++++------ src/main/lisp/swank/swank-abcl.lisp | 3 +- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/en_circle/slt/plugin/ui/debug/SltDebuggerImpl.java b/src/main/java/com/en_circle/slt/plugin/ui/debug/SltDebuggerImpl.java index b82a21c..92770e4 100644 --- a/src/main/java/com/en_circle/slt/plugin/ui/debug/SltDebuggerImpl.java +++ b/src/main/java/com/en_circle/slt/plugin/ui/debug/SltDebuggerImpl.java @@ -113,10 +113,6 @@ public void redraw(SltDebugInfo debugInfo) { splitter.setFirstComponent(splitter2); JPanel actionsPanel = new JPanel(); - GridBagConstraints cons = new GridBagConstraints(); - cons.fill = GridBagConstraints.HORIZONTAL; - cons.weightx = 1; - cons.gridx = 0; actionsPanel.setLayout(new GridBagLayout()); for (SltDebugAction action : debugInfo.getActions()) { @@ -146,20 +142,25 @@ public void mouseClicked(MouseEvent e) { actionInfo.add(labelPanel); actionInfo.add(new JScrollPane(textArea)); + GridBagConstraints cons = new GridBagConstraints(); + cons.fill = GridBagConstraints.HORIZONTAL; + cons.weightx = 1; + cons.gridx = 0; actionsPanel.add(actionInfo, cons); } + GridBagConstraints cons = new GridBagConstraints(); + cons.fill = GridBagConstraints.HORIZONTAL; + cons.weighty = 1; + cons.gridx = 0; + actionsPanel.add(Box.createVerticalGlue(), cons); JBScrollPane pane = new JBScrollPane(actionsPanel); JPanel actionsPanelDecorator = new JPanel(new BorderLayout()); actionsPanelDecorator.setBorder(BorderFactory.createTitledBorder(SltBundle.message("slt.ui.debugger.actions"))); - actionsPanelDecorator.add(pane, BorderLayout.NORTH); + actionsPanelDecorator.add(pane, BorderLayout.CENTER); splitter2.setFirstComponent(actionsPanelDecorator); JPanel stackframes = new JPanel(); - cons = new GridBagConstraints(); - cons.fill = GridBagConstraints.HORIZONTAL; - cons.weightx = 1; - cons.gridx = 0; stackframes.setLayout(new GridBagLayout()); for (SltDebugStackTraceElement element : debugInfo.getStacktrace()) { JLabel label = new JLabel(element.getLine()); @@ -173,14 +174,24 @@ public void mouseClicked(MouseEvent e) { JPanel p = new JPanel(new BorderLayout()); p.setBackground(SltUIConstants.DEBUG_FRAMES_COLOR); p.add(label, BorderLayout.CENTER); + + cons = new GridBagConstraints(); + cons.fill = GridBagConstraints.HORIZONTAL; + cons.weightx = 1; + cons.gridx = 0; stackframes.add(p, cons); this.stackframes.add(p); } + cons = new GridBagConstraints(); + cons.fill = GridBagConstraints.HORIZONTAL; + cons.weighty = 1; + cons.gridx = 0; + stackframes.add(Box.createVerticalGlue(), cons); JPanel stackframesContainer = new JPanel(new BorderLayout()); stackframesContainer.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), SltBundle.message("slt.ui.debugger.frames"))); - stackframesContainer.add(ScrollPaneFactory.createScrollPane(stackframes), BorderLayout.NORTH); + stackframesContainer.add(ScrollPaneFactory.createScrollPane(stackframes), BorderLayout.CENTER); splitter2.setSecondComponent(stackframesContainer); singleFrameComponent = new JPanel(new BorderLayout()); diff --git a/src/main/lisp/swank/swank-abcl.lisp b/src/main/lisp/swank/swank-abcl.lisp index ee2a26d..b0441b5 100644 --- a/src/main/lisp/swank/swank-abcl.lisp +++ b/src/main/lisp/swank/swank-abcl.lisp @@ -6,12 +6,11 @@ (error (c) (format t "Error: ~A~%" c) NIL))) -(defmethod source-location ((func (eql :ANONYMOUS-INTERPRETED-FUNCTION))) +(defmethod source-location ((func T)) NIL) (defun slime-location-from-source-annotation (sym it) (destructuring-bind (what path pos) it - (let* ((isfunction ;; all of these are (defxxx forms, which is what :function locations look for in slime (and (consp what) (member (car what) From e87b9a66acccb613cb83198d969cae0225651fc0 Mon Sep 17 00:00:00 2001 From: Peter Date: Sun, 5 Feb 2023 10:58:54 +0100 Subject: [PATCH 07/40] selective functions depending on LispFeatures --- CHANGELOG.md | 5 + FEATURES.md | 25 +++++ README.md | 4 +- .../slt/plugin/SltDocumentationProvider.java | 58 +++++----- .../slt/plugin/actions/EvalActionBase.java | 7 +- .../slt/plugin/actions/EvalFile.java | 2 +- .../autocomplete/HeadCompletionProvider.java | 4 +- .../contributors/SltSymbolContributor.java | 18 +-- .../slt/plugin/environment/Environment.java | 105 +----------------- .../environment/EnvironmentDefinition.java | 28 +++++ .../slt/plugin/environment/LispFeatures.java | 15 +++ .../abcl/ABCLEnvironmentDefinition.java | 73 ++++++++++++ .../sbcl/SBCLEnvironmentDefinition.java | 75 +++++++++++++ .../slt/plugin/references/SltReference.java | 8 +- .../services/lisp/LispEnvironmentService.java | 4 + .../lisp/LispEnvironmentServiceImpl.java | 10 ++ .../slt/plugin/ui/SltCoreWindow.java | 4 +- .../slt/plugin/ui/console/SltConsole.java | 7 +- .../slt/plugin/ui/debug/SltFrameInfo.java | 47 ++++---- .../slt/plugin/ui/debug/SltInspector.java | 7 +- 20 files changed, 342 insertions(+), 164 deletions(-) create mode 100644 FEATURES.md create mode 100644 src/main/java/com/en_circle/slt/plugin/environment/EnvironmentDefinition.java create mode 100644 src/main/java/com/en_circle/slt/plugin/environment/LispFeatures.java create mode 100644 src/main/java/com/en_circle/slt/plugin/environment/abcl/ABCLEnvironmentDefinition.java create mode 100644 src/main/java/com/en_circle/slt/plugin/environment/sbcl/SBCLEnvironmentDefinition.java diff --git a/CHANGELOG.md b/CHANGELOG.md index b7b16ff..9097390 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,15 @@ ### Added +- Support for multiple lisp interprets +- ABCL Support + ### Fixes ### Changes +- Feature list for each interpret changes availability of functions in IDE + ## 0.3.1 230203 ### Added diff --git a/FEATURES.md b/FEATURES.md new file mode 100644 index 0000000..7ce93d5 --- /dev/null +++ b/FEATURES.md @@ -0,0 +1,25 @@ +Since I started supporting multiple interprets I have learned that some features just don't work well across them or even in swank. +This is a list of features that are supported. + +| Legend | | +|--------|----------------------------------------| +| ✅️ | Implemented | +| ❓ | Not implemented but might be in future | +| ❎ | Is not supported | + +| Features / Lisp | SBCL | ABCL | +|-------------------|------|------| +| REPL | ✅️ | ✅️ | +| Buffer Evaluation | ✅️ | ✅️ | +| Documentation | ✅ | ✅ | +| Macroexpand | ✅ | ✅ | +| Debugger | ✅ | ✅ | +| Stepping Debugger | ❎ | ❎ | +| References | ✅ | ❎ | +| Inspector | ✅¹ | ✅² | +| Autocomplete | ✅ | ✅ | +| Find References | ✅ | ✅ | + +1 - Only read-only inspector available + +2 - Only read-only inspector available, also no history support \ No newline at end of file diff --git a/README.md b/README.md index e7d5291..2eafa69 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ This plugin is providing support for Common Lisp for JetBrains IDEs. Using modified SLIME/Swank protocol to commmunicate with SBCL providing IDE capabilities for Common Lisp. -# (Somewhat)Detailed Installation and Usage Guide +# (Somewhat) Detailed Installation and Usage Guide https://github.com/Enerccio/SLT/wiki/User-Guide @@ -83,7 +83,7 @@ You can also open this as a project in Intellij Idea. this is implemented manually. * [x] Download SBCL and quicklisp for user * [x] Automatic download of lisp interpret and quicklisp -* [ ] Different lisp interpreter support +* [x] Different lisp interpreter support * [ ] Remote connections to interpreters * [ ] Rewrite everything into ABCL just for purity’s sake lol diff --git a/src/main/java/com/en_circle/slt/plugin/SltDocumentationProvider.java b/src/main/java/com/en_circle/slt/plugin/SltDocumentationProvider.java index ae7cd00..d6f1002 100644 --- a/src/main/java/com/en_circle/slt/plugin/SltDocumentationProvider.java +++ b/src/main/java/com/en_circle/slt/plugin/SltDocumentationProvider.java @@ -1,6 +1,7 @@ package com.en_circle.slt.plugin; import com.en_circle.slt.plugin.SymbolState.SymbolBinding; +import com.en_circle.slt.plugin.environment.LispFeatures; import com.en_circle.slt.plugin.lisp.LispParserUtil; import com.en_circle.slt.plugin.lisp.psi.LispList; import com.en_circle.slt.plugin.lisp.psi.LispSymbol; @@ -54,17 +55,20 @@ public class SltDocumentationProvider extends AbstractDocumentationProvider { @Override public @Nullable @Nls String generateDoc(PsiElement element, @Nullable PsiElement originalElement) { - if (originalElement != null) - element = decideOnElement(element, originalElement); + if (LispEnvironmentService.getInstance(element.getProject()).hasFeature(LispFeatures.DOCUMENTATION) || + LispEnvironmentService.getInstance(element.getProject()).hasFeature(LispFeatures.MACROEXPAND)) { + if (originalElement != null) + element = decideOnElement(element, originalElement); - if (!(element instanceof LispSymbol)) - element = PsiTreeUtil.getParentOfType(element, LispSymbol.class); + if (!(element instanceof LispSymbol)) + element = PsiTreeUtil.getParentOfType(element, LispSymbol.class); - if (element != null) { - String text = element.getText(); - String packageName = LispParserUtil.getPackage(element); - SymbolState state = LispEnvironmentService.getInstance(element.getProject()).refreshSymbolFromServer(packageName, text); - return asHtml(state, packageName, element); + if (element != null) { + String text = element.getText(); + String packageName = LispParserUtil.getPackage(element); + SymbolState state = LispEnvironmentService.getInstance(element.getProject()).refreshSymbolFromServer(packageName, text); + return asHtml(state, packageName, element); + } } return null; } @@ -79,23 +83,27 @@ private PsiElement decideOnElement(PsiElement element, PsiElement originalElemen private String asHtml(SymbolState state, String packageName, PsiElement element) { HtmlBuilder builder = new HtmlBuilder(); - String documentation = StringUtils.replace(StringUtils.replace(state.documentation, " ", " "), - "\n", HtmlChunk.br().toString()); - builder.append(documentation == null ? HtmlChunk.raw("") : HtmlChunk.raw(documentation)); + if (LispEnvironmentService.getInstance(element.getProject()).hasFeature(LispFeatures.DOCUMENTATION)) { + String documentation = StringUtils.replace(StringUtils.replace(state.documentation, " ", " "), + "\n", HtmlChunk.br().toString()); + builder.append(documentation == null ? HtmlChunk.raw("") : HtmlChunk.raw(documentation)); + } - LispList form = LispParserUtil.getIfHead(element); - if (form != null && state.binding == SymbolBinding.MACRO) { - String macroExpand = LispEnvironmentService.getInstance(element.getProject()).macroexpand(form, packageName); - if (macroExpand != null) { - macroExpand = StringUtils.replace(StringUtils.replace(macroExpand, " ", " "), - "\n", HtmlChunk.br().toString()); - builder.append(HtmlChunk.hr()); - builder.append(HtmlChunk.text(SltBundle.message("slt.documentation.macroexpand"))); - builder.append(HtmlChunk.br()); - builder.append(HtmlChunk.raw(macroExpand)); - } else { - builder.append(HtmlChunk.hr()); - builder.append(HtmlChunk.text(SltBundle.message("slt.documentation.macroexpand.generating"))); + if (LispEnvironmentService.getInstance(element.getProject()).hasFeature(LispFeatures.MACROEXPAND)) { + LispList form = LispParserUtil.getIfHead(element); + if (form != null && state.binding == SymbolBinding.MACRO) { + String macroExpand = LispEnvironmentService.getInstance(element.getProject()).macroexpand(form, packageName); + if (macroExpand != null) { + macroExpand = StringUtils.replace(StringUtils.replace(macroExpand, " ", " "), + "\n", HtmlChunk.br().toString()); + builder.append(HtmlChunk.hr()); + builder.append(HtmlChunk.text(SltBundle.message("slt.documentation.macroexpand"))); + builder.append(HtmlChunk.br()); + builder.append(HtmlChunk.raw(macroExpand)); + } else { + builder.append(HtmlChunk.hr()); + builder.append(HtmlChunk.text(SltBundle.message("slt.documentation.macroexpand.generating"))); + } } } diff --git a/src/main/java/com/en_circle/slt/plugin/actions/EvalActionBase.java b/src/main/java/com/en_circle/slt/plugin/actions/EvalActionBase.java index 28be35e..bce4189 100644 --- a/src/main/java/com/en_circle/slt/plugin/actions/EvalActionBase.java +++ b/src/main/java/com/en_circle/slt/plugin/actions/EvalActionBase.java @@ -2,6 +2,7 @@ import com.en_circle.slt.plugin.SltBundle; import com.en_circle.slt.plugin.SltCommonLispFileType; +import com.en_circle.slt.plugin.environment.LispFeatures; import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService; import com.en_circle.slt.plugin.swank.requests.Eval; import com.en_circle.slt.plugin.swank.requests.EvalFromVirtualFile; @@ -34,7 +35,7 @@ public void update(@NotNull AnActionEvent event) { if (editor != null && event.getProject() != null) { PsiFile file = PsiDocumentManager.getInstance(Objects.requireNonNull(editor.getProject())).getPsiFile(editor.getDocument()); if (file != null && SltCommonLispFileType.INSTANCE.equals(file.getFileType())) { - event.getPresentation().setEnabledAndVisible(true); + event.getPresentation().setEnabledAndVisible(isEnabledInLisp(event.getProject())); } } } @@ -68,6 +69,10 @@ public static void evaluateFile(Project project, String filename, VirtualFile vi } } + protected boolean isEnabledInLisp(Project project) { + return LispEnvironmentService.getInstance(project).hasFeature(LispFeatures.REPL); + } + @Override public @NotNull ActionUpdateThread getActionUpdateThread() { return ActionUpdateThread.BGT; diff --git a/src/main/java/com/en_circle/slt/plugin/actions/EvalFile.java b/src/main/java/com/en_circle/slt/plugin/actions/EvalFile.java index 961af8d..2aafcd0 100644 --- a/src/main/java/com/en_circle/slt/plugin/actions/EvalFile.java +++ b/src/main/java/com/en_circle/slt/plugin/actions/EvalFile.java @@ -33,7 +33,7 @@ public void update(@NotNull AnActionEvent event) { VirtualFile vf = event.getData(CommonDataKeys.VIRTUAL_FILE); if (vf != null) { if (vf.getFileType().equals(SltCommonLispFileType.INSTANCE)) { - event.getPresentation().setEnabledAndVisible(true); + event.getPresentation().setEnabledAndVisible(isEnabledInLisp(event.getProject())); } } } diff --git a/src/main/java/com/en_circle/slt/plugin/autocomplete/HeadCompletionProvider.java b/src/main/java/com/en_circle/slt/plugin/autocomplete/HeadCompletionProvider.java index fbcbadf..e8fbfff 100644 --- a/src/main/java/com/en_circle/slt/plugin/autocomplete/HeadCompletionProvider.java +++ b/src/main/java/com/en_circle/slt/plugin/autocomplete/HeadCompletionProvider.java @@ -2,6 +2,7 @@ import com.en_circle.slt.plugin.SymbolState; import com.en_circle.slt.plugin.SymbolState.SymbolBinding; +import com.en_circle.slt.plugin.environment.LispFeatures; import com.en_circle.slt.plugin.lisp.LispParserUtil; import com.en_circle.slt.plugin.lisp.lisp.LispContainer; import com.en_circle.slt.plugin.lisp.lisp.LispElement; @@ -28,7 +29,8 @@ protected void addCompletions(@NotNull CompletionParameters parameters, @NotNull Project project = parameters.getEditor().getProject(); assert project != null; - if (LispEnvironmentService.getInstance(project).getState() == LispEnvironmentState.READY) { + if (LispEnvironmentService.getInstance(project).getState() == LispEnvironmentState.READY && + LispEnvironmentService.getInstance(project).hasFeature(LispFeatures.AUTOCOMPLETE)) { String startedSymbol = result.getPrefixMatcher().getPrefix(); String packageName = LispParserUtil.getPackage(parameters.getOriginalFile(), parameters.getOffset()); List builderList = SltApplicationUtils.getAsyncResultNoThrow(project, finishRequest -> SimpleCompletion diff --git a/src/main/java/com/en_circle/slt/plugin/contributors/SltSymbolContributor.java b/src/main/java/com/en_circle/slt/plugin/contributors/SltSymbolContributor.java index f87636a..c8f0a54 100644 --- a/src/main/java/com/en_circle/slt/plugin/contributors/SltSymbolContributor.java +++ b/src/main/java/com/en_circle/slt/plugin/contributors/SltSymbolContributor.java @@ -1,10 +1,12 @@ package com.en_circle.slt.plugin.contributors; +import com.en_circle.slt.plugin.environment.LispFeatures; import com.en_circle.slt.plugin.lisp.LispSymbolPresentation; import com.en_circle.slt.plugin.lisp.lisp.LispContainer; import com.en_circle.slt.plugin.lisp.lisp.LispElement; import com.en_circle.slt.plugin.lisp.lisp.LispString; import com.en_circle.slt.plugin.lisp.psi.LispSymbol; +import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService; import com.en_circle.slt.plugin.swank.components.SourceLocation; import com.en_circle.slt.plugin.swank.requests.CompleteSearch; import com.en_circle.slt.plugin.swank.requests.CompleteSearch.SearchFilter; @@ -39,13 +41,15 @@ protected SearchFilter getFilter() { @Override public String @NotNull [] getNames(Project project, boolean includeNonProjectItems) { - String[] names; - try { - names = SltApplicationUtils.getAsyncResultCheckCancellation(project, - finishRequest -> CompleteSearch.search("", getFilter(), form -> - finishRequest.accept(getNames(form, includeNonProjectItems, project))), false); - } catch (Exception e) { - return new String[0]; + String[] names = null; + if (LispEnvironmentService.getInstance(project).hasFeature(LispFeatures.SEARCH)) { + try { + names = SltApplicationUtils.getAsyncResultCheckCancellation(project, + finishRequest -> CompleteSearch.search("", getFilter(), form -> + finishRequest.accept(getNames(form, includeNonProjectItems, project))), false); + } catch (Exception e) { + return new String[0]; + } } if (names == null) { diff --git a/src/main/java/com/en_circle/slt/plugin/environment/Environment.java b/src/main/java/com/en_circle/slt/plugin/environment/Environment.java index 3f760a1..82cf490 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/Environment.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/Environment.java @@ -1,97 +1,12 @@ package com.en_circle.slt.plugin.environment; -import com.en_circle.slt.plugin.SltBundle; -import com.en_circle.slt.plugin.environment.SltLispEnvironmentConfiguration.Builder; -import com.en_circle.slt.plugin.environment.abcl.ABCLOverrides; -import com.en_circle.slt.plugin.environment.sbcl.SBCLOverrides; -import com.en_circle.slt.plugin.sdk.LispSdk; -import com.en_circle.slt.plugin.services.lisp.LispSltOverrides; -import com.en_circle.slt.plugin.ui.sdk.SdkConfigurationABCLProcess; -import com.en_circle.slt.plugin.ui.sdk.SdkConfigurationSBCLProcess; -import com.en_circle.slt.plugin.ui.sdk.SdkDialogProvider; -import com.en_circle.slt.tools.platform.DownloadLispAction; -import com.en_circle.slt.tools.platform.DownloadSBCLAction; -import com.intellij.openapi.project.Project; - -import java.util.function.Supplier; +import com.en_circle.slt.plugin.environment.abcl.ABCLEnvironmentDefinition; +import com.en_circle.slt.plugin.environment.sbcl.SBCLEnvironmentDefinition; public enum Environment { - ABCL_PROCESS(new EnvironmentDefinition() { - @Override - public String getName() { - return SltBundle.message("slt.environment.abcl"); - } - - @Override - public Class getDownloadActionDef() { - return null; - } - - @Override - public Supplier getEnvironmentCreator() { - return SltABCLEnvironment::new; - } - - @SuppressWarnings("unchecked") - @Override - public , R extends SltLispEnvironmentConfiguration> Builder - buildConfiguration(LispSdk sdk, Project project) { - return (Builder) new SltABCLEnvironmentConfiguration.Builder() - .setJvm(sdk.abclJvm) - .setJvmArgs(sdk.abclJvmArgs) - .setAbclJar(sdk.abclJar) - .setQuicklispStartScriptPath(sdk.quickLispPath) - .setProjectDirectory(project.getBasePath()); - } - - @Override - public SdkDialogProvider getDialogProvider() { - return SdkConfigurationABCLProcess::new; - } - - @Override - public LispSltOverrides getOverrides() { - return new ABCLOverrides(); - } - }), - SBCL_PROCESS(new EnvironmentDefinition() { - @Override - public String getName() { - return SltBundle.message("slt.environment.sbcl"); - } - - @Override - public Class getDownloadActionDef() { - return DownloadSBCLAction.class; - } - - @Override - public Supplier getEnvironmentCreator() { - return SltSBCLEnvironment::new; - } - - @SuppressWarnings("unchecked") - @Override - public , R extends SltLispEnvironmentConfiguration> - SltLispEnvironmentConfiguration.Builder buildConfiguration(LispSdk sdk, Project project) { - return (Builder) new SltSBCLEnvironmentConfiguration.Builder() - .setExecutable(sdk.sbclExecutable) - .setCore(sdk.sbclCorePath) - .setQuicklispStartScriptPath(sdk.quickLispPath) - .setProjectDirectory(project.getBasePath()); - } - - @Override - public SdkDialogProvider getDialogProvider() { - return SdkConfigurationSBCLProcess::new; - } - - @Override - public LispSltOverrides getOverrides() { - return new SBCLOverrides(); - } - }) + ABCL_PROCESS(new ABCLEnvironmentDefinition()), + SBCL_PROCESS(new SBCLEnvironmentDefinition()) ; @@ -105,16 +20,4 @@ public EnvironmentDefinition getDefinition() { return definition; } - public static abstract class EnvironmentDefinition { - - public abstract String getName(); - public abstract Class getDownloadActionDef(); - public abstract Supplier getEnvironmentCreator(); - public abstract , R extends SltLispEnvironmentConfiguration> - SltLispEnvironmentConfiguration.Builder buildConfiguration(LispSdk sdk, Project project); - public abstract SdkDialogProvider getDialogProvider(); - public abstract LispSltOverrides getOverrides(); - - } - } diff --git a/src/main/java/com/en_circle/slt/plugin/environment/EnvironmentDefinition.java b/src/main/java/com/en_circle/slt/plugin/environment/EnvironmentDefinition.java new file mode 100644 index 0000000..53a6933 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/environment/EnvironmentDefinition.java @@ -0,0 +1,28 @@ +package com.en_circle.slt.plugin.environment; + +import com.en_circle.slt.plugin.sdk.LispSdk; +import com.en_circle.slt.plugin.services.lisp.LispSltOverrides; +import com.en_circle.slt.plugin.ui.sdk.SdkDialogProvider; +import com.en_circle.slt.tools.platform.DownloadLispAction; +import com.intellij.openapi.project.Project; + +import java.util.function.Supplier; + +public abstract class EnvironmentDefinition { + + public abstract String getName(); + + public abstract Class getDownloadActionDef(); + + public abstract Supplier getEnvironmentCreator(); + + public abstract , R extends SltLispEnvironmentConfiguration> + SltLispEnvironmentConfiguration.Builder buildConfiguration(LispSdk sdk, Project project); + + public abstract SdkDialogProvider getDialogProvider(); + + public abstract LispSltOverrides getOverrides(); + + public abstract boolean hasFeature(LispFeatures feature); + +} diff --git a/src/main/java/com/en_circle/slt/plugin/environment/LispFeatures.java b/src/main/java/com/en_circle/slt/plugin/environment/LispFeatures.java new file mode 100644 index 0000000..62a4ba8 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/environment/LispFeatures.java @@ -0,0 +1,15 @@ +package com.en_circle.slt.plugin.environment; + +public enum LispFeatures { + + REPL, + EVALUATION, + DOCUMENTATION, + MACROEXPAND, + INSPECTOR, + INSPECTOR_HISTORY, + AUTOCOMPLETE, + SEARCH, + XREFS + +} diff --git a/src/main/java/com/en_circle/slt/plugin/environment/abcl/ABCLEnvironmentDefinition.java b/src/main/java/com/en_circle/slt/plugin/environment/abcl/ABCLEnvironmentDefinition.java new file mode 100644 index 0000000..3e0bf86 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/environment/abcl/ABCLEnvironmentDefinition.java @@ -0,0 +1,73 @@ +package com.en_circle.slt.plugin.environment.abcl; + +import com.en_circle.slt.plugin.SltBundle; +import com.en_circle.slt.plugin.environment.*; +import com.en_circle.slt.plugin.environment.SltLispEnvironmentConfiguration.Builder; +import com.en_circle.slt.plugin.sdk.LispSdk; +import com.en_circle.slt.plugin.services.lisp.LispSltOverrides; +import com.en_circle.slt.plugin.ui.sdk.SdkConfigurationABCLProcess; +import com.en_circle.slt.plugin.ui.sdk.SdkDialogProvider; +import com.en_circle.slt.tools.platform.DownloadLispAction; +import com.intellij.openapi.project.Project; + +import java.util.HashSet; +import java.util.Set; +import java.util.function.Supplier; + +public class ABCLEnvironmentDefinition extends EnvironmentDefinition { + + private final Set features = new HashSet<>(); + + public ABCLEnvironmentDefinition() { + features.add(LispFeatures.REPL); + features.add(LispFeatures.EVALUATION); + features.add(LispFeatures.DOCUMENTATION); + features.add(LispFeatures.MACROEXPAND); + features.add(LispFeatures.INSPECTOR); + features.add(LispFeatures.AUTOCOMPLETE); + features.add(LispFeatures.SEARCH); + } + + @Override + public String getName() { + return SltBundle.message("slt.environment.abcl"); + } + + @Override + public Class getDownloadActionDef() { + return null; + } + + @Override + public Supplier getEnvironmentCreator() { + return SltABCLEnvironment::new; + } + + @SuppressWarnings("unchecked") + @Override + public , R extends SltLispEnvironmentConfiguration> Builder + buildConfiguration(LispSdk sdk, Project project) { + return (Builder) new SltABCLEnvironmentConfiguration.Builder() + .setJvm(sdk.abclJvm) + .setJvmArgs(sdk.abclJvmArgs) + .setAbclJar(sdk.abclJar) + .setQuicklispStartScriptPath(sdk.quickLispPath) + .setProjectDirectory(project.getBasePath()); + } + + @Override + public SdkDialogProvider getDialogProvider() { + return SdkConfigurationABCLProcess::new; + } + + @Override + public LispSltOverrides getOverrides() { + return new ABCLOverrides(); + } + + @Override + public boolean hasFeature(LispFeatures feature) { + return features.contains(feature); + } + +} diff --git a/src/main/java/com/en_circle/slt/plugin/environment/sbcl/SBCLEnvironmentDefinition.java b/src/main/java/com/en_circle/slt/plugin/environment/sbcl/SBCLEnvironmentDefinition.java new file mode 100644 index 0000000..352a2a5 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/environment/sbcl/SBCLEnvironmentDefinition.java @@ -0,0 +1,75 @@ +package com.en_circle.slt.plugin.environment.sbcl; + +import com.en_circle.slt.plugin.SltBundle; +import com.en_circle.slt.plugin.environment.*; +import com.en_circle.slt.plugin.environment.SltLispEnvironmentConfiguration.Builder; +import com.en_circle.slt.plugin.sdk.LispSdk; +import com.en_circle.slt.plugin.services.lisp.LispSltOverrides; +import com.en_circle.slt.plugin.ui.sdk.SdkConfigurationSBCLProcess; +import com.en_circle.slt.plugin.ui.sdk.SdkDialogProvider; +import com.en_circle.slt.tools.platform.DownloadLispAction; +import com.en_circle.slt.tools.platform.DownloadSBCLAction; +import com.intellij.openapi.project.Project; + +import java.util.HashSet; +import java.util.Set; +import java.util.function.Supplier; + +public class SBCLEnvironmentDefinition extends EnvironmentDefinition { + + private final Set features = new HashSet<>(); + + public SBCLEnvironmentDefinition() { + features.add(LispFeatures.REPL); + features.add(LispFeatures.EVALUATION); + features.add(LispFeatures.DOCUMENTATION); + features.add(LispFeatures.MACROEXPAND); + features.add(LispFeatures.INSPECTOR); + features.add(LispFeatures.INSPECTOR_HISTORY); + features.add(LispFeatures.AUTOCOMPLETE); + features.add(LispFeatures.SEARCH); + features.add(LispFeatures.XREFS); + } + + @Override + public String getName() { + return SltBundle.message("slt.environment.sbcl"); + } + + @Override + public Class getDownloadActionDef() { + return DownloadSBCLAction.class; + } + + @Override + public Supplier getEnvironmentCreator() { + return SltSBCLEnvironment::new; + } + + @SuppressWarnings("unchecked") + @Override + public , R extends SltLispEnvironmentConfiguration> + SltLispEnvironmentConfiguration.Builder buildConfiguration(LispSdk sdk, Project project) { + return (Builder) new SltSBCLEnvironmentConfiguration.Builder() + .setExecutable(sdk.sbclExecutable) + .setCore(sdk.sbclCorePath) + .setQuicklispStartScriptPath(sdk.quickLispPath) + .setProjectDirectory(project.getBasePath()); + } + + @Override + public SdkDialogProvider getDialogProvider() { + return SdkConfigurationSBCLProcess::new; + } + + @Override + public LispSltOverrides getOverrides() { + return new SBCLOverrides(); + } + + @Override + public boolean hasFeature(LispFeatures feature) { + return features.contains(feature); + } + +} diff --git a/src/main/java/com/en_circle/slt/plugin/references/SltReference.java b/src/main/java/com/en_circle/slt/plugin/references/SltReference.java index bf97784..dca9ae7 100644 --- a/src/main/java/com/en_circle/slt/plugin/references/SltReference.java +++ b/src/main/java/com/en_circle/slt/plugin/references/SltReference.java @@ -1,6 +1,7 @@ package com.en_circle.slt.plugin.references; import com.en_circle.slt.plugin.SymbolState; +import com.en_circle.slt.plugin.environment.LispFeatures; import com.en_circle.slt.plugin.lisp.LispParserUtil; import com.en_circle.slt.plugin.lisp.lisp.LispContainer; import com.en_circle.slt.plugin.lisp.lisp.LispElement; @@ -8,6 +9,7 @@ import com.en_circle.slt.plugin.lisp.psi.LispSymbol; import com.en_circle.slt.plugin.lisp.psi.LispToplevel; import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService; +import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService.LispEnvironmentState; import com.en_circle.slt.plugin.swank.components.SourceLocation; import com.en_circle.slt.plugin.swank.requests.Xrefs; import com.en_circle.slt.plugin.swank.requests.Xrefs.XrefType; @@ -40,11 +42,15 @@ public SltReference(@NotNull LispSymbol element) { @Override public ResolveResult @NotNull [] multiResolve(boolean incompleteCode) { - PsiElement element = getElement(); return cache.resolve(this); } ResolveResult[] resolveInner(boolean incompleteCode) { + if (LispEnvironmentService.getInstance(myElement.getProject()).getState() != LispEnvironmentState.READY) + return new ResolveResult[0]; + if (!LispEnvironmentService.getInstance(myElement.getProject()).hasFeature(LispFeatures.XREFS)) + return new ResolveResult[0]; + PsiManager manager = PsiManager.getInstance(myElement.getProject()); String symbolName = myElement.getName(); diff --git a/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentService.java b/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentService.java index 5642764..0247d4f 100644 --- a/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentService.java +++ b/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentService.java @@ -1,6 +1,7 @@ package com.en_circle.slt.plugin.services.lisp; import com.en_circle.slt.plugin.SymbolState; +import com.en_circle.slt.plugin.environment.LispFeatures; import com.en_circle.slt.plugin.environment.SltLispEnvironment; import com.en_circle.slt.plugin.environment.SltLispEnvironment.SltLispOutputChangedListener; import com.en_circle.slt.plugin.lisp.lisp.LispElement; @@ -57,6 +58,9 @@ static LispEnvironmentService getInstance(Project project) { LispSltOverrides getOverrides(); + boolean hasFeature(LispFeatures xrefs); + + enum LispEnvironmentState { STOPPED, READY, INITIALIZING } diff --git a/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentServiceImpl.java b/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentServiceImpl.java index fd5893d..db7ce3e 100644 --- a/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentServiceImpl.java +++ b/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentServiceImpl.java @@ -2,6 +2,7 @@ import com.en_circle.slt.plugin.SltBundle; import com.en_circle.slt.plugin.SymbolState; +import com.en_circle.slt.plugin.environment.LispFeatures; import com.en_circle.slt.plugin.environment.SltLispEnvironment; import com.en_circle.slt.plugin.environment.SltLispEnvironment.SltOutput; import com.en_circle.slt.plugin.environment.SltLispEnvironmentConfiguration; @@ -208,6 +209,7 @@ private void doStop() throws Exception { try { client.close(); } finally { + overrides = null; indentationContainer.clear(); indentationContainer.clear(); symbolCache.clear(); @@ -330,6 +332,14 @@ public LispSltOverrides getOverrides() { return overrides; } + @Override + public boolean hasFeature(LispFeatures feature) { + if (environment != null) { + return environment.getType().getDefinition().hasFeature(feature); + } + return false; + } + @Override public void dispose() { try { diff --git a/src/main/java/com/en_circle/slt/plugin/ui/SltCoreWindow.java b/src/main/java/com/en_circle/slt/plugin/ui/SltCoreWindow.java index d0d8c06..0d74410 100644 --- a/src/main/java/com/en_circle/slt/plugin/ui/SltCoreWindow.java +++ b/src/main/java/com/en_circle/slt/plugin/ui/SltCoreWindow.java @@ -1,6 +1,7 @@ package com.en_circle.slt.plugin.ui; import com.en_circle.slt.plugin.SltBundle; +import com.en_circle.slt.plugin.environment.LispFeatures; import com.en_circle.slt.plugin.environment.SltLispEnvironment.SltOutput; import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService; import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService.LispEnvironmentListener; @@ -236,7 +237,8 @@ public void actionPerformed(@NotNull AnActionEvent e) { public void update(@NotNull AnActionEvent e) { super.update(e); - e.getPresentation().setEnabled(LispEnvironmentService.getInstance(project).getState() == LispEnvironmentState.READY); + e.getPresentation().setEnabled(LispEnvironmentService.getInstance(project).getState() == LispEnvironmentState.READY && + LispEnvironmentService.getInstance(project).hasFeature(LispFeatures.REPL)); } } diff --git a/src/main/java/com/en_circle/slt/plugin/ui/console/SltConsole.java b/src/main/java/com/en_circle/slt/plugin/ui/console/SltConsole.java index 511024d..fe95344 100644 --- a/src/main/java/com/en_circle/slt/plugin/ui/console/SltConsole.java +++ b/src/main/java/com/en_circle/slt/plugin/ui/console/SltConsole.java @@ -2,6 +2,7 @@ import com.en_circle.slt.plugin.SltBundle; import com.en_circle.slt.plugin.SltCommonLispLanguage; +import com.en_circle.slt.plugin.environment.LispFeatures; import com.en_circle.slt.plugin.environment.SltLispEnvironment.SltOutput; import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService; import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService.LispEnvironmentState; @@ -99,7 +100,11 @@ public void onPreStart() { @Override public void onPostStart() { - + if (!LispEnvironmentService.getInstance(project).hasFeature(LispFeatures.REPL)) { + languageConsole.setEditable(false); + } else { + languageConsole.setEditable(true); + } } @Override diff --git a/src/main/java/com/en_circle/slt/plugin/ui/debug/SltFrameInfo.java b/src/main/java/com/en_circle/slt/plugin/ui/debug/SltFrameInfo.java index 416f5d7..5725974 100644 --- a/src/main/java/com/en_circle/slt/plugin/ui/debug/SltFrameInfo.java +++ b/src/main/java/com/en_circle/slt/plugin/ui/debug/SltFrameInfo.java @@ -2,6 +2,7 @@ import com.en_circle.slt.plugin.SltBundle; import com.en_circle.slt.plugin.SltUIConstants; +import com.en_circle.slt.plugin.environment.LispFeatures; import com.en_circle.slt.plugin.lisp.lisp.*; import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService; import com.en_circle.slt.plugin.swank.requests.FrameLocalsAndCatchTags; @@ -56,23 +57,25 @@ public SltFrameInfo(Project project, BigInteger threadId, BigInteger frameId, St private void create() { tabs = new JBTabsImpl(project); - localsTable = new JBTable(new FrameTableModel(new ArrayList<>())); + localsTable = new JBTable(new FrameListTableModel(new ArrayList<>())); localsTable.setFillsViewportHeight(true); localsTable.setFocusable(false); localsTable.setRowSelectionAllowed(false); - localsTable.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - int row = localsTable.rowAtPoint(e.getPoint()); - if (row >= 0) { - int column = localsTable.columnAtPoint(e.getPoint()); - if (column == 0) { - FrameTableModel model = (FrameTableModel) localsTable.getModel(); - openInspector(model.locals.get(row)); + if (LispEnvironmentService.getInstance(project).hasFeature(LispFeatures.INSPECTOR)) { + localsTable.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + int row = localsTable.rowAtPoint(e.getPoint()); + if (row >= 0) { + int column = localsTable.columnAtPoint(e.getPoint()); + if (column == 0) { + FrameListTableModel model = (FrameListTableModel) localsTable.getModel(); + openInspector(model.locals.get(row)); + } } } - } - }); + }); + } resetRenderer(); TabInfo locals = new TabInfo(new JBScrollPane(localsTable)); @@ -97,11 +100,13 @@ private void resetRenderer() { public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - Map attributes = new HashMap<>(getFont().getAttributes()); - attributes.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON); - setFont(getFont().deriveFont(attributes)); - setForeground(SltUIConstants.HYPERLINK_COLOR); - setCursor(new Cursor(Cursor.HAND_CURSOR)); + if (LispEnvironmentService.getInstance(project).hasFeature(LispFeatures.INSPECTOR)) { + Map attributes = new HashMap<>(getFont().getAttributes()); + attributes.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON); + setFont(getFont().deriveFont(attributes)); + setForeground(SltUIConstants.HYPERLINK_COLOR); + setCursor(new Cursor(Cursor.HAND_CURSOR)); + } return this; } @@ -152,10 +157,10 @@ public void refreshFrameValues(LispElement localsAndTags) { parsedLocals.add(l); } } - localsTable.setModel(new FrameTableModel(parsedLocals)); + localsTable.setModel(new FrameListTableModel(parsedLocals)); resetRenderer(); } else { - localsTable.setModel(new FrameTableModel(new ArrayList<>())); + localsTable.setModel(new FrameListTableModel(new ArrayList<>())); resetRenderer(); } } @@ -168,11 +173,11 @@ public static class Local { } - private static class FrameTableModel extends AbstractTableModel { + private static class FrameListTableModel extends AbstractTableModel { private final List locals; - private FrameTableModel(List locals) { + private FrameListTableModel(List locals) { this.locals = locals; } diff --git a/src/main/java/com/en_circle/slt/plugin/ui/debug/SltInspector.java b/src/main/java/com/en_circle/slt/plugin/ui/debug/SltInspector.java index f3bad4f..79060e2 100644 --- a/src/main/java/com/en_circle/slt/plugin/ui/debug/SltInspector.java +++ b/src/main/java/com/en_circle/slt/plugin/ui/debug/SltInspector.java @@ -2,6 +2,7 @@ import com.en_circle.slt.plugin.SltBundle; import com.en_circle.slt.plugin.SltUIConstants; +import com.en_circle.slt.plugin.environment.LispFeatures; import com.en_circle.slt.plugin.lisp.lisp.LispContainer; import com.en_circle.slt.plugin.lisp.lisp.LispElement; import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService; @@ -170,7 +171,8 @@ public void actionPerformed(@NotNull AnActionEvent event) { public void update(@NotNull AnActionEvent e) { super.update(e); - e.getPresentation().setEnabled(LispEnvironmentService.getInstance(project).getState() == LispEnvironmentState.READY); + e.getPresentation().setEnabled(LispEnvironmentService.getInstance(project).getState() == LispEnvironmentState.READY && + LispEnvironmentService.getInstance(project).hasFeature(LispFeatures.INSPECTOR_HISTORY)); } } @@ -196,7 +198,8 @@ public void actionPerformed(@NotNull AnActionEvent event) { public void update(@NotNull AnActionEvent e) { super.update(e); - e.getPresentation().setEnabled(LispEnvironmentService.getInstance(project).getState() ==LispEnvironmentState.READY); + e.getPresentation().setEnabled(LispEnvironmentService.getInstance(project).getState() ==LispEnvironmentState.READY && + LispEnvironmentService.getInstance(project).hasFeature(LispFeatures.INSPECTOR_HISTORY)); } } From cdb83ee30d211f9e53ac88e96f9683581b586ca2 Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Sun, 5 Feb 2023 13:13:24 +0100 Subject: [PATCH 08/40] fixed abcl not evaluating correctly + removed useless feature advert since without evaluation such interpret is useless --- FEATURES.md | 2 +- .../slt/plugin/actions/EvalActionBase.java | 7 +------ .../en_circle/slt/plugin/actions/EvalFile.java | 2 +- .../slt/plugin/environment/LispFeatures.java | 1 - .../abcl/ABCLEnvironmentDefinition.java | 2 -- .../sbcl/SBCLEnvironmentDefinition.java | 1 - src/main/lisp/swank/swank-abcl.lisp | 15 +++++++++++++++ 7 files changed, 18 insertions(+), 12 deletions(-) diff --git a/FEATURES.md b/FEATURES.md index 7ce93d5..52a5aaa 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -18,7 +18,7 @@ This is a list of features that are supported. | References | ✅ | ❎ | | Inspector | ✅¹ | ✅² | | Autocomplete | ✅ | ✅ | -| Find References | ✅ | ✅ | +| Find References | ✅ | ❎ | 1 - Only read-only inspector available diff --git a/src/main/java/com/en_circle/slt/plugin/actions/EvalActionBase.java b/src/main/java/com/en_circle/slt/plugin/actions/EvalActionBase.java index bce4189..28be35e 100644 --- a/src/main/java/com/en_circle/slt/plugin/actions/EvalActionBase.java +++ b/src/main/java/com/en_circle/slt/plugin/actions/EvalActionBase.java @@ -2,7 +2,6 @@ import com.en_circle.slt.plugin.SltBundle; import com.en_circle.slt.plugin.SltCommonLispFileType; -import com.en_circle.slt.plugin.environment.LispFeatures; import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService; import com.en_circle.slt.plugin.swank.requests.Eval; import com.en_circle.slt.plugin.swank.requests.EvalFromVirtualFile; @@ -35,7 +34,7 @@ public void update(@NotNull AnActionEvent event) { if (editor != null && event.getProject() != null) { PsiFile file = PsiDocumentManager.getInstance(Objects.requireNonNull(editor.getProject())).getPsiFile(editor.getDocument()); if (file != null && SltCommonLispFileType.INSTANCE.equals(file.getFileType())) { - event.getPresentation().setEnabledAndVisible(isEnabledInLisp(event.getProject())); + event.getPresentation().setEnabledAndVisible(true); } } } @@ -69,10 +68,6 @@ public static void evaluateFile(Project project, String filename, VirtualFile vi } } - protected boolean isEnabledInLisp(Project project) { - return LispEnvironmentService.getInstance(project).hasFeature(LispFeatures.REPL); - } - @Override public @NotNull ActionUpdateThread getActionUpdateThread() { return ActionUpdateThread.BGT; diff --git a/src/main/java/com/en_circle/slt/plugin/actions/EvalFile.java b/src/main/java/com/en_circle/slt/plugin/actions/EvalFile.java index 2aafcd0..961af8d 100644 --- a/src/main/java/com/en_circle/slt/plugin/actions/EvalFile.java +++ b/src/main/java/com/en_circle/slt/plugin/actions/EvalFile.java @@ -33,7 +33,7 @@ public void update(@NotNull AnActionEvent event) { VirtualFile vf = event.getData(CommonDataKeys.VIRTUAL_FILE); if (vf != null) { if (vf.getFileType().equals(SltCommonLispFileType.INSTANCE)) { - event.getPresentation().setEnabledAndVisible(isEnabledInLisp(event.getProject())); + event.getPresentation().setEnabledAndVisible(true); } } } diff --git a/src/main/java/com/en_circle/slt/plugin/environment/LispFeatures.java b/src/main/java/com/en_circle/slt/plugin/environment/LispFeatures.java index 62a4ba8..04f9f1c 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/LispFeatures.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/LispFeatures.java @@ -3,7 +3,6 @@ public enum LispFeatures { REPL, - EVALUATION, DOCUMENTATION, MACROEXPAND, INSPECTOR, diff --git a/src/main/java/com/en_circle/slt/plugin/environment/abcl/ABCLEnvironmentDefinition.java b/src/main/java/com/en_circle/slt/plugin/environment/abcl/ABCLEnvironmentDefinition.java index 3e0bf86..837b8af 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/abcl/ABCLEnvironmentDefinition.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/abcl/ABCLEnvironmentDefinition.java @@ -20,12 +20,10 @@ public class ABCLEnvironmentDefinition extends EnvironmentDefinition { public ABCLEnvironmentDefinition() { features.add(LispFeatures.REPL); - features.add(LispFeatures.EVALUATION); features.add(LispFeatures.DOCUMENTATION); features.add(LispFeatures.MACROEXPAND); features.add(LispFeatures.INSPECTOR); features.add(LispFeatures.AUTOCOMPLETE); - features.add(LispFeatures.SEARCH); } @Override diff --git a/src/main/java/com/en_circle/slt/plugin/environment/sbcl/SBCLEnvironmentDefinition.java b/src/main/java/com/en_circle/slt/plugin/environment/sbcl/SBCLEnvironmentDefinition.java index 352a2a5..c670697 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/sbcl/SBCLEnvironmentDefinition.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/sbcl/SBCLEnvironmentDefinition.java @@ -21,7 +21,6 @@ public class SBCLEnvironmentDefinition extends EnvironmentDefinition { public SBCLEnvironmentDefinition() { features.add(LispFeatures.REPL); - features.add(LispFeatures.EVALUATION); features.add(LispFeatures.DOCUMENTATION); features.add(LispFeatures.MACROEXPAND); features.add(LispFeatures.INSPECTOR); diff --git a/src/main/lisp/swank/swank-abcl.lisp b/src/main/lisp/swank/swank-abcl.lisp index b0441b5..412ddcf 100644 --- a/src/main/lisp/swank/swank-abcl.lisp +++ b/src/main/lisp/swank/swank-abcl.lisp @@ -47,6 +47,21 @@ , (:align t)))))))) +(defimplementation swank-compile-string (string &key buffer position filename + line column policy) + (declare (ignore filename line column policy)) + (let ((jvm::*resignal-compiler-warnings* t) + (*abcl-signaled-conditions* nil)) + (handler-bind ((warning #'handle-compiler-warning)) + (let ((*buffer-name* buffer) + (*buffer-start-position* position) + (*buffer-string* string) + (sys::*source* (pathname buffer)) + (sys::*source-position* position)) + (funcall (compile nil (read-from-string + (format nil "(~S () ~A)" 'lambda string)))) + t)))) + (in-package :swank) (defslimefun backtrace (start end) From f691797577083635289803b4a3195e4c26a614b8 Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Sun, 5 Feb 2023 09:28:14 -0800 Subject: [PATCH 09/40] ABCL does not support frame eval, another feature into feature list then to filter out for unsupported platforms --- FEATURES.md | 1 + .../en_circle/slt/plugin/environment/LispFeatures.java | 1 + .../environment/sbcl/SBCLEnvironmentDefinition.java | 1 + .../com/en_circle/slt/plugin/ui/debug/SltFrameInfo.java | 8 +++++--- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/FEATURES.md b/FEATURES.md index 52a5aaa..7975b55 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -14,6 +14,7 @@ This is a list of features that are supported. | Documentation | ✅ | ✅ | | Macroexpand | ✅ | ✅ | | Debugger | ✅ | ✅ | +| Frame Eval | ✅ | ❎ | | Stepping Debugger | ❎ | ❎ | | References | ✅ | ❎ | | Inspector | ✅¹ | ✅² | diff --git a/src/main/java/com/en_circle/slt/plugin/environment/LispFeatures.java b/src/main/java/com/en_circle/slt/plugin/environment/LispFeatures.java index 04f9f1c..8e3337e 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/LispFeatures.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/LispFeatures.java @@ -5,6 +5,7 @@ public enum LispFeatures { REPL, DOCUMENTATION, MACROEXPAND, + FRAME_EVAL, INSPECTOR, INSPECTOR_HISTORY, AUTOCOMPLETE, diff --git a/src/main/java/com/en_circle/slt/plugin/environment/sbcl/SBCLEnvironmentDefinition.java b/src/main/java/com/en_circle/slt/plugin/environment/sbcl/SBCLEnvironmentDefinition.java index c670697..b62bcdb 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/sbcl/SBCLEnvironmentDefinition.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/sbcl/SBCLEnvironmentDefinition.java @@ -23,6 +23,7 @@ public SBCLEnvironmentDefinition() { features.add(LispFeatures.REPL); features.add(LispFeatures.DOCUMENTATION); features.add(LispFeatures.MACROEXPAND); + features.add(LispFeatures.FRAME_EVAL); features.add(LispFeatures.INSPECTOR); features.add(LispFeatures.INSPECTOR_HISTORY); features.add(LispFeatures.AUTOCOMPLETE); diff --git a/src/main/java/com/en_circle/slt/plugin/ui/debug/SltFrameInfo.java b/src/main/java/com/en_circle/slt/plugin/ui/debug/SltFrameInfo.java index 5725974..9dd91cd 100644 --- a/src/main/java/com/en_circle/slt/plugin/ui/debug/SltFrameInfo.java +++ b/src/main/java/com/en_circle/slt/plugin/ui/debug/SltFrameInfo.java @@ -82,9 +82,11 @@ public void mouseClicked(MouseEvent e) { locals.setText(SltBundle.message("slt.ui.debugger.frame.locals")); tabs.addTab(locals); - SltFrameConsole frameConsole = new SltFrameConsole(project, threadId, frameId, this::reloadLocals, module); - TabInfo consoleTab = frameConsole.create(); - tabs.addTab(consoleTab); + if (LispEnvironmentService.getInstance(project).hasFeature(LispFeatures.FRAME_EVAL)) { + SltFrameConsole frameConsole = new SltFrameConsole(project, threadId, frameId, this::reloadLocals, module); + TabInfo consoleTab = frameConsole.create(); + tabs.addTab(consoleTab); + } inspector = new SltInspector(project, threadId); inspectorTab = new TabInfo(new JBScrollPane(inspector.getContent())); From 316fc1db62da0eecb734a1737dd38c5de5a3855c Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Mon, 6 Feb 2023 02:25:08 +0100 Subject: [PATCH 10/40] fixed documentation escaping --- .../slt/plugin/SltDocumentationProvider.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/en_circle/slt/plugin/SltDocumentationProvider.java b/src/main/java/com/en_circle/slt/plugin/SltDocumentationProvider.java index d6f1002..e8c2530 100644 --- a/src/main/java/com/en_circle/slt/plugin/SltDocumentationProvider.java +++ b/src/main/java/com/en_circle/slt/plugin/SltDocumentationProvider.java @@ -11,6 +11,7 @@ import com.intellij.openapi.util.text.HtmlChunk; import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiTreeUtil; +import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.Nullable; @@ -25,7 +26,7 @@ public class SltDocumentationProvider extends AbstractDocumentationProvider { if (!(element instanceof LispSymbol)) element = PsiTreeUtil.getParentOfType(element, LispSymbol.class); - if (element instanceof LispSymbol) { + if (element != null) { String text = ((LispSymbol) element).getName(); String packageName = LispParserUtil.getPackage(element); SymbolState state = LispEnvironmentService.getInstance(element.getProject()).refreshSymbolFromServer(packageName, text); @@ -84,9 +85,10 @@ private PsiElement decideOnElement(PsiElement element, PsiElement originalElemen private String asHtml(SymbolState state, String packageName, PsiElement element) { HtmlBuilder builder = new HtmlBuilder(); if (LispEnvironmentService.getInstance(element.getProject()).hasFeature(LispFeatures.DOCUMENTATION)) { - String documentation = StringUtils.replace(StringUtils.replace(state.documentation, " ", " "), + String documentation = StringUtils.replace(StringUtils.replace(escape(state.documentation), " ", " "), "\n", HtmlChunk.br().toString()); - builder.append(documentation == null ? HtmlChunk.raw("") : HtmlChunk.raw(documentation)); + builder.append(documentation == null ? HtmlChunk.raw("") : + HtmlChunk.raw(documentation)); } if (LispEnvironmentService.getInstance(element.getProject()).hasFeature(LispFeatures.MACROEXPAND)) { @@ -94,7 +96,7 @@ private String asHtml(SymbolState state, String packageName, PsiElement element) if (form != null && state.binding == SymbolBinding.MACRO) { String macroExpand = LispEnvironmentService.getInstance(element.getProject()).macroexpand(form, packageName); if (macroExpand != null) { - macroExpand = StringUtils.replace(StringUtils.replace(macroExpand, " ", " "), + macroExpand = StringUtils.replace(StringUtils.replace(escape(macroExpand), " ", " "), "\n", HtmlChunk.br().toString()); builder.append(HtmlChunk.hr()); builder.append(HtmlChunk.text(SltBundle.message("slt.documentation.macroexpand"))); @@ -110,4 +112,9 @@ private String asHtml(SymbolState state, String packageName, PsiElement element) String doc = builder.toString(); return StringUtils.isBlank(doc) ? null : doc; } + + + private String escape(String s) { + return StringEscapeUtils.escapeHtml(s); + } } From 85a75df5db6c41464ddc873230e3246269c0564d Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Mon, 6 Feb 2023 02:25:30 +0100 Subject: [PATCH 11/40] fixed references --- .../com/en_circle/slt/plugin/references/SltReference.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/en_circle/slt/plugin/references/SltReference.java b/src/main/java/com/en_circle/slt/plugin/references/SltReference.java index dca9ae7..cee56e9 100644 --- a/src/main/java/com/en_circle/slt/plugin/references/SltReference.java +++ b/src/main/java/com/en_circle/slt/plugin/references/SltReference.java @@ -202,7 +202,10 @@ private ResolveResult convertLocationToReference(SourceLocation location, String PsiFile f = PsiManager.getInstance(myElement.getProject()).findFile(vf); if (f == null) return null; - return new SltLazyElementResolve(f, location.getPosition()); + PsiElement element = f.findElementAt(location.getPosition()); + if (element == null) + return null; + return new PsiElementResolveResult(element); } @Override From c2fa32ff1681f2de99e1234d25a1f892361bf1b6 Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Mon, 6 Feb 2023 02:26:02 +0100 Subject: [PATCH 12/40] ccl init support --- FEATURES.md | 34 +-- .../slt/plugin/environment/Environment.java | 4 +- .../slt/plugin/environment/LispInterpret.java | 3 +- .../plugin/environment/SltCCLEnvironment.java | 170 +++++++++++++ .../SltCCLEnvironmentConfiguration.java | 94 +++++++ .../clisp/CCLEnvironmentDefinition.java | 74 ++++++ .../environment/clisp/CCLOverrides.java | 10 + .../references/SltLazyElementResolve.java | 27 --- .../com/en_circle/slt/plugin/sdk/LispSdk.java | 4 + .../lisp/LispEnvironmentServiceImpl.java | 38 +-- .../slt/plugin/ui/SltGeneralLog.java | 2 +- .../ui/sdk/SdkConfigurationCCLProcess.java | 229 ++++++++++++++++++ .../ui/sdk/SdkConfigurationSBCLProcess.java | 4 +- .../slt/plugin/ui/sdk/SdkSelector.java | 2 + .../com/en_circle/slt/tools/ABCLUtils.java | 27 ++- .../com/en_circle/slt/tools/CCLUtils.java | 93 +++++++ .../com/en_circle/slt/tools/SBCLUtils.java | 27 ++- src/main/lisp/load.lisp | 5 + src/main/lisp/slt/slt-ccl.lisp | 4 + src/main/lisp/slt/slt.lisp | 2 + src/main/lisp/swank/swank-abcl.lisp | 62 +++-- src/main/lisp/swank/swank-ccl.lisp | 19 ++ src/main/lisp/swank/swank.lisp | 7 +- .../resources/messages/SltBundle.properties | 14 +- .../resources/templates/en_US/initscript.cl | 2 +- src/main/resources/templates/en_US/verify.cl | 3 +- 26 files changed, 861 insertions(+), 99 deletions(-) create mode 100644 src/main/java/com/en_circle/slt/plugin/environment/SltCCLEnvironment.java create mode 100644 src/main/java/com/en_circle/slt/plugin/environment/SltCCLEnvironmentConfiguration.java create mode 100644 src/main/java/com/en_circle/slt/plugin/environment/clisp/CCLEnvironmentDefinition.java create mode 100644 src/main/java/com/en_circle/slt/plugin/environment/clisp/CCLOverrides.java delete mode 100644 src/main/java/com/en_circle/slt/plugin/references/SltLazyElementResolve.java create mode 100644 src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationCCLProcess.java create mode 100644 src/main/java/com/en_circle/slt/tools/CCLUtils.java create mode 100644 src/main/lisp/slt/slt-ccl.lisp create mode 100644 src/main/lisp/swank/swank-ccl.lisp diff --git a/FEATURES.md b/FEATURES.md index 7975b55..68bed0b 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -7,20 +7,24 @@ This is a list of features that are supported. | ❓ | Not implemented but might be in future | | ❎ | Is not supported | -| Features / Lisp | SBCL | ABCL | -|-------------------|------|------| -| REPL | ✅️ | ✅️ | -| Buffer Evaluation | ✅️ | ✅️ | -| Documentation | ✅ | ✅ | -| Macroexpand | ✅ | ✅ | -| Debugger | ✅ | ✅ | -| Frame Eval | ✅ | ❎ | -| Stepping Debugger | ❎ | ❎ | -| References | ✅ | ❎ | -| Inspector | ✅¹ | ✅² | -| Autocomplete | ✅ | ✅ | -| Find References | ✅ | ❎ | +Unsupported and will not be supported implementations: -1 - Only read-only inspector available +* CLISP - does not work, maybe with threads, but single threaded it's useless and crashes on debug attempt -2 - Only read-only inspector available, also no history support \ No newline at end of file +| Features / Lisp | SBCL | ABCL | CCL | +|-------------------|------|------|-----| +| REPL | ✅️ | ✅️ | ✅️ | +| Buffer Evaluation | ✅️ | ✅️ | ✅️ | +| Documentation | ✅ | ✅ | ✅️ | +| Macroexpand | ✅ | ✅ | ✅️ | +| Debugger | ✅ | ✅ | ️ | +| Frame Eval | ✅ | ❎ | ️ | +| Stepping Debugger | ❎ | ❎ | ️ | +| References | ✅ | ❎ | ✅️ | +| Inspector | ✅¹ | ✅² | ️ | +| Autocomplete | ✅ | ✅ | | +| Find References | ✅ | ❎ | ️ | + +¹Only read-only inspector available + +²Only read-only inspector available, also no history support \ No newline at end of file diff --git a/src/main/java/com/en_circle/slt/plugin/environment/Environment.java b/src/main/java/com/en_circle/slt/plugin/environment/Environment.java index 82cf490..ecfbef0 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/Environment.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/Environment.java @@ -1,12 +1,14 @@ package com.en_circle.slt.plugin.environment; import com.en_circle.slt.plugin.environment.abcl.ABCLEnvironmentDefinition; +import com.en_circle.slt.plugin.environment.clisp.CCLEnvironmentDefinition; import com.en_circle.slt.plugin.environment.sbcl.SBCLEnvironmentDefinition; public enum Environment { ABCL_PROCESS(new ABCLEnvironmentDefinition()), - SBCL_PROCESS(new SBCLEnvironmentDefinition()) + SBCL_PROCESS(new SBCLEnvironmentDefinition()), + CCL_PROCESS(new CCLEnvironmentDefinition()), ; diff --git a/src/main/java/com/en_circle/slt/plugin/environment/LispInterpret.java b/src/main/java/com/en_circle/slt/plugin/environment/LispInterpret.java index 6bc4939..cebae97 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/LispInterpret.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/LispInterpret.java @@ -3,7 +3,8 @@ public enum LispInterpret { ABCL(":abcl"), - SBCL(":sbcl") + SBCL(":sbcl"), + CCL(":ccl"), ; diff --git a/src/main/java/com/en_circle/slt/plugin/environment/SltCCLEnvironment.java b/src/main/java/com/en_circle/slt/plugin/environment/SltCCLEnvironment.java new file mode 100644 index 0000000..40f45c9 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/environment/SltCCLEnvironment.java @@ -0,0 +1,170 @@ +package com.en_circle.slt.plugin.environment; + +import com.en_circle.slt.plugin.SltLibrary; +import com.en_circle.slt.plugin.environment.SltProcessStreamGobbler.ProcessInitializationWaiter; +import com.en_circle.slt.plugin.environment.SltProcessStreamGobbler.WaitForOccurrence; +import com.en_circle.slt.tools.PluginPath; +import com.intellij.openapi.util.io.FileUtil; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; +import org.watertemplate.Template; + +import java.io.File; +import java.io.IOException; +import java.net.ServerSocket; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +public class SltCCLEnvironment extends SltLispEnvironmentProcess { + + private int port; + + @Override + public int getSwankPort() { + return port; + } + + @Override + public SltLispProcessInformation getInformation() { + return new SltCCLLispProcessInformation(); + } + + @Override + public Environment getType() { + return Environment.CCL_PROCESS; + } + + @Override + protected Object prepareProcessEnvironment(SltLispEnvironmentProcessConfiguration configuration) throws SltProcessException { + SltCCLEnvironmentConfiguration c = getConfiguration(configuration); + CCLEnvironment e = new CCLEnvironment(); + try { + e.port = getFreePort(); + if (e.port == 0) { + throw new IOException("no free port available"); + } + + File tempDir = FileUtil.createTempDirectory(PluginPath.getPluginFolder(), + "SLTinit", ""); + + e.sltCore = SltLibrary.getLibraryInitFile(); + + e.serverStartSetup = new File(tempDir, "startServer.cl"); + e.serverStartSetup.deleteOnExit(); + String sltCorePath = e.sltCore.getAbsolutePath(); + String startScriptTemplate = new CCLInitScriptTemplate(c, sltCorePath, e.port).render(); + FileUtils.write(e.serverStartSetup, startScriptTemplate, StandardCharsets.UTF_8); + + tempDir.deleteOnExit(); + } catch (Exception ex) { + throw new SltProcessException(ex); + } + return e; + } + + @Override + protected File getProcessWorkDirectory(SltLispEnvironmentProcessConfiguration configuration, Object environment) throws SltProcessException { + SltCCLEnvironmentConfiguration c = getConfiguration(configuration); + CCLEnvironment e = getEnvironment(environment); + + return e.serverStartSetup.getParentFile(); + } + + @Override + protected String[] getProcessCommand(SltLispEnvironmentProcessConfiguration configuration, Object environment) throws SltProcessException { + SltCCLEnvironmentConfiguration c = getConfiguration(configuration); + CCLEnvironment e = getEnvironment(environment); + this.port = e.port; + + List parameters = new ArrayList<>(); + parameters.add(c.getExecutablePath()); + if (StringUtils.isNotBlank(c.getMemoryImage())) { + parameters.add("-I"); + parameters.add(c.getMemoryImage()); + } + + parameters.add("-l"); + parameters.add(e.serverStartSetup.getName()); + + return parameters.toArray(new String[0]); + } + + @Override + protected ProcessInitializationWaiter waitForFullInitialization(SltLispEnvironmentProcessConfiguration configuration, Object environment) throws SltProcessException { + SltCCLEnvironmentConfiguration c = getConfiguration(configuration); + CCLEnvironment e = getEnvironment(environment); + + WaitForOccurrence wait = new WaitForOccurrence("Swank started at port"); + errorController.addUpdateListener(wait); + + return wait; + } + + private SltCCLEnvironmentConfiguration getConfiguration(SltLispEnvironmentProcessConfiguration configuration) throws SltProcessException { + if (!(configuration instanceof SltCCLEnvironmentConfiguration)) + throw new SltProcessException("Configuration must be SltCCLEnvironmentConfiguration"); + return (SltCCLEnvironmentConfiguration) configuration; + } + + private CCLEnvironment getEnvironment(Object environment) { + assert (environment instanceof CCLEnvironment); + + return (CCLEnvironment) environment; + } + + private int getFreePort() { + var freePort = 0; + try (ServerSocket s = new ServerSocket(0)) { + freePort = s.getLocalPort(); + } catch (Exception ignored) { + + } + + return freePort; + } + + + private class SltCCLLispProcessInformation implements SltLispProcessInformation { + + @Override + public String getPid() { + return "" + process.pid(); + } + } + + private static class CCLEnvironment { + + File sltCore; + File serverStartSetup; + int port; + + } + + private static class CCLInitScriptTemplate extends Template { + + public CCLInitScriptTemplate(SltCCLEnvironmentConfiguration configuration, String sltCoreScript, int port) { + String quicklispPath = configuration.getQuicklispStartScript(); + if (quicklispPath.contains("\\")) { + quicklispPath = StringUtils.replace(quicklispPath, "\\", "\\\\"); + } + String cwd = configuration.getProjectDirectory(); + if (cwd.contains("\\")) { + cwd = StringUtils.replace(cwd, "\\", "\\\\"); + } + if (sltCoreScript.contains("\\")) { + sltCoreScript = StringUtils.replace(sltCoreScript, "\\", "\\\\"); + } + add("qlpath", quicklispPath); + add("port", "" + port); + add("cwd", cwd); + add("corefile", sltCoreScript); + add("interpret", LispInterpret.CCL.symbolName); + } + + @Override + protected String getFilePath() { + return "initscript.cl"; + } + } +} diff --git a/src/main/java/com/en_circle/slt/plugin/environment/SltCCLEnvironmentConfiguration.java b/src/main/java/com/en_circle/slt/plugin/environment/SltCCLEnvironmentConfiguration.java new file mode 100644 index 0000000..eb45ab7 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/environment/SltCCLEnvironmentConfiguration.java @@ -0,0 +1,94 @@ +package com.en_circle.slt.plugin.environment; + + +import com.en_circle.slt.plugin.environment.SltLispEnvironment.SltLispOutputChangedListener; +import com.en_circle.slt.plugin.environment.SltLispEnvironmentProcess.SltLispEnvironmentProcessConfiguration; + +public class SltCCLEnvironmentConfiguration implements SltLispEnvironmentProcessConfiguration { + + private String executablePath = "clisp"; + private String memoryImage = ""; + private String quicklispStartScript = "~/quicklisp/setup.lisp"; + private String projectDirectory = "/tmp"; + private SltLispOutputChangedListener listener = null; + + private SltCCLEnvironmentConfiguration() { + + } + + public String getExecutablePath() { + return executablePath; + } + + public String getMemoryImage() { + return memoryImage; + } + + public String getQuicklispStartScript() { + return quicklispStartScript; + } + + public String getProjectDirectory() { + return projectDirectory; + } + + @Override + public SltLispOutputChangedListener getListener() { + return listener; + } + + public static class Builder implements SltLispEnvironmentConfiguration.Builder { + + private final SltCCLEnvironmentConfiguration c = new SltCCLEnvironmentConfiguration(); + private boolean built = false; + + public Builder setExecutable(String executable) { + checkNotBuilt(); + + c.executablePath = executable; + return this; + } + + public Builder setMemoryImage(String memoryImage) { + checkNotBuilt(); + + c.memoryImage = memoryImage; + return this; + } + + public Builder setQuicklispStartScriptPath(String quicklispStartScript) { + checkNotBuilt(); + + c.quicklispStartScript = quicklispStartScript; + return this; + } + + public Builder setProjectDirectory(String projectDirectory) { + checkNotBuilt(); + + c.projectDirectory = projectDirectory; + return this; + } + + @Override + public Builder setListener(SltLispOutputChangedListener listener) { + checkNotBuilt(); + + c.listener = listener; + return this; + } + + @Override + public SltCCLEnvironmentConfiguration build() { + checkNotBuilt(); + built = true; + return c; + } + + private void checkNotBuilt() { + if (built) throw new IllegalStateException("already built"); + } + + } + +} diff --git a/src/main/java/com/en_circle/slt/plugin/environment/clisp/CCLEnvironmentDefinition.java b/src/main/java/com/en_circle/slt/plugin/environment/clisp/CCLEnvironmentDefinition.java new file mode 100644 index 0000000..9f44208 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/environment/clisp/CCLEnvironmentDefinition.java @@ -0,0 +1,74 @@ +package com.en_circle.slt.plugin.environment.clisp; + +import com.en_circle.slt.plugin.SltBundle; +import com.en_circle.slt.plugin.environment.*; +import com.en_circle.slt.plugin.environment.SltLispEnvironmentConfiguration.Builder; +import com.en_circle.slt.plugin.sdk.LispSdk; +import com.en_circle.slt.plugin.services.lisp.LispSltOverrides; +import com.en_circle.slt.plugin.ui.sdk.SdkConfigurationCCLProcess; +import com.en_circle.slt.plugin.ui.sdk.SdkDialogProvider; +import com.en_circle.slt.tools.platform.DownloadLispAction; +import com.intellij.openapi.project.Project; + +import java.util.HashSet; +import java.util.Set; +import java.util.function.Supplier; + +public class CCLEnvironmentDefinition extends EnvironmentDefinition { + + private final Set features = new HashSet<>(); + + public CCLEnvironmentDefinition() { + features.add(LispFeatures.REPL); + features.add(LispFeatures.DOCUMENTATION); + features.add(LispFeatures.MACROEXPAND); + features.add(LispFeatures.FRAME_EVAL); + features.add(LispFeatures.INSPECTOR); + features.add(LispFeatures.INSPECTOR_HISTORY); + features.add(LispFeatures.AUTOCOMPLETE); + features.add(LispFeatures.SEARCH); + features.add(LispFeatures.XREFS); + } + + @Override + public String getName() { + return SltBundle.message("slt.environment.ccl"); + } + + @Override + public Class getDownloadActionDef() { + return null; + } + + @Override + public Supplier getEnvironmentCreator() { + return SltCCLEnvironment::new; + } + + @SuppressWarnings("unchecked") + @Override + public , R extends SltLispEnvironmentConfiguration> + Builder buildConfiguration(LispSdk sdk, Project project) { + return (Builder) new SltCCLEnvironmentConfiguration.Builder() + .setExecutable(sdk.cclExecutable) + .setMemoryImage(sdk.cclMemoryImage) + .setQuicklispStartScriptPath(sdk.quickLispPath) + .setProjectDirectory(project.getBasePath()); + } + + @Override + public SdkDialogProvider getDialogProvider() { + return SdkConfigurationCCLProcess::new; + } + + @Override + public LispSltOverrides getOverrides() { + return new CCLOverrides(); + } + + @Override + public boolean hasFeature(LispFeatures feature) { + return features.contains(feature); + } + +} diff --git a/src/main/java/com/en_circle/slt/plugin/environment/clisp/CCLOverrides.java b/src/main/java/com/en_circle/slt/plugin/environment/clisp/CCLOverrides.java new file mode 100644 index 0000000..5fc9235 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/environment/clisp/CCLOverrides.java @@ -0,0 +1,10 @@ +package com.en_circle.slt.plugin.environment.clisp; + +import com.en_circle.slt.plugin.environment.LispSltOverridesBase; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CCLOverrides extends LispSltOverridesBase { + private static final Logger log = LoggerFactory.getLogger(CCLOverrides.class); + +} diff --git a/src/main/java/com/en_circle/slt/plugin/references/SltLazyElementResolve.java b/src/main/java/com/en_circle/slt/plugin/references/SltLazyElementResolve.java deleted file mode 100644 index 093856e..0000000 --- a/src/main/java/com/en_circle/slt/plugin/references/SltLazyElementResolve.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.en_circle.slt.plugin.references; - -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.ResolveResult; -import org.jetbrains.annotations.Nullable; - -public class SltLazyElementResolve implements ResolveResult { - - private final PsiFile file; - private final int offset; - - public SltLazyElementResolve(PsiFile file, int offset) { - this.file = file; - this.offset = offset; - } - - @Override - public @Nullable PsiElement getElement() { - return file.findElementAt(offset); - } - - @Override - public boolean isValidResult() { - return false; - } -} diff --git a/src/main/java/com/en_circle/slt/plugin/sdk/LispSdk.java b/src/main/java/com/en_circle/slt/plugin/sdk/LispSdk.java index 5d8b0d8..789071e 100644 --- a/src/main/java/com/en_circle/slt/plugin/sdk/LispSdk.java +++ b/src/main/java/com/en_circle/slt/plugin/sdk/LispSdk.java @@ -24,6 +24,10 @@ public class LispSdk implements PersistentStateComponent { public String abclJvmArgs; public String abclJar; + // CCL Process used + public String cclExecutable; + public String cclMemoryImage; + public Environment getEnvironment() { if (environment == null) { // backwards compat diff --git a/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentServiceImpl.java b/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentServiceImpl.java index db7ce3e..15470f8 100644 --- a/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentServiceImpl.java +++ b/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentServiceImpl.java @@ -72,6 +72,8 @@ public LispEnvironmentServiceImpl(Project project) { @Override public void resetConfiguration() { this.configurationBuilder = null; + this.environmentProvider = null; + this.configuration = null; } public boolean configured() { @@ -172,24 +174,26 @@ private boolean doStart() throws Exception { } environment = environmentProvider.get(); environment.start(configuration); - overrides = environment.getType().getDefinition().getOverrides(); + if (environment != null) { + overrides = environment.getType().getDefinition().getOverrides(); + + slimeListener = new SlimeListener(project, true, e -> { + for (LispEnvironmentListener listener : serverListeners) { + String text = ExceptionUtil.getThrowableText(e); + listener.onOutputChanged(SltOutput.STDERR, text); + } + }, logger, debugInterface); + client = new SwankClient("127.0.0.1", environment.getSwankPort(), slimeListener); - slimeListener = new SlimeListener(project, true, e -> { for (LispEnvironmentListener listener : serverListeners) { - String text = ExceptionUtil.getThrowableText(e); - listener.onOutputChanged(SltOutput.STDERR, text); + listener.onPostStart(); } - }, logger, debugInterface); - client = new SwankClient("127.0.0.1", environment.getSwankPort(), slimeListener); - for (LispEnvironmentListener listener : serverListeners) { - listener.onPostStart(); + ApplicationManager.getApplication().invokeLaterOnWriteThread(() -> { + ParameterHintsPassFactory.forceHintsUpdateOnNextPass(); + DaemonCodeAnalyzer.getInstance(project).restart(); + }); } - - ApplicationManager.getApplication().invokeLaterOnWriteThread(() -> { - ParameterHintsPassFactory.forceHintsUpdateOnNextPass(); - DaemonCodeAnalyzer.getInstance(project).restart(); - }); } finally { starting = false; } @@ -207,7 +211,8 @@ private void doStop() throws Exception { listener.onPreStop(); } try { - client.close(); + if (client != null) + client.close(); } finally { overrides = null; indentationContainer.clear(); @@ -219,6 +224,11 @@ private void doStop() throws Exception { } } + ApplicationManager.getApplication().invokeLaterOnWriteThread(() -> { + ParameterHintsPassFactory.forceHintsUpdateOnNextPass(); + DaemonCodeAnalyzer.getInstance(project).restart(); + }); + for (LispEnvironmentListener listener : serverListeners) { listener.onPostStop(); } diff --git a/src/main/java/com/en_circle/slt/plugin/ui/SltGeneralLog.java b/src/main/java/com/en_circle/slt/plugin/ui/SltGeneralLog.java index 9a9cc93..9f65720 100644 --- a/src/main/java/com/en_circle/slt/plugin/ui/SltGeneralLog.java +++ b/src/main/java/com/en_circle/slt/plugin/ui/SltGeneralLog.java @@ -79,7 +79,7 @@ public String getTitle() { @Override public void logRequest(String request) { request = StringUtils.truncate(request, 0, 4069); - consoleView.print("\n\n" + request, ConsoleViewContentType.LOG_INFO_OUTPUT); + consoleView.print("\n\n" + request, ConsoleViewContentType.LOG_ERROR_OUTPUT); } @Override diff --git a/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationCCLProcess.java b/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationCCLProcess.java new file mode 100644 index 0000000..5c55659 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationCCLProcess.java @@ -0,0 +1,229 @@ +package com.en_circle.slt.plugin.ui.sdk; + +import com.en_circle.slt.plugin.SltBundle; +import com.en_circle.slt.plugin.sdk.LispSdk; +import com.en_circle.slt.plugin.ui.SltGlobalUIService; +import com.en_circle.slt.plugin.ui.sdk.SdkDialogProvider.OnSave; +import com.en_circle.slt.tools.CCLUtils; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.fileChooser.FileChooserDescriptor; +import com.intellij.openapi.fileChooser.FileChooserFactory; +import com.intellij.openapi.fileChooser.FileTextField; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.impl.ProgressResult; +import com.intellij.openapi.progress.impl.ProgressRunner; +import com.intellij.openapi.progress.impl.ProgressRunner.ThreadToUse; +import com.intellij.openapi.progress.util.ProgressWindow; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.ui.TextFieldWithBrowseButton; +import com.intellij.openapi.util.Disposer; +import com.intellij.ui.components.JBTextField; +import com.intellij.util.ui.FormBuilder; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.event.CaretListener; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.io.File; + +public class SdkConfigurationCCLProcess extends DialogWrapper { + + private final Disposable parentDisposable; + private final LispSdk instance; + private final OnSave onSave; + private boolean isVerified = false; + + private JBTextField name; + private FileTextField cclExecutable; + private FileTextField cclImage; + private FileTextField quicklispPath; + + public SdkConfigurationCCLProcess(@NotNull Component parent, LispSdk instance, String title, OnSave onSave) { + super(parent, true); + + this.parentDisposable = SltGlobalUIService.getInstance(); + this.instance = instance; + this.onSave = onSave; + + setTitle(title); + setSize(700, 0); + init(); + } + + @Override + protected @Nullable JComponent createCenterPanel() { + name = new JBTextField(); + name.addCaretListener(createChangeListener()); + name.setText(instance.userName); + if (StringUtils.isBlank(instance.userName)) { + name.setText(SltBundle.message("slt.ui.settings.sdk.editor.name.ccl.default")); + } + + FileChooserDescriptor descriptor = new FileChooserDescriptor(true, false, false, + false, false, false); + + cclExecutable = FileChooserFactory.getInstance() + .createFileTextField(descriptor, true, parentDisposable); + cclExecutable.getField().addCaretListener(createChangeListener()); + cclExecutable.getField().setText(instance.sbclExecutable); + cclImage = FileChooserFactory.getInstance() + .createFileTextField(descriptor, true, parentDisposable); + cclImage.getField().addCaretListener(createChangeListener()); + cclImage.getField().setText(instance.sbclCorePath); + quicklispPath = FileChooserFactory.getInstance() + .createFileTextField(descriptor, true, parentDisposable); + quicklispPath.getField().addCaretListener(createChangeListener()); + quicklispPath.getField().setText(instance.quickLispPath); + + TextFieldWithBrowseButton cclExecutablePicker = new TextFieldWithBrowseButton(cclExecutable.getField()); + cclExecutablePicker.addBrowseFolderListener( + SltBundle.message("slt.ui.settings.sdk.editor.ccl.executable.select"), "", null, descriptor); + TextFieldWithBrowseButton cclImagePicker = new TextFieldWithBrowseButton(cclImage.getField()); + cclImagePicker.addBrowseFolderListener( + SltBundle.message("slt.ui.settings.sdk.editor.ccl.image.select"), "", null, descriptor); + TextFieldWithBrowseButton quicklispPathPicker = new TextFieldWithBrowseButton(quicklispPath.getField()); + //noinspection DialogTitleCapitalization + quicklispPathPicker.addBrowseFolderListener( + SltBundle.message("slt.ui.settings.sdk.editor.quicklisp.select"), "", null, descriptor); + + return new FormBuilder() + .addLabeledComponent(SltBundle.message("slt.ui.settings.sdk.editor.name"), name, 1, false) + .addLabeledComponent(SltBundle.message("slt.ui.settings.sdk.editor.ccl.process.executable"), + cclExecutablePicker, 1, false) + .addLabeledComponent(SltBundle.message("slt.ui.settings.sdk.editor.ccl.process.image"), + cclImagePicker, 1, false) + .addLabeledComponent(SltBundle.message("slt.ui.settings.sdk.editor.quicklisp"), + quicklispPathPicker, 1, false) + .addComponentFillVertically(new JPanel(), 0) + .getPanel(); + } + + private CaretListener createChangeListener() { + return e -> isVerified = false; + } + + private void verifySdk() { + name.putClientProperty("JComponent.outline", null); + cclExecutable.getField().putClientProperty("JComponent.outline", null); + cclImage.getField().putClientProperty("JComponent.outline", null); + quicklispPath.getField().putClientProperty("JComponent.outline", null); + + boolean verified = true; + + if (StringUtils.isBlank(name.getText())) { + verified = false; + name.putClientProperty("JComponent.outline", "error"); + } + + String executable = cclExecutable.getField().getText(); + if (StringUtils.isBlank(executable)) { + verified = false; + cclExecutable.getField().putClientProperty("JComponent.outline", "error"); + } + + String core = cclImage.getField().getText(); + if (StringUtils.isNotBlank(core)) { + File file = new File(core); + if (!file.exists()) { + verified = false; + cclImage.getField().putClientProperty("JComponent.outline", "error"); + } + } + + String quicklisp = quicklispPath.getField().getText(); + if (StringUtils.isBlank(quicklisp)) { + verified = false; + quicklispPath.getField().putClientProperty("JComponent.outline", "error"); + } else { + File qlFile = new File(quicklisp); + if (!qlFile.exists() || !qlFile.isFile()) { + verified = false; + quicklispPath.getField().putClientProperty("JComponent.outline", "error"); + } + } + + name.repaint(); + cclExecutable.getField().repaint(); + cclImage.getField().repaint(); + quicklispPath.getField().repaint(); + + if (verified) + verified = checkAndLoadClispCore(executable, core, quicklisp); + if (!verified) { + Messages.showErrorDialog(SltBundle.message("slt.ui.settings.sdk.editor.ccl.process.verifying.error"), + SltBundle.message("slt.ui.settings.sdk.editor.verifying.error.title")); + } + + isVerified = verified; + } + + private boolean checkAndLoadClispCore(String executable, String memory, String quicklisp) { + ProgressWindow verifyWindow = new ProgressWindow(true, false, null, + getRootPane(), SltBundle.message("slt.ui.settings.sdk.editor.verifying.cancel")); + verifyWindow.setTitle(SltBundle.message("slt.ui.settings.sdk.editor.verifying.ccl")); + Disposer.register(parentDisposable, verifyWindow); + + ProgressResult result = new ProgressRunner<>(pi -> verifyClisp(pi, executable, memory, quicklisp)) + .sync() + .onThread(ThreadToUse.POOLED) + .withProgress(verifyWindow) + .modal() + .submitAndGet(); + return Boolean.TRUE.equals(result.getResult()); + } + + private boolean verifyClisp(ProgressIndicator pi, String executable, String core, String quicklisp) { + return CCLUtils.verifyAndInstallDependencies(executable, core, quicklisp, pi); + } + + private void save() { + instance.userName = name.getText(); + instance.cclExecutable = cclExecutable.getField().getText(); + instance.cclMemoryImage = cclImage.getField().getText(); + instance.quickLispPath = quicklispPath.getField().getText(); + onSave.saveAction(instance); + close(0); + } + + @Override + protected Action @NotNull [] createActions() { + return new Action[] { + new VerifyAction(), new SaveAction() + }; + } + + public class VerifyAction extends AbstractAction { + + public VerifyAction() { + super(SltBundle.message("slt.ui.settings.sdk.editor.ccl.process.verify")); + } + + @Override + public void actionPerformed(ActionEvent e) { + verifySdk(); + } + } + + public class SaveAction extends AbstractAction { + + public SaveAction() { + super(SltBundle.message("slt.ui.settings.sdk.editor.save")); + } + + @Override + public void actionPerformed(ActionEvent e) { + if (!isVerified) { + Messages.showInfoMessage(SltBundle.message("slt.ui.settings.sdk.editor.notverified.title"), + SltBundle.message("slt.ui.settings.sdk.editor.ccl.process.notverified.message")); + return; + } + + save(); + } + } + +} diff --git a/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationSBCLProcess.java b/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationSBCLProcess.java index 844f43e..7ec1ac5 100644 --- a/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationSBCLProcess.java +++ b/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationSBCLProcess.java @@ -81,10 +81,10 @@ public SdkConfigurationSBCLProcess(@NotNull Component parent, LispSdk instance, TextFieldWithBrowseButton sbclExecutablePicker = new TextFieldWithBrowseButton(sbclExecutable.getField()); sbclExecutablePicker.addBrowseFolderListener( - SltBundle.message("slt.ui.settings.sdk.editor.executable.select"), "", null, descriptor); + SltBundle.message("slt.ui.settings.sdk.editor.sbcl.executable.select"), "", null, descriptor); TextFieldWithBrowseButton sbclCorePicker = new TextFieldWithBrowseButton(sbclCore.getField()); sbclCorePicker.addBrowseFolderListener( - SltBundle.message("slt.ui.settings.sdk.editor.core.select"), "", null, descriptor); + SltBundle.message("slt.ui.settings.sdk.editor.sbcl.core.select"), "", null, descriptor); TextFieldWithBrowseButton quicklispPathPicker = new TextFieldWithBrowseButton(quicklispPath.getField()); //noinspection DialogTitleCapitalization quicklispPathPicker.addBrowseFolderListener( diff --git a/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkSelector.java b/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkSelector.java index 9c40023..0e4568e 100644 --- a/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkSelector.java +++ b/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkSelector.java @@ -120,6 +120,8 @@ public void apply() throws ConfigurationException { LispEnvironmentService.getInstance(project).stop(); LispEnvironmentService.getInstance(project).start(); } + } else { + LispEnvironmentService.getInstance(project).resetConfiguration(); } } diff --git a/src/main/java/com/en_circle/slt/tools/ABCLUtils.java b/src/main/java/com/en_circle/slt/tools/ABCLUtils.java index 6f827f8..2b30a4e 100644 --- a/src/main/java/com/en_circle/slt/tools/ABCLUtils.java +++ b/src/main/java/com/en_circle/slt/tools/ABCLUtils.java @@ -14,6 +14,7 @@ import java.util.List; import java.util.StringTokenizer; import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; public class ABCLUtils { @@ -43,14 +44,30 @@ public static boolean verifyAndInstallDependencies(String jvm, String jvmArgs, S ProcessBuilder processBuilder = new ProcessBuilder(args.toArray(new String[0])); Process process = processBuilder.start(); - StringBuilder returnValue = new StringBuilder(); - StringBuilder textValue = new StringBuilder(); + StringBuilder displayValue = new StringBuilder(); + StringBuilder errorValue = new StringBuilder(); + StringBuilder outputValue = new StringBuilder(); SltProcessStreamGobbler errorController = new SltProcessStreamGobbler(process.getErrorStream()); SltProcessStreamGobbler outputController = new SltProcessStreamGobbler(process.getInputStream()); - errorController.addUpdateListener(returnValue::append); - outputController.addUpdateListener(textValue::append); + errorController.addUpdateListener(errorValue::append); + outputController.addUpdateListener(outputValue::append); WaitForOccurrence waiter = new WaitForOccurrence("SltVerified"); outputController.addUpdateListener(waiter); + outputController.addUpdateListener(data -> { + displayValue.append(data); + String str = displayValue.toString(); + if (str.contains("\n")) { + String[] lines = str.split(Pattern.quote("\n")); + for (String line : lines) { + if (StringUtils.isNotBlank(line)) + pi.setText(line); + } + if (StringUtils.isNotBlank(lines[lines.length-1])) { + displayValue.setLength(0); + displayValue.append(lines[lines.length-1]); + } + } + }); errorController.start(); outputController.start(); if (!waiter.awaitFor(null, outputController, 10, TimeUnit.MINUTES, pi::isCanceled)) { @@ -67,7 +84,7 @@ public static boolean verifyAndInstallDependencies(String jvm, String jvmArgs, S errorController.join(); outputController.join(); - return textValue.toString().contains("SltVerified"); + return outputValue.toString().contains("SltVerified"); } finally { tempTestFile.delete(); } diff --git a/src/main/java/com/en_circle/slt/tools/CCLUtils.java b/src/main/java/com/en_circle/slt/tools/CCLUtils.java new file mode 100644 index 0000000..2e03199 --- /dev/null +++ b/src/main/java/com/en_circle/slt/tools/CCLUtils.java @@ -0,0 +1,93 @@ +package com.en_circle.slt.tools; + +import com.en_circle.slt.plugin.environment.SltProcessStreamGobbler; +import com.en_circle.slt.plugin.environment.SltProcessStreamGobbler.WaitForOccurrence; +import com.en_circle.slt.templates.VerifyTemplate; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.util.io.FileUtil; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; + +public class CCLUtils { + + @SuppressWarnings("ResultOfMethodCallIgnored") + public static boolean verifyAndInstallDependencies(String executable, String memoryImage, String quicklisp, ProgressIndicator pi) { + try { + List args = new ArrayList<>(); + args.add(executable); + if (StringUtils.isNotBlank(memoryImage)) { + args.add("-I"); + args.add(memoryImage); + } + args.add("-b"); + + File tempTestFile = FileUtil.createTempFile("testSBCL", ".cl"); + if (tempTestFile.exists()) + tempTestFile.delete(); + FileUtils.writeStringToFile(tempTestFile, new VerifyTemplate(quicklisp).render(), StandardCharsets.UTF_8); + tempTestFile.deleteOnExit(); + + args.add("-l"); + args.add(tempTestFile.getAbsolutePath()); + + try { + ProcessBuilder processBuilder = new ProcessBuilder(args.toArray(new String[0])); + Process process = processBuilder.start(); + + StringBuilder errorValue = new StringBuilder(); + StringBuilder displayValue = new StringBuilder(); + StringBuilder outputValue = new StringBuilder(); + SltProcessStreamGobbler errorController = new SltProcessStreamGobbler(process.getErrorStream()); + SltProcessStreamGobbler outputController = new SltProcessStreamGobbler(process.getInputStream()); + errorController.addUpdateListener(errorValue::append); + outputController.addUpdateListener(outputValue::append); + WaitForOccurrence waiter = new WaitForOccurrence("SltVerified"); + errorController.addUpdateListener(waiter); + outputController.addUpdateListener(data -> { + displayValue.append(data); + String str = displayValue.toString(); + if (str.contains("\n")) { + String[] lines = str.split(Pattern.quote("\n")); + for (String line : lines) { + if (StringUtils.isNotBlank(line)) + pi.setText(line); + } + if (StringUtils.isNotBlank(lines[lines.length-1])) { + displayValue.setLength(0); + displayValue.append(lines[lines.length-1]); + } + } + }); + errorController.start(); + outputController.start(); + if (!waiter.awaitFor(null, errorController, 10, TimeUnit.MINUTES, pi::isCanceled)) { + if (process.isAlive()) + process.destroy(); + + errorController.join(); + outputController.join(); + return false; + } + + if (process.isAlive()) + process.destroy(); + + errorController.join(); + outputController.join(); + return errorValue.toString().contains("SltVerified"); + } finally { + tempTestFile.delete(); + } + } catch (Exception ignored) { + return false; + } + } + +} diff --git a/src/main/java/com/en_circle/slt/tools/SBCLUtils.java b/src/main/java/com/en_circle/slt/tools/SBCLUtils.java index 87c2647..13792fd 100644 --- a/src/main/java/com/en_circle/slt/tools/SBCLUtils.java +++ b/src/main/java/com/en_circle/slt/tools/SBCLUtils.java @@ -13,6 +13,7 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; public class SBCLUtils { @@ -40,14 +41,30 @@ public static boolean verifyAndInstallDependencies(String executable, String cor ProcessBuilder processBuilder = new ProcessBuilder(args.toArray(new String[0])); Process process = processBuilder.start(); - StringBuilder returnValue = new StringBuilder(); - StringBuilder textValue = new StringBuilder(); + StringBuilder displayValue = new StringBuilder(); + StringBuilder errorValue = new StringBuilder(); + StringBuilder outputValue = new StringBuilder(); SltProcessStreamGobbler errorController = new SltProcessStreamGobbler(process.getErrorStream()); SltProcessStreamGobbler outputController = new SltProcessStreamGobbler(process.getInputStream()); - errorController.addUpdateListener(returnValue::append); - outputController.addUpdateListener(textValue::append); + errorController.addUpdateListener(errorValue::append); + outputController.addUpdateListener(outputValue::append); WaitForOccurrence waiter = new WaitForOccurrence("SltVerified"); errorController.addUpdateListener(waiter); + errorController.addUpdateListener(data -> { + displayValue.append(data); + String str = displayValue.toString(); + if (str.contains("\n")) { + String[] lines = str.split(Pattern.quote("\n")); + for (String line : lines) { + if (StringUtils.isNotBlank(line)) + pi.setText(line); + } + if (StringUtils.isNotBlank(lines[lines.length-1])) { + displayValue.setLength(0); + displayValue.append(lines[lines.length-1]); + } + } + }); errorController.start(); outputController.start(); if (!waiter.awaitFor(null, errorController, 10, TimeUnit.MINUTES, pi::isCanceled)) { @@ -64,7 +81,7 @@ public static boolean verifyAndInstallDependencies(String executable, String cor errorController.join(); outputController.join(); - return returnValue.toString().contains("SltVerified"); + return errorValue.toString().contains("SltVerified"); } finally { tempTestFile.delete(); } diff --git a/src/main/lisp/load.lisp b/src/main/lisp/load.lisp index be3d598..c059f3d 100644 --- a/src/main/lisp/load.lisp +++ b/src/main/lisp/load.lisp @@ -2,6 +2,7 @@ (format T "SLT Interpret ~S~%" slt:+slt-interpret+)) (defun portable-quit (&optional code) + (declare (ignorable code)) ;; This group from "clocc-port/ext.lisp" #+allegro (excl:exit code) #+clisp (#+lisp=cl ext:quit #-lisp=cl lisp:quit code) @@ -32,6 +33,10 @@ (lisp-implementation-type)) (format *error-output* "Invalid lisp instance. Maybe a configuration error? This is not ABCL!~%") (portable-quit 1))) + (:ccl (unless (string= "Clozure Common Lisp" + (lisp-implementation-type)) + (format *error-output* "Invalid lisp instance. Maybe a configuration error? This is not Clozure Common Lisp!~%") + (portable-quit 1))) (otherwise (format *error-output* "Unsupported lisp instance. Maybe a configuration error?~%") (portable-quit 1))) diff --git a/src/main/lisp/slt/slt-ccl.lisp b/src/main/lisp/slt/slt-ccl.lisp new file mode 100644 index 0000000..db612f8 --- /dev/null +++ b/src/main/lisp/slt/slt-ccl.lisp @@ -0,0 +1,4 @@ +(in-package :slt-core) + +(defun specialp (test-sym) + (eq (ccl:variable-information test-sym) :special)) \ No newline at end of file diff --git a/src/main/lisp/slt/slt.lisp b/src/main/lisp/slt/slt.lisp index 060c00d..50dc827 100644 --- a/src/main/lisp/slt/slt.lisp +++ b/src/main/lisp/slt/slt.lisp @@ -8,6 +8,8 @@ (load (merge-pathnames "slt-sbcl.lisp" *load-truename*))) (when (eq slt:+slt-interpret+ :abcl) (load (merge-pathnames "slt-abcl.lisp" *load-truename*))) +(when (eq slt:+slt-interpret+ :ccl) + (load (merge-pathnames "slt-ccl.lisp" *load-truename*))) (in-package :slt-core) diff --git a/src/main/lisp/swank/swank-abcl.lisp b/src/main/lisp/swank/swank-abcl.lisp index 412ddcf..7baf992 100644 --- a/src/main/lisp/swank/swank-abcl.lisp +++ b/src/main/lisp/swank/swank-abcl.lisp @@ -12,19 +12,37 @@ (defun slime-location-from-source-annotation (sym it) (destructuring-bind (what path pos) it (let* ((isfunction - ;; all of these are (defxxx forms, which is what :function locations look for in slime - (and (consp what) (member (car what) - '(:function :generic-function :macro :class :compiler-macro - :type :constant :variable :package :structure :condition)))) - (ismethod (and (consp what) (eq (car what) :method))) - ( (cond (isfunction (list :function-name (princ-to-string (second what)) :position (1+ (or pos 0)))) - (ismethod (stringify-method-specs what) :position (1+ (or pos 0))) - (t (list :position (1+ (or pos 0)))))) - (path2 (if (eq path :top-level) - ;; this is bogus - figure out some way to guess which is the repl associated with :toplevel - ;; or get rid of this - "emacs-buffer:*slime-repl*" - (maybe-redirect-to-jar path)))) + ;; all of these are (defxxx forms, which is what :function + ;; locations look for in slime + (and (consp what) + (member (car what) + '(:function :generic-function :macro :class :compiler-macro + :type :constant :variable :package :structure :condition)))) + (ismethod + (and (consp what) + (eq (car what) :method))) + ;;; docstring for + ;;; slime-goto-source-location constains the position to a + ;;; single clause. We prioritize a :POSITION clause over + ;;; others. + ( + (cond (isfunction + (if pos + `(:position ,(1+ (or pos 0))) + `(:function-name ,(princ-to-string (second what))))) + (ismethod + (if pos + `(:position ,(1+ (or pos 0))) + (stringify-method-specs what))) + (t ;; Are we ever called with a nil POS? + `(:position ,(1+ (or pos 0)))))) + (path2 + (if (eq path :top-level) + ;; this is bogus - figure out some way to guess which + ;; is the repl associated with :toplevel or get + ;; rid of this + "emacs-buffer:*slime-repl*" + (maybe-redirect-to-jar path)))) (when (atom what) (setq what (list what sym))) (list (definition-specifier what) @@ -37,15 +55,15 @@ ;; conspire with swank-compile-string to keep the ;; buffer name in a pathname whose device is ;; "emacs-buffer". - (if (eql 0 (search "emacs-buffer:" path2)) - `(:location - (:buffer ,(subseq path2 (load-time-value (length "emacs-buffer:")))) - , - (:align t)) - `(:location - (:file ,path2) - , - (:align t)))))))) + (if (eql 0 (search "emacs-buffer:" path2)) + `(:location + (:buffer ,(subseq path2 (load-time-value (length "emacs-buffer:")))) + , + (:align t)) + `(:location + (:file ,path2) + , + (:align t)))))))) (defimplementation swank-compile-string (string &key buffer position filename line column policy) diff --git a/src/main/lisp/swank/swank-ccl.lisp b/src/main/lisp/swank/swank-ccl.lisp new file mode 100644 index 0000000..f201af2 --- /dev/null +++ b/src/main/lisp/swank/swank-ccl.lisp @@ -0,0 +1,19 @@ +(in-package :swank/ccl) + +(in-package :swank) + +(defslimefun backtrace (start end) + (loop for frame in (compute-backtrace start end) + for i from start collect + (list i (frame-to-string frame) + (format NIL "~A" (print-frame-call-place frame)) + (frame-source-location i) + (let ((pkg (frame-package i))) + (cond + (pkg (package-name pkg)) + (T NIL)))))) + +(defun print-frame-call-place (frame) + (destructuring-bind (p context) (rest frame) + (let ((lfun (ccl:frame-function p context))) + (or (ccl:function-name lfun) lfun)))) \ No newline at end of file diff --git a/src/main/lisp/swank/swank.lisp b/src/main/lisp/swank/swank.lisp index a0b947e..b799704 100644 --- a/src/main/lisp/swank/swank.lisp +++ b/src/main/lisp/swank/swank.lisp @@ -1,11 +1,13 @@ (load (merge-pathnames "swank-backend.lisp" *load-truename*)) -(when (eq +slt-interpret+ :sbcl) +(when (eq slt:+slt-interpret+ :sbcl) (load (merge-pathnames "swank-sbcl.lisp" *load-truename*)) (in-package swank/source-file-cache) (setf *source-snippet-size* 0)) -(when (eq +slt-interpret+ :abcl) +(when (eq slt:+slt-interpret+ :abcl) (load (merge-pathnames "swank-abcl.lisp" *load-truename*))) +(when (eq slt:+slt-interpret+ :ccl) + (load (merge-pathnames "swank-ccl.lisp" *load-truename*))) (in-package :swank) @@ -67,6 +69,7 @@ format suitable for Emacs." collect data-pair))) (defun find-reference-class-filter (symbol package) + (declare (ignorable package)) (find-class symbol NIL)) (defun get-reference-prefix (prefix type) diff --git a/src/main/resources/messages/SltBundle.properties b/src/main/resources/messages/SltBundle.properties index 3d6ed62..3f96e26 100644 --- a/src/main/resources/messages/SltBundle.properties +++ b/src/main/resources/messages/SltBundle.properties @@ -10,6 +10,7 @@ slt.argslist.error=No contextual help found # SDK slt.environment.abcl=ABCL slt.environment.sbcl=SBCL +slt.environment.ccl=Clozure Common Lisp # Documentation elements slt.documentation.types.symbol=Symbol @@ -116,17 +117,23 @@ slt.ui.settings.sdk.editor.title.new=New Common Lisp SDK slt.ui.settings.sdk.editor.title.edit=Editing Common Lisp SDK slt.ui.settings.sdk.editor.sbcl.process.verify=Verify SBCL SDK slt.ui.settings.sdk.editor.abcl.process.verify=Verify ABCL SDK +slt.ui.settings.sdk.editor.ccl.process.verify=Verify Clisp SDK slt.ui.settings.sdk.editor.save=Save slt.ui.settings.sdk.editor.name=SDK name slt.ui.settings.sdk.editor.name.sbcl.default=SBCL slt.ui.settings.sdk.editor.name.abcl.default=ABCL +slt.ui.settings.sdk.editor.name.ccl.default=Clozure Common Lisp slt.ui.settings.sdk.editor.sbcl.process.executable=SBCL executable +slt.ui.settings.sdk.editor.ccl.process.executable=CCL executable slt.ui.settings.sdk.editor.abcl.jvm.executable=Java executable slt.ui.settings.sdk.editor.abcl.jvm.args=Java JVM arguments (optional) -slt.ui.settings.sdk.editor.executable.select=Please Select SBCL Executable +slt.ui.settings.sdk.editor.sbcl.executable.select=Please Select SBCL Executable +slt.ui.settings.sdk.editor.ccl.executable.select=Please Select CCL Executable slt.ui.settings.sdk.editor.sbcl.process.core=SBCL core (optional) +slt.ui.settings.sdk.editor.ccl.process.image=CCL image (optional) slt.ui.settings.sdk.editor.abcl.jar=ABCL jar -slt.ui.settings.sdk.editor.core.select=Please Select SBCL Core +slt.ui.settings.sdk.editor.sbcl.core.select=Please Select SBCL Core +slt.ui.settings.sdk.editor.ccl.image.select=Please Select CCL Memory Image slt.ui.settings.sdk.editor.quicklisp=Quicklisp path slt.ui.settings.sdk.editor.jvm.select=Please Select Java Executable slt.ui.settings.sdk.editor.abcl.select=Please Select ABCL Jar File @@ -136,11 +143,14 @@ slt.ui.settings.sdk.editor.quicklisp.select=Please Select Quicklisp setup.lisp F slt.ui.settings.sdk.editor.notverified.title=Verification required slt.ui.settings.sdk.editor.sbcl.process.notverified.message=Please Verify SBCL SDK slt.ui.settings.sdk.editor.abcl.process.notverified.message=Please Verify ABCL SDK +slt.ui.settings.sdk.editor.ccl.process.notverified.message=Please Verify CCL SDK slt.ui.settings.sdk.editor.verifying.sbcl=Verifying SBCL SDK slt.ui.settings.sdk.editor.verifying.abcl=Verifying ABCL SDK +slt.ui.settings.sdk.editor.verifying.ccl=Verifying CCL SDK slt.ui.settings.sdk.editor.verifying.cancel=Cancel Verification slt.ui.settings.sdk.editor.sbcl.process.verifying.error=Failed to verify SBCL installation slt.ui.settings.sdk.editor.abcl.process.verifying.error=Failed to verify ABCL installation +slt.ui.settings.sdk.editor.ccl.process.verifying.error=Failed to verify CCL installation slt.ui.settings.sdk.editor.verifying.error.title=Failed slt.ui.settings.sdk.download.sbcl.downloading=Downloading SBCL slt.ui.settings.sdk.download.cancel=Cancel diff --git a/src/main/resources/templates/en_US/initscript.cl b/src/main/resources/templates/en_US/initscript.cl index 5621ebf..79057a3 100644 --- a/src/main/resources/templates/en_US/initscript.cl +++ b/src/main/resources/templates/en_US/initscript.cl @@ -10,7 +10,7 @@ (ql:quickload :swank) (ql:quickload :eclector) -(setf *default-pathname-defaults* (truename "~cwd~")) +(setf *default-pathname-defaults* #P"~cwd~") (load "~corefile~") diff --git a/src/main/resources/templates/en_US/verify.cl b/src/main/resources/templates/en_US/verify.cl index 89c9708..17db304 100644 --- a/src/main/resources/templates/en_US/verify.cl +++ b/src/main/resources/templates/en_US/verify.cl @@ -2,4 +2,5 @@ (ql:quickload :swank) (ql:quickload :eclector) -(format *error-output* "SltVerified~%") \ No newline at end of file +(format *error-output* "SltVerified~%") +(finish-output *error-output*) \ No newline at end of file From aa1bdd9e8b883d1e6596ef1f6b286eaf2e9a2886 Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Mon, 6 Feb 2023 02:25:30 +0100 Subject: [PATCH 13/40] fixed references --- .../com/en_circle/slt/plugin/references/SltReference.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/en_circle/slt/plugin/references/SltReference.java b/src/main/java/com/en_circle/slt/plugin/references/SltReference.java index bf97784..5d4a41d 100644 --- a/src/main/java/com/en_circle/slt/plugin/references/SltReference.java +++ b/src/main/java/com/en_circle/slt/plugin/references/SltReference.java @@ -196,7 +196,10 @@ private ResolveResult convertLocationToReference(SourceLocation location, String PsiFile f = PsiManager.getInstance(myElement.getProject()).findFile(vf); if (f == null) return null; - return new SltLazyElementResolve(f, location.getPosition()); + PsiElement element = f.findElementAt(location.getPosition()); + if (element == null) + return null; + return new PsiElementResolveResult(element); } @Override From 3b21c8938ec736f2f3dbdc8420ed85502a7971f4 Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Mon, 6 Feb 2023 02:25:08 +0100 Subject: [PATCH 14/40] fixed documentation escaping --- .../en_circle/slt/plugin/SltDocumentationProvider.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/en_circle/slt/plugin/SltDocumentationProvider.java b/src/main/java/com/en_circle/slt/plugin/SltDocumentationProvider.java index ae7cd00..b66b73a 100644 --- a/src/main/java/com/en_circle/slt/plugin/SltDocumentationProvider.java +++ b/src/main/java/com/en_circle/slt/plugin/SltDocumentationProvider.java @@ -10,6 +10,7 @@ import com.intellij.openapi.util.text.HtmlChunk; import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiTreeUtil; +import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.Nullable; @@ -79,7 +80,7 @@ private PsiElement decideOnElement(PsiElement element, PsiElement originalElemen private String asHtml(SymbolState state, String packageName, PsiElement element) { HtmlBuilder builder = new HtmlBuilder(); - String documentation = StringUtils.replace(StringUtils.replace(state.documentation, " ", " "), + String documentation = StringUtils.replace(StringUtils.replace(escape(state.documentation), " ", " "), "\n", HtmlChunk.br().toString()); builder.append(documentation == null ? HtmlChunk.raw("") : HtmlChunk.raw(documentation)); @@ -87,7 +88,7 @@ private String asHtml(SymbolState state, String packageName, PsiElement element) if (form != null && state.binding == SymbolBinding.MACRO) { String macroExpand = LispEnvironmentService.getInstance(element.getProject()).macroexpand(form, packageName); if (macroExpand != null) { - macroExpand = StringUtils.replace(StringUtils.replace(macroExpand, " ", " "), + macroExpand = StringUtils.replace(StringUtils.replace(escape(macroExpand), " ", " "), "\n", HtmlChunk.br().toString()); builder.append(HtmlChunk.hr()); builder.append(HtmlChunk.text(SltBundle.message("slt.documentation.macroexpand"))); @@ -102,4 +103,8 @@ private String asHtml(SymbolState state, String packageName, PsiElement element) String doc = builder.toString(); return StringUtils.isBlank(doc) ? null : doc; } + + private String escape(String s) { + return StringEscapeUtils.escapeHtml(s); + } } From 18075d2f8f7afc3b7961542847cb6453a9b4af55 Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Mon, 6 Feb 2023 13:20:03 +0100 Subject: [PATCH 15/40] #40 CCL supported --- FEATURES.md | 35 +++--- README.md | 2 +- .../slt/plugin/environment/LispFeatures.java | 1 + .../abcl/ABCLEnvironmentDefinition.java | 1 + .../sbcl/SBCLEnvironmentDefinition.java | 1 + .../services/lisp/LispEnvironmentService.java | 2 +- .../slt/plugin/ui/debug/SltDebuggerImpl.java | 105 +++++++++++++++--- .../slt/plugin/ui/debug/SltInspector.java | 4 + src/main/lisp/swank/swank-abcl.lisp | 5 + src/main/lisp/swank/swank-ccl.lisp | 5 + src/main/lisp/swank/swank-sbcl.lisp | 5 + src/main/lisp/swank/swank.lisp | 2 +- .../resources/messages/SltBundle.properties | 4 + src/test/java/SwankTest.java | 43 ++++--- 14 files changed, 157 insertions(+), 58 deletions(-) diff --git a/FEATURES.md b/FEATURES.md index 68bed0b..0729ace 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -11,20 +11,27 @@ Unsupported and will not be supported implementations: * CLISP - does not work, maybe with threads, but single threaded it's useless and crashes on debug attempt -| Features / Lisp | SBCL | ABCL | CCL | -|-------------------|------|------|-----| -| REPL | ✅️ | ✅️ | ✅️ | -| Buffer Evaluation | ✅️ | ✅️ | ✅️ | -| Documentation | ✅ | ✅ | ✅️ | -| Macroexpand | ✅ | ✅ | ✅️ | -| Debugger | ✅ | ✅ | ️ | -| Frame Eval | ✅ | ❎ | ️ | -| Stepping Debugger | ❎ | ❎ | ️ | -| References | ✅ | ❎ | ✅️ | -| Inspector | ✅¹ | ✅² | ️ | -| Autocomplete | ✅ | ✅ | | -| Find References | ✅ | ❎ | ️ | +| Features / Lisp | SBCL | ABCL | CCL | +|-------------------|------|------|------| +| REPL | ✅️ | ✅️ | ✅️ | +| Buffer Evaluation | ✅️ | ✅️ | ✅️ | +| Documentation | ✅ | ✅ | ✅️ | +| Macroexpand | ✅ | ✅ | ✅️ | +| Debugger | ✅ | ✅ | ✅️ | +| Debugger Actions | ✅ | ✅ | ✅️³️ | +| Frame REPL | ✅ | ❎ | ✅️ | +| Stepping Debugger | ❎ | ❎ | ❓ | +| References | ✅ | ❎ | ✅️ | +| Inspector | ✅¹ | ✅² | ✅️ | +| Autocomplete | ✅ | ✅ | ✅ | +| Find References | ✅ | ❎ | ✅️ | ¹Only read-only inspector available -²Only read-only inspector available, also no history support \ No newline at end of file +²Only read-only inspector available, also no history support + +³️Due to how CCL optimizes restarts into tag jumps and is not storing arglist, +there is no automatic restart action argument detection. +Thus, you need to supply your own and every action has "invoke with arguments" or "invoke without arguments" +option, so you have to decide. FML for ansi common lisp not having ansi way to get restart +arguments because fuck you that's why. \ No newline at end of file diff --git a/README.md b/README.md index 2eafa69..523e897 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ **THIS PLUGIN IS EXPERIMENTAL and can crash at any time! Please report all bugs!** This plugin is providing support for Common Lisp for JetBrains IDEs. -Using modified SLIME/Swank protocol to commmunicate with SBCL providing +Using modified SLIME/Swank protocol to communicate with lisp runtime providing IDE capabilities for Common Lisp. # (Somewhat) Detailed Installation and Usage Guide diff --git a/src/main/java/com/en_circle/slt/plugin/environment/LispFeatures.java b/src/main/java/com/en_circle/slt/plugin/environment/LispFeatures.java index 8e3337e..5d97a4f 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/LispFeatures.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/LispFeatures.java @@ -7,6 +7,7 @@ public enum LispFeatures { MACROEXPAND, FRAME_EVAL, INSPECTOR, + DEBUGGER_ACTION_ARGLIST, INSPECTOR_HISTORY, AUTOCOMPLETE, SEARCH, diff --git a/src/main/java/com/en_circle/slt/plugin/environment/abcl/ABCLEnvironmentDefinition.java b/src/main/java/com/en_circle/slt/plugin/environment/abcl/ABCLEnvironmentDefinition.java index 837b8af..7d8b951 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/abcl/ABCLEnvironmentDefinition.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/abcl/ABCLEnvironmentDefinition.java @@ -23,6 +23,7 @@ public ABCLEnvironmentDefinition() { features.add(LispFeatures.DOCUMENTATION); features.add(LispFeatures.MACROEXPAND); features.add(LispFeatures.INSPECTOR); + features.add(LispFeatures.DEBUGGER_ACTION_ARGLIST); features.add(LispFeatures.AUTOCOMPLETE); } diff --git a/src/main/java/com/en_circle/slt/plugin/environment/sbcl/SBCLEnvironmentDefinition.java b/src/main/java/com/en_circle/slt/plugin/environment/sbcl/SBCLEnvironmentDefinition.java index b62bcdb..2841014 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/sbcl/SBCLEnvironmentDefinition.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/sbcl/SBCLEnvironmentDefinition.java @@ -24,6 +24,7 @@ public SBCLEnvironmentDefinition() { features.add(LispFeatures.DOCUMENTATION); features.add(LispFeatures.MACROEXPAND); features.add(LispFeatures.FRAME_EVAL); + features.add(LispFeatures.DEBUGGER_ACTION_ARGLIST); features.add(LispFeatures.INSPECTOR); features.add(LispFeatures.INSPECTOR_HISTORY); features.add(LispFeatures.AUTOCOMPLETE); diff --git a/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentService.java b/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentService.java index 0247d4f..e510fa0 100644 --- a/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentService.java +++ b/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentService.java @@ -58,7 +58,7 @@ static LispEnvironmentService getInstance(Project project) { LispSltOverrides getOverrides(); - boolean hasFeature(LispFeatures xrefs); + boolean hasFeature(LispFeatures feature); enum LispEnvironmentState { diff --git a/src/main/java/com/en_circle/slt/plugin/ui/debug/SltDebuggerImpl.java b/src/main/java/com/en_circle/slt/plugin/ui/debug/SltDebuggerImpl.java index 92770e4..0e7c446 100644 --- a/src/main/java/com/en_circle/slt/plugin/ui/debug/SltDebuggerImpl.java +++ b/src/main/java/com/en_circle/slt/plugin/ui/debug/SltDebuggerImpl.java @@ -2,6 +2,7 @@ import com.en_circle.slt.plugin.SltBundle; import com.en_circle.slt.plugin.SltUIConstants; +import com.en_circle.slt.plugin.environment.LispFeatures; import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService; import com.en_circle.slt.plugin.swank.debug.SltDebugAction; import com.en_circle.slt.plugin.swank.debug.SltDebugArgument; @@ -116,30 +117,73 @@ public void redraw(SltDebugInfo debugInfo) { actionsPanel.setLayout(new GridBagLayout()); for (SltDebugAction action : debugInfo.getActions()) { - JLabel label = new JLabel(getText(action.getActionName())); - label.setCursor(new Cursor(Cursor.HAND_CURSOR)); - Map attributes = new HashMap<>(label.getFont().getAttributes()); - attributes.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON); - label.setFont(label.getFont().deriveFont(attributes)); - label.setForeground(SltUIConstants.HYPERLINK_COLOR); - label.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - actionAccepted(action, debugInfo); + JPanel actionInfo = new JPanel(); + actionInfo.setLayout(new BoxLayout(actionInfo, BoxLayout.Y_AXIS)); + + if (LispEnvironmentService.getInstance(parent.getProject()).hasFeature(LispFeatures.DEBUGGER_ACTION_ARGLIST)) { + JLabel label = new JLabel(getText(action.getActionName())); + label.setCursor(new Cursor(Cursor.HAND_CURSOR)); + Map attributes = new HashMap<>(label.getFont().getAttributes()); + attributes.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON); + label.setFont(label.getFont().deriveFont(attributes)); + label.setForeground(SltUIConstants.HYPERLINK_COLOR); + label.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + actionAccepted(action, debugInfo); + } + }); + JPanel labelPanel = new JPanel(new BorderLayout()); + labelPanel.add(label, BorderLayout.CENTER); + actionInfo.add(labelPanel); + } else { + JLabel label = new JLabel(getText(action.getActionName())); + JPanel labelPanel = new JPanel(new BorderLayout()); + labelPanel.add(label, BorderLayout.CENTER); + actionInfo.add(labelPanel); + + { + JLabel actionNoArg = new JLabel(SltBundle.message("slt.ui.debugger.actions.invoke.noarg")); + actionNoArg.setCursor(new Cursor(Cursor.HAND_CURSOR)); + Map attributes = new HashMap<>(actionNoArg.getFont().getAttributes()); + attributes.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON); + actionNoArg.setFont(actionNoArg.getFont().deriveFont(attributes)); + actionNoArg.setForeground(SltUIConstants.HYPERLINK_COLOR); + actionNoArg.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + actionAcceptedNoArg(action, debugInfo); + } + }); + labelPanel = new JPanel(new BorderLayout()); + labelPanel.add(actionNoArg, BorderLayout.CENTER); + actionInfo.add(labelPanel); } - }); - JPanel labelPanel = new JPanel(new BorderLayout()); - labelPanel.add(label, BorderLayout.CENTER); + + { + JLabel actionNoArg = new JLabel(SltBundle.message("slt.ui.debugger.actions.invoke.arg")); + actionNoArg.setCursor(new Cursor(Cursor.HAND_CURSOR)); + Map attributes = new HashMap<>(actionNoArg.getFont().getAttributes()); + attributes.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON); + actionNoArg.setFont(actionNoArg.getFont().deriveFont(attributes)); + actionNoArg.setForeground(SltUIConstants.HYPERLINK_COLOR); + actionNoArg.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + actionAcceptedArg(action, debugInfo); + } + }); + labelPanel = new JPanel(new BorderLayout()); + labelPanel.add(actionNoArg, BorderLayout.CENTER); + actionInfo.add(labelPanel); + } + } JBTextArea textArea = new JBTextArea(action.getActionDescription()); textArea.setEditable(false); textArea.setWrapStyleWord(true); textArea.setLineWrap(true); - JPanel actionInfo = new JPanel(); - actionInfo.setLayout(new BoxLayout(actionInfo, BoxLayout.Y_AXIS)); - - actionInfo.add(labelPanel); actionInfo.add(new JScrollPane(textArea)); GridBagConstraints cons = new GridBagConstraints(); @@ -241,7 +285,34 @@ private void actionAccepted(SltDebugAction action, SltDebugInfo debugInfo) { Messages.showErrorDialog(parent.getProject(), e.getMessage(), SltBundle.message("slt.ui.errors.lisp.start")); } } + } + + private void actionAcceptedNoArg(SltDebugAction action, SltDebugInfo debugInfo) { + int ix = debugInfo.getActions().indexOf(action); + try { + LispEnvironmentService.getInstance(parent.getProject()).sendToLisp(InvokeNthRestart.nthRestart(debugInfo.getThreadId(), + BigInteger.valueOf(ix), debugInfo.getDebugLevel(), "NIL", "NIL", () -> {})); + } catch (Exception e) { + log.warn(SltBundle.message("slt.error.start"), e); + Messages.showErrorDialog(parent.getProject(), e.getMessage(), SltBundle.message("slt.ui.errors.lisp.start")); + } + } + private void actionAcceptedArg(SltDebugAction action, SltDebugInfo debugInfo) { + int ix = debugInfo.getActions().indexOf(action); + String rest = "NIL"; + String arg = Messages.showInputDialog(SltBundle.message("slt.ui.debugger.args.text"), + SltBundle.message("slt.ui.debugger.args.title"),null); + if (StringUtils.isNotBlank(arg)) { + rest = "(" + arg + ")"; + } + try { + LispEnvironmentService.getInstance(parent.getProject()).sendToLisp(InvokeNthRestart.nthRestart(debugInfo.getThreadId(), + BigInteger.valueOf(ix), debugInfo.getDebugLevel(), "NIL", rest, () -> {})); + } catch (Exception e) { + log.warn(SltBundle.message("slt.error.start"), e); + Messages.showErrorDialog(parent.getProject(), e.getMessage(), SltBundle.message("slt.ui.errors.lisp.start")); + } } private void stackframeClicked(SltDebugStackTraceElement element, SltDebugInfo debugInfo) { diff --git a/src/main/java/com/en_circle/slt/plugin/ui/debug/SltInspector.java b/src/main/java/com/en_circle/slt/plugin/ui/debug/SltInspector.java index 79060e2..ab541d6 100644 --- a/src/main/java/com/en_circle/slt/plugin/ui/debug/SltInspector.java +++ b/src/main/java/com/en_circle/slt/plugin/ui/debug/SltInspector.java @@ -22,6 +22,7 @@ import com.intellij.openapi.util.text.HtmlBuilder; import com.intellij.openapi.util.text.HtmlChunk; import com.intellij.ui.components.JBScrollPane; +import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -116,6 +117,9 @@ private void loadInspectedObject(SltInspectedObject inspectedObject) { contentBuilder.append(HtmlChunk.br()); } } + if (parts.length == 0 && StringUtils.isNotBlank(text)) { + contentBuilder.append(HtmlChunk.br()); + } } else { contentBuilder.append(HtmlChunk.link(mkLink(inspectedObject, element), text) .style("color:" + SltUIConstants.colorToHex(SltUIConstants.HYPERLINK_COLOR))); diff --git a/src/main/lisp/swank/swank-abcl.lisp b/src/main/lisp/swank/swank-abcl.lisp index 7baf992..e054671 100644 --- a/src/main/lisp/swank/swank-abcl.lisp +++ b/src/main/lisp/swank/swank-abcl.lisp @@ -1,3 +1,8 @@ +(in-package :swank/backend) + +(defun get-restart-function-args (restart) + (swank-backend:arglist (system::restart-function restart))) + (in-package :swank/abcl) (defimplementation find-source-location (obj) diff --git a/src/main/lisp/swank/swank-ccl.lisp b/src/main/lisp/swank/swank-ccl.lisp index f201af2..c018a4d 100644 --- a/src/main/lisp/swank/swank-ccl.lisp +++ b/src/main/lisp/swank/swank-ccl.lisp @@ -1,3 +1,8 @@ +(in-package :swank/backend) + +(defun get-restart-function-args (restart) + NIL) + (in-package :swank/ccl) (in-package :swank) diff --git a/src/main/lisp/swank/swank-sbcl.lisp b/src/main/lisp/swank/swank-sbcl.lisp index 8d5c30e..3cbaa58 100644 --- a/src/main/lisp/swank/swank-sbcl.lisp +++ b/src/main/lisp/swank/swank-sbcl.lisp @@ -1,3 +1,8 @@ +(in-package :swank/backend) + +(defun get-restart-function-args (restart) + (swank-backend:arglist (sb-kernel::restart-function restart))) + (in-package :swank/sbcl) (defun form-number-position (definition-source stream) diff --git a/src/main/lisp/swank/swank.lisp b/src/main/lisp/swank/swank.lisp index b799704..b0eb5f0 100644 --- a/src/main/lisp/swank/swank.lisp +++ b/src/main/lisp/swank/swank.lisp @@ -44,7 +44,7 @@ format suitable for Emacs." :stream stream :msg "<>") (princ restart stream))) - (swank-backend:arglist (slot-value restart 'function)))))) + (swank-backend::get-restart-function-args restart))))) (defslimefun compile-string-region-slt (string buffer offset filename package) (with-buffer-syntax () diff --git a/src/main/resources/messages/SltBundle.properties b/src/main/resources/messages/SltBundle.properties index 3f96e26..0f0a498 100644 --- a/src/main/resources/messages/SltBundle.properties +++ b/src/main/resources/messages/SltBundle.properties @@ -71,12 +71,16 @@ slt.ui.debugger.close=Close slt.ui.debugger.errormessage=Error Message: slt.ui.debugger.condition=Condition: slt.ui.debugger.actions=Actions +slt.ui.debugger.actions.invoke.noarg=Invoke +slt.ui.debugger.actions.invoke.arg=Invoke with arguments slt.ui.debugger.frames=Frames slt.ui.debugger.frame=Frame slt.ui.debugger.arg.text=Single value or sexpression for argument: slt.ui.debugger.arg.title=Specify Restart Argument slt.ui.debugger.rest.text=Expressions separated by space for rest argument: slt.ui.debugger.rest.title=Specify Restart Rest Arguments +slt.ui.debugger.args.title=Specify Restart Arguments +slt.ui.debugger.args.text=Expressions separated by space for arguments to restart slt.ui.debugger.frame.repl=Frame REPL slt.ui.debugger.frame.locals=Locals slt.ui.debugger.frame.inspector=Inspector diff --git a/src/test/java/SwankTest.java b/src/test/java/SwankTest.java index 45b6262..990fd5f 100644 --- a/src/test/java/SwankTest.java +++ b/src/test/java/SwankTest.java @@ -1,14 +1,11 @@ import com.en_circle.slt.plugin.SltCommonLispFileType; import com.en_circle.slt.plugin.SltCommonLispLanguage; import com.en_circle.slt.plugin.SltCommonLispParserDefinition; -import com.en_circle.slt.plugin.environment.SltLispEnvironment; -import com.en_circle.slt.plugin.environment.SltLispEnvironment.SltOutput; -import com.en_circle.slt.plugin.environment.SltSBCLEnvironment; -import com.en_circle.slt.plugin.environment.SltSBCLEnvironmentConfiguration; import com.en_circle.slt.plugin.lisp.lisp.LispElement; import com.en_circle.slt.plugin.lisp.lisp.LispUtils; import com.en_circle.slt.plugin.lisp.psi.LispCoreProjectEnvironment; import com.en_circle.slt.plugin.swank.SwankClient; +import com.en_circle.slt.plugin.swank.SwankClient.SwankReply; import com.en_circle.slt.plugin.swank.SwankPacket; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFileFactory; @@ -19,30 +16,29 @@ public class SwankTest { public static void main(String[] args) throws Exception { try { - SltSBCLEnvironmentConfiguration c = new SltSBCLEnvironmentConfiguration.Builder() - .setListener((output, newData) -> { - if (output == SltOutput.STDERR) { - System.err.print(newData); - } - }) - .build(); - SltLispEnvironment environment = new SltSBCLEnvironment(); - environment.start(c); - try (SwankClient client = new SwankClient("127.0.0.1", 4005, packet -> { - LispCoreProjectEnvironment projectEnvironment = new LispCoreProjectEnvironment(); - projectEnvironment.getEnvironment() - .registerParserDefinition(SltCommonLispLanguage.INSTANCE, new SltCommonLispParserDefinition()); - PsiFileFactory factory = PsiFileFactory.getInstance(projectEnvironment.getProject()); - PsiFile source = factory.createFileFromText("swank-reply.cl", SltCommonLispFileType.INSTANCE, packet.getSentData()); - List elements = LispUtils.convertAst(source); - for (LispElement e : elements) { - System.out.println(e.toPrettyString() + "\n"); + try (SwankClient client = new SwankClient("127.0.0.1", 12345, new SwankReply() { + @Override + public void onSwankMessage(SwankPacket packet) { + LispCoreProjectEnvironment projectEnvironment = new LispCoreProjectEnvironment(); + projectEnvironment.getEnvironment() + .registerParserDefinition(SltCommonLispLanguage.INSTANCE, new SltCommonLispParserDefinition()); + PsiFileFactory factory = PsiFileFactory.getInstance(projectEnvironment.getProject()); + PsiFile source = factory.createFileFromText("swank-reply.cl", SltCommonLispFileType.INSTANCE, packet.getSentData()); + List elements = LispUtils.convertAst(source); + for (LispElement e : elements) { + System.out.println(e.toPrettyString() + "\n"); + } + } + + @Override + public void onReadError(Exception e) { + e.printStackTrace(); } })) { // client.swankSend(SlimePacket.swankInteractiveEval("(+ 1 1)", "cl-user", 1)); // client.swankSend(new SlimePacket("(:emacs-rex (swank:describe-definition-for-emacs \"XAXA\" :function) \"cl-user\" T 2)")); - client.swankSend(new SwankPacket("(:emacs-rex (swank:eval-and-grab-output \"(special-operator-p 'if)\") \"cl-user\" T 1)")); + client.swankSend(new SwankPacket("(:emacs-rex (swank:interactive-eval \"(error \"1\")\") \"cl-user\" T 1)")); // - finding symbols! // client.swankSend(new SlimePacket("(:emacs-rex (swank:apropos-list-for-emacs \"defun\") \"cl-user\" T 2)")); // client.swankSend(new SlimePacket("(:emacs-rex (swank:apropos-list-for-emacs \"defun\") \"cl-user\" T 2)")); @@ -52,7 +48,6 @@ public static void main(String[] args) throws Exception { Thread.sleep(10000); } - environment.stop(); } catch (Exception e) { e.printStackTrace(); } From 29655b2383d1fa9855ba4f85956d6ef003d47d81 Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Mon, 6 Feb 2023 13:21:13 +0100 Subject: [PATCH 16/40] #40 changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9097390..08a951d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Support for multiple lisp interprets - ABCL Support +- CCL Support ### Fixes From 98ad70e627a638f6a4f5301f33794669a5eca6d9 Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Mon, 6 Feb 2023 13:32:26 +0100 Subject: [PATCH 17/40] Fixes #41 --- CHANGELOG.md | 2 ++ .../en_circle/slt/plugin/lisp/LispParser.java | 25 +++++++++++++++++-- .../com/en_circle/slt/plugin/lisp/Lisp.bnf | 2 +- src/test/java/ParserTest.java | 4 +-- 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08a951d..8e534f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ ### Fixes +- Fixed parser issue with dot + ### Changes - Feature list for each interpret changes availability of functions in IDE diff --git a/src/main/gen/com/en_circle/slt/plugin/lisp/LispParser.java b/src/main/gen/com/en_circle/slt/plugin/lisp/LispParser.java index 2ccf24e..bba0218 100644 --- a/src/main/gen/com/en_circle/slt/plugin/lisp/LispParser.java +++ b/src/main/gen/com/en_circle/slt/plugin/lisp/LispParser.java @@ -97,7 +97,7 @@ private static boolean compound_symbol_0(PsiBuilder b, int l) { // tested | evaled | pathname | UNDEFINED_SEQUENCE | BIT_ARRAY | CHARACTER | REFERENCE_LABEL // | number | real_pair // | compound_symbol - // | string | vector | array | structure | list | pair + // | string | vector | array | structure | pair | list public static boolean datum(PsiBuilder b, int l) { if (!recursion_guard_(b, l, "datum")) return false; boolean r; @@ -116,8 +116,8 @@ public static boolean datum(PsiBuilder b, int l) { if (!r) r = vector(b, l + 1); if (!r) r = array(b, l + 1); if (!r) r = structure(b, l + 1); - if (!r) r = list(b, l + 1); if (!r) r = pair(b, l + 1); + if (!r) r = list(b, l + 1); exit_section_(b, l, m, r, false, null); return r; } @@ -293,6 +293,27 @@ private static boolean pair_1(PsiBuilder b, int l) { return r; } + /* ********************************************************** */ + // !(DOT | RPAREN | sexpr) + static boolean pair_recovery(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "pair_recovery")) return false; + boolean r; + Marker m = enter_section_(b, l, _NOT_); + r = !pair_recovery_0(b, l + 1); + exit_section_(b, l, m, r, false, null); + return r; + } + + // DOT | RPAREN | sexpr + private static boolean pair_recovery_0(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "pair_recovery_0")) return false; + boolean r; + r = consumeToken(b, DOT); + if (!r) r = consumeToken(b, RPAREN); + if (!r) r = sexpr(b, l + 1); + return r; + } + /* ********************************************************** */ // PATHNAME_INDICATOR sexpr public static boolean pathname(PsiBuilder b, int l) { diff --git a/src/main/java/com/en_circle/slt/plugin/lisp/Lisp.bnf b/src/main/java/com/en_circle/slt/plugin/lisp/Lisp.bnf index a19a2e1..eb86c88 100644 --- a/src/main/java/com/en_circle/slt/plugin/lisp/Lisp.bnf +++ b/src/main/java/com/en_circle/slt/plugin/lisp/Lisp.bnf @@ -29,7 +29,7 @@ enhancement ::= REFERENCE_SET | TEST_SUCCESS | UNQUOTE | UNQUOTE_SPLICE | BACKQU datum ::= tested | evaled | pathname | UNDEFINED_SEQUENCE | BIT_ARRAY | CHARACTER | REFERENCE_LABEL | number | real_pair | compound_symbol - | string | vector | array | structure | list | pair + | string | vector | array | structure | pair | list tested ::= (TEST_SUCCESS | TEST_FALURE) sexpr diff --git a/src/test/java/ParserTest.java b/src/test/java/ParserTest.java index 440c070..f3d405d 100644 --- a/src/test/java/ParserTest.java +++ b/src/test/java/ParserTest.java @@ -12,9 +12,7 @@ public class ParserTest { public static void main(String[] args) { String data = """ - (defun - buble - (a b c) 0 + (a . """; LispCoreProjectEnvironment projectEnvironment = new LispCoreProjectEnvironment(); From 30d04b543a446c3d46291cdcc58ba7c9eee170cc Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Mon, 6 Feb 2023 13:49:58 +0100 Subject: [PATCH 18/40] merge --- src/main/java/com/en_circle/slt/plugin/ui/SltCoreWindow.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/java/com/en_circle/slt/plugin/ui/SltCoreWindow.java b/src/main/java/com/en_circle/slt/plugin/ui/SltCoreWindow.java index 3b26d97..0d74410 100644 --- a/src/main/java/com/en_circle/slt/plugin/ui/SltCoreWindow.java +++ b/src/main/java/com/en_circle/slt/plugin/ui/SltCoreWindow.java @@ -185,11 +185,6 @@ public void actionPerformed(@NotNull AnActionEvent e) { return ActionUpdateThread.EDT; } - @Override - public @NotNull ActionUpdateThread getActionUpdateThread() { - return ActionUpdateThread.EDT; - } - @Override public void update(@NotNull AnActionEvent e) { super.update(e); From 3a643a2fc82484e826935fff2012742dac835ded Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Mon, 6 Feb 2023 14:22:47 +0100 Subject: [PATCH 19/40] debugger refactor --- .../slt/plugin/environment/LispFeatures.java | 2 + .../clisp/CCLEnvironmentDefinition.java | 2 + .../sbcl/SBCLEnvironmentDefinition.java | 1 + .../lisp/components/SltBreakpoint.java | 29 +------- .../components/SltBreakpointContainer.java | 19 +---- .../ui/debug/SltBreakpointProperties.java | 9 ++- .../ui/debug/SltSymbolBreakpointType.java | 73 +++---------------- src/main/lisp/slt/slt-abcl.lisp | 4 + src/main/lisp/slt/slt-ccl.lisp | 6 ++ src/main/lisp/slt/slt-sbcl.lisp | 6 ++ src/main/lisp/slt/slt.lisp | 1 + 11 files changed, 42 insertions(+), 110 deletions(-) diff --git a/src/main/java/com/en_circle/slt/plugin/environment/LispFeatures.java b/src/main/java/com/en_circle/slt/plugin/environment/LispFeatures.java index 5d97a4f..0492c26 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/LispFeatures.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/LispFeatures.java @@ -8,6 +8,8 @@ public enum LispFeatures { FRAME_EVAL, INSPECTOR, DEBUGGER_ACTION_ARGLIST, + BREAKPOINTS, + BREAKPOINT_STEPPING, INSPECTOR_HISTORY, AUTOCOMPLETE, SEARCH, diff --git a/src/main/java/com/en_circle/slt/plugin/environment/clisp/CCLEnvironmentDefinition.java b/src/main/java/com/en_circle/slt/plugin/environment/clisp/CCLEnvironmentDefinition.java index 9f44208..b1c5153 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/clisp/CCLEnvironmentDefinition.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/clisp/CCLEnvironmentDefinition.java @@ -23,6 +23,8 @@ public CCLEnvironmentDefinition() { features.add(LispFeatures.DOCUMENTATION); features.add(LispFeatures.MACROEXPAND); features.add(LispFeatures.FRAME_EVAL); + features.add(LispFeatures.BREAKPOINTS); + features.add(LispFeatures.BREAKPOINT_STEPPING); features.add(LispFeatures.INSPECTOR); features.add(LispFeatures.INSPECTOR_HISTORY); features.add(LispFeatures.AUTOCOMPLETE); diff --git a/src/main/java/com/en_circle/slt/plugin/environment/sbcl/SBCLEnvironmentDefinition.java b/src/main/java/com/en_circle/slt/plugin/environment/sbcl/SBCLEnvironmentDefinition.java index 2841014..0eea5c3 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/sbcl/SBCLEnvironmentDefinition.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/sbcl/SBCLEnvironmentDefinition.java @@ -25,6 +25,7 @@ public SBCLEnvironmentDefinition() { features.add(LispFeatures.MACROEXPAND); features.add(LispFeatures.FRAME_EVAL); features.add(LispFeatures.DEBUGGER_ACTION_ARGLIST); + features.add(LispFeatures.BREAKPOINTS); features.add(LispFeatures.INSPECTOR); features.add(LispFeatures.INSPECTOR_HISTORY); features.add(LispFeatures.AUTOCOMPLETE); diff --git a/src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltBreakpoint.java b/src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltBreakpoint.java index 563d49b..da4d191 100644 --- a/src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltBreakpoint.java +++ b/src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltBreakpoint.java @@ -12,13 +12,9 @@ public class SltBreakpoint implements Comparable { private static final Comparator COMPARATOR = - Comparator.comparing(SltBreakpoint::getSymbol) - .thenComparing(SltBreakpoint::getParentSymbol, Comparator.nullsFirst(String::compareTo)) - .thenComparing(SltBreakpoint::getParentBindType, Comparator.nullsFirst(String::compareTo)); + Comparator.comparing(SltBreakpoint::getSymbol); private String symbol; - private String parentSymbol; - private String parentBindType; private SltBreakpointType type = SltBreakpointType.STANDARD; private boolean installed; @@ -57,22 +53,6 @@ public void setSymbol(String symbol) { this.symbol = symbol; } - public String getParentSymbol() { - return parentSymbol; - } - - public void setParentSymbol(String parentSymbol) { - this.parentSymbol = parentSymbol; - } - - public String getParentBindType() { - return parentBindType; - } - - public void setParentBindType(String parentBindType) { - this.parentBindType = parentBindType; - } - @Override public String toString() { return "Breakpoint(" + symbol + ")"; @@ -86,17 +66,12 @@ public boolean equals(Object o) { SltBreakpoint that = (SltBreakpoint) o; if (!Objects.equals(symbol, that.symbol)) return false; - if (!Objects.equals(parentSymbol, that.parentSymbol)) return false; - if (!Objects.equals(parentBindType, that.parentBindType)) - return false; return type == that.type; } @Override public int hashCode() { int result = symbol != null ? symbol.hashCode() : 0; - result = 31 * result + (parentSymbol != null ? parentSymbol.hashCode() : 0); - result = 31 * result + (parentBindType != null ? parentBindType.hashCode() : 0); result = 31 * result + (type != null ? type.hashCode() : 0); return result; } @@ -117,7 +92,7 @@ public int compareTo(@NotNull SltBreakpoint o) { public enum SltBreakpointType { - STANDARD, INNER, METHOD + STANDARD, METHOD } } diff --git a/src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltBreakpointContainer.java b/src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltBreakpointContainer.java index c8a0365..f5e3314 100644 --- a/src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltBreakpointContainer.java +++ b/src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltBreakpointContainer.java @@ -3,7 +3,6 @@ import com.en_circle.slt.plugin.environment.SltLispEnvironment.SltOutput; import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService; import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService.LispEnvironmentListener; -import com.en_circle.slt.plugin.services.lisp.components.SltBreakpoint.SltBreakpointType; import com.en_circle.slt.plugin.swank.SlimeRequest; import com.en_circle.slt.plugin.swank.requests.Eval; import com.en_circle.slt.plugin.ui.debug.SltBreakpointProperties; @@ -52,13 +51,7 @@ public void removeBreakpoint(XBreakpoint nativeBreakpoi private SltBreakpoint getBreakpoint(XBreakpoint nativeBreakpoint) { SltBreakpointProperties p = nativeBreakpoint.getProperties(); - SltBreakpoint breakpoint = new SltBreakpoint(getBreakpointSymbol(p.packageName, p.symbolName)); - if (p.fletType != null) { - breakpoint.setParentBindType(p.fletType); - breakpoint.setParentSymbol(getBreakpointSymbol(p.ppackageName, p.psymbolName)); - breakpoint.setType(SltBreakpointType.INNER); - } - return breakpoint; + return new SltBreakpoint(getBreakpointSymbol(p.packageName, p.symbolName)); } public void onUpdate(XBreakpoint nativeBreakpoint) { @@ -112,11 +105,6 @@ private SlimeRequest installRequest(SltBreakpoint breakpoint) { case STANDARD -> { return Eval.eval("(slt-core:install-breakpoint '" + breakpoint.getSymbol() + ")", r -> installResult(r, breakpoint)); } - case INNER -> { - return Eval.eval("(slt-core:install-inner-breakpoint '" + breakpoint.getSymbol() - + " '" + breakpoint.getParentSymbol() + " '" + breakpoint.getParentBindType() - + ")", r -> installResult(r, breakpoint)); - } case METHOD -> { // TODO return null; @@ -145,11 +133,6 @@ private SlimeRequest uninstallRequest(SltBreakpoint breakpoint) { case STANDARD -> { return Eval.eval("(slt-core:uninstall-breakpoint '" + breakpoint.getSymbol() + ")", r -> uninstallResult(r, breakpoint)); } - case INNER -> { - return Eval.eval("(slt-core:uninstall-inner-breakpoint '" + breakpoint.getSymbol() - + " '" + breakpoint.getParentSymbol() + " '" + breakpoint.getParentBindType() - + ")", r -> uninstallResult(r, breakpoint)); - } case METHOD -> { // TODO return null; diff --git a/src/main/java/com/en_circle/slt/plugin/ui/debug/SltBreakpointProperties.java b/src/main/java/com/en_circle/slt/plugin/ui/debug/SltBreakpointProperties.java index dda7b48..1b95f68 100644 --- a/src/main/java/com/en_circle/slt/plugin/ui/debug/SltBreakpointProperties.java +++ b/src/main/java/com/en_circle/slt/plugin/ui/debug/SltBreakpointProperties.java @@ -1,19 +1,22 @@ package com.en_circle.slt.plugin.ui.debug; import com.en_circle.slt.plugin.ui.debug.SltSymbolBreakpointType.SymbolType; +import com.intellij.openapi.components.State; +import com.intellij.openapi.components.Storage; import com.intellij.util.xmlb.XmlSerializerUtil; import com.intellij.xdebugger.breakpoints.XBreakpointProperties; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +@State( + name = "SltBreakpointProperties", + storages = @Storage("SltBreakpoints.xml") +) public class SltBreakpointProperties extends XBreakpointProperties { public String symbolName; public String packageName; public SymbolType symbolType; - public String psymbolName; - public String ppackageName; - public String fletType; public int offset; public String file; diff --git a/src/main/java/com/en_circle/slt/plugin/ui/debug/SltSymbolBreakpointType.java b/src/main/java/com/en_circle/slt/plugin/ui/debug/SltSymbolBreakpointType.java index 17c0dd3..f78d51d 100644 --- a/src/main/java/com/en_circle/slt/plugin/ui/debug/SltSymbolBreakpointType.java +++ b/src/main/java/com/en_circle/slt/plugin/ui/debug/SltSymbolBreakpointType.java @@ -90,31 +90,17 @@ private List getPossiblePsiDebugSymbols(VirtualFile file, int if (sexprs.size() > 1) { addIfSymbol(sexprs.get(1), infoList, offset, endoffset, SymbolType.MACRO, head.getName()); } - } else if ("defmethod".equalsIgnoreCase(head.getName())) { - // TODO: handle method special stuff to get tracing working correctly and not just on defgeneric level - if (sexprs.size() > 1) { - addIfSymbol(sexprs.get(1), infoList, offset, endoffset, SymbolType.METHOD, head.getName()); - if (sexprs.size() > 2) { - addIfSymbol(sexprs.get(2), infoList, offset, endoffset, SymbolType.METHOD, head.getName()); - } - } - } else if ("flet".equalsIgnoreCase(head.getName()) || - "labels".equalsIgnoreCase(head.getName())) { - // macrolet not supported in SBCL :( - if (sexprs.size() > 1) { - if (sexprs.get(1).getDatum() != null && Objects.requireNonNull(sexprs.get(1).getDatum()).getList() != null) { - LispList funcdefList = Objects.requireNonNull(Objects.requireNonNull(sexprs.get(1).getDatum()).getList()); - for (LispSexpr sexpr : funcdefList.getSexprList()) { - if (sexpr.getDatum() != null && sexpr.getDatum().getList() != null) { - LispList definition = sexpr.getDatum().getList(); - if (definition.getSexprList().size() > 0) { - addIfSymbol(definition.getSexprList().get(0), infoList, offset, endoffset, SymbolType.FUNCTION, head.getName()); - } - } - } - } - } } + // TODO +// } else if ("defmethod".equalsIgnoreCase(head.getName())) { +// // TODO: handle method special stuff to get tracing working correctly and not just on defgeneric level +// if (sexprs.size() > 1) { +// addIfSymbol(sexprs.get(1), infoList, offset, endoffset, SymbolType.METHOD, head.getName()); +// if (sexprs.size() > 2) { +// addIfSymbol(sexprs.get(2), infoList, offset, endoffset, SymbolType.METHOD, head.getName()); +// } +// } +// } // TODO: add defclass accessor/setter/getter into the mix! } @@ -140,38 +126,7 @@ private void addIfSymbol(LispSexpr sexpr, List infoList, int if (soffset >= offset && soffset <= endoffset) { String packageName = LispParserUtil.getPackage(symbol); SltBreakpointInfo info = new SltBreakpointInfo(symbol, packageName, symbolType); - if ("flet".equalsIgnoreCase(headName) || "labels".equalsIgnoreCase(headName)) { - info.fletType = headName.toLowerCase(); - LispList list = PsiTreeUtil.getParentOfType(sexpr, LispList.class); - do { - if (list != null) { - List sexprs = list.getSexprList(); - if (sexprs.size() > 0) { - if (sexprs.get(0).getDatum() != null && - Objects.requireNonNull(sexprs.get(0).getDatum()).getCompoundSymbol() != null) { - LispSymbol head = Objects.requireNonNull(Objects.requireNonNull(sexprs.get(0).getDatum()).getCompoundSymbol()).getSymbol(); - - if ("defun".equalsIgnoreCase(head.getName())) { - if (sexprs.size() > 1) { - LispSexpr psym = sexprs.get(1); - if (psym.getDatum() != null) { - if (psym.getDatum().getCompoundSymbol() != null) { - LispSymbol parentSymbol = psym.getDatum().getCompoundSymbol().getSymbol(); - info.ppackageName = info.packageName; - info.pelement = parentSymbol; - infoList.add(info); - } - } - } - } - } - } - } - list = PsiTreeUtil.getParentOfType(list, LispList.class); - } while (list != null); - } else { - infoList.add(info); - } + infoList.add(info); } } } @@ -207,9 +162,6 @@ private class SltBreakpointInfo extends XLineBreakpointVariant { private final LispSymbol element; private final String packageName; private final SymbolType symbolType; - private String fletType; - private LispSymbol pelement; - private String ppackageName; private SltBreakpointInfo(@NotNull LispSymbol element, String packageName, SymbolType symbolType) { this.element = element; @@ -243,9 +195,6 @@ private SltBreakpointInfo(@NotNull LispSymbol element, String packageName, Symbo properties.symbolName = element.getName(); properties.packageName = packageName; properties.symbolType = symbolType; - properties.psymbolName = pelement == null ? null : pelement.getName(); - properties.ppackageName = ppackageName; - properties.fletType = fletType; properties.file = element.getContainingFile().getVirtualFile().getUrl(); properties.offset = element.getTextOffset(); diff --git a/src/main/lisp/slt/slt-abcl.lisp b/src/main/lisp/slt/slt-abcl.lisp index 82daa99..e1bd0df 100644 --- a/src/main/lisp/slt/slt-abcl.lisp +++ b/src/main/lisp/slt/slt-abcl.lisp @@ -1,4 +1,8 @@ (in-package :slt-core) +(defun install-breakpoint (symbol) ) + +(defun uninstall-breakpoint (symbol) ) + (defun specialp (test-sym) NIL) \ No newline at end of file diff --git a/src/main/lisp/slt/slt-ccl.lisp b/src/main/lisp/slt/slt-ccl.lisp index db612f8..6479ef7 100644 --- a/src/main/lisp/slt/slt-ccl.lisp +++ b/src/main/lisp/slt/slt-ccl.lisp @@ -1,4 +1,10 @@ (in-package :slt-core) +(defun install-breakpoint (symbol) + (ignore-errors (eval `(trace ,symbol :break-before T)) T)) + +(defun uninstall-breakpoint (symbol) + (ignore-errors (eval `(untrace ,symbol)) T)) + (defun specialp (test-sym) (eq (ccl:variable-information test-sym) :special)) \ No newline at end of file diff --git a/src/main/lisp/slt/slt-sbcl.lisp b/src/main/lisp/slt/slt-sbcl.lisp index 58d2443..2dbbb02 100644 --- a/src/main/lisp/slt/slt-sbcl.lisp +++ b/src/main/lisp/slt/slt-sbcl.lisp @@ -1,4 +1,10 @@ (in-package :slt-core) +(defun install-breakpoint (symbol) + (ignore-errors (eval `(trace ,symbol :break-before T)) T)) + +(defun uninstall-breakpoint (symbol) + (ignore-errors (eval `(untrace ,symbol)) T)) + (defun specialp (test-sym) (eq (sb-cltl2:variable-information test-sym) :special)) \ No newline at end of file diff --git a/src/main/lisp/slt/slt.lisp b/src/main/lisp/slt/slt.lisp index 50dc827..02caa36 100644 --- a/src/main/lisp/slt/slt.lisp +++ b/src/main/lisp/slt/slt.lisp @@ -2,6 +2,7 @@ (:use :slt :cl :swank) (:export analyze-symbol analyze-symbols read-fix-packages list-package-names initialize-or-get-debug-context debug-context debug-frame-variable register-variable + install-breakpoint uninstall-breakpoint )) (when (eq slt:+slt-interpret+ :sbcl) From d8e1cfed07f71e90fd1838ff050a1f9af5de2a92 Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Tue, 7 Feb 2023 17:02:34 +0100 Subject: [PATCH 20/40] psi speedup --- CHANGELOG.md | 1 + .../contributors/SltSymbolContributor.java | 34 +++---- .../slt/plugin/lisp/LispParserUtil.java | 2 +- .../lisp/LispPotentialSymbolPresentation.java | 69 ++++++++++++++ .../slt/plugin/lisp/LispStubTypes.java | 7 ++ .../slt/plugin/lisp/psi/LispSymbolType.java | 5 - .../plugin/references/SltFakePsiElement.java | 91 +++++++++++++++++++ .../slt/plugin/references/SltReference.java | 5 +- .../en_circle/slt/tools/LocationUtils.java | 49 ++-------- src/main/lisp/swank/swank.lisp | 2 +- src/main/resources/META-INF/plugin.xml | 1 + 11 files changed, 192 insertions(+), 74 deletions(-) create mode 100644 src/main/java/com/en_circle/slt/plugin/lisp/LispPotentialSymbolPresentation.java create mode 100644 src/main/java/com/en_circle/slt/plugin/lisp/LispStubTypes.java delete mode 100644 src/main/java/com/en_circle/slt/plugin/lisp/psi/LispSymbolType.java create mode 100644 src/main/java/com/en_circle/slt/plugin/references/SltFakePsiElement.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e534f9..8e19657 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### Fixes - Fixed parser issue with dot +- Fixed xref speed issues with large psi ### Changes diff --git a/src/main/java/com/en_circle/slt/plugin/contributors/SltSymbolContributor.java b/src/main/java/com/en_circle/slt/plugin/contributors/SltSymbolContributor.java index c8f0a54..b87316a 100644 --- a/src/main/java/com/en_circle/slt/plugin/contributors/SltSymbolContributor.java +++ b/src/main/java/com/en_circle/slt/plugin/contributors/SltSymbolContributor.java @@ -1,11 +1,10 @@ package com.en_circle.slt.plugin.contributors; import com.en_circle.slt.plugin.environment.LispFeatures; -import com.en_circle.slt.plugin.lisp.LispSymbolPresentation; +import com.en_circle.slt.plugin.lisp.LispPotentialSymbolPresentation; import com.en_circle.slt.plugin.lisp.lisp.LispContainer; import com.en_circle.slt.plugin.lisp.lisp.LispElement; import com.en_circle.slt.plugin.lisp.lisp.LispString; -import com.en_circle.slt.plugin.lisp.psi.LispSymbol; import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService; import com.en_circle.slt.plugin.swank.components.SourceLocation; import com.en_circle.slt.plugin.swank.requests.CompleteSearch; @@ -16,15 +15,14 @@ import com.intellij.navigation.ItemPresentation; import com.intellij.navigation.NavigationItem; import com.intellij.navigation.PsiElementNavigationItem; -import com.intellij.openapi.fileEditor.FileEditorManager; -import com.intellij.openapi.fileEditor.OpenFileDescriptor; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.util.NlsSafe; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; +import com.intellij.psi.NavigatablePsiElement; import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiNamedElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -136,17 +134,21 @@ private NavigationItem[] getItems(LispElement form, boolean includeNonProjectIte private NavigationItem createNavigationElement(Project project, LispContainer c) { String name = ((LispString) c.getItems().get(0)).getValue(); + String packageName = null; + if (c.getItems().get(1) instanceof LispString pkgName) + packageName = pkgName.getValue(); + final String packageNameFinal = packageName; LispElement location = c.getItems().get(3); if (location instanceof LispContainer loc) { SourceLocation l = new SourceLocation(loc); return LocationUtils.convertFromLocationToSymbol(project, l, name, - s -> mkElement(project, s)); + s -> mkElement(project, s, packageNameFinal, l.getPosition())); } return null; } - private NavigationItem mkElement(Project project, LispSymbol symbol) { + private NavigationItem mkElement(Project project, PsiNamedElement symbol, String packageName, int offset) { return new PsiElementNavigationItem() { @Override public @Nullable PsiElement getTargetElement() { @@ -160,28 +162,18 @@ private NavigationItem mkElement(Project project, LispSymbol symbol) { @Override public @Nullable ItemPresentation getPresentation() { - return new LispSymbolPresentation(symbol); + return new LispPotentialSymbolPresentation(project, symbol.getContainingFile(), + symbol.getName(), packageName, offset); } @Override public void navigate(boolean requestFocus) { - ProjectFileIndex index = ProjectFileIndex.getInstance(project); - PsiFile psiFile = symbol.getContainingFile(); - VirtualFile vf = psiFile.getVirtualFile(); - if (vf != null) { - if (index.isInSource(vf)) { - FileEditorManager.getInstance(project) - .openTextEditor(new OpenFileDescriptor(project, vf, symbol.getTextOffset()), true); - } else { - FileEditorManager.getInstance(project) - .openEditor(new OpenFileDescriptor(project, vf, symbol.getTextOffset()), true); - } - } + ((NavigatablePsiElement) symbol).navigate(requestFocus); } @Override public boolean canNavigate() { - return true; + return ((NavigatablePsiElement) symbol).canNavigate(); } @Override diff --git a/src/main/java/com/en_circle/slt/plugin/lisp/LispParserUtil.java b/src/main/java/com/en_circle/slt/plugin/lisp/LispParserUtil.java index 614b39a..67fa17b 100644 --- a/src/main/java/com/en_circle/slt/plugin/lisp/LispParserUtil.java +++ b/src/main/java/com/en_circle/slt/plugin/lisp/LispParserUtil.java @@ -41,7 +41,7 @@ public static String getPackage(PsiFile psiFile, int offset, Function notFoundSupplier) { diff --git a/src/main/java/com/en_circle/slt/plugin/lisp/LispPotentialSymbolPresentation.java b/src/main/java/com/en_circle/slt/plugin/lisp/LispPotentialSymbolPresentation.java new file mode 100644 index 0000000..7b7a3ec --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/lisp/LispPotentialSymbolPresentation.java @@ -0,0 +1,69 @@ +package com.en_circle.slt.plugin.lisp; + +import com.en_circle.slt.plugin.SymbolState; +import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService; +import com.intellij.icons.AllIcons.Nodes; +import com.intellij.navigation.ItemPresentation; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.NlsSafe; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiFile; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; + +public class LispPotentialSymbolPresentation implements ItemPresentation { + + private final Project project; + private PsiFile file; + private final String symbol; + private final String packageName; + private final int offset; + + public LispPotentialSymbolPresentation(Project project, PsiFile file, String name, String packageName, int offset) { + this.project = project; + this.file = file; + this.symbol = name; + this.packageName = packageName; + this.offset = offset; + } + + @Override + public @NlsSafe @Nullable String getPresentableText() { + return symbol; + } + + private SymbolState refreshState() { + return LispEnvironmentService.getInstance(project) + .refreshSymbolFromServer(packageName, symbol); + } + + @Override + public @NlsSafe @Nullable String getLocationString() { + if (file != null) { + Document d = PsiDocumentManager.getInstance(project).getDocument(file); + if (d != null) { + int line = d.getLineNumber(offset); + return file.getName() + ": " + line; + } + return file.getName(); + } + return null; + } + + @Override + public @Nullable Icon getIcon(boolean unused) { + SymbolState state = refreshState(); + + return switch (state.binding) { + case NONE, SPECIAL_FORM -> null; + case FUNCTION -> Nodes.Function; + case MACRO -> Nodes.Template; + case CONSTANT, KEYWORD -> Nodes.Constant; + case SPECIAL_VARIABLE -> Nodes.Gvariable; + case CLASS -> Nodes.Class; + case METHOD -> Nodes.Method; + }; + } +} diff --git a/src/main/java/com/en_circle/slt/plugin/lisp/LispStubTypes.java b/src/main/java/com/en_circle/slt/plugin/lisp/LispStubTypes.java new file mode 100644 index 0000000..d54876b --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/lisp/LispStubTypes.java @@ -0,0 +1,7 @@ +package com.en_circle.slt.plugin.lisp; + +public interface LispStubTypes { + + + +} diff --git a/src/main/java/com/en_circle/slt/plugin/lisp/psi/LispSymbolType.java b/src/main/java/com/en_circle/slt/plugin/lisp/psi/LispSymbolType.java deleted file mode 100644 index caa4b2f..0000000 --- a/src/main/java/com/en_circle/slt/plugin/lisp/psi/LispSymbolType.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.en_circle.slt.plugin.lisp.psi; - -public class LispSymbolType { - -} diff --git a/src/main/java/com/en_circle/slt/plugin/references/SltFakePsiElement.java b/src/main/java/com/en_circle/slt/plugin/references/SltFakePsiElement.java new file mode 100644 index 0000000..851f9b9 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/references/SltFakePsiElement.java @@ -0,0 +1,91 @@ +package com.en_circle.slt.plugin.references; + +import com.en_circle.slt.plugin.SltCommonLispLanguage; +import com.intellij.ide.util.PsiNavigationSupport; +import com.intellij.lang.Language; +import com.intellij.navigation.ItemPresentation; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.NlsSafe; +import com.intellij.pom.Navigatable; +import com.intellij.psi.NavigatablePsiElement; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.impl.FakePsiElement; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; + +public class SltFakePsiElement extends FakePsiElement implements NavigatablePsiElement { + + private final PsiFile f; + private final int position; + private final String name; + private final ItemPresentation presentation; + + public SltFakePsiElement(PsiFile f, String name, int position) { + this.f = f; + this.name = name; + this.position = position; + this.presentation = new ItemPresentation() { + @Override + public @NlsSafe @Nullable String getPresentableText() { + return name; + } + + @Override + public @Nullable Icon getIcon(boolean unused) { + return null; + } + + }; + } + + @Override + public @NotNull Project getProject() { + return f.getProject(); + } + + @Override + public PsiElement getParent() { + return f; + } + + @Override + public int getTextOffset() { + return position; + } + + @Override + public int getStartOffsetInParent() { + return position; + } + + @Override + public String getName() { + return name; + } + + @Override + public ItemPresentation getPresentation() { + return presentation; + } + + @Override + public @NotNull Language getLanguage() { + return SltCommonLispLanguage.INSTANCE; + } + + @Override + public void navigate(boolean requestFocus) { + Navigatable descriptor = PsiNavigationSupport.getInstance().getDescriptor(this); + if (descriptor != null) { + descriptor.navigate(requestFocus); + } + } + + @Override + public boolean canNavigate() { + return true; + } +} diff --git a/src/main/java/com/en_circle/slt/plugin/references/SltReference.java b/src/main/java/com/en_circle/slt/plugin/references/SltReference.java index cee56e9..c3936a5 100644 --- a/src/main/java/com/en_circle/slt/plugin/references/SltReference.java +++ b/src/main/java/com/en_circle/slt/plugin/references/SltReference.java @@ -202,10 +202,7 @@ private ResolveResult convertLocationToReference(SourceLocation location, String PsiFile f = PsiManager.getInstance(myElement.getProject()).findFile(vf); if (f == null) return null; - PsiElement element = f.findElementAt(location.getPosition()); - if (element == null) - return null; - return new PsiElementResolveResult(element); + return new PsiElementResolveResult(new SltFakePsiElement(f, name, location.getPosition())); } @Override diff --git a/src/main/java/com/en_circle/slt/tools/LocationUtils.java b/src/main/java/com/en_circle/slt/tools/LocationUtils.java index 3662cc8..adc2f5b 100644 --- a/src/main/java/com/en_circle/slt/tools/LocationUtils.java +++ b/src/main/java/com/en_circle/slt/tools/LocationUtils.java @@ -1,62 +1,27 @@ package com.en_circle.slt.tools; -import com.en_circle.slt.plugin.lisp.psi.LispSymbol; -import com.en_circle.slt.plugin.lisp.psi.LispToplevel; +import com.en_circle.slt.plugin.references.SltFakePsiElement; import com.en_circle.slt.plugin.swank.components.SourceLocation; -import com.intellij.lang.ASTNode; -import com.intellij.lang.FileASTNode; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; -import com.intellij.psi.util.CachedValueProvider; -import com.intellij.psi.util.CachedValuesManager; -import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.PsiNamedElement; import java.io.File; -import java.util.Collection; import java.util.function.Function; public class LocationUtils { - public static X convertFromLocationToSymbol(Project project, SourceLocation location, String name, Function converter) { + public static X convertFromLocationToSymbol(Project project, SourceLocation location, String name, + Function converter) { if (location.isFile()) { VirtualFile vf = LocalFileSystem.getInstance().findFileByIoFile(new File(location.getLocation())); if (vf != null) { - CachedValuesManager cachedValuesManager = CachedValuesManager.getManager(project); - PsiFile file = cachedValuesManager.createCachedValue(() -> { - PsiFile f = PsiManager.getInstance(project).findFile(vf); - if (f != null) { - FileASTNode node = f.getNode(); - node.getFirstChildNode(); - } - return CachedValueProvider.Result.create(f, vf); - }).getValue(); - if (file != null) { - FileASTNode node = file.getNode(); - if (node != null) { - ASTNode reference = node.findLeafElementAt(location.getPosition()); - - if (reference != null) { - PsiElement referenceElement = reference.getPsi(); - LispSymbol ls = PsiTreeUtil.getParentOfType(referenceElement, LispSymbol.class); - if (ls != null && ls.getName() != null && ls.getName().equalsIgnoreCase(name)) { - return converter.apply(ls); - } - - PsiElement parent = PsiTreeUtil.getParentOfType(referenceElement, LispToplevel.class); - if (parent != null) { - Collection symbols = PsiTreeUtil.findChildrenOfType(parent, LispSymbol.class); - for (LispSymbol symbol : symbols) { - if (symbol.getName() != null && symbol.getName().equalsIgnoreCase(name)) { - return converter.apply(symbol); - } - } - } - } - } + PsiFile f = PsiManager.getInstance(project).findFile(vf); + if (f != null) { + return converter.apply(new SltFakePsiElement(f, name, location.getPosition())); } } } diff --git a/src/main/lisp/swank/swank.lisp b/src/main/lisp/swank/swank.lisp index b0eb5f0..91c7a28 100644 --- a/src/main/lisp/swank/swank.lisp +++ b/src/main/lisp/swank/swank.lisp @@ -83,7 +83,7 @@ format suitable for Emacs." (let* ((symbol (second data-pair)) (str (first data-pair)) (package (third data-pair)) - (strpackage (format NIL "~S" package))) + (strpackage (package-name package))) (cond ((not symbol) (list str strpackage NIL NIL)) ((macro-function symbol) (list str strpackage :macro (find-source-location (symbol-function symbol)))) diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index aa9e7a2..db55da2 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -108,6 +108,7 @@ + From b85a855c1ed927858af49340f62122ad8f2aff32 Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Wed, 8 Feb 2023 22:13:18 +0100 Subject: [PATCH 21/40] working on allegro support --- FEATURES.md | 28 +-- .../en_circle/slt/plugin/lisp/LispParser.java | 65 +---- .../slt/plugin/lisp/impl/LispDatumImpl.java | 6 - .../slt/plugin/lisp/impl/LispPairImpl.java | 37 --- .../slt/plugin/lisp/psi/LispDatum.java | 3 - .../slt/plugin/lisp/psi/LispPair.java | 13 - .../slt/plugin/lisp/psi/LispTypes.java | 5 +- .../slt/plugin/lisp/psi/LispVisitor.java | 4 - .../slt/plugin/environment/Environment.java | 4 +- .../slt/plugin/environment/LispInterpret.java | 1 + .../environment/SltAllegroCLEnvironment.java | 170 +++++++++++++ .../SltAllegroCLEnvironmentConfiguration.java | 94 +++++++ .../AllegroCLEnvironmentDefinition.java | 74 ++++++ .../allegro/AllegroCLOverrides.java | 10 + .../CCLEnvironmentDefinition.java | 2 +- .../{clisp => ccl}/CCLOverrides.java | 2 +- .../com/en_circle/slt/plugin/lisp/Lisp.bnf | 6 +- .../slt/plugin/lisp/lisp/LispContainer.java | 21 +- .../slt/plugin/lisp/lisp/LispUtils.java | 12 +- .../com/en_circle/slt/plugin/sdk/LispSdk.java | 48 ++++ .../ui/sdk/SdkConfigurationABCLProcess.java | 4 +- .../sdk/SdkConfigurationAllegroCLProcess.java | 229 ++++++++++++++++++ .../ui/sdk/SdkConfigurationCCLProcess.java | 8 +- .../ui/sdk/SdkConfigurationSBCLProcess.java | 4 +- .../en_circle/slt/tools/AllegroCLUtils.java | 94 +++++++ .../com/en_circle/slt/tools/CCLUtils.java | 2 +- src/main/lisp/load.lisp | 11 + src/main/lisp/slt/slt-allegro.lisp | 4 + src/main/lisp/slt/slt.lisp | 2 + src/main/lisp/swank/swank-allegro.lisp | 24 ++ src/main/lisp/swank/swank.lisp | 2 + .../resources/messages/SltBundle.properties | 11 +- .../templates/en_US/initscript.allegro.cl | 18 ++ src/test/java/ParserTest.java | 2 +- 34 files changed, 837 insertions(+), 183 deletions(-) delete mode 100644 src/main/gen/com/en_circle/slt/plugin/lisp/impl/LispPairImpl.java delete mode 100644 src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispPair.java create mode 100644 src/main/java/com/en_circle/slt/plugin/environment/SltAllegroCLEnvironment.java create mode 100644 src/main/java/com/en_circle/slt/plugin/environment/SltAllegroCLEnvironmentConfiguration.java create mode 100644 src/main/java/com/en_circle/slt/plugin/environment/allegro/AllegroCLEnvironmentDefinition.java create mode 100644 src/main/java/com/en_circle/slt/plugin/environment/allegro/AllegroCLOverrides.java rename src/main/java/com/en_circle/slt/plugin/environment/{clisp => ccl}/CCLEnvironmentDefinition.java (97%) rename src/main/java/com/en_circle/slt/plugin/environment/{clisp => ccl}/CCLOverrides.java (83%) create mode 100644 src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationAllegroCLProcess.java create mode 100644 src/main/java/com/en_circle/slt/tools/AllegroCLUtils.java create mode 100644 src/main/lisp/slt/slt-allegro.lisp create mode 100644 src/main/lisp/swank/swank-allegro.lisp create mode 100644 src/main/resources/templates/en_US/initscript.allegro.cl diff --git a/FEATURES.md b/FEATURES.md index 0729ace..5cdb97f 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -11,20 +11,20 @@ Unsupported and will not be supported implementations: * CLISP - does not work, maybe with threads, but single threaded it's useless and crashes on debug attempt -| Features / Lisp | SBCL | ABCL | CCL | -|-------------------|------|------|------| -| REPL | ✅️ | ✅️ | ✅️ | -| Buffer Evaluation | ✅️ | ✅️ | ✅️ | -| Documentation | ✅ | ✅ | ✅️ | -| Macroexpand | ✅ | ✅ | ✅️ | -| Debugger | ✅ | ✅ | ✅️ | -| Debugger Actions | ✅ | ✅ | ✅️³️ | -| Frame REPL | ✅ | ❎ | ✅️ | -| Stepping Debugger | ❎ | ❎ | ❓ | -| References | ✅ | ❎ | ✅️ | -| Inspector | ✅¹ | ✅² | ✅️ | -| Autocomplete | ✅ | ✅ | ✅ | -| Find References | ✅ | ❎ | ✅️ | +| Features / Lisp | SBCL | ABCL | CCL | Allegro CL | Lispworks | CMUCL | +|-------------------|------|------|------|------------|-----------|-------| +| REPL | ✅️ | ✅️ | ✅️ | ✅️ | | | +| Buffer Evaluation | ✅️ | ✅️ | ✅️ | ✅️ | | | +| Documentation | ✅ | ✅ | ✅️ | ✅️ | | | +| Macroexpand | ✅ | ✅ | ✅️ | ✅️ | | | +| Debugger | ✅ | ✅ | ✅️ | | | | +| Debugger Actions | ✅ | ✅ | ✅️³️ | | | | +| Frame REPL | ✅ | ❎ | ✅️ | | | | +| Stepping Debugger | ❎ | ❎ | ❎ | ✅ | ✅ | ✅ | +| References | ✅ | ❎ | ✅️ | | | | +| Inspector | ✅¹ | ✅² | ✅️¹ | | | | +| Autocomplete | ✅ | ✅ | ✅ | ✅ | | | +| Find References | ✅ | ❎ | ✅️ | | | | ¹Only read-only inspector available diff --git a/src/main/gen/com/en_circle/slt/plugin/lisp/LispParser.java b/src/main/gen/com/en_circle/slt/plugin/lisp/LispParser.java index bba0218..35625ac 100644 --- a/src/main/gen/com/en_circle/slt/plugin/lisp/LispParser.java +++ b/src/main/gen/com/en_circle/slt/plugin/lisp/LispParser.java @@ -96,7 +96,7 @@ private static boolean compound_symbol_0(PsiBuilder b, int l) { /* ********************************************************** */ // tested | evaled | pathname | UNDEFINED_SEQUENCE | BIT_ARRAY | CHARACTER | REFERENCE_LABEL // | number | real_pair - // | compound_symbol + // | compound_symbol | DOT // | string | vector | array | structure | pair | list public static boolean datum(PsiBuilder b, int l) { if (!recursion_guard_(b, l, "datum")) return false; @@ -112,11 +112,12 @@ public static boolean datum(PsiBuilder b, int l) { if (!r) r = number(b, l + 1); if (!r) r = real_pair(b, l + 1); if (!r) r = compound_symbol(b, l + 1); + if (!r) r = consumeToken(b, DOT); if (!r) r = string(b, l + 1); if (!r) r = vector(b, l + 1); if (!r) r = array(b, l + 1); if (!r) r = structure(b, l + 1); - if (!r) r = pair(b, l + 1); + if (!r) r = consumeToken(b, PAIR); if (!r) r = list(b, l + 1); exit_section_(b, l, m, r, false, null); return r; @@ -214,7 +215,7 @@ private static boolean list_1(PsiBuilder b, int l) { } /* ********************************************************** */ - // !(RPAREN | sexpr) + // !(sexpr | RPAREN) static boolean list_recovery(PsiBuilder b, int l) { if (!recursion_guard_(b, l, "list_recovery")) return false; boolean r; @@ -224,12 +225,12 @@ static boolean list_recovery(PsiBuilder b, int l) { return r; } - // RPAREN | sexpr + // sexpr | RPAREN private static boolean list_recovery_0(PsiBuilder b, int l) { if (!recursion_guard_(b, l, "list_recovery_0")) return false; boolean r; - r = consumeToken(b, RPAREN); - if (!r) r = sexpr(b, l + 1); + r = sexpr(b, l + 1); + if (!r) r = consumeToken(b, RPAREN); return r; } @@ -262,58 +263,6 @@ public static boolean octal_number(PsiBuilder b, int l) { return r; } - /* ********************************************************** */ - // LPAREN sexpr+ DOT sexpr RPAREN - public static boolean pair(PsiBuilder b, int l) { - if (!recursion_guard_(b, l, "pair")) return false; - if (!nextTokenIs(b, LPAREN)) return false; - boolean r; - Marker m = enter_section_(b); - r = consumeToken(b, LPAREN); - r = r && pair_1(b, l + 1); - r = r && consumeToken(b, DOT); - r = r && sexpr(b, l + 1); - r = r && consumeToken(b, RPAREN); - exit_section_(b, m, PAIR, r); - return r; - } - - // sexpr+ - private static boolean pair_1(PsiBuilder b, int l) { - if (!recursion_guard_(b, l, "pair_1")) return false; - boolean r; - Marker m = enter_section_(b); - r = sexpr(b, l + 1); - while (r) { - int c = current_position_(b); - if (!sexpr(b, l + 1)) break; - if (!empty_element_parsed_guard_(b, "pair_1", c)) break; - } - exit_section_(b, m, null, r); - return r; - } - - /* ********************************************************** */ - // !(DOT | RPAREN | sexpr) - static boolean pair_recovery(PsiBuilder b, int l) { - if (!recursion_guard_(b, l, "pair_recovery")) return false; - boolean r; - Marker m = enter_section_(b, l, _NOT_); - r = !pair_recovery_0(b, l + 1); - exit_section_(b, l, m, r, false, null); - return r; - } - - // DOT | RPAREN | sexpr - private static boolean pair_recovery_0(PsiBuilder b, int l) { - if (!recursion_guard_(b, l, "pair_recovery_0")) return false; - boolean r; - r = consumeToken(b, DOT); - if (!r) r = consumeToken(b, RPAREN); - if (!r) r = sexpr(b, l + 1); - return r; - } - /* ********************************************************** */ // PATHNAME_INDICATOR sexpr public static boolean pathname(PsiBuilder b, int l) { diff --git a/src/main/gen/com/en_circle/slt/plugin/lisp/impl/LispDatumImpl.java b/src/main/gen/com/en_circle/slt/plugin/lisp/impl/LispDatumImpl.java index c773511..b3670f8 100644 --- a/src/main/gen/com/en_circle/slt/plugin/lisp/impl/LispDatumImpl.java +++ b/src/main/gen/com/en_circle/slt/plugin/lisp/impl/LispDatumImpl.java @@ -54,12 +54,6 @@ public LispNumber getNumber() { return findChildByClass(LispNumber.class); } - @Override - @Nullable - public LispPair getPair() { - return findChildByClass(LispPair.class); - } - @Override @Nullable public LispPathname getPathname() { diff --git a/src/main/gen/com/en_circle/slt/plugin/lisp/impl/LispPairImpl.java b/src/main/gen/com/en_circle/slt/plugin/lisp/impl/LispPairImpl.java deleted file mode 100644 index 03988b8..0000000 --- a/src/main/gen/com/en_circle/slt/plugin/lisp/impl/LispPairImpl.java +++ /dev/null @@ -1,37 +0,0 @@ -// This is a generated file. Not intended for manual editing. -package com.en_circle.slt.plugin.lisp.impl; - -import java.util.List; -import org.jetbrains.annotations.*; -import com.intellij.lang.ASTNode; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiElementVisitor; -import com.intellij.psi.util.PsiTreeUtil; -import static com.en_circle.slt.plugin.lisp.psi.LispTypes.*; -import com.intellij.extapi.psi.ASTWrapperPsiElement; -import com.en_circle.slt.plugin.lisp.psi.*; -import com.en_circle.slt.plugin.lisp.psi.impl.LispPsiImplUtil; - -public class LispPairImpl extends ASTWrapperPsiElement implements LispPair { - - public LispPairImpl(@NotNull ASTNode node) { - super(node); - } - - public void accept(@NotNull LispVisitor visitor) { - visitor.visitPair(this); - } - - @Override - public void accept(@NotNull PsiElementVisitor visitor) { - if (visitor instanceof LispVisitor) accept((LispVisitor)visitor); - else super.accept(visitor); - } - - @Override - @NotNull - public List getSexprList() { - return PsiTreeUtil.getChildrenOfTypeAsList(this, LispSexpr.class); - } - -} diff --git a/src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispDatum.java b/src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispDatum.java index 3f4b610..8a71e28 100644 --- a/src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispDatum.java +++ b/src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispDatum.java @@ -21,9 +21,6 @@ public interface LispDatum extends PsiElement { @Nullable LispNumber getNumber(); - @Nullable - LispPair getPair(); - @Nullable LispPathname getPathname(); diff --git a/src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispPair.java b/src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispPair.java deleted file mode 100644 index 332b83a..0000000 --- a/src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispPair.java +++ /dev/null @@ -1,13 +0,0 @@ -// This is a generated file. Not intended for manual editing. -package com.en_circle.slt.plugin.lisp.psi; - -import java.util.List; -import org.jetbrains.annotations.*; -import com.intellij.psi.PsiElement; - -public interface LispPair extends PsiElement { - - @NotNull - List getSexprList(); - -} diff --git a/src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispTypes.java b/src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispTypes.java index a0b832d..a70f881 100644 --- a/src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispTypes.java +++ b/src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispTypes.java @@ -20,7 +20,6 @@ public interface LispTypes { IElementType LIST = new LispElementType("LIST"); IElementType NUMBER = new LispElementType("NUMBER"); IElementType OCTAL_NUMBER = new LispElementType("OCTAL_NUMBER"); - IElementType PAIR = new LispElementType("PAIR"); IElementType PATHNAME = new LispElementType("PATHNAME"); IElementType RADIX_NUMBER = new LispElementType("RADIX_NUMBER"); IElementType RATIO = new LispElementType("RATIO"); @@ -49,6 +48,7 @@ public interface LispTypes { IElementType LINE_COMMENT = new LispTokenType("LINE_COMMENT"); IElementType LPAREN = new LispTokenType("LPAREN"); IElementType OCTAL_NUMBER_TOKEN = new LispTokenType("OCTAL_NUMBER_TOKEN"); + IElementType PAIR = new LispTokenType("pair"); IElementType PATHNAME_INDICATOR = new LispTokenType("PATHNAME_INDICATOR"); IElementType QUOTE = new LispTokenType("QUOTE"); IElementType RADIX_NUMBER_TOKEN = new LispTokenType("RADIX_NUMBER_TOKEN"); @@ -107,9 +107,6 @@ else if (type == NUMBER) { else if (type == OCTAL_NUMBER) { return new LispOctalNumberImpl(node); } - else if (type == PAIR) { - return new LispPairImpl(node); - } else if (type == PATHNAME) { return new LispPathnameImpl(node); } diff --git a/src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispVisitor.java b/src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispVisitor.java index 6b9dd62..aa6b4ce 100644 --- a/src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispVisitor.java +++ b/src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispVisitor.java @@ -55,10 +55,6 @@ public void visitOctalNumber(@NotNull LispOctalNumber o) { visitPsiElement(o); } - public void visitPair(@NotNull LispPair o) { - visitPsiElement(o); - } - public void visitPathname(@NotNull LispPathname o) { visitPsiElement(o); } diff --git a/src/main/java/com/en_circle/slt/plugin/environment/Environment.java b/src/main/java/com/en_circle/slt/plugin/environment/Environment.java index ecfbef0..754bcf9 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/Environment.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/Environment.java @@ -1,7 +1,8 @@ package com.en_circle.slt.plugin.environment; import com.en_circle.slt.plugin.environment.abcl.ABCLEnvironmentDefinition; -import com.en_circle.slt.plugin.environment.clisp.CCLEnvironmentDefinition; +import com.en_circle.slt.plugin.environment.allegro.AllegroCLEnvironmentDefinition; +import com.en_circle.slt.plugin.environment.ccl.CCLEnvironmentDefinition; import com.en_circle.slt.plugin.environment.sbcl.SBCLEnvironmentDefinition; public enum Environment { @@ -9,6 +10,7 @@ public enum Environment { ABCL_PROCESS(new ABCLEnvironmentDefinition()), SBCL_PROCESS(new SBCLEnvironmentDefinition()), CCL_PROCESS(new CCLEnvironmentDefinition()), + ALLEGRO_PROCESS(new AllegroCLEnvironmentDefinition()), ; diff --git a/src/main/java/com/en_circle/slt/plugin/environment/LispInterpret.java b/src/main/java/com/en_circle/slt/plugin/environment/LispInterpret.java index cebae97..2432914 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/LispInterpret.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/LispInterpret.java @@ -5,6 +5,7 @@ public enum LispInterpret { ABCL(":abcl"), SBCL(":sbcl"), CCL(":ccl"), + ALLEGRO(":allegro") ; diff --git a/src/main/java/com/en_circle/slt/plugin/environment/SltAllegroCLEnvironment.java b/src/main/java/com/en_circle/slt/plugin/environment/SltAllegroCLEnvironment.java new file mode 100644 index 0000000..8a4df94 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/environment/SltAllegroCLEnvironment.java @@ -0,0 +1,170 @@ +package com.en_circle.slt.plugin.environment; + +import com.en_circle.slt.plugin.SltLibrary; +import com.en_circle.slt.plugin.environment.SltProcessStreamGobbler.ProcessInitializationWaiter; +import com.en_circle.slt.plugin.environment.SltProcessStreamGobbler.WaitForOccurrence; +import com.en_circle.slt.tools.PluginPath; +import com.intellij.openapi.util.io.FileUtil; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; +import org.watertemplate.Template; + +import java.io.File; +import java.io.IOException; +import java.net.ServerSocket; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +public class SltAllegroCLEnvironment extends SltLispEnvironmentProcess { + + private int port; + + @Override + public int getSwankPort() { + return port; + } + + @Override + public SltLispProcessInformation getInformation() { + return new SltAllegroCLLispProcessInformation(); + } + + @Override + public Environment getType() { + return Environment.CCL_PROCESS; + } + + @Override + protected Object prepareProcessEnvironment(SltLispEnvironmentProcessConfiguration configuration) throws SltProcessException { + SltAllegroCLEnvironmentConfiguration c = getConfiguration(configuration); + AllegroCLEnvironment e = new AllegroCLEnvironment(); + try { + e.port = getFreePort(); + if (e.port == 0) { + throw new IOException("no free port available"); + } + + File tempDir = FileUtil.createTempDirectory(PluginPath.getPluginFolder(), + "SLTinit", ""); + + e.sltCore = SltLibrary.getLibraryInitFile(); + + e.serverStartSetup = new File(tempDir, "startServer.cl"); + e.serverStartSetup.deleteOnExit(); + String sltCorePath = e.sltCore.getAbsolutePath(); + String startScriptTemplate = new AllegroCLInitScriptTemplate(c, sltCorePath, e.port).render(); + FileUtils.write(e.serverStartSetup, startScriptTemplate, StandardCharsets.UTF_8); + + tempDir.deleteOnExit(); + } catch (Exception ex) { + throw new SltProcessException(ex); + } + return e; + } + + @Override + protected File getProcessWorkDirectory(SltLispEnvironmentProcessConfiguration configuration, Object environment) throws SltProcessException { + SltAllegroCLEnvironmentConfiguration c = getConfiguration(configuration); + AllegroCLEnvironment e = getEnvironment(environment); + + return PluginPath.getPluginFolder(); + } + + @Override + protected String[] getProcessCommand(SltLispEnvironmentProcessConfiguration configuration, Object environment) throws SltProcessException { + SltAllegroCLEnvironmentConfiguration c = getConfiguration(configuration); + AllegroCLEnvironment e = getEnvironment(environment); + this.port = e.port; + + List parameters = new ArrayList<>(); + parameters.add(c.getExecutablePath()); + if (StringUtils.isNotBlank(c.getMemoryImage())) { + parameters.add("-I"); + parameters.add(c.getMemoryImage()); + } + + parameters.add("-L"); + parameters.add(e.serverStartSetup.getAbsolutePath()); + + return parameters.toArray(new String[0]); + } + + @Override + protected ProcessInitializationWaiter waitForFullInitialization(SltLispEnvironmentProcessConfiguration configuration, Object environment) throws SltProcessException { + SltAllegroCLEnvironmentConfiguration c = getConfiguration(configuration); + AllegroCLEnvironment e = getEnvironment(environment); + + WaitForOccurrence wait = new WaitForOccurrence("Swank started at port"); + outputController.addUpdateListener(wait); + + return wait; + } + + private SltAllegroCLEnvironmentConfiguration getConfiguration(SltLispEnvironmentProcessConfiguration configuration) throws SltProcessException { + if (!(configuration instanceof SltAllegroCLEnvironmentConfiguration)) + throw new SltProcessException("Configuration must be SltAllegroCLEnvironmentConfiguration"); + return (SltAllegroCLEnvironmentConfiguration) configuration; + } + + private AllegroCLEnvironment getEnvironment(Object environment) { + assert (environment instanceof AllegroCLEnvironment); + + return (AllegroCLEnvironment) environment; + } + + private int getFreePort() { + var freePort = 0; + try (ServerSocket s = new ServerSocket(0)) { + freePort = s.getLocalPort(); + } catch (Exception ignored) { + + } + + return freePort; + } + + + private class SltAllegroCLLispProcessInformation implements SltLispProcessInformation { + + @Override + public String getPid() { + return "" + process.pid(); + } + } + + private static class AllegroCLEnvironment { + + File sltCore; + File serverStartSetup; + int port; + + } + + private static class AllegroCLInitScriptTemplate extends Template { + + public AllegroCLInitScriptTemplate(SltAllegroCLEnvironmentConfiguration configuration, String sltCoreScript, int port) { + String quicklispPath = configuration.getQuicklispStartScript(); + if (quicklispPath.contains("\\")) { + quicklispPath = StringUtils.replace(quicklispPath, "\\", "\\\\"); + } + String cwd = configuration.getProjectDirectory(); + if (cwd.contains("\\")) { + cwd = StringUtils.replace(cwd, "\\", "\\\\"); + } + if (sltCoreScript.contains("\\")) { + sltCoreScript = StringUtils.replace(sltCoreScript, "\\", "\\\\"); + } + add("qlpath", quicklispPath); + add("port", "" + port); + add("cwd", cwd); + add("corefile", sltCoreScript); + add("interpret", LispInterpret.ALLEGRO.symbolName); + } + + @Override + protected String getFilePath() { + return "initscript.allegro.cl"; + } + } +} diff --git a/src/main/java/com/en_circle/slt/plugin/environment/SltAllegroCLEnvironmentConfiguration.java b/src/main/java/com/en_circle/slt/plugin/environment/SltAllegroCLEnvironmentConfiguration.java new file mode 100644 index 0000000..72dc77f --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/environment/SltAllegroCLEnvironmentConfiguration.java @@ -0,0 +1,94 @@ +package com.en_circle.slt.plugin.environment; + + +import com.en_circle.slt.plugin.environment.SltLispEnvironment.SltLispOutputChangedListener; +import com.en_circle.slt.plugin.environment.SltLispEnvironmentProcess.SltLispEnvironmentProcessConfiguration; + +public class SltAllegroCLEnvironmentConfiguration implements SltLispEnvironmentProcessConfiguration { + + private String executablePath = ""; + private String memoryImage = ""; + private String quicklispStartScript = "~/quicklisp/setup.lisp"; + private String projectDirectory = "/tmp"; + private SltLispOutputChangedListener listener = null; + + private SltAllegroCLEnvironmentConfiguration() { + + } + + public String getExecutablePath() { + return executablePath; + } + + public String getMemoryImage() { + return memoryImage; + } + + public String getQuicklispStartScript() { + return quicklispStartScript; + } + + public String getProjectDirectory() { + return projectDirectory; + } + + @Override + public SltLispOutputChangedListener getListener() { + return listener; + } + + public static class Builder implements SltLispEnvironmentConfiguration.Builder { + + private final SltAllegroCLEnvironmentConfiguration c = new SltAllegroCLEnvironmentConfiguration(); + private boolean built = false; + + public Builder setExecutable(String executable) { + checkNotBuilt(); + + c.executablePath = executable; + return this; + } + + public Builder setMemoryImage(String memoryImage) { + checkNotBuilt(); + + c.memoryImage = memoryImage; + return this; + } + + public Builder setQuicklispStartScriptPath(String quicklispStartScript) { + checkNotBuilt(); + + c.quicklispStartScript = quicklispStartScript; + return this; + } + + public Builder setProjectDirectory(String projectDirectory) { + checkNotBuilt(); + + c.projectDirectory = projectDirectory; + return this; + } + + @Override + public Builder setListener(SltLispOutputChangedListener listener) { + checkNotBuilt(); + + c.listener = listener; + return this; + } + + @Override + public SltAllegroCLEnvironmentConfiguration build() { + checkNotBuilt(); + built = true; + return c; + } + + private void checkNotBuilt() { + if (built) throw new IllegalStateException("already built"); + } + + } + +} diff --git a/src/main/java/com/en_circle/slt/plugin/environment/allegro/AllegroCLEnvironmentDefinition.java b/src/main/java/com/en_circle/slt/plugin/environment/allegro/AllegroCLEnvironmentDefinition.java new file mode 100644 index 0000000..62f395c --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/environment/allegro/AllegroCLEnvironmentDefinition.java @@ -0,0 +1,74 @@ +package com.en_circle.slt.plugin.environment.allegro; + +import com.en_circle.slt.plugin.SltBundle; +import com.en_circle.slt.plugin.environment.*; +import com.en_circle.slt.plugin.environment.SltLispEnvironmentConfiguration.Builder; +import com.en_circle.slt.plugin.sdk.LispSdk; +import com.en_circle.slt.plugin.services.lisp.LispSltOverrides; +import com.en_circle.slt.plugin.ui.sdk.SdkConfigurationAllegroCLProcess; +import com.en_circle.slt.plugin.ui.sdk.SdkDialogProvider; +import com.en_circle.slt.tools.platform.DownloadLispAction; +import com.intellij.openapi.project.Project; + +import java.util.HashSet; +import java.util.Set; +import java.util.function.Supplier; + +public class AllegroCLEnvironmentDefinition extends EnvironmentDefinition { + + private final Set features = new HashSet<>(); + + public AllegroCLEnvironmentDefinition() { + features.add(LispFeatures.REPL); + features.add(LispFeatures.DOCUMENTATION); + features.add(LispFeatures.MACROEXPAND); + features.add(LispFeatures.FRAME_EVAL); + features.add(LispFeatures.INSPECTOR); + features.add(LispFeatures.INSPECTOR_HISTORY); + features.add(LispFeatures.AUTOCOMPLETE); + features.add(LispFeatures.SEARCH); + features.add(LispFeatures.XREFS); + } + + @Override + public String getName() { + return SltBundle.message("slt.environment.allegro"); + } + + @Override + public Class getDownloadActionDef() { + return null; + } + + @Override + public Supplier getEnvironmentCreator() { + return SltAllegroCLEnvironment::new; + } + + @SuppressWarnings("unchecked") + @Override + public , R extends SltLispEnvironmentConfiguration> + Builder buildConfiguration(LispSdk sdk, Project project) { + return (Builder) new SltAllegroCLEnvironmentConfiguration.Builder() + .setExecutable(sdk.allegroExecutable) + .setMemoryImage(sdk.allegroMemoryImage) + .setQuicklispStartScriptPath(sdk.quickLispPath) + .setProjectDirectory(project.getBasePath()); + } + + @Override + public SdkDialogProvider getDialogProvider() { + return SdkConfigurationAllegroCLProcess::new; + } + + @Override + public LispSltOverrides getOverrides() { + return new AllegroCLOverrides(); + } + + @Override + public boolean hasFeature(LispFeatures feature) { + return features.contains(feature); + } + +} diff --git a/src/main/java/com/en_circle/slt/plugin/environment/allegro/AllegroCLOverrides.java b/src/main/java/com/en_circle/slt/plugin/environment/allegro/AllegroCLOverrides.java new file mode 100644 index 0000000..7d6c496 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/environment/allegro/AllegroCLOverrides.java @@ -0,0 +1,10 @@ +package com.en_circle.slt.plugin.environment.allegro; + +import com.en_circle.slt.plugin.environment.LispSltOverridesBase; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class AllegroCLOverrides extends LispSltOverridesBase { + private static final Logger log = LoggerFactory.getLogger(AllegroCLOverrides.class); + +} diff --git a/src/main/java/com/en_circle/slt/plugin/environment/clisp/CCLEnvironmentDefinition.java b/src/main/java/com/en_circle/slt/plugin/environment/ccl/CCLEnvironmentDefinition.java similarity index 97% rename from src/main/java/com/en_circle/slt/plugin/environment/clisp/CCLEnvironmentDefinition.java rename to src/main/java/com/en_circle/slt/plugin/environment/ccl/CCLEnvironmentDefinition.java index 9f44208..76a82a7 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/clisp/CCLEnvironmentDefinition.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/ccl/CCLEnvironmentDefinition.java @@ -1,4 +1,4 @@ -package com.en_circle.slt.plugin.environment.clisp; +package com.en_circle.slt.plugin.environment.ccl; import com.en_circle.slt.plugin.SltBundle; import com.en_circle.slt.plugin.environment.*; diff --git a/src/main/java/com/en_circle/slt/plugin/environment/clisp/CCLOverrides.java b/src/main/java/com/en_circle/slt/plugin/environment/ccl/CCLOverrides.java similarity index 83% rename from src/main/java/com/en_circle/slt/plugin/environment/clisp/CCLOverrides.java rename to src/main/java/com/en_circle/slt/plugin/environment/ccl/CCLOverrides.java index 5fc9235..1ec34d8 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/clisp/CCLOverrides.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/ccl/CCLOverrides.java @@ -1,4 +1,4 @@ -package com.en_circle.slt.plugin.environment.clisp; +package com.en_circle.slt.plugin.environment.ccl; import com.en_circle.slt.plugin.environment.LispSltOverridesBase; import org.slf4j.Logger; diff --git a/src/main/java/com/en_circle/slt/plugin/lisp/Lisp.bnf b/src/main/java/com/en_circle/slt/plugin/lisp/Lisp.bnf index eb86c88..536781b 100644 --- a/src/main/java/com/en_circle/slt/plugin/lisp/Lisp.bnf +++ b/src/main/java/com/en_circle/slt/plugin/lisp/Lisp.bnf @@ -28,7 +28,7 @@ enhancement ::= REFERENCE_SET | TEST_SUCCESS | UNQUOTE | UNQUOTE_SPLICE | BACKQU datum ::= tested | evaled | pathname | UNDEFINED_SEQUENCE | BIT_ARRAY | CHARACTER | REFERENCE_LABEL | number | real_pair - | compound_symbol + | compound_symbol | DOT | string | vector | array | structure | pair | list tested ::= (TEST_SUCCESS | TEST_FALURE) sexpr @@ -44,9 +44,7 @@ array ::= ARRAY_START list structure ::= STRUCTURE_TOKEN list list ::= LPAREN sexpr* RPAREN { pin = 2 recoverWhile=list_recovery } -private list_recovery ::= !(RPAREN | sexpr) - -pair ::= LPAREN sexpr+ DOT sexpr RPAREN +private list_recovery ::= !(sexpr | RPAREN) string ::= STRING_TOKEN diff --git a/src/main/java/com/en_circle/slt/plugin/lisp/lisp/LispContainer.java b/src/main/java/com/en_circle/slt/plugin/lisp/lisp/LispContainer.java index 92af0b7..1818077 100644 --- a/src/main/java/com/en_circle/slt/plugin/lisp/lisp/LispContainer.java +++ b/src/main/java/com/en_circle/slt/plugin/lisp/lisp/LispContainer.java @@ -20,13 +20,9 @@ public List getItems() { public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getStartingSymbol()); - for (int i=0; i 0) sb.append("\n"); - for (int i=0; i self = stack.pop(); - LispContainer container = new LispContainer(self, ContainerType.PAIR); + LispContainer container = new LispContainer(self, ContainerType.LIST); stack.peek().add(container); addOffset(o, container); } @@ -209,16 +209,6 @@ public void visitVector(@NotNull LispVector o) { addOffset(o, container); } - @Override - public void visitPair(@NotNull LispPair o) { - stack.push(new ArrayList<>()); - super.visitPair(o); - List self = stack.pop(); - LispContainer container = new LispContainer(self, ContainerType.PAIR); - stack.peek().add(container); - addOffset(o, container); - } - private void addOffset(PsiElement element, LispElement elementDef) { if (lineOffsets != null) { OffsetInfo offsetInfo = new OffsetInfo(); diff --git a/src/main/java/com/en_circle/slt/plugin/sdk/LispSdk.java b/src/main/java/com/en_circle/slt/plugin/sdk/LispSdk.java index 789071e..a313dd7 100644 --- a/src/main/java/com/en_circle/slt/plugin/sdk/LispSdk.java +++ b/src/main/java/com/en_circle/slt/plugin/sdk/LispSdk.java @@ -6,6 +6,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Objects; + public class LispSdk implements PersistentStateComponent { public String uuid; @@ -28,6 +30,10 @@ public class LispSdk implements PersistentStateComponent { public String cclExecutable; public String cclMemoryImage; + // AllegroCL Process used + public String allegroExecutable; + public String allegroMemoryImage; + public Environment getEnvironment() { if (environment == null) { // backwards compat @@ -54,4 +60,46 @@ public void loadState(@NotNull LispSdk state) { } } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + LispSdk sdk = (LispSdk) o; + + if (!Objects.equals(uuid, sdk.uuid)) return false; + if (!Objects.equals(userName, sdk.userName)) return false; + if (environment != sdk.environment) return false; + if (!Objects.equals(quickLispPath, sdk.quickLispPath)) return false; + if (!Objects.equals(sbclExecutable, sdk.sbclExecutable)) + return false; + if (!Objects.equals(sbclCorePath, sdk.sbclCorePath)) return false; + if (!Objects.equals(abclJvm, sdk.abclJvm)) return false; + if (!Objects.equals(abclJvmArgs, sdk.abclJvmArgs)) return false; + if (!Objects.equals(abclJar, sdk.abclJar)) return false; + if (!Objects.equals(cclExecutable, sdk.cclExecutable)) return false; + if (!Objects.equals(cclMemoryImage, sdk.cclMemoryImage)) + return false; + if (!Objects.equals(allegroExecutable, sdk.allegroExecutable)) + return false; + return Objects.equals(allegroMemoryImage, sdk.allegroMemoryImage); + } + + @Override + public int hashCode() { + int result = uuid != null ? uuid.hashCode() : 0; + result = 31 * result + (userName != null ? userName.hashCode() : 0); + result = 31 * result + (environment != null ? environment.hashCode() : 0); + result = 31 * result + (quickLispPath != null ? quickLispPath.hashCode() : 0); + result = 31 * result + (sbclExecutable != null ? sbclExecutable.hashCode() : 0); + result = 31 * result + (sbclCorePath != null ? sbclCorePath.hashCode() : 0); + result = 31 * result + (abclJvm != null ? abclJvm.hashCode() : 0); + result = 31 * result + (abclJvmArgs != null ? abclJvmArgs.hashCode() : 0); + result = 31 * result + (abclJar != null ? abclJar.hashCode() : 0); + result = 31 * result + (cclExecutable != null ? cclExecutable.hashCode() : 0); + result = 31 * result + (cclMemoryImage != null ? cclMemoryImage.hashCode() : 0); + result = 31 * result + (allegroExecutable != null ? allegroExecutable.hashCode() : 0); + result = 31 * result + (allegroMemoryImage != null ? allegroMemoryImage.hashCode() : 0); + return result; + } } diff --git a/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationABCLProcess.java b/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationABCLProcess.java index 451f815..1014b1d 100644 --- a/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationABCLProcess.java +++ b/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationABCLProcess.java @@ -162,7 +162,7 @@ private void verifySdk() { quicklispPath.getField().repaint(); if (verified) - verified = checkAndLoadAbclCore(jvm, jvmArgs, jar, quicklisp); + verified = checkAndLoadAbcl(jvm, jvmArgs, jar, quicklisp); if (!verified) { Messages.showErrorDialog(SltBundle.message("slt.ui.settings.sdk.editor.abcl.process.verifying.error"), SltBundle.message("slt.ui.settings.sdk.editor.verifying.error.title")); @@ -171,7 +171,7 @@ private void verifySdk() { isVerified = verified; } - private boolean checkAndLoadAbclCore(String jvm, String jvmArgs, String jar, String quicklisp) { + private boolean checkAndLoadAbcl(String jvm, String jvmArgs, String jar, String quicklisp) { ProgressWindow verifyWindow = new ProgressWindow(true, false, null, getRootPane(), SltBundle.message("slt.ui.settings.sdk.editor.verifying.cancel")); verifyWindow.setTitle(SltBundle.message("slt.ui.settings.sdk.editor.verifying.abcl")); diff --git a/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationAllegroCLProcess.java b/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationAllegroCLProcess.java new file mode 100644 index 0000000..448a243 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationAllegroCLProcess.java @@ -0,0 +1,229 @@ +package com.en_circle.slt.plugin.ui.sdk; + +import com.en_circle.slt.plugin.SltBundle; +import com.en_circle.slt.plugin.sdk.LispSdk; +import com.en_circle.slt.plugin.ui.SltGlobalUIService; +import com.en_circle.slt.plugin.ui.sdk.SdkDialogProvider.OnSave; +import com.en_circle.slt.tools.AllegroCLUtils; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.fileChooser.FileChooserDescriptor; +import com.intellij.openapi.fileChooser.FileChooserFactory; +import com.intellij.openapi.fileChooser.FileTextField; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.impl.ProgressResult; +import com.intellij.openapi.progress.impl.ProgressRunner; +import com.intellij.openapi.progress.impl.ProgressRunner.ThreadToUse; +import com.intellij.openapi.progress.util.ProgressWindow; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.ui.TextFieldWithBrowseButton; +import com.intellij.openapi.util.Disposer; +import com.intellij.ui.components.JBTextField; +import com.intellij.util.ui.FormBuilder; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.event.CaretListener; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.io.File; + +public class SdkConfigurationAllegroCLProcess extends DialogWrapper { + + private final Disposable parentDisposable; + private final LispSdk instance; + private final OnSave onSave; + private boolean isVerified = false; + + private JBTextField name; + private FileTextField allegroExecutable; + private FileTextField allegroImage; + private FileTextField quicklispPath; + + public SdkConfigurationAllegroCLProcess(@NotNull Component parent, LispSdk instance, String title, OnSave onSave) { + super(parent, true); + + this.parentDisposable = SltGlobalUIService.getInstance(); + this.instance = instance; + this.onSave = onSave; + + setTitle(title); + setSize(700, 0); + init(); + } + + @Override + protected @Nullable JComponent createCenterPanel() { + name = new JBTextField(); + name.addCaretListener(createChangeListener()); + name.setText(instance.userName); + if (StringUtils.isBlank(instance.userName)) { + name.setText(SltBundle.message("slt.ui.settings.sdk.editor.name.allegro.default")); + } + + FileChooserDescriptor descriptor = new FileChooserDescriptor(true, false, false, + false, false, false); + + allegroExecutable = FileChooserFactory.getInstance() + .createFileTextField(descriptor, true, parentDisposable); + allegroExecutable.getField().addCaretListener(createChangeListener()); + allegroExecutable.getField().setText(instance.sbclExecutable); + allegroImage = FileChooserFactory.getInstance() + .createFileTextField(descriptor, true, parentDisposable); + allegroImage.getField().addCaretListener(createChangeListener()); + allegroImage.getField().setText(instance.sbclCorePath); + quicklispPath = FileChooserFactory.getInstance() + .createFileTextField(descriptor, true, parentDisposable); + quicklispPath.getField().addCaretListener(createChangeListener()); + quicklispPath.getField().setText(instance.quickLispPath); + + TextFieldWithBrowseButton cclExecutablePicker = new TextFieldWithBrowseButton(allegroExecutable.getField()); + cclExecutablePicker.addBrowseFolderListener( + SltBundle.message("slt.ui.settings.sdk.editor.allegro.executable.select"), "", null, descriptor); + TextFieldWithBrowseButton cclImagePicker = new TextFieldWithBrowseButton(allegroImage.getField()); + cclImagePicker.addBrowseFolderListener( + SltBundle.message("slt.ui.settings.sdk.editor.allegro.image.select"), "", null, descriptor); + TextFieldWithBrowseButton quicklispPathPicker = new TextFieldWithBrowseButton(quicklispPath.getField()); + //noinspection DialogTitleCapitalization + quicklispPathPicker.addBrowseFolderListener( + SltBundle.message("slt.ui.settings.sdk.editor.quicklisp.select"), "", null, descriptor); + + return new FormBuilder() + .addLabeledComponent(SltBundle.message("slt.ui.settings.sdk.editor.name"), name, 1, false) + .addLabeledComponent(SltBundle.message("slt.ui.settings.sdk.editor.allegro.process.executable"), + cclExecutablePicker, 1, false) + .addLabeledComponent(SltBundle.message("slt.ui.settings.sdk.editor.allegro.process.image"), + cclImagePicker, 1, false) + .addLabeledComponent(SltBundle.message("slt.ui.settings.sdk.editor.quicklisp"), + quicklispPathPicker, 1, false) + .addComponentFillVertically(new JPanel(), 0) + .getPanel(); + } + + private CaretListener createChangeListener() { + return e -> isVerified = false; + } + + private void verifySdk() { + name.putClientProperty("JComponent.outline", null); + allegroExecutable.getField().putClientProperty("JComponent.outline", null); + allegroImage.getField().putClientProperty("JComponent.outline", null); + quicklispPath.getField().putClientProperty("JComponent.outline", null); + + boolean verified = true; + + if (StringUtils.isBlank(name.getText())) { + verified = false; + name.putClientProperty("JComponent.outline", "error"); + } + + String executable = allegroExecutable.getField().getText(); + if (StringUtils.isBlank(executable)) { + verified = false; + allegroExecutable.getField().putClientProperty("JComponent.outline", "error"); + } + + String core = allegroImage.getField().getText(); + if (StringUtils.isNotBlank(core)) { + File file = new File(core); + if (!file.exists()) { + verified = false; + allegroImage.getField().putClientProperty("JComponent.outline", "error"); + } + } + + String quicklisp = quicklispPath.getField().getText(); + if (StringUtils.isBlank(quicklisp)) { + verified = false; + quicklispPath.getField().putClientProperty("JComponent.outline", "error"); + } else { + File qlFile = new File(quicklisp); + if (!qlFile.exists() || !qlFile.isFile()) { + verified = false; + quicklispPath.getField().putClientProperty("JComponent.outline", "error"); + } + } + + name.repaint(); + allegroExecutable.getField().repaint(); + allegroImage.getField().repaint(); + quicklispPath.getField().repaint(); + + if (verified) + verified = checkAndLoadAllegro(executable, core, quicklisp); + if (!verified) { + Messages.showErrorDialog(SltBundle.message("slt.ui.settings.sdk.editor.allegro.process.verifying.error"), + SltBundle.message("slt.ui.settings.sdk.editor.verifying.error.title")); + } + + isVerified = verified; + } + + private boolean checkAndLoadAllegro(String executable, String memory, String quicklisp) { + ProgressWindow verifyWindow = new ProgressWindow(true, false, null, + getRootPane(), SltBundle.message("slt.ui.settings.sdk.editor.verifying.cancel")); + verifyWindow.setTitle(SltBundle.message("slt.ui.settings.sdk.editor.verifying.ccl")); + Disposer.register(parentDisposable, verifyWindow); + + ProgressResult result = new ProgressRunner<>(pi -> verifyAllegro(pi, executable, memory, quicklisp)) + .sync() + .onThread(ThreadToUse.POOLED) + .withProgress(verifyWindow) + .modal() + .submitAndGet(); + return Boolean.TRUE.equals(result.getResult()); + } + + private boolean verifyAllegro(ProgressIndicator pi, String executable, String core, String quicklisp) { + return AllegroCLUtils.verifyAndInstallDependencies(executable, core, quicklisp, pi); + } + + private void save() { + instance.userName = name.getText(); + instance.allegroExecutable = allegroExecutable.getField().getText(); + instance.allegroMemoryImage = allegroImage.getField().getText(); + instance.quickLispPath = quicklispPath.getField().getText(); + onSave.saveAction(instance); + close(0); + } + + @Override + protected Action @NotNull [] createActions() { + return new Action[] { + new VerifyAction(), new SaveAction() + }; + } + + public class VerifyAction extends AbstractAction { + + public VerifyAction() { + super(SltBundle.message("slt.ui.settings.sdk.editor.allegro.process.verify")); + } + + @Override + public void actionPerformed(ActionEvent e) { + verifySdk(); + } + } + + public class SaveAction extends AbstractAction { + + public SaveAction() { + super(SltBundle.message("slt.ui.settings.sdk.editor.save")); + } + + @Override + public void actionPerformed(ActionEvent e) { + if (!isVerified) { + Messages.showInfoMessage(SltBundle.message("slt.ui.settings.sdk.editor.notverified.title"), + SltBundle.message("slt.ui.settings.sdk.editor.allegro.process.notverified.message")); + return; + } + + save(); + } + } + +} diff --git a/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationCCLProcess.java b/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationCCLProcess.java index 5c55659..46dd092 100644 --- a/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationCCLProcess.java +++ b/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationCCLProcess.java @@ -152,7 +152,7 @@ private void verifySdk() { quicklispPath.getField().repaint(); if (verified) - verified = checkAndLoadClispCore(executable, core, quicklisp); + verified = checkAndLoadCCL(executable, core, quicklisp); if (!verified) { Messages.showErrorDialog(SltBundle.message("slt.ui.settings.sdk.editor.ccl.process.verifying.error"), SltBundle.message("slt.ui.settings.sdk.editor.verifying.error.title")); @@ -161,13 +161,13 @@ private void verifySdk() { isVerified = verified; } - private boolean checkAndLoadClispCore(String executable, String memory, String quicklisp) { + private boolean checkAndLoadCCL(String executable, String memory, String quicklisp) { ProgressWindow verifyWindow = new ProgressWindow(true, false, null, getRootPane(), SltBundle.message("slt.ui.settings.sdk.editor.verifying.cancel")); verifyWindow.setTitle(SltBundle.message("slt.ui.settings.sdk.editor.verifying.ccl")); Disposer.register(parentDisposable, verifyWindow); - ProgressResult result = new ProgressRunner<>(pi -> verifyClisp(pi, executable, memory, quicklisp)) + ProgressResult result = new ProgressRunner<>(pi -> verifyCCL(pi, executable, memory, quicklisp)) .sync() .onThread(ThreadToUse.POOLED) .withProgress(verifyWindow) @@ -176,7 +176,7 @@ private boolean checkAndLoadClispCore(String executable, String memory, String q return Boolean.TRUE.equals(result.getResult()); } - private boolean verifyClisp(ProgressIndicator pi, String executable, String core, String quicklisp) { + private boolean verifyCCL(ProgressIndicator pi, String executable, String core, String quicklisp) { return CCLUtils.verifyAndInstallDependencies(executable, core, quicklisp, pi); } diff --git a/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationSBCLProcess.java b/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationSBCLProcess.java index 7ec1ac5..42cd968 100644 --- a/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationSBCLProcess.java +++ b/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationSBCLProcess.java @@ -152,7 +152,7 @@ private void verifySdk() { quicklispPath.getField().repaint(); if (verified) - verified = checkAndLoadSbclCore(executable, core, quicklisp); + verified = checkAndLoadSbcl(executable, core, quicklisp); if (!verified) { Messages.showErrorDialog(SltBundle.message("slt.ui.settings.sdk.editor.sbcl.process.verifying.error"), SltBundle.message("slt.ui.settings.sdk.editor.verifying.error.title")); @@ -161,7 +161,7 @@ private void verifySdk() { isVerified = verified; } - private boolean checkAndLoadSbclCore(String executable, String core, String quicklisp) { + private boolean checkAndLoadSbcl(String executable, String core, String quicklisp) { ProgressWindow verifyWindow = new ProgressWindow(true, false, null, getRootPane(), SltBundle.message("slt.ui.settings.sdk.editor.verifying.cancel")); verifyWindow.setTitle(SltBundle.message("slt.ui.settings.sdk.editor.verifying.sbcl")); diff --git a/src/main/java/com/en_circle/slt/tools/AllegroCLUtils.java b/src/main/java/com/en_circle/slt/tools/AllegroCLUtils.java new file mode 100644 index 0000000..e149836 --- /dev/null +++ b/src/main/java/com/en_circle/slt/tools/AllegroCLUtils.java @@ -0,0 +1,94 @@ +package com.en_circle.slt.tools; + +import com.en_circle.slt.plugin.environment.SltProcessStreamGobbler; +import com.en_circle.slt.plugin.environment.SltProcessStreamGobbler.WaitForOccurrence; +import com.en_circle.slt.templates.VerifyTemplate; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.util.io.FileUtil; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; + +public class AllegroCLUtils { + + @SuppressWarnings("ResultOfMethodCallIgnored") + public static boolean verifyAndInstallDependencies(String executable, String memoryImage, String quicklisp, ProgressIndicator pi) { + try { + List args = new ArrayList<>(); + args.add(executable); + if (StringUtils.isNotBlank(memoryImage)) { + args.add("-I"); + args.add(memoryImage); + } + args.add("--batch"); + + File tempTestFile = FileUtil.createTempFile("testAllegro", ".cl"); + if (tempTestFile.exists()) + tempTestFile.delete(); + FileUtils.writeStringToFile(tempTestFile, new VerifyTemplate(quicklisp).render(), StandardCharsets.UTF_8); + tempTestFile.deleteOnExit(); + + args.add("-L"); + args.add(tempTestFile.getAbsolutePath()); + args.add("--kill"); + + try { + ProcessBuilder processBuilder = new ProcessBuilder(args.toArray(new String[0])); + Process process = processBuilder.start(); + + StringBuilder errorValue = new StringBuilder(); + StringBuilder displayValue = new StringBuilder(); + StringBuilder outputValue = new StringBuilder(); + SltProcessStreamGobbler errorController = new SltProcessStreamGobbler(process.getErrorStream()); + SltProcessStreamGobbler outputController = new SltProcessStreamGobbler(process.getInputStream()); + errorController.addUpdateListener(errorValue::append); + outputController.addUpdateListener(outputValue::append); + WaitForOccurrence waiter = new WaitForOccurrence("SltVerified"); + outputController.addUpdateListener(waiter); + outputController.addUpdateListener(data -> { + displayValue.append(data); + String str = displayValue.toString(); + if (str.contains("\n")) { + String[] lines = str.split(Pattern.quote("\n")); + for (String line : lines) { + if (StringUtils.isNotBlank(line)) + pi.setText(line); + } + if (StringUtils.isNotBlank(lines[lines.length-1])) { + displayValue.setLength(0); + displayValue.append(lines[lines.length-1]); + } + } + }); + errorController.start(); + outputController.start(); + if (!waiter.awaitFor(null, errorController, 10, TimeUnit.MINUTES, pi::isCanceled)) { + if (process.isAlive()) + process.destroy(); + + errorController.join(); + outputController.join(); + return false; + } + + if (process.isAlive()) + process.destroy(); + + errorController.join(); + outputController.join(); + return outputValue.toString().contains("SltVerified"); + } finally { + tempTestFile.delete(); + } + } catch (Exception ignored) { + return false; + } + } + +} diff --git a/src/main/java/com/en_circle/slt/tools/CCLUtils.java b/src/main/java/com/en_circle/slt/tools/CCLUtils.java index 2e03199..058e0e2 100644 --- a/src/main/java/com/en_circle/slt/tools/CCLUtils.java +++ b/src/main/java/com/en_circle/slt/tools/CCLUtils.java @@ -28,7 +28,7 @@ public static boolean verifyAndInstallDependencies(String executable, String mem } args.add("-b"); - File tempTestFile = FileUtil.createTempFile("testSBCL", ".cl"); + File tempTestFile = FileUtil.createTempFile("testCCL", ".cl"); if (tempTestFile.exists()) tempTestFile.delete(); FileUtils.writeStringToFile(tempTestFile, new VerifyTemplate(quicklisp).render(), StandardCharsets.UTF_8); diff --git a/src/main/lisp/load.lisp b/src/main/lisp/load.lisp index c059f3d..ba2f0dc 100644 --- a/src/main/lisp/load.lisp +++ b/src/main/lisp/load.lisp @@ -24,6 +24,13 @@ kcl scl openmcl mcl abcl ecl) (error 'not-implemented :proc (list 'quit code))) +(defun starts-with-p (start s) + (let ((start-length (length (string start)))) + (when (>= (length s) start-length) + (string-equal s start :start1 0 :end1 start-length)))) + +(format T "Current lisp interpret: ~S~%" (lisp-implementation-type)) + (case slt:+slt-interpret+ (:sbcl (unless (string= "SBCL" (lisp-implementation-type)) @@ -37,6 +44,10 @@ (lisp-implementation-type)) (format *error-output* "Invalid lisp instance. Maybe a configuration error? This is not Clozure Common Lisp!~%") (portable-quit 1))) + (:allegro (unless (starts-with-p "International Allegro CL" + (lisp-implementation-type)) + (format *error-output* "Invalid lisp instance. Maybe a configuration error? This is not Allegro Common Lisp!~%") + (portable-quit 1))) (otherwise (format *error-output* "Unsupported lisp instance. Maybe a configuration error?~%") (portable-quit 1))) diff --git a/src/main/lisp/slt/slt-allegro.lisp b/src/main/lisp/slt/slt-allegro.lisp new file mode 100644 index 0000000..de06193 --- /dev/null +++ b/src/main/lisp/slt/slt-allegro.lisp @@ -0,0 +1,4 @@ +(in-package :slt-core) + +(defun specialp (test-sym) + (eq (system:variable-information test-sym) :special)) \ No newline at end of file diff --git a/src/main/lisp/slt/slt.lisp b/src/main/lisp/slt/slt.lisp index 50dc827..154071e 100644 --- a/src/main/lisp/slt/slt.lisp +++ b/src/main/lisp/slt/slt.lisp @@ -10,6 +10,8 @@ (load (merge-pathnames "slt-abcl.lisp" *load-truename*))) (when (eq slt:+slt-interpret+ :ccl) (load (merge-pathnames "slt-ccl.lisp" *load-truename*))) +(when (eq slt:+slt-interpret+ :allegro) + (load (merge-pathnames "slt-allegro.lisp" *load-truename*))) (in-package :slt-core) diff --git a/src/main/lisp/swank/swank-allegro.lisp b/src/main/lisp/swank/swank-allegro.lisp new file mode 100644 index 0000000..8425fc7 --- /dev/null +++ b/src/main/lisp/swank/swank-allegro.lisp @@ -0,0 +1,24 @@ +(in-package :swank/backend) + +(defun get-restart-function-args (restart) + NIL) + +(in-package :swank/allegro) + +(in-package :swank) + +(defslimefun backtrace (start end) + (loop for frame in (compute-backtrace start end) + for i from start collect + (list i (frame-to-string frame) + (format NIL "~A" (print-frame-call-place frame)) + (frame-source-location i) + (let ((pkg (frame-package i))) + (cond + (pkg (package-name pkg)) + (T NIL)))))) + +(defun print-frame-call-place (frame) + (multiple-value-bind (x fun xx xxx pc) (debugger::dyn-fd-analyze frame) + (declare (ignore x xx xxx)) + (cross-reference::object-to-function-name fun))) \ No newline at end of file diff --git a/src/main/lisp/swank/swank.lisp b/src/main/lisp/swank/swank.lisp index 91c7a28..867a245 100644 --- a/src/main/lisp/swank/swank.lisp +++ b/src/main/lisp/swank/swank.lisp @@ -8,6 +8,8 @@ (load (merge-pathnames "swank-abcl.lisp" *load-truename*))) (when (eq slt:+slt-interpret+ :ccl) (load (merge-pathnames "swank-ccl.lisp" *load-truename*))) +(when (eq slt:+slt-interpret+ :allegro) + (load (merge-pathnames "swank-allegro.lisp" *load-truename*))) (in-package :swank) diff --git a/src/main/resources/messages/SltBundle.properties b/src/main/resources/messages/SltBundle.properties index 0f0a498..c5ebe73 100644 --- a/src/main/resources/messages/SltBundle.properties +++ b/src/main/resources/messages/SltBundle.properties @@ -11,6 +11,7 @@ slt.argslist.error=No contextual help found slt.environment.abcl=ABCL slt.environment.sbcl=SBCL slt.environment.ccl=Clozure Common Lisp +slt.environment.allegro=Allegro CL # Documentation elements slt.documentation.types.symbol=Symbol @@ -121,23 +122,29 @@ slt.ui.settings.sdk.editor.title.new=New Common Lisp SDK slt.ui.settings.sdk.editor.title.edit=Editing Common Lisp SDK slt.ui.settings.sdk.editor.sbcl.process.verify=Verify SBCL SDK slt.ui.settings.sdk.editor.abcl.process.verify=Verify ABCL SDK -slt.ui.settings.sdk.editor.ccl.process.verify=Verify Clisp SDK +slt.ui.settings.sdk.editor.ccl.process.verify=Verify CCL SDK +slt.ui.settings.sdk.editor.allegro.process.verify=Verify Allegro CL SDK slt.ui.settings.sdk.editor.save=Save slt.ui.settings.sdk.editor.name=SDK name slt.ui.settings.sdk.editor.name.sbcl.default=SBCL slt.ui.settings.sdk.editor.name.abcl.default=ABCL slt.ui.settings.sdk.editor.name.ccl.default=Clozure Common Lisp +slt.ui.settings.sdk.editor.name.allegro.default=Allegro CL slt.ui.settings.sdk.editor.sbcl.process.executable=SBCL executable slt.ui.settings.sdk.editor.ccl.process.executable=CCL executable +slt.ui.settings.sdk.editor.allegro.process.executable=Allegro CL executable slt.ui.settings.sdk.editor.abcl.jvm.executable=Java executable slt.ui.settings.sdk.editor.abcl.jvm.args=Java JVM arguments (optional) slt.ui.settings.sdk.editor.sbcl.executable.select=Please Select SBCL Executable slt.ui.settings.sdk.editor.ccl.executable.select=Please Select CCL Executable +slt.ui.settings.sdk.editor.allegro.executable.select=Please Select Allegro CL Executable slt.ui.settings.sdk.editor.sbcl.process.core=SBCL core (optional) slt.ui.settings.sdk.editor.ccl.process.image=CCL image (optional) +slt.ui.settings.sdk.editor.allegro.process.image=Allegro CL memory image (optional) slt.ui.settings.sdk.editor.abcl.jar=ABCL jar slt.ui.settings.sdk.editor.sbcl.core.select=Please Select SBCL Core slt.ui.settings.sdk.editor.ccl.image.select=Please Select CCL Memory Image +slt.ui.settings.sdk.editor.allegro.image.select=Please Select Allegro CL Memory Image slt.ui.settings.sdk.editor.quicklisp=Quicklisp path slt.ui.settings.sdk.editor.jvm.select=Please Select Java Executable slt.ui.settings.sdk.editor.abcl.select=Please Select ABCL Jar File @@ -148,6 +155,7 @@ slt.ui.settings.sdk.editor.notverified.title=Verification required slt.ui.settings.sdk.editor.sbcl.process.notverified.message=Please Verify SBCL SDK slt.ui.settings.sdk.editor.abcl.process.notverified.message=Please Verify ABCL SDK slt.ui.settings.sdk.editor.ccl.process.notverified.message=Please Verify CCL SDK +slt.ui.settings.sdk.editor.allegro.process.notverified.message=Please Verify Allegro CL SDK slt.ui.settings.sdk.editor.verifying.sbcl=Verifying SBCL SDK slt.ui.settings.sdk.editor.verifying.abcl=Verifying ABCL SDK slt.ui.settings.sdk.editor.verifying.ccl=Verifying CCL SDK @@ -155,6 +163,7 @@ slt.ui.settings.sdk.editor.verifying.cancel=Cancel Verification slt.ui.settings.sdk.editor.sbcl.process.verifying.error=Failed to verify SBCL installation slt.ui.settings.sdk.editor.abcl.process.verifying.error=Failed to verify ABCL installation slt.ui.settings.sdk.editor.ccl.process.verifying.error=Failed to verify CCL installation +slt.ui.settings.sdk.editor.allegro.process.verifying.error=Failed to verify Allegro CL installation slt.ui.settings.sdk.editor.verifying.error.title=Failed slt.ui.settings.sdk.download.sbcl.downloading=Downloading SBCL slt.ui.settings.sdk.download.cancel=Cancel diff --git a/src/main/resources/templates/en_US/initscript.allegro.cl b/src/main/resources/templates/en_US/initscript.allegro.cl new file mode 100644 index 0000000..9c61b3e --- /dev/null +++ b/src/main/resources/templates/en_US/initscript.allegro.cl @@ -0,0 +1,18 @@ +(defpackage :slt + (:use :cl) + (:export +slt-interpret+)) + +(in-package :slt) +(defconstant +slt-interpret+ ~interpret~ + "Defines current slt interpret.") + +(load "~qlpath~") +(ql:quickload :swank) +(ql:quickload :eclector) + +; Does not work in allegro... +; (setf *default-pathname-defaults* #P"~cwd~") + +(load "~corefile~") + +(swank:create-server :port ~port~ :dont-close nil) \ No newline at end of file diff --git a/src/test/java/ParserTest.java b/src/test/java/ParserTest.java index f3d405d..cb3c5ea 100644 --- a/src/test/java/ParserTest.java +++ b/src/test/java/ParserTest.java @@ -12,7 +12,7 @@ public class ParserTest { public static void main(String[] args) { String data = """ - (a . + ((a) . B) """; LispCoreProjectEnvironment projectEnvironment = new LispCoreProjectEnvironment(); From af23cb5fa6f5eacf5b25c787641e163a1389b52d Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Wed, 8 Feb 2023 23:26:41 +0100 Subject: [PATCH 22/40] allegro support --- FEATURES.md | 12 ++++++------ .../plugin/environment/SltAllegroCLEnvironment.java | 2 +- .../allegro/AllegroCLEnvironmentDefinition.java | 2 +- .../en_circle/slt/plugin/ui/instance/ThreadList.java | 3 ++- src/main/lisp/swank/swank-allegro.lisp | 5 ++++- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/FEATURES.md b/FEATURES.md index 5cdb97f..1f55b5c 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -17,14 +17,14 @@ Unsupported and will not be supported implementations: | Buffer Evaluation | ✅️ | ✅️ | ✅️ | ✅️ | | | | Documentation | ✅ | ✅ | ✅️ | ✅️ | | | | Macroexpand | ✅ | ✅ | ✅️ | ✅️ | | | -| Debugger | ✅ | ✅ | ✅️ | | | | -| Debugger Actions | ✅ | ✅ | ✅️³️ | | | | -| Frame REPL | ✅ | ❎ | ✅️ | | | | +| Debugger | ✅ | ✅ | ✅️ | ✅ | | | +| Debugger Actions | ✅ | ✅ | ✅️³️ | ✅ | | | +| Frame REPL | ✅ | ❎ | ✅️ | ✅ | | | | Stepping Debugger | ❎ | ❎ | ❎ | ✅ | ✅ | ✅ | -| References | ✅ | ❎ | ✅️ | | | | -| Inspector | ✅¹ | ✅² | ✅️¹ | | | | +| References | ✅ | ❎ | ✅️ | ✅️ | | | +| Inspector | ✅¹ | ✅² | ✅️¹ | ✅️¹ | | | | Autocomplete | ✅ | ✅ | ✅ | ✅ | | | -| Find References | ✅ | ❎ | ✅️ | | | | +| Find References | ✅ | ❎ | ✅️ | ❎ | | | ¹Only read-only inspector available diff --git a/src/main/java/com/en_circle/slt/plugin/environment/SltAllegroCLEnvironment.java b/src/main/java/com/en_circle/slt/plugin/environment/SltAllegroCLEnvironment.java index 8a4df94..fb8f859 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/SltAllegroCLEnvironment.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/SltAllegroCLEnvironment.java @@ -32,7 +32,7 @@ public SltLispProcessInformation getInformation() { @Override public Environment getType() { - return Environment.CCL_PROCESS; + return Environment.ALLEGRO_PROCESS; } @Override diff --git a/src/main/java/com/en_circle/slt/plugin/environment/allegro/AllegroCLEnvironmentDefinition.java b/src/main/java/com/en_circle/slt/plugin/environment/allegro/AllegroCLEnvironmentDefinition.java index 62f395c..4ac36eb 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/allegro/AllegroCLEnvironmentDefinition.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/allegro/AllegroCLEnvironmentDefinition.java @@ -22,11 +22,11 @@ public AllegroCLEnvironmentDefinition() { features.add(LispFeatures.REPL); features.add(LispFeatures.DOCUMENTATION); features.add(LispFeatures.MACROEXPAND); + features.add(LispFeatures.DEBUGGER_ACTION_ARGLIST); features.add(LispFeatures.FRAME_EVAL); features.add(LispFeatures.INSPECTOR); features.add(LispFeatures.INSPECTOR_HISTORY); features.add(LispFeatures.AUTOCOMPLETE); - features.add(LispFeatures.SEARCH); features.add(LispFeatures.XREFS); } diff --git a/src/main/java/com/en_circle/slt/plugin/ui/instance/ThreadList.java b/src/main/java/com/en_circle/slt/plugin/ui/instance/ThreadList.java index 3690d8d..9fdaf86 100644 --- a/src/main/java/com/en_circle/slt/plugin/ui/instance/ThreadList.java +++ b/src/main/java/com/en_circle/slt/plugin/ui/instance/ThreadList.java @@ -22,6 +22,7 @@ import javax.swing.*; import javax.swing.event.TableModelListener; +import javax.swing.table.DefaultTableModel; import javax.swing.table.TableModel; import java.awt.*; import java.math.BigInteger; @@ -176,7 +177,7 @@ public void onPostStart() { @Override public void onPreStop() { - tableContainer.removeAll(); + table.setModel(new DefaultTableModel()); } @Override diff --git a/src/main/lisp/swank/swank-allegro.lisp b/src/main/lisp/swank/swank-allegro.lisp index 8425fc7..84c402e 100644 --- a/src/main/lisp/swank/swank-allegro.lisp +++ b/src/main/lisp/swank/swank-allegro.lisp @@ -1,7 +1,10 @@ (in-package :swank/backend) (defun get-restart-function-args (restart) - NIL) + (let ((function (slot-value restart 'function))) + (when function + (describe function) + (excl:arglist function)))) (in-package :swank/allegro) From 5dcf2566155a9d0e5e14f68e4c4e14653c313bcc Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Thu, 9 Feb 2023 09:16:32 +0100 Subject: [PATCH 23/40] LispWorks investigations --- FEATURES.md | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/FEATURES.md b/FEATURES.md index 1f55b5c..33c7f3a 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -11,20 +11,22 @@ Unsupported and will not be supported implementations: * CLISP - does not work, maybe with threads, but single threaded it's useless and crashes on debug attempt -| Features / Lisp | SBCL | ABCL | CCL | Allegro CL | Lispworks | CMUCL | -|-------------------|------|------|------|------------|-----------|-------| -| REPL | ✅️ | ✅️ | ✅️ | ✅️ | | | -| Buffer Evaluation | ✅️ | ✅️ | ✅️ | ✅️ | | | -| Documentation | ✅ | ✅ | ✅️ | ✅️ | | | -| Macroexpand | ✅ | ✅ | ✅️ | ✅️ | | | -| Debugger | ✅ | ✅ | ✅️ | ✅ | | | -| Debugger Actions | ✅ | ✅ | ✅️³️ | ✅ | | | -| Frame REPL | ✅ | ❎ | ✅️ | ✅ | | | -| Stepping Debugger | ❎ | ❎ | ❎ | ✅ | ✅ | ✅ | -| References | ✅ | ❎ | ✅️ | ✅️ | | | -| Inspector | ✅¹ | ✅² | ✅️¹ | ✅️¹ | | | -| Autocomplete | ✅ | ✅ | ✅ | ✅ | | | -| Find References | ✅ | ❎ | ✅️ | ❎ | | | +* LispWorks - unfortunately free version only works as GUI so not usable. + +| Features / Lisp | SBCL | ABCL | CCL | Allegro CL | CMUCL | +|-------------------|------|------|------|------------|-------| +| REPL | ✅️ | ✅️ | ✅️ | ✅️ | | +| Buffer Evaluation | ✅️ | ✅️ | ✅️ | ✅️ | | +| Documentation | ✅ | ✅ | ✅️ | ✅️ | | +| Macroexpand | ✅ | ✅ | ✅️ | ✅️ | | +| Debugger | ✅ | ✅ | ✅️ | ✅ | | +| Debugger Actions | ✅ | ✅ | ✅️³️ | ✅ | | +| Frame REPL | ✅ | ❎ | ✅️ | ✅ | | +| Stepping Debugger | ❎ | ❎ | ❎ | ✅ | ✅ | +| References | ✅ | ❎ | ✅️ | ✅️ | | +| Inspector | ✅¹ | ✅² | ✅️¹ | ✅️¹ | | +| Autocomplete | ✅ | ✅ | ✅ | ✅ | | +| Find References | ✅ | ❎ | ✅️ | ❎ | | ¹Only read-only inspector available From cd2ab3e7d5d13e761725ac2f840ff059fe4e0215 Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Thu, 9 Feb 2023 19:32:53 +0100 Subject: [PATCH 24/40] roswell works just fine if I tokenize it! --- FEATURES.md | 34 ++++++++++-------- .../environment/SltAllegroCLEnvironment.java | 3 +- .../plugin/environment/SltCCLEnvironment.java | 3 +- .../environment/SltSBCLEnvironment.java | 3 +- .../en_circle/slt/tools/AllegroCLUtils.java | 2 +- .../com/en_circle/slt/tools/CCLUtils.java | 2 +- .../com/en_circle/slt/tools/SBCLUtils.java | 2 +- .../com/en_circle/slt/tools/SltUtils.java | 36 +++++++++++++++++++ 8 files changed, 64 insertions(+), 21 deletions(-) create mode 100644 src/main/java/com/en_circle/slt/tools/SltUtils.java diff --git a/FEATURES.md b/FEATURES.md index 33c7f3a..7bdc90e 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -13,20 +13,21 @@ Unsupported and will not be supported implementations: * LispWorks - unfortunately free version only works as GUI so not usable. -| Features / Lisp | SBCL | ABCL | CCL | Allegro CL | CMUCL | -|-------------------|------|------|------|------------|-------| -| REPL | ✅️ | ✅️ | ✅️ | ✅️ | | -| Buffer Evaluation | ✅️ | ✅️ | ✅️ | ✅️ | | -| Documentation | ✅ | ✅ | ✅️ | ✅️ | | -| Macroexpand | ✅ | ✅ | ✅️ | ✅️ | | -| Debugger | ✅ | ✅ | ✅️ | ✅ | | -| Debugger Actions | ✅ | ✅ | ✅️³️ | ✅ | | -| Frame REPL | ✅ | ❎ | ✅️ | ✅ | | -| Stepping Debugger | ❎ | ❎ | ❎ | ✅ | ✅ | -| References | ✅ | ❎ | ✅️ | ✅️ | | -| Inspector | ✅¹ | ✅² | ✅️¹ | ✅️¹ | | -| Autocomplete | ✅ | ✅ | ✅ | ✅ | | -| Find References | ✅ | ❎ | ✅️ | ❎ | | +| Features / Lisp | SBCL | ABCL | CCL | Allegro CL | CMUCL | +|------------------------------|------|------|------|------------|-------| +| REPL | ✅️ | ✅️ | ✅️ | ✅️ | | +| Buffer Evaluation | ✅️ | ✅️ | ✅️ | ✅️ | | +| Documentation | ✅ | ✅ | ✅️ | ✅️ | | +| Macroexpand | ✅ | ✅ | ✅️ | ✅️ | | +| Debugger | ✅ | ✅ | ✅️ | ✅ | | +| Debugger Actions | ✅ | ✅ | ✅️³️ | ✅⁴ | | +| Frame REPL | ✅ | ❎ | ✅️ | ✅ | | +| Stepping Debugger | ❎ | ❎ | ❎ | ✅ | ✅ | +| References | ✅ | ❎ | ✅️ | ✅️ | | +| Inspector | ✅¹ | ✅² | ✅️¹ | ✅️¹ | | +| Autocomplete | ✅ | ✅ | ✅ | ✅ | | +| Find References | ✅ | ❎ | ✅️ | ❎ | | +| Automatic Download - Windows | ✅ | ❓ | ❓ | ❎ | ❓ | ¹Only read-only inspector available @@ -36,4 +37,7 @@ Unsupported and will not be supported implementations: there is no automatic restart action argument detection. Thus, you need to supply your own and every action has "invoke with arguments" or "invoke without arguments" option, so you have to decide. FML for ansi common lisp not having ansi way to get restart -arguments because fuck you that's why. \ No newline at end of file +arguments because fuck you that's why. + +⁴Allegro CL restarts have correct arglists so actions work but for some reason all restarts from SWANK have arguments, +event abort ones... \ No newline at end of file diff --git a/src/main/java/com/en_circle/slt/plugin/environment/SltAllegroCLEnvironment.java b/src/main/java/com/en_circle/slt/plugin/environment/SltAllegroCLEnvironment.java index fb8f859..c39ba99 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/SltAllegroCLEnvironment.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/SltAllegroCLEnvironment.java @@ -4,6 +4,7 @@ import com.en_circle.slt.plugin.environment.SltProcessStreamGobbler.ProcessInitializationWaiter; import com.en_circle.slt.plugin.environment.SltProcessStreamGobbler.WaitForOccurrence; import com.en_circle.slt.tools.PluginPath; +import com.en_circle.slt.tools.SltUtils; import com.intellij.openapi.util.io.FileUtil; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; @@ -78,7 +79,7 @@ protected String[] getProcessCommand(SltLispEnvironmentProcessConfiguration conf this.port = e.port; List parameters = new ArrayList<>(); - parameters.add(c.getExecutablePath()); + SltUtils.addExecutable(parameters, c.getExecutablePath()); if (StringUtils.isNotBlank(c.getMemoryImage())) { parameters.add("-I"); parameters.add(c.getMemoryImage()); diff --git a/src/main/java/com/en_circle/slt/plugin/environment/SltCCLEnvironment.java b/src/main/java/com/en_circle/slt/plugin/environment/SltCCLEnvironment.java index 40f45c9..b933177 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/SltCCLEnvironment.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/SltCCLEnvironment.java @@ -4,6 +4,7 @@ import com.en_circle.slt.plugin.environment.SltProcessStreamGobbler.ProcessInitializationWaiter; import com.en_circle.slt.plugin.environment.SltProcessStreamGobbler.WaitForOccurrence; import com.en_circle.slt.tools.PluginPath; +import com.en_circle.slt.tools.SltUtils; import com.intellij.openapi.util.io.FileUtil; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; @@ -78,7 +79,7 @@ protected String[] getProcessCommand(SltLispEnvironmentProcessConfiguration conf this.port = e.port; List parameters = new ArrayList<>(); - parameters.add(c.getExecutablePath()); + SltUtils.addExecutable(parameters, c.getExecutablePath()); if (StringUtils.isNotBlank(c.getMemoryImage())) { parameters.add("-I"); parameters.add(c.getMemoryImage()); diff --git a/src/main/java/com/en_circle/slt/plugin/environment/SltSBCLEnvironment.java b/src/main/java/com/en_circle/slt/plugin/environment/SltSBCLEnvironment.java index 16bb5b7..d557913 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/SltSBCLEnvironment.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/SltSBCLEnvironment.java @@ -4,6 +4,7 @@ import com.en_circle.slt.plugin.environment.SltProcessStreamGobbler.ProcessInitializationWaiter; import com.en_circle.slt.plugin.environment.SltProcessStreamGobbler.WaitForOccurrence; import com.en_circle.slt.tools.PluginPath; +import com.en_circle.slt.tools.SltUtils; import com.intellij.openapi.util.io.FileUtil; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; @@ -78,7 +79,7 @@ protected String[] getProcessCommand(SltLispEnvironmentProcessConfiguration conf this.port = e.port; List parameters = new ArrayList<>(); - parameters.add(c.getExecutablePath()); + SltUtils.addExecutable(parameters, c.getExecutablePath()); if (StringUtils.isNotBlank(c.getCorePath())) { parameters.add("--core"); parameters.add(c.getCorePath()); diff --git a/src/main/java/com/en_circle/slt/tools/AllegroCLUtils.java b/src/main/java/com/en_circle/slt/tools/AllegroCLUtils.java index e149836..d1bf909 100644 --- a/src/main/java/com/en_circle/slt/tools/AllegroCLUtils.java +++ b/src/main/java/com/en_circle/slt/tools/AllegroCLUtils.java @@ -21,7 +21,7 @@ public class AllegroCLUtils { public static boolean verifyAndInstallDependencies(String executable, String memoryImage, String quicklisp, ProgressIndicator pi) { try { List args = new ArrayList<>(); - args.add(executable); + SltUtils.addExecutable(args, executable); if (StringUtils.isNotBlank(memoryImage)) { args.add("-I"); args.add(memoryImage); diff --git a/src/main/java/com/en_circle/slt/tools/CCLUtils.java b/src/main/java/com/en_circle/slt/tools/CCLUtils.java index 058e0e2..d01ce5e 100644 --- a/src/main/java/com/en_circle/slt/tools/CCLUtils.java +++ b/src/main/java/com/en_circle/slt/tools/CCLUtils.java @@ -21,7 +21,7 @@ public class CCLUtils { public static boolean verifyAndInstallDependencies(String executable, String memoryImage, String quicklisp, ProgressIndicator pi) { try { List args = new ArrayList<>(); - args.add(executable); + SltUtils.addExecutable(args, executable); if (StringUtils.isNotBlank(memoryImage)) { args.add("-I"); args.add(memoryImage); diff --git a/src/main/java/com/en_circle/slt/tools/SBCLUtils.java b/src/main/java/com/en_circle/slt/tools/SBCLUtils.java index 13792fd..e1650c1 100644 --- a/src/main/java/com/en_circle/slt/tools/SBCLUtils.java +++ b/src/main/java/com/en_circle/slt/tools/SBCLUtils.java @@ -21,7 +21,7 @@ public class SBCLUtils { public static boolean verifyAndInstallDependencies(String executable, String core, String quicklisp, ProgressIndicator pi) { try { List args = new ArrayList<>(); - args.add(executable); + SltUtils.addExecutable(args, executable); if (StringUtils.isNotBlank(core)) { args.add("--core"); args.add(core); diff --git a/src/main/java/com/en_circle/slt/tools/SltUtils.java b/src/main/java/com/en_circle/slt/tools/SltUtils.java new file mode 100644 index 0000000..e0fb45a --- /dev/null +++ b/src/main/java/com/en_circle/slt/tools/SltUtils.java @@ -0,0 +1,36 @@ +package com.en_circle.slt.tools; + +import com.intellij.execution.configurations.CommandLineTokenizer; + +import java.io.File; +import java.util.List; +import java.util.StringTokenizer; + +public class SltUtils { + + public static void addExecutable(List parameters, String executable) { + File file = new File(executable); + if (file.getParentFile() != null) { + String exec = file.getName(); + String path = file.getParent(); + addExecutable(parameters, exec, path); + } else { + addExecutable(parameters, executable, null); + } + } + + public static void addExecutable(List parameters, String exec, String path) { + StringTokenizer tokenizer = new CommandLineTokenizer(exec); + boolean first = true; + while (tokenizer.hasMoreTokens()) { + String part = tokenizer.nextToken(); + if (path != null && first) { + parameters.add(new File(new File(path), part).getPath()); + first = false; + } else { + parameters.add(part); + } + } + } + +} From ea3058a59459ed42e855f7f29b03c3bd954492c1 Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Thu, 9 Feb 2023 23:42:09 +0100 Subject: [PATCH 25/40] cmucl support closing #45 --- CHANGELOG.md | 2 + FEATURES.md | 22 +- .../slt/plugin/environment/Environment.java | 2 + .../slt/plugin/environment/LispInterpret.java | 3 +- .../SltCCLEnvironmentConfiguration.java | 2 +- .../environment/SltCMUCLEnvironment.java | 171 +++++++++++++ .../SltCMUCLEnvironmentConfiguration.java | 94 +++++++ .../cmucl/CMUCLEnvironmentDefinition.java | 73 ++++++ .../environment/cmucl/CMUCLOverrides.java | 10 + .../com/en_circle/slt/plugin/sdk/LispSdk.java | 4 + .../SltLispEnvironmentSymbolCache.java | 82 ++++--- .../sdk/SdkConfigurationAllegroCLProcess.java | 2 +- .../ui/sdk/SdkConfigurationCMUCLProcess.java | 229 ++++++++++++++++++ .../com/en_circle/slt/tools/CMUCLUtils.java | 93 +++++++ src/main/lisp/load.lisp | 4 + src/main/lisp/slt/slt-cmucl.lisp | 4 + src/main/lisp/slt/slt.lisp | 2 + src/main/lisp/swank/swank-allegro.lisp | 1 - src/main/lisp/swank/swank-cmucl.lisp | 40 +++ src/main/lisp/swank/swank.lisp | 4 + .../resources/messages/SltBundle.properties | 11 + 21 files changed, 801 insertions(+), 54 deletions(-) create mode 100644 src/main/java/com/en_circle/slt/plugin/environment/SltCMUCLEnvironment.java create mode 100644 src/main/java/com/en_circle/slt/plugin/environment/SltCMUCLEnvironmentConfiguration.java create mode 100644 src/main/java/com/en_circle/slt/plugin/environment/cmucl/CMUCLEnvironmentDefinition.java create mode 100644 src/main/java/com/en_circle/slt/plugin/environment/cmucl/CMUCLOverrides.java create mode 100644 src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationCMUCLProcess.java create mode 100644 src/main/java/com/en_circle/slt/tools/CMUCLUtils.java create mode 100644 src/main/lisp/slt/slt-cmucl.lisp create mode 100644 src/main/lisp/swank/swank-cmucl.lisp diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e19657..052d6de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ - Support for multiple lisp interprets - ABCL Support - CCL Support +- AllegroCL Support +- CMUCL Support ### Fixes diff --git a/FEATURES.md b/FEATURES.md index 7bdc90e..5afb947 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -15,18 +15,18 @@ Unsupported and will not be supported implementations: | Features / Lisp | SBCL | ABCL | CCL | Allegro CL | CMUCL | |------------------------------|------|------|------|------------|-------| -| REPL | ✅️ | ✅️ | ✅️ | ✅️ | | -| Buffer Evaluation | ✅️ | ✅️ | ✅️ | ✅️ | | -| Documentation | ✅ | ✅ | ✅️ | ✅️ | | -| Macroexpand | ✅ | ✅ | ✅️ | ✅️ | | -| Debugger | ✅ | ✅ | ✅️ | ✅ | | -| Debugger Actions | ✅ | ✅ | ✅️³️ | ✅⁴ | | -| Frame REPL | ✅ | ❎ | ✅️ | ✅ | | +| REPL | ✅️ | ✅️ | ✅️ | ✅️ | ✅️ | +| Buffer Evaluation | ✅️ | ✅️ | ✅️ | ✅️ | ✅️ | +| Documentation | ✅ | ✅ | ✅️ | ✅️ | ✅️ | +| Macroexpand | ✅ | ✅ | ✅️ | ✅️ | ✅️ | +| Debugger | ✅ | ✅ | ✅️ | ✅ | ✅ | +| Debugger Actions | ✅ | ✅ | ✅️³️ | ✅⁴ | ✅ | +| Frame REPL | ✅ | ❎ | ✅️ | ✅ | ✅ | | Stepping Debugger | ❎ | ❎ | ❎ | ✅ | ✅ | -| References | ✅ | ❎ | ✅️ | ✅️ | | -| Inspector | ✅¹ | ✅² | ✅️¹ | ✅️¹ | | -| Autocomplete | ✅ | ✅ | ✅ | ✅ | | -| Find References | ✅ | ❎ | ✅️ | ❎ | | +| References | ✅ | ❎ | ✅️ | ✅️ | ❎ | +| Inspector | ✅¹ | ✅² | ✅️¹ | ✅️¹ | ✅️¹ | +| Autocomplete | ✅ | ✅ | ✅ | ✅ | ✅ | +| Find References | ✅ | ❎ | ✅️ | ❎ | ❎ | | Automatic Download - Windows | ✅ | ❓ | ❓ | ❎ | ❓ | ¹Only read-only inspector available diff --git a/src/main/java/com/en_circle/slt/plugin/environment/Environment.java b/src/main/java/com/en_circle/slt/plugin/environment/Environment.java index 754bcf9..871013e 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/Environment.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/Environment.java @@ -3,6 +3,7 @@ import com.en_circle.slt.plugin.environment.abcl.ABCLEnvironmentDefinition; import com.en_circle.slt.plugin.environment.allegro.AllegroCLEnvironmentDefinition; import com.en_circle.slt.plugin.environment.ccl.CCLEnvironmentDefinition; +import com.en_circle.slt.plugin.environment.cmucl.CMUCLEnvironmentDefinition; import com.en_circle.slt.plugin.environment.sbcl.SBCLEnvironmentDefinition; public enum Environment { @@ -11,6 +12,7 @@ public enum Environment { SBCL_PROCESS(new SBCLEnvironmentDefinition()), CCL_PROCESS(new CCLEnvironmentDefinition()), ALLEGRO_PROCESS(new AllegroCLEnvironmentDefinition()), + CMUCL_PROCESS(new CMUCLEnvironmentDefinition()), ; diff --git a/src/main/java/com/en_circle/slt/plugin/environment/LispInterpret.java b/src/main/java/com/en_circle/slt/plugin/environment/LispInterpret.java index 2432914..377fca5 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/LispInterpret.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/LispInterpret.java @@ -5,7 +5,8 @@ public enum LispInterpret { ABCL(":abcl"), SBCL(":sbcl"), CCL(":ccl"), - ALLEGRO(":allegro") + ALLEGRO(":allegro"), + CMUCL(":cmucl") ; diff --git a/src/main/java/com/en_circle/slt/plugin/environment/SltCCLEnvironmentConfiguration.java b/src/main/java/com/en_circle/slt/plugin/environment/SltCCLEnvironmentConfiguration.java index eb45ab7..15afb05 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/SltCCLEnvironmentConfiguration.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/SltCCLEnvironmentConfiguration.java @@ -6,7 +6,7 @@ public class SltCCLEnvironmentConfiguration implements SltLispEnvironmentProcessConfiguration { - private String executablePath = "clisp"; + private String executablePath = "ccl"; private String memoryImage = ""; private String quicklispStartScript = "~/quicklisp/setup.lisp"; private String projectDirectory = "/tmp"; diff --git a/src/main/java/com/en_circle/slt/plugin/environment/SltCMUCLEnvironment.java b/src/main/java/com/en_circle/slt/plugin/environment/SltCMUCLEnvironment.java new file mode 100644 index 0000000..20ece2c --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/environment/SltCMUCLEnvironment.java @@ -0,0 +1,171 @@ +package com.en_circle.slt.plugin.environment; + +import com.en_circle.slt.plugin.SltLibrary; +import com.en_circle.slt.plugin.environment.SltProcessStreamGobbler.ProcessInitializationWaiter; +import com.en_circle.slt.plugin.environment.SltProcessStreamGobbler.WaitForOccurrence; +import com.en_circle.slt.tools.PluginPath; +import com.en_circle.slt.tools.SltUtils; +import com.intellij.openapi.util.io.FileUtil; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; +import org.watertemplate.Template; + +import java.io.File; +import java.io.IOException; +import java.net.ServerSocket; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +public class SltCMUCLEnvironment extends SltLispEnvironmentProcess { + + private int port; + + @Override + public int getSwankPort() { + return port; + } + + @Override + public SltLispProcessInformation getInformation() { + return new SltCMUCLLispProcessInformation(); + } + + @Override + public Environment getType() { + return Environment.CMUCL_PROCESS; + } + + @Override + protected Object prepareProcessEnvironment(SltLispEnvironmentProcessConfiguration configuration) throws SltProcessException { + SltCMUCLEnvironmentConfiguration c = getConfiguration(configuration); + CMUCLEnvironment e = new CMUCLEnvironment(); + try { + e.port = getFreePort(); + if (e.port == 0) { + throw new IOException("no free port available"); + } + + File tempDir = FileUtil.createTempDirectory(PluginPath.getPluginFolder(), + "SLTinit", ""); + + e.sltCore = SltLibrary.getLibraryInitFile(); + + e.serverStartSetup = new File(tempDir, "startServer.cl"); + e.serverStartSetup.deleteOnExit(); + String sltCorePath = e.sltCore.getAbsolutePath(); + String startScriptTemplate = new CMUCLInitScriptTemplate(c, sltCorePath, e.port).render(); + FileUtils.write(e.serverStartSetup, startScriptTemplate, StandardCharsets.UTF_8); + + tempDir.deleteOnExit(); + } catch (Exception ex) { + throw new SltProcessException(ex); + } + return e; + } + + @Override + protected File getProcessWorkDirectory(SltLispEnvironmentProcessConfiguration configuration, Object environment) throws SltProcessException { + SltCMUCLEnvironmentConfiguration c = getConfiguration(configuration); + CMUCLEnvironment e = getEnvironment(environment); + + return e.serverStartSetup.getParentFile(); + } + + @Override + protected String[] getProcessCommand(SltLispEnvironmentProcessConfiguration configuration, Object environment) throws SltProcessException { + SltCMUCLEnvironmentConfiguration c = getConfiguration(configuration); + CMUCLEnvironment e = getEnvironment(environment); + this.port = e.port; + + List parameters = new ArrayList<>(); + SltUtils.addExecutable(parameters, c.getExecutablePath()); + if (StringUtils.isNotBlank(c.getMemoryImage())) { + parameters.add("-core"); + parameters.add(c.getMemoryImage()); + } + + parameters.add("-load"); + parameters.add(e.serverStartSetup.getName()); + + return parameters.toArray(new String[0]); + } + + @Override + protected ProcessInitializationWaiter waitForFullInitialization(SltLispEnvironmentProcessConfiguration configuration, Object environment) throws SltProcessException { + SltCMUCLEnvironmentConfiguration c = getConfiguration(configuration); + CMUCLEnvironment e = getEnvironment(environment); + + WaitForOccurrence wait = new WaitForOccurrence("Swank started at port"); + errorController.addUpdateListener(wait); + + return wait; + } + + private SltCMUCLEnvironmentConfiguration getConfiguration(SltLispEnvironmentProcessConfiguration configuration) throws SltProcessException { + if (!(configuration instanceof SltCMUCLEnvironmentConfiguration)) + throw new SltProcessException("Configuration must be SltCMUCLEnvironmentConfiguration"); + return (SltCMUCLEnvironmentConfiguration) configuration; + } + + private CMUCLEnvironment getEnvironment(Object environment) { + assert (environment instanceof CMUCLEnvironment); + + return (CMUCLEnvironment) environment; + } + + private int getFreePort() { + var freePort = 0; + try (ServerSocket s = new ServerSocket(0)) { + freePort = s.getLocalPort(); + } catch (Exception ignored) { + + } + + return freePort; + } + + + private class SltCMUCLLispProcessInformation implements SltLispProcessInformation { + + @Override + public String getPid() { + return "" + process.pid(); + } + } + + private static class CMUCLEnvironment { + + File sltCore; + File serverStartSetup; + int port; + + } + + private static class CMUCLInitScriptTemplate extends Template { + + public CMUCLInitScriptTemplate(SltCMUCLEnvironmentConfiguration configuration, String sltCoreScript, int port) { + String quicklispPath = configuration.getQuicklispStartScript(); + if (quicklispPath.contains("\\")) { + quicklispPath = StringUtils.replace(quicklispPath, "\\", "\\\\"); + } + String cwd = configuration.getProjectDirectory(); + if (cwd.contains("\\")) { + cwd = StringUtils.replace(cwd, "\\", "\\\\"); + } + if (sltCoreScript.contains("\\")) { + sltCoreScript = StringUtils.replace(sltCoreScript, "\\", "\\\\"); + } + add("qlpath", quicklispPath); + add("port", "" + port); + add("cwd", cwd); + add("corefile", sltCoreScript); + add("interpret", LispInterpret.CMUCL.symbolName); + } + + @Override + protected String getFilePath() { + return "initscript.cl"; + } + } +} diff --git a/src/main/java/com/en_circle/slt/plugin/environment/SltCMUCLEnvironmentConfiguration.java b/src/main/java/com/en_circle/slt/plugin/environment/SltCMUCLEnvironmentConfiguration.java new file mode 100644 index 0000000..ad7ded0 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/environment/SltCMUCLEnvironmentConfiguration.java @@ -0,0 +1,94 @@ +package com.en_circle.slt.plugin.environment; + + +import com.en_circle.slt.plugin.environment.SltLispEnvironment.SltLispOutputChangedListener; +import com.en_circle.slt.plugin.environment.SltLispEnvironmentProcess.SltLispEnvironmentProcessConfiguration; + +public class SltCMUCLEnvironmentConfiguration implements SltLispEnvironmentProcessConfiguration { + + private String executablePath = "cmucl"; + private String memoryImage = ""; + private String quicklispStartScript = "~/quicklisp/setup.lisp"; + private String projectDirectory = "/tmp"; + private SltLispOutputChangedListener listener = null; + + private SltCMUCLEnvironmentConfiguration() { + + } + + public String getExecutablePath() { + return executablePath; + } + + public String getMemoryImage() { + return memoryImage; + } + + public String getQuicklispStartScript() { + return quicklispStartScript; + } + + public String getProjectDirectory() { + return projectDirectory; + } + + @Override + public SltLispOutputChangedListener getListener() { + return listener; + } + + public static class Builder implements SltLispEnvironmentConfiguration.Builder { + + private final SltCMUCLEnvironmentConfiguration c = new SltCMUCLEnvironmentConfiguration(); + private boolean built = false; + + public Builder setExecutable(String executable) { + checkNotBuilt(); + + c.executablePath = executable; + return this; + } + + public Builder setMemoryImage(String memoryImage) { + checkNotBuilt(); + + c.memoryImage = memoryImage; + return this; + } + + public Builder setQuicklispStartScriptPath(String quicklispStartScript) { + checkNotBuilt(); + + c.quicklispStartScript = quicklispStartScript; + return this; + } + + public Builder setProjectDirectory(String projectDirectory) { + checkNotBuilt(); + + c.projectDirectory = projectDirectory; + return this; + } + + @Override + public Builder setListener(SltLispOutputChangedListener listener) { + checkNotBuilt(); + + c.listener = listener; + return this; + } + + @Override + public SltCMUCLEnvironmentConfiguration build() { + checkNotBuilt(); + built = true; + return c; + } + + private void checkNotBuilt() { + if (built) throw new IllegalStateException("already built"); + } + + } + +} diff --git a/src/main/java/com/en_circle/slt/plugin/environment/cmucl/CMUCLEnvironmentDefinition.java b/src/main/java/com/en_circle/slt/plugin/environment/cmucl/CMUCLEnvironmentDefinition.java new file mode 100644 index 0000000..9c0a9d0 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/environment/cmucl/CMUCLEnvironmentDefinition.java @@ -0,0 +1,73 @@ +package com.en_circle.slt.plugin.environment.cmucl; + +import com.en_circle.slt.plugin.SltBundle; +import com.en_circle.slt.plugin.environment.*; +import com.en_circle.slt.plugin.environment.SltLispEnvironmentConfiguration.Builder; +import com.en_circle.slt.plugin.sdk.LispSdk; +import com.en_circle.slt.plugin.services.lisp.LispSltOverrides; +import com.en_circle.slt.plugin.ui.sdk.SdkConfigurationCMUCLProcess; +import com.en_circle.slt.plugin.ui.sdk.SdkDialogProvider; +import com.en_circle.slt.tools.platform.DownloadLispAction; +import com.intellij.openapi.project.Project; + +import java.util.HashSet; +import java.util.Set; +import java.util.function.Supplier; + +public class CMUCLEnvironmentDefinition extends EnvironmentDefinition { + + private final Set features = new HashSet<>(); + + public CMUCLEnvironmentDefinition() { + features.add(LispFeatures.REPL); + features.add(LispFeatures.DOCUMENTATION); + features.add(LispFeatures.MACROEXPAND); + features.add(LispFeatures.FRAME_EVAL); + features.add(LispFeatures.DEBUGGER_ACTION_ARGLIST); + features.add(LispFeatures.INSPECTOR); + features.add(LispFeatures.INSPECTOR_HISTORY); + features.add(LispFeatures.AUTOCOMPLETE); + } + + @Override + public String getName() { + return SltBundle.message("slt.environment.cmucl"); + } + + @Override + public Class getDownloadActionDef() { + return null; + } + + @Override + public Supplier getEnvironmentCreator() { + return SltCMUCLEnvironment::new; + } + + @SuppressWarnings("unchecked") + @Override + public , R extends SltLispEnvironmentConfiguration> + Builder buildConfiguration(LispSdk sdk, Project project) { + return (Builder) new SltCMUCLEnvironmentConfiguration.Builder() + .setExecutable(sdk.cmuclExecutable) + .setMemoryImage(sdk.cmuclMemoryImage) + .setQuicklispStartScriptPath(sdk.quickLispPath) + .setProjectDirectory(project.getBasePath()); + } + + @Override + public SdkDialogProvider getDialogProvider() { + return SdkConfigurationCMUCLProcess::new; + } + + @Override + public LispSltOverrides getOverrides() { + return new CMUCLOverrides(); + } + + @Override + public boolean hasFeature(LispFeatures feature) { + return features.contains(feature); + } + +} diff --git a/src/main/java/com/en_circle/slt/plugin/environment/cmucl/CMUCLOverrides.java b/src/main/java/com/en_circle/slt/plugin/environment/cmucl/CMUCLOverrides.java new file mode 100644 index 0000000..7ae7353 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/environment/cmucl/CMUCLOverrides.java @@ -0,0 +1,10 @@ +package com.en_circle.slt.plugin.environment.cmucl; + +import com.en_circle.slt.plugin.environment.LispSltOverridesBase; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CMUCLOverrides extends LispSltOverridesBase { + private static final Logger log = LoggerFactory.getLogger(CMUCLOverrides.class); + +} diff --git a/src/main/java/com/en_circle/slt/plugin/sdk/LispSdk.java b/src/main/java/com/en_circle/slt/plugin/sdk/LispSdk.java index a313dd7..5e8af46 100644 --- a/src/main/java/com/en_circle/slt/plugin/sdk/LispSdk.java +++ b/src/main/java/com/en_circle/slt/plugin/sdk/LispSdk.java @@ -34,6 +34,10 @@ public class LispSdk implements PersistentStateComponent { public String allegroExecutable; public String allegroMemoryImage; + // CMUCL Process used + public String cmuclExecutable; + public String cmuclMemoryImage; + public Environment getEnvironment() { if (environment == null) { // backwards compat diff --git a/src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltLispEnvironmentSymbolCache.java b/src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltLispEnvironmentSymbolCache.java index 09aa654..5578ec7 100644 --- a/src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltLispEnvironmentSymbolCache.java +++ b/src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltLispEnvironmentSymbolCache.java @@ -180,47 +180,51 @@ private void refreshSymbolsBatched(List refreshStates, Consumer result = new ProgressRunner<>(pi -> verifyAllegro(pi, executable, memory, quicklisp)) diff --git a/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationCMUCLProcess.java b/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationCMUCLProcess.java new file mode 100644 index 0000000..e5db99f --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/ui/sdk/SdkConfigurationCMUCLProcess.java @@ -0,0 +1,229 @@ +package com.en_circle.slt.plugin.ui.sdk; + +import com.en_circle.slt.plugin.SltBundle; +import com.en_circle.slt.plugin.sdk.LispSdk; +import com.en_circle.slt.plugin.ui.SltGlobalUIService; +import com.en_circle.slt.plugin.ui.sdk.SdkDialogProvider.OnSave; +import com.en_circle.slt.tools.CMUCLUtils; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.fileChooser.FileChooserDescriptor; +import com.intellij.openapi.fileChooser.FileChooserFactory; +import com.intellij.openapi.fileChooser.FileTextField; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.impl.ProgressResult; +import com.intellij.openapi.progress.impl.ProgressRunner; +import com.intellij.openapi.progress.impl.ProgressRunner.ThreadToUse; +import com.intellij.openapi.progress.util.ProgressWindow; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.ui.TextFieldWithBrowseButton; +import com.intellij.openapi.util.Disposer; +import com.intellij.ui.components.JBTextField; +import com.intellij.util.ui.FormBuilder; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.event.CaretListener; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.io.File; + +public class SdkConfigurationCMUCLProcess extends DialogWrapper { + + private final Disposable parentDisposable; + private final LispSdk instance; + private final OnSave onSave; + private boolean isVerified = false; + + private JBTextField name; + private FileTextField cmuclExecutable; + private FileTextField cmuclImage; + private FileTextField quicklispPath; + + public SdkConfigurationCMUCLProcess(@NotNull Component parent, LispSdk instance, String title, OnSave onSave) { + super(parent, true); + + this.parentDisposable = SltGlobalUIService.getInstance(); + this.instance = instance; + this.onSave = onSave; + + setTitle(title); + setSize(700, 0); + init(); + } + + @Override + protected @Nullable JComponent createCenterPanel() { + name = new JBTextField(); + name.addCaretListener(createChangeListener()); + name.setText(instance.userName); + if (StringUtils.isBlank(instance.userName)) { + name.setText(SltBundle.message("slt.ui.settings.sdk.editor.name.cmucl.default")); + } + + FileChooserDescriptor descriptor = new FileChooserDescriptor(true, false, false, + false, false, false); + + cmuclExecutable = FileChooserFactory.getInstance() + .createFileTextField(descriptor, true, parentDisposable); + cmuclExecutable.getField().addCaretListener(createChangeListener()); + cmuclExecutable.getField().setText(instance.sbclExecutable); + cmuclImage = FileChooserFactory.getInstance() + .createFileTextField(descriptor, true, parentDisposable); + cmuclImage.getField().addCaretListener(createChangeListener()); + cmuclImage.getField().setText(instance.sbclCorePath); + quicklispPath = FileChooserFactory.getInstance() + .createFileTextField(descriptor, true, parentDisposable); + quicklispPath.getField().addCaretListener(createChangeListener()); + quicklispPath.getField().setText(instance.quickLispPath); + + TextFieldWithBrowseButton cmuclExecutablePicker = new TextFieldWithBrowseButton(cmuclExecutable.getField()); + cmuclExecutablePicker.addBrowseFolderListener( + SltBundle.message("slt.ui.settings.sdk.editor.cmucl.executable.select"), "", null, descriptor); + TextFieldWithBrowseButton cmuclImagePicker = new TextFieldWithBrowseButton(cmuclImage.getField()); + cmuclImagePicker.addBrowseFolderListener( + SltBundle.message("slt.ui.settings.sdk.editor.cmucl.image.select"), "", null, descriptor); + TextFieldWithBrowseButton quicklispPathPicker = new TextFieldWithBrowseButton(quicklispPath.getField()); + //noinspection DialogTitleCapitalization + quicklispPathPicker.addBrowseFolderListener( + SltBundle.message("slt.ui.settings.sdk.editor.quicklisp.select"), "", null, descriptor); + + return new FormBuilder() + .addLabeledComponent(SltBundle.message("slt.ui.settings.sdk.editor.name"), name, 1, false) + .addLabeledComponent(SltBundle.message("slt.ui.settings.sdk.editor.cmucl.process.executable"), + cmuclExecutablePicker, 1, false) + .addLabeledComponent(SltBundle.message("slt.ui.settings.sdk.editor.cmucl.process.image"), + cmuclImagePicker, 1, false) + .addLabeledComponent(SltBundle.message("slt.ui.settings.sdk.editor.quicklisp"), + quicklispPathPicker, 1, false) + .addComponentFillVertically(new JPanel(), 0) + .getPanel(); + } + + private CaretListener createChangeListener() { + return e -> isVerified = false; + } + + private void verifySdk() { + name.putClientProperty("JComponent.outline", null); + cmuclExecutable.getField().putClientProperty("JComponent.outline", null); + cmuclImage.getField().putClientProperty("JComponent.outline", null); + quicklispPath.getField().putClientProperty("JComponent.outline", null); + + boolean verified = true; + + if (StringUtils.isBlank(name.getText())) { + verified = false; + name.putClientProperty("JComponent.outline", "error"); + } + + String executable = cmuclExecutable.getField().getText(); + if (StringUtils.isBlank(executable)) { + verified = false; + cmuclExecutable.getField().putClientProperty("JComponent.outline", "error"); + } + + String core = cmuclImage.getField().getText(); + if (StringUtils.isNotBlank(core)) { + File file = new File(core); + if (!file.exists()) { + verified = false; + cmuclImage.getField().putClientProperty("JComponent.outline", "error"); + } + } + + String quicklisp = quicklispPath.getField().getText(); + if (StringUtils.isBlank(quicklisp)) { + verified = false; + quicklispPath.getField().putClientProperty("JComponent.outline", "error"); + } else { + File qlFile = new File(quicklisp); + if (!qlFile.exists() || !qlFile.isFile()) { + verified = false; + quicklispPath.getField().putClientProperty("JComponent.outline", "error"); + } + } + + name.repaint(); + cmuclExecutable.getField().repaint(); + cmuclImage.getField().repaint(); + quicklispPath.getField().repaint(); + + if (verified) + verified = checkAndLoadCMUCL(executable, core, quicklisp); + if (!verified) { + Messages.showErrorDialog(SltBundle.message("slt.ui.settings.sdk.editor.cmucl.process.verifying.error"), + SltBundle.message("slt.ui.settings.sdk.editor.verifying.error.title")); + } + + isVerified = verified; + } + + private boolean checkAndLoadCMUCL(String executable, String memory, String quicklisp) { + ProgressWindow verifyWindow = new ProgressWindow(true, false, null, + getRootPane(), SltBundle.message("slt.ui.settings.sdk.editor.verifying.cancel")); + verifyWindow.setTitle(SltBundle.message("slt.ui.settings.sdk.editor.verifying.cmucl")); + Disposer.register(parentDisposable, verifyWindow); + + ProgressResult result = new ProgressRunner<>(pi -> verifyCMUCL(pi, executable, memory, quicklisp)) + .sync() + .onThread(ThreadToUse.POOLED) + .withProgress(verifyWindow) + .modal() + .submitAndGet(); + return Boolean.TRUE.equals(result.getResult()); + } + + private boolean verifyCMUCL(ProgressIndicator pi, String executable, String core, String quicklisp) { + return CMUCLUtils.verifyAndInstallDependencies(executable, core, quicklisp, pi); + } + + private void save() { + instance.userName = name.getText(); + instance.cmuclExecutable = cmuclExecutable.getField().getText(); + instance.cmuclMemoryImage = cmuclImage.getField().getText(); + instance.quickLispPath = quicklispPath.getField().getText(); + onSave.saveAction(instance); + close(0); + } + + @Override + protected Action @NotNull [] createActions() { + return new Action[] { + new VerifyAction(), new SaveAction() + }; + } + + public class VerifyAction extends AbstractAction { + + public VerifyAction() { + super(SltBundle.message("slt.ui.settings.sdk.editor.cmucl.process.verify")); + } + + @Override + public void actionPerformed(ActionEvent e) { + verifySdk(); + } + } + + public class SaveAction extends AbstractAction { + + public SaveAction() { + super(SltBundle.message("slt.ui.settings.sdk.editor.save")); + } + + @Override + public void actionPerformed(ActionEvent e) { + if (!isVerified) { + Messages.showInfoMessage(SltBundle.message("slt.ui.settings.sdk.editor.notverified.title"), + SltBundle.message("slt.ui.settings.sdk.editor.cmucl.process.notverified.message")); + return; + } + + save(); + } + } + +} diff --git a/src/main/java/com/en_circle/slt/tools/CMUCLUtils.java b/src/main/java/com/en_circle/slt/tools/CMUCLUtils.java new file mode 100644 index 0000000..7a7b802 --- /dev/null +++ b/src/main/java/com/en_circle/slt/tools/CMUCLUtils.java @@ -0,0 +1,93 @@ +package com.en_circle.slt.tools; + +import com.en_circle.slt.plugin.environment.SltProcessStreamGobbler; +import com.en_circle.slt.plugin.environment.SltProcessStreamGobbler.WaitForOccurrence; +import com.en_circle.slt.templates.VerifyTemplate; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.util.io.FileUtil; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; + +public class CMUCLUtils { + + @SuppressWarnings("ResultOfMethodCallIgnored") + public static boolean verifyAndInstallDependencies(String executable, String memoryImage, String quicklisp, ProgressIndicator pi) { + try { + List args = new ArrayList<>(); + SltUtils.addExecutable(args, executable); + if (StringUtils.isNotBlank(memoryImage)) { + args.add("-core"); + args.add(memoryImage); + } + args.add("-batch"); + + File tempTestFile = FileUtil.createTempFile("testCMUCL", ".cl"); + if (tempTestFile.exists()) + tempTestFile.delete(); + FileUtils.writeStringToFile(tempTestFile, new VerifyTemplate(quicklisp).render(), StandardCharsets.UTF_8); + tempTestFile.deleteOnExit(); + + args.add("-load"); + args.add(tempTestFile.getAbsolutePath()); + + try { + ProcessBuilder processBuilder = new ProcessBuilder(args.toArray(new String[0])); + Process process = processBuilder.start(); + + StringBuilder errorValue = new StringBuilder(); + StringBuilder displayValue = new StringBuilder(); + StringBuilder outputValue = new StringBuilder(); + SltProcessStreamGobbler errorController = new SltProcessStreamGobbler(process.getErrorStream()); + SltProcessStreamGobbler outputController = new SltProcessStreamGobbler(process.getInputStream()); + errorController.addUpdateListener(errorValue::append); + outputController.addUpdateListener(outputValue::append); + WaitForOccurrence waiter = new WaitForOccurrence("SltVerified"); + errorController.addUpdateListener(waiter); + outputController.addUpdateListener(data -> { + displayValue.append(data); + String str = displayValue.toString(); + if (str.contains("\n")) { + String[] lines = str.split(Pattern.quote("\n")); + for (String line : lines) { + if (StringUtils.isNotBlank(line)) + pi.setText(line); + } + if (StringUtils.isNotBlank(lines[lines.length-1])) { + displayValue.setLength(0); + displayValue.append(lines[lines.length-1]); + } + } + }); + errorController.start(); + outputController.start(); + if (!waiter.awaitFor(null, errorController, 10, TimeUnit.MINUTES, pi::isCanceled)) { + if (process.isAlive()) + process.destroy(); + + errorController.join(); + outputController.join(); + return false; + } + + if (process.isAlive()) + process.destroy(); + + errorController.join(); + outputController.join(); + return errorValue.toString().contains("SltVerified"); + } finally { + tempTestFile.delete(); + } + } catch (Exception ignored) { + return false; + } + } + +} diff --git a/src/main/lisp/load.lisp b/src/main/lisp/load.lisp index ba2f0dc..8e38b8f 100644 --- a/src/main/lisp/load.lisp +++ b/src/main/lisp/load.lisp @@ -48,6 +48,10 @@ (lisp-implementation-type)) (format *error-output* "Invalid lisp instance. Maybe a configuration error? This is not Allegro Common Lisp!~%") (portable-quit 1))) + (:cmucl (unless (string= "CMU Common Lisp" + (lisp-implementation-type)) + (format *error-output* "Invalid lisp instance. Maybe a configuration error? This is not CMU Common Lisp!~%") + (portable-quit 1))) (otherwise (format *error-output* "Unsupported lisp instance. Maybe a configuration error?~%") (portable-quit 1))) diff --git a/src/main/lisp/slt/slt-cmucl.lisp b/src/main/lisp/slt/slt-cmucl.lisp new file mode 100644 index 0000000..25abcec --- /dev/null +++ b/src/main/lisp/slt/slt-cmucl.lisp @@ -0,0 +1,4 @@ +(in-package :slt-core) + +(defun specialp (test-sym) + (eq (extensions:variable-information test-sym) :special)) \ No newline at end of file diff --git a/src/main/lisp/slt/slt.lisp b/src/main/lisp/slt/slt.lisp index 154071e..76fe94c 100644 --- a/src/main/lisp/slt/slt.lisp +++ b/src/main/lisp/slt/slt.lisp @@ -12,6 +12,8 @@ (load (merge-pathnames "slt-ccl.lisp" *load-truename*))) (when (eq slt:+slt-interpret+ :allegro) (load (merge-pathnames "slt-allegro.lisp" *load-truename*))) +(when (eq slt:+slt-interpret+ :cmucl) + (load (merge-pathnames "slt-cmucl.lisp" *load-truename*))) (in-package :slt-core) diff --git a/src/main/lisp/swank/swank-allegro.lisp b/src/main/lisp/swank/swank-allegro.lisp index 84c402e..9235404 100644 --- a/src/main/lisp/swank/swank-allegro.lisp +++ b/src/main/lisp/swank/swank-allegro.lisp @@ -3,7 +3,6 @@ (defun get-restart-function-args (restart) (let ((function (slot-value restart 'function))) (when function - (describe function) (excl:arglist function)))) (in-package :swank/allegro) diff --git a/src/main/lisp/swank/swank-cmucl.lisp b/src/main/lisp/swank/swank-cmucl.lisp new file mode 100644 index 0000000..8ef5afb --- /dev/null +++ b/src/main/lisp/swank/swank-cmucl.lisp @@ -0,0 +1,40 @@ +(in-package :swank/backend) + +(defun get-restart-function-args (restart) + (handler-case + (progn + (let* + ((fnc (slot-value restart 'function)) + (header (kernel:get-type fnc))) + (cond ((= header vm:function-header-type) + (kernel:%function-arglist fnc)) + ((= header vm:closure-header-type) + NIL) + ((eval::interpreted-function-p fnc) + (kernel:%function-type fnc)) + (t NIL)))) + (error (e) + (declare (ignore e)) + NIL))) + +(in-package :swank/cmucl) + +(in-package :swank) + +(defun print-frame-call-place (frame) + (handler-case + (di:debug-function-name (di:frame-debug-function (di::frame-real-frame frame))) + (error (e) + (declare (ignore e)) + "Unknown frame"))) + +(defslimefun backtrace (start end) + (loop for frame in (compute-backtrace start end) + for i from start collect + (list i (frame-to-string frame) + (format NIL "~A" (print-frame-call-place frame)) + (frame-source-location i) + (let ((pkg (frame-package i))) + (cond + (pkg (package-name pkg)) + (T NIL)))))) \ No newline at end of file diff --git a/src/main/lisp/swank/swank.lisp b/src/main/lisp/swank/swank.lisp index 867a245..c2b6e58 100644 --- a/src/main/lisp/swank/swank.lisp +++ b/src/main/lisp/swank/swank.lisp @@ -10,6 +10,10 @@ (load (merge-pathnames "swank-ccl.lisp" *load-truename*))) (when (eq slt:+slt-interpret+ :allegro) (load (merge-pathnames "swank-allegro.lisp" *load-truename*))) +(when (eq slt:+slt-interpret+ :cmucl) + (load (merge-pathnames "swank-cmucl.lisp" *load-truename*)) + (in-package swank/source-file-cache) + (setf *source-snippet-size* 0)) (in-package :swank) diff --git a/src/main/resources/messages/SltBundle.properties b/src/main/resources/messages/SltBundle.properties index c5ebe73..59b6514 100644 --- a/src/main/resources/messages/SltBundle.properties +++ b/src/main/resources/messages/SltBundle.properties @@ -12,6 +12,7 @@ slt.environment.abcl=ABCL slt.environment.sbcl=SBCL slt.environment.ccl=Clozure Common Lisp slt.environment.allegro=Allegro CL +slt.environment.cmucl=CMUCL # Documentation elements slt.documentation.types.symbol=Symbol @@ -124,27 +125,33 @@ slt.ui.settings.sdk.editor.sbcl.process.verify=Verify SBCL SDK slt.ui.settings.sdk.editor.abcl.process.verify=Verify ABCL SDK slt.ui.settings.sdk.editor.ccl.process.verify=Verify CCL SDK slt.ui.settings.sdk.editor.allegro.process.verify=Verify Allegro CL SDK +slt.ui.settings.sdk.editor.cmucl.process.verify=Verify CMUCL SDK slt.ui.settings.sdk.editor.save=Save slt.ui.settings.sdk.editor.name=SDK name slt.ui.settings.sdk.editor.name.sbcl.default=SBCL slt.ui.settings.sdk.editor.name.abcl.default=ABCL slt.ui.settings.sdk.editor.name.ccl.default=Clozure Common Lisp slt.ui.settings.sdk.editor.name.allegro.default=Allegro CL +slt.ui.settings.sdk.editor.name.cmucl.default=CMUCL slt.ui.settings.sdk.editor.sbcl.process.executable=SBCL executable slt.ui.settings.sdk.editor.ccl.process.executable=CCL executable slt.ui.settings.sdk.editor.allegro.process.executable=Allegro CL executable +slt.ui.settings.sdk.editor.cmucl.process.executable=CMUCL executable slt.ui.settings.sdk.editor.abcl.jvm.executable=Java executable slt.ui.settings.sdk.editor.abcl.jvm.args=Java JVM arguments (optional) slt.ui.settings.sdk.editor.sbcl.executable.select=Please Select SBCL Executable slt.ui.settings.sdk.editor.ccl.executable.select=Please Select CCL Executable slt.ui.settings.sdk.editor.allegro.executable.select=Please Select Allegro CL Executable +slt.ui.settings.sdk.editor.cmucl.executable.select=Please Select CMUCL Executable slt.ui.settings.sdk.editor.sbcl.process.core=SBCL core (optional) slt.ui.settings.sdk.editor.ccl.process.image=CCL image (optional) slt.ui.settings.sdk.editor.allegro.process.image=Allegro CL memory image (optional) +slt.ui.settings.sdk.editor.cmucl.process.image=CMUCL memory image (optional) slt.ui.settings.sdk.editor.abcl.jar=ABCL jar slt.ui.settings.sdk.editor.sbcl.core.select=Please Select SBCL Core slt.ui.settings.sdk.editor.ccl.image.select=Please Select CCL Memory Image slt.ui.settings.sdk.editor.allegro.image.select=Please Select Allegro CL Memory Image +slt.ui.settings.sdk.editor.cmucl.image.select=Please Select CMUCL Memory Image slt.ui.settings.sdk.editor.quicklisp=Quicklisp path slt.ui.settings.sdk.editor.jvm.select=Please Select Java Executable slt.ui.settings.sdk.editor.abcl.select=Please Select ABCL Jar File @@ -156,14 +163,18 @@ slt.ui.settings.sdk.editor.sbcl.process.notverified.message=Please Verify SBCL S slt.ui.settings.sdk.editor.abcl.process.notverified.message=Please Verify ABCL SDK slt.ui.settings.sdk.editor.ccl.process.notverified.message=Please Verify CCL SDK slt.ui.settings.sdk.editor.allegro.process.notverified.message=Please Verify Allegro CL SDK +slt.ui.settings.sdk.editor.cmucl.process.notverified.message=Please Verify CMUCL SDK slt.ui.settings.sdk.editor.verifying.sbcl=Verifying SBCL SDK slt.ui.settings.sdk.editor.verifying.abcl=Verifying ABCL SDK slt.ui.settings.sdk.editor.verifying.ccl=Verifying CCL SDK +slt.ui.settings.sdk.editor.verifying.allegro=Verifying Allegro CL SDK +slt.ui.settings.sdk.editor.verifying.cmucl=Verifying CMUCL SDK slt.ui.settings.sdk.editor.verifying.cancel=Cancel Verification slt.ui.settings.sdk.editor.sbcl.process.verifying.error=Failed to verify SBCL installation slt.ui.settings.sdk.editor.abcl.process.verifying.error=Failed to verify ABCL installation slt.ui.settings.sdk.editor.ccl.process.verifying.error=Failed to verify CCL installation slt.ui.settings.sdk.editor.allegro.process.verifying.error=Failed to verify Allegro CL installation +slt.ui.settings.sdk.editor.cmucl.process.verifying.error=Failed to verify CMUCL installation slt.ui.settings.sdk.editor.verifying.error.title=Failed slt.ui.settings.sdk.download.sbcl.downloading=Downloading SBCL slt.ui.settings.sdk.download.cancel=Cancel From 4a1f3e20bc4748efbbdb1658f2706bc508c1776d Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Thu, 9 Feb 2023 23:58:26 +0100 Subject: [PATCH 26/40] testing for arglist, all passes --- FEATURES.md | 1 + README.md | 1 - .../slt/plugin/environment/LispFeatures.java | 3 ++- .../abcl/ABCLEnvironmentDefinition.java | 1 + .../AllegroCLEnvironmentDefinition.java | 1 + .../ccl/CCLEnvironmentDefinition.java | 1 + .../cmucl/CMUCLEnvironmentDefinition.java | 1 + .../sbcl/SBCLEnvironmentDefinition.java | 1 + .../params/SltParameterInfoHandler.java | 25 +++---------------- .../lisp/LispEnvironmentServiceImpl.java | 2 +- 10 files changed, 12 insertions(+), 25 deletions(-) diff --git a/FEATURES.md b/FEATURES.md index 5afb947..7bad6be 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -27,6 +27,7 @@ Unsupported and will not be supported implementations: | Inspector | ✅¹ | ✅² | ✅️¹ | ✅️¹ | ✅️¹ | | Autocomplete | ✅ | ✅ | ✅ | ✅ | ✅ | | Find References | ✅ | ❎ | ✅️ | ❎ | ❎ | +| Function Arguments | ✅ | ✅ | ✅️ | ✅️ | ✅️ | | Automatic Download - Windows | ✅ | ❓ | ❓ | ❎ | ❓ | ¹Only read-only inspector available diff --git a/README.md b/README.md index 523e897..67f0d14 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,6 @@ You can also open this as a project in Intellij Idea. * [ ] Inspection eval * [ ] Walkable debugger without actions * [ ] Breakpoints - * Currently impossible to do correctly with sbcl, investigating other options * [x] Documentation * [x] Macro expand in documentation * Macro expand requires you to hover element twice for now diff --git a/src/main/java/com/en_circle/slt/plugin/environment/LispFeatures.java b/src/main/java/com/en_circle/slt/plugin/environment/LispFeatures.java index 5d97a4f..d9a7cfe 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/LispFeatures.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/LispFeatures.java @@ -11,6 +11,7 @@ public enum LispFeatures { INSPECTOR_HISTORY, AUTOCOMPLETE, SEARCH, - XREFS + XREFS, + FUNC_ARGS, } diff --git a/src/main/java/com/en_circle/slt/plugin/environment/abcl/ABCLEnvironmentDefinition.java b/src/main/java/com/en_circle/slt/plugin/environment/abcl/ABCLEnvironmentDefinition.java index 7d8b951..0919b4f 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/abcl/ABCLEnvironmentDefinition.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/abcl/ABCLEnvironmentDefinition.java @@ -25,6 +25,7 @@ public ABCLEnvironmentDefinition() { features.add(LispFeatures.INSPECTOR); features.add(LispFeatures.DEBUGGER_ACTION_ARGLIST); features.add(LispFeatures.AUTOCOMPLETE); + features.add(LispFeatures.FUNC_ARGS); } @Override diff --git a/src/main/java/com/en_circle/slt/plugin/environment/allegro/AllegroCLEnvironmentDefinition.java b/src/main/java/com/en_circle/slt/plugin/environment/allegro/AllegroCLEnvironmentDefinition.java index 4ac36eb..b786ad1 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/allegro/AllegroCLEnvironmentDefinition.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/allegro/AllegroCLEnvironmentDefinition.java @@ -28,6 +28,7 @@ public AllegroCLEnvironmentDefinition() { features.add(LispFeatures.INSPECTOR_HISTORY); features.add(LispFeatures.AUTOCOMPLETE); features.add(LispFeatures.XREFS); + features.add(LispFeatures.FUNC_ARGS); } @Override diff --git a/src/main/java/com/en_circle/slt/plugin/environment/ccl/CCLEnvironmentDefinition.java b/src/main/java/com/en_circle/slt/plugin/environment/ccl/CCLEnvironmentDefinition.java index 76a82a7..7f3a25c 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/ccl/CCLEnvironmentDefinition.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/ccl/CCLEnvironmentDefinition.java @@ -28,6 +28,7 @@ public CCLEnvironmentDefinition() { features.add(LispFeatures.AUTOCOMPLETE); features.add(LispFeatures.SEARCH); features.add(LispFeatures.XREFS); + features.add(LispFeatures.FUNC_ARGS); } @Override diff --git a/src/main/java/com/en_circle/slt/plugin/environment/cmucl/CMUCLEnvironmentDefinition.java b/src/main/java/com/en_circle/slt/plugin/environment/cmucl/CMUCLEnvironmentDefinition.java index 9c0a9d0..74c90a2 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/cmucl/CMUCLEnvironmentDefinition.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/cmucl/CMUCLEnvironmentDefinition.java @@ -27,6 +27,7 @@ public CMUCLEnvironmentDefinition() { features.add(LispFeatures.INSPECTOR); features.add(LispFeatures.INSPECTOR_HISTORY); features.add(LispFeatures.AUTOCOMPLETE); + features.add(LispFeatures.FUNC_ARGS); } @Override diff --git a/src/main/java/com/en_circle/slt/plugin/environment/sbcl/SBCLEnvironmentDefinition.java b/src/main/java/com/en_circle/slt/plugin/environment/sbcl/SBCLEnvironmentDefinition.java index 2841014..c01c9d5 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/sbcl/SBCLEnvironmentDefinition.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/sbcl/SBCLEnvironmentDefinition.java @@ -30,6 +30,7 @@ public SBCLEnvironmentDefinition() { features.add(LispFeatures.AUTOCOMPLETE); features.add(LispFeatures.SEARCH); features.add(LispFeatures.XREFS); + features.add(LispFeatures.FUNC_ARGS); } @Override diff --git a/src/main/java/com/en_circle/slt/plugin/params/SltParameterInfoHandler.java b/src/main/java/com/en_circle/slt/plugin/params/SltParameterInfoHandler.java index 5aa0189..316ebc7 100644 --- a/src/main/java/com/en_circle/slt/plugin/params/SltParameterInfoHandler.java +++ b/src/main/java/com/en_circle/slt/plugin/params/SltParameterInfoHandler.java @@ -1,5 +1,6 @@ package com.en_circle.slt.plugin.params; +import com.en_circle.slt.plugin.environment.LispFeatures; import com.en_circle.slt.plugin.lisp.LispParserUtil; import com.en_circle.slt.plugin.lisp.LispParserUtil.QuoteState; import com.en_circle.slt.plugin.lisp.psi.LispList; @@ -26,7 +27,8 @@ public class SltParameterInfoHandler implements ParameterInfoHandler arguments = new ArrayList<>(); -// QuoteState quoteState = LispParserUtil.getQuoteState(o); -// if (quoteState == QuoteState.ERROR_STATE || quoteState == QuoteState.NO_STATE) { -// for (int i=1; i Date: Fri, 10 Feb 2023 08:04:34 +0100 Subject: [PATCH 27/40] fixed unnecessary string --- .../en_circle/slt/plugin/lisp/lisp/LispElement.java | 10 ++++++++++ .../plugin/swank/debug/SltDebugStackTraceElement.java | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/en_circle/slt/plugin/lisp/lisp/LispElement.java b/src/main/java/com/en_circle/slt/plugin/lisp/lisp/LispElement.java index a5ad71b..af57480 100644 --- a/src/main/java/com/en_circle/slt/plugin/lisp/lisp/LispElement.java +++ b/src/main/java/com/en_circle/slt/plugin/lisp/lisp/LispElement.java @@ -1,10 +1,20 @@ package com.en_circle.slt.plugin.lisp.lisp; +import java.util.Objects; + public interface LispElement { LispElementType getType(); + default String toPrettyString() { return toString(); } + default String nativeString() { + if (this instanceof LispAtom atom) { + return Objects.toString(atom.getValue()); + } else { + return toString(); + } + } } diff --git a/src/main/java/com/en_circle/slt/plugin/swank/debug/SltDebugStackTraceElement.java b/src/main/java/com/en_circle/slt/plugin/swank/debug/SltDebugStackTraceElement.java index c1c990d..47cd9ff 100644 --- a/src/main/java/com/en_circle/slt/plugin/swank/debug/SltDebugStackTraceElement.java +++ b/src/main/java/com/en_circle/slt/plugin/swank/debug/SltDebugStackTraceElement.java @@ -14,7 +14,7 @@ public class SltDebugStackTraceElement { private final SourceLocation location; public SltDebugStackTraceElement(LispContainer source) { - String text = source.getItems().get(2).toString(); + String text = source.getItems().get(2).nativeString(); this.line = LispUtils.unescape(text); text = source.getItems().get(1).toString(); this.detailedCall = LispUtils.unescape(text); From cba2cb2d86935c487c0ed7690f47a39002ad7ba4 Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Fri, 10 Feb 2023 11:15:07 +0100 Subject: [PATCH 28/40] #46 --- FEATURES.md | 7 +- README.md | 20 +++- .../AllegroCLEnvironmentDefinition.java | 1 + .../ccl/CCLEnvironmentDefinition.java | 2 - .../sbcl/SBCLEnvironmentDefinition.java | 1 - .../services/lisp/LispEnvironmentService.java | 2 + .../lisp/LispEnvironmentServiceImpl.java | 25 +++- .../lisp/components/SltBreakpoint.java | 32 +---- .../components/SltBreakpointContainer.java | 68 ++++------- .../SltLispEnvironmentSymbolCache.java | 2 +- .../slt/plugin/swank/SlimeListener.java | 2 +- .../slt/plugin/swank/SlimeRequest.java | 4 +- .../slt/plugin/swank/SwankPacket.java | 113 +++++++++++------- .../slt/plugin/swank/requests/Argslist.java | 3 +- .../plugin/swank/requests/CompleteSearch.java | 3 +- .../slt/plugin/swank/requests/Eval.java | 23 +++- .../plugin/swank/requests/EvalAndGrab.java | 20 ++-- .../swank/requests/EvalFromVirtualFile.java | 7 +- .../swank/requests/EvalStringInFrameEval.java | 9 +- .../requests/FrameLocalsAndCatchTags.java | 3 +- .../swank/requests/InspectFrameVar.java | 3 +- .../slt/plugin/swank/requests/InspectNth.java | 3 +- .../swank/requests/InspectorAction.java | 3 +- .../swank/requests/InvokeNthRestart.java | 3 +- .../slt/plugin/swank/requests/KillThread.java | 3 +- .../plugin/swank/requests/ListThreads.java | 3 +- .../slt/plugin/swank/requests/LoadFile.java | 7 +- .../plugin/swank/requests/MacroexpandAll.java | 3 +- .../swank/requests/SimpleCompletion.java | 3 +- .../plugin/swank/requests/StepperAction.java | 3 +- .../plugin/swank/requests/SuspendThread.java | 3 +- .../swank/requests/ThrowToToplevel.java | 3 +- .../slt/plugin/swank/requests/Xrefs.java | 3 +- .../plugin/ui/PackageSelectorComponent.java | 2 +- .../slt/plugin/ui/SltGeneralLog.java | 3 - .../ui/debug/SltBreakpointProperties.java | 8 -- .../slt/plugin/ui/debug/SltDebuggerImpl.java | 42 +++---- .../ui/debug/SltSymbolBreakpointType.java | 46 ++----- src/main/lisp/load.lisp | 7 ++ src/main/lisp/slt/slt-abcl.lisp | 12 +- src/main/lisp/slt/slt-allegro.lisp | 17 +++ src/main/lisp/slt/slt-ccl.lisp | 14 ++- src/main/lisp/slt/slt-cmucl.lisp | 17 +++ src/main/lisp/slt/slt-sbcl.lisp | 14 ++- src/main/lisp/slt/slt.lisp | 9 +- src/main/lisp/swank/swank-allegro.lisp | 5 + src/main/lisp/swank/swank-cmucl.lisp | 50 ++++---- src/main/lisp/swank/swank.lisp | 10 +- .../templates/en_US/initscript.allegro.cl | 1 + .../resources/templates/en_US/initscript.cl | 1 + .../resources/templates/en_US/verify.abcl.cl | 1 + src/main/resources/templates/en_US/verify.cl | 1 + src/test/java/SlimeTest.java | 2 +- 53 files changed, 373 insertions(+), 279 deletions(-) diff --git a/FEATURES.md b/FEATURES.md index 7bad6be..784383e 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -22,7 +22,8 @@ Unsupported and will not be supported implementations: | Debugger | ✅ | ✅ | ✅️ | ✅ | ✅ | | Debugger Actions | ✅ | ✅ | ✅️³️ | ✅⁴ | ✅ | | Frame REPL | ✅ | ❎ | ✅️ | ✅ | ✅ | -| Stepping Debugger | ❎ | ❎ | ❎ | ✅ | ✅ | +| Breakpoints | ❎ | ❎ | ❎ | ✅ | ❎ | +| Stepping Debugger⁵ | ❎ | ❎ | ❎ | ❎ | ❎ | | References | ✅ | ❎ | ✅️ | ✅️ | ❎ | | Inspector | ✅¹ | ✅² | ✅️¹ | ✅️¹ | ✅️¹ | | Autocomplete | ✅ | ✅ | ✅ | ✅ | ✅ | @@ -41,4 +42,6 @@ option, so you have to decide. FML for ansi common lisp not having ansi way to g arguments because fuck you that's why. ⁴Allegro CL restarts have correct arglists so actions work but for some reason all restarts from SWANK have arguments, -event abort ones... \ No newline at end of file +event abort ones... + +⁵No implementation in Slime supports this. \ No newline at end of file diff --git a/README.md b/README.md index 67f0d14..0e0f791 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![0.1.0](https://badgen.net/github/milestones/enerccio/SLT/1)](https://github.com/enerccio/SLT/milestone/1) [![0.2.0](https://badgen.net/github/milestones/enerccio/SLT/2)](https://github.com/enerccio/SLT/milestone/2) [![0.3.0](https://badgen.net/github/milestones/enerccio/SLT/4)](https://github.com/enerccio/SLT/milestone/4) -[![0.3.0](https://badgen.net/github/milestones/enerccio/SLT/5)](https://github.com/enerccio/SLT/milestone/5) +[![0.4.0](https://badgen.net/github/milestones/enerccio/SLT/5)](https://github.com/enerccio/SLT/milestone/5) ![GitHub all releases](https://img.shields.io/github/downloads/Enerccio/SLT/total) ![GitHub last commit](https://img.shields.io/github/last-commit/Enerccio/SLT) ![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/Enerccio/SLT) @@ -58,12 +58,11 @@ You can also open this as a project in Intellij Idea. * [x] REPL * [x] Interactive debugging * [x] Argument help (Ctrl+P) -* [ ] Inspection +* [x] Inspection * [x] Basic inspection * [ ] Actions * [ ] Inspection eval -* [ ] Walkable debugger without actions - * [ ] Breakpoints +* [x] Breakpoints * [x] Documentation * [x] Macro expand in documentation * Macro expand requires you to hover element twice for now @@ -90,7 +89,18 @@ this is implemented manually. This project is licensed under [Apache License v2](LICENSE.txt). -### What does SLT even mean? +# How to help? + +* Please install the plugin and try it out! +* Report all bugs so I can catch them all. Click [here](https://github.com/enerccio/SLT/issues/new) to report. +* If you like the project, consider starring it or spreading info about it +* This project is OSS so if you have knowledge and want to implement something you can fork the repo and then create PR! + +## Donations + +If you feel like it, you can donate to support my work via [Liberapay](https://liberapay.com/Enerccio/donate). + +# What does SLT even mean? SLT - Speech Language Therapy. Only cure for LISP! diff --git a/src/main/java/com/en_circle/slt/plugin/environment/allegro/AllegroCLEnvironmentDefinition.java b/src/main/java/com/en_circle/slt/plugin/environment/allegro/AllegroCLEnvironmentDefinition.java index b786ad1..ab5fa8d 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/allegro/AllegroCLEnvironmentDefinition.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/allegro/AllegroCLEnvironmentDefinition.java @@ -23,6 +23,7 @@ public AllegroCLEnvironmentDefinition() { features.add(LispFeatures.DOCUMENTATION); features.add(LispFeatures.MACROEXPAND); features.add(LispFeatures.DEBUGGER_ACTION_ARGLIST); + features.add(LispFeatures.BREAKPOINTS); features.add(LispFeatures.FRAME_EVAL); features.add(LispFeatures.INSPECTOR); features.add(LispFeatures.INSPECTOR_HISTORY); diff --git a/src/main/java/com/en_circle/slt/plugin/environment/ccl/CCLEnvironmentDefinition.java b/src/main/java/com/en_circle/slt/plugin/environment/ccl/CCLEnvironmentDefinition.java index a3a1033..7f3a25c 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/ccl/CCLEnvironmentDefinition.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/ccl/CCLEnvironmentDefinition.java @@ -23,8 +23,6 @@ public CCLEnvironmentDefinition() { features.add(LispFeatures.DOCUMENTATION); features.add(LispFeatures.MACROEXPAND); features.add(LispFeatures.FRAME_EVAL); - features.add(LispFeatures.BREAKPOINTS); - features.add(LispFeatures.BREAKPOINT_STEPPING); features.add(LispFeatures.INSPECTOR); features.add(LispFeatures.INSPECTOR_HISTORY); features.add(LispFeatures.AUTOCOMPLETE); diff --git a/src/main/java/com/en_circle/slt/plugin/environment/sbcl/SBCLEnvironmentDefinition.java b/src/main/java/com/en_circle/slt/plugin/environment/sbcl/SBCLEnvironmentDefinition.java index cb3fcac..c01c9d5 100644 --- a/src/main/java/com/en_circle/slt/plugin/environment/sbcl/SBCLEnvironmentDefinition.java +++ b/src/main/java/com/en_circle/slt/plugin/environment/sbcl/SBCLEnvironmentDefinition.java @@ -25,7 +25,6 @@ public SBCLEnvironmentDefinition() { features.add(LispFeatures.MACROEXPAND); features.add(LispFeatures.FRAME_EVAL); features.add(LispFeatures.DEBUGGER_ACTION_ARGLIST); - features.add(LispFeatures.BREAKPOINTS); features.add(LispFeatures.INSPECTOR); features.add(LispFeatures.INSPECTOR_HISTORY); features.add(LispFeatures.AUTOCOMPLETE); diff --git a/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentService.java b/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentService.java index 5a8a71c..c9400f1 100644 --- a/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentService.java +++ b/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentService.java @@ -73,6 +73,8 @@ static LispEnvironmentService getInstance(Project project) { boolean hasFeature(LispFeatures feature); + String getBreakpointsForInstall(); + enum LispEnvironmentState { STOPPED, READY, INITIALIZING } diff --git a/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentServiceImpl.java b/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentServiceImpl.java index 3a239f1..47f07f6 100644 --- a/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentServiceImpl.java +++ b/src/main/java/com/en_circle/slt/plugin/services/lisp/LispEnvironmentServiceImpl.java @@ -12,20 +12,17 @@ import com.en_circle.slt.plugin.sdk.LispProjectSdk; import com.en_circle.slt.plugin.sdk.LispSdk; import com.en_circle.slt.plugin.sdk.SdkList; -import com.en_circle.slt.plugin.services.lisp.components.SltIndentationContainer; -import com.en_circle.slt.plugin.services.lisp.components.SltLispEnvironmentMacroExpandCache; -import com.en_circle.slt.plugin.services.lisp.components.SltLispEnvironmentSymbolCache; -import com.en_circle.slt.plugin.services.lisp.components.SltLispEnvironmentSymbolCache.BatchedSymbolRefreshAction; import com.en_circle.slt.plugin.services.lisp.components.*; +import com.en_circle.slt.plugin.services.lisp.components.SltLispEnvironmentSymbolCache.BatchedSymbolRefreshAction; import com.en_circle.slt.plugin.swank.SlimeListener; import com.en_circle.slt.plugin.swank.SlimeListener.DebugInterface; import com.en_circle.slt.plugin.swank.SlimeListener.RequestResponseLogger; import com.en_circle.slt.plugin.swank.SlimeRequest; import com.en_circle.slt.plugin.swank.SwankClient; +import com.en_circle.slt.plugin.ui.debug.SltBreakpointProperties; +import com.en_circle.slt.plugin.ui.debug.SltSymbolBreakpointType; import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; import com.intellij.codeInsight.hints.ParameterHintsPassFactory; -import com.en_circle.slt.plugin.ui.debug.SltBreakpointProperties; -import com.en_circle.slt.tools.ProjectUtils; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; @@ -34,7 +31,10 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.util.ExceptionUtil; +import com.intellij.xdebugger.XDebuggerManager; import com.intellij.xdebugger.breakpoints.XBreakpoint; +import com.intellij.xdebugger.breakpoints.XBreakpointManager; +import com.intellij.xdebugger.breakpoints.XLineBreakpoint; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -201,6 +201,12 @@ private boolean doStart() throws Exception { ApplicationManager.getApplication().invokeLaterOnWriteThread(() -> { ParameterHintsPassFactory.forceHintsUpdateOnNextPass(); DaemonCodeAnalyzer.getInstance(project).restart(); + + XBreakpointManager breakpointManager = XDebuggerManager.getInstance(project).getBreakpointManager(); + for (XLineBreakpoint breakpoint : + breakpointManager.getBreakpoints(SltSymbolBreakpointType.class)) { + addBreakpoint(breakpoint); + } }); } } finally { @@ -379,6 +385,13 @@ public boolean hasFeature(LispFeatures feature) { return false; } + @Override + public String getBreakpointsForInstall() { + if (hasFeature(LispFeatures.BREAKPOINTS)) + return breakpointContainer.getInstallBreakpoints(); + return null; + } + @Override public void dispose() { try { diff --git a/src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltBreakpoint.java b/src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltBreakpoint.java index da4d191..5ea308b 100644 --- a/src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltBreakpoint.java +++ b/src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltBreakpoint.java @@ -15,9 +15,6 @@ public class SltBreakpoint implements Comparable { Comparator.comparing(SltBreakpoint::getSymbol); private String symbol; - private SltBreakpointType type = SltBreakpointType.STANDARD; - - private boolean installed; private final Set> nativeBreakpoints = new HashSet<>(); @@ -25,26 +22,10 @@ public SltBreakpoint(String breakpointSymbol) { this.symbol = breakpointSymbol; } - public SltBreakpointType getType() { - return type; - } - - public void setType(SltBreakpointType type) { - this.type = type; - } - public Set> getNativeBreakpoints() { return nativeBreakpoints; } - public boolean isInstalled() { - return installed; - } - - public void setInstalled(boolean installed) { - this.installed = installed; - } - public String getSymbol() { return symbol; } @@ -65,15 +46,12 @@ public boolean equals(Object o) { SltBreakpoint that = (SltBreakpoint) o; - if (!Objects.equals(symbol, that.symbol)) return false; - return type == that.type; + return Objects.equals(symbol, that.symbol); } @Override public int hashCode() { - int result = symbol != null ? symbol.hashCode() : 0; - result = 31 * result + (type != null ? type.hashCode() : 0); - return result; + return symbol != null ? symbol.hashCode() : 0; } public boolean shouldBeInstalled() { @@ -89,10 +67,4 @@ public boolean shouldBeInstalled() { public int compareTo(@NotNull SltBreakpoint o) { return COMPARATOR.compare(this, o); } - - public enum SltBreakpointType { - - STANDARD, METHOD - - } } diff --git a/src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltBreakpointContainer.java b/src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltBreakpointContainer.java index f5e3314..dd3e8fa 100644 --- a/src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltBreakpointContainer.java +++ b/src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltBreakpointContainer.java @@ -11,6 +11,7 @@ import org.apache.commons.lang3.StringUtils; import java.util.Collection; +import java.util.Set; import java.util.TreeSet; public class SltBreakpointContainer implements LispEnvironmentListener { @@ -41,11 +42,8 @@ public void removeBreakpoint(XBreakpoint nativeBreakpoi breakpoint.getNativeBreakpoints().remove(nativeBreakpoint); if (breakpoint.getNativeBreakpoints().isEmpty()) { - if (breakpoint.isInstalled()) - uninstallBreakpoint(breakpoint); - breakpoints.remove(breakpoint); - } else { uninstallBreakpoint(breakpoint); + breakpoints.remove(breakpoint); } } @@ -80,14 +78,8 @@ private SltBreakpoint search(SltBreakpoint key) { } private void updateBreakpoint(SltBreakpoint breakpoint) { - if (breakpoint.isInstalled()) { - if (!breakpoint.shouldBeInstalled()) { - uninstallBreakpoint(breakpoint); - } - } else { - if (breakpoint.shouldBeInstalled()) { - installBreakpoint(breakpoint); - } + if (breakpoint.shouldBeInstalled()) { + installBreakpoint(breakpoint); } } @@ -101,22 +93,8 @@ private void installBreakpoint(SltBreakpoint breakpoint) { } private SlimeRequest installRequest(SltBreakpoint breakpoint) { - switch (breakpoint.getType()) { - case STANDARD -> { - return Eval.eval("(slt-core:install-breakpoint '" + breakpoint.getSymbol() + ")", r -> installResult(r, breakpoint)); - } - case METHOD -> { - // TODO - return null; - } - } - return null; - } - - private void installResult(String s, SltBreakpoint breakpoint) { - if ("T".equalsIgnoreCase(s)) { - breakpoint.setInstalled(true); - } + return Eval.eval("(slt-core:install-breakpoint \"" + breakpoint.getSymbol() + "\")", + false, r -> {}); } private void uninstallBreakpoint(SltBreakpoint breakpoint) { @@ -129,22 +107,8 @@ private void uninstallBreakpoint(SltBreakpoint breakpoint) { } private SlimeRequest uninstallRequest(SltBreakpoint breakpoint) { - switch (breakpoint.getType()) { - case STANDARD -> { - return Eval.eval("(slt-core:uninstall-breakpoint '" + breakpoint.getSymbol() + ")", r -> uninstallResult(r, breakpoint)); - } - case METHOD -> { - // TODO - return null; - } - } - return null; - } - - private void uninstallResult(String s, SltBreakpoint breakpoint) { - if ("T".equalsIgnoreCase(s)) { - breakpoint.setInstalled(false); - } + return Eval.eval("(slt-core:uninstall-breakpoint '" + breakpoint.getSymbol() + ")", + false, r -> {}); } public Collection getAllBreakpoints() { @@ -170,9 +134,7 @@ public void onPostStart() { @Override public void onPreStop() { - for (SltBreakpoint breakpoint : breakpoints) { - breakpoint.setInstalled(false); - } + } @Override @@ -180,4 +142,16 @@ public void onPostStop() { } + public String getInstallBreakpoints() { + Set breakpointsToInstall = new TreeSet<>(); + for (SltBreakpoint breakpoint : breakpoints) { + if (breakpoint.shouldBeInstalled()) { + breakpointsToInstall.add(breakpoint.getSymbol()); + } + } + if (breakpointsToInstall.isEmpty()) { + return null; + } + return String.join(" ", breakpointsToInstall); + } } diff --git a/src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltLispEnvironmentSymbolCache.java b/src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltLispEnvironmentSymbolCache.java index 5578ec7..7270c6d 100644 --- a/src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltLispEnvironmentSymbolCache.java +++ b/src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltLispEnvironmentSymbolCache.java @@ -165,7 +165,7 @@ private void refreshSymbolsBatched(List refreshStates, Consumer { + LispEnvironmentService.getInstance(project).getGlobalPackage(), false, true, (result, stdout, parsed) -> { if (parsed.size() == 1 && parsed.get(0).getType() == LispElementType.CONTAINER) { int ix = 0; LispContainer data = (LispContainer) parsed.get(0); diff --git a/src/main/java/com/en_circle/slt/plugin/swank/SlimeListener.java b/src/main/java/com/en_circle/slt/plugin/swank/SlimeListener.java index 2b93892..cea5581 100644 --- a/src/main/java/com/en_circle/slt/plugin/swank/SlimeListener.java +++ b/src/main/java/com/en_circle/slt/plugin/swank/SlimeListener.java @@ -45,7 +45,7 @@ public SlimeListener(Project project, boolean fromUi, Consumer onRead public void call(SlimeRequest request, SwankClient client) { BigInteger requestId = request.getRequestId() == null ? nextRpc() : request.getRequestId(); requests.put(requestId, request); - SwankPacket packet = request.createPacket(requestId); + SwankPacket packet = request.createPacket(requestId, project); if (logger != null) { logger.logRequest(packet.getSentData()); } diff --git a/src/main/java/com/en_circle/slt/plugin/swank/SlimeRequest.java b/src/main/java/com/en_circle/slt/plugin/swank/SlimeRequest.java index 5e4b568..d32d07c 100644 --- a/src/main/java/com/en_circle/slt/plugin/swank/SlimeRequest.java +++ b/src/main/java/com/en_circle/slt/plugin/swank/SlimeRequest.java @@ -1,10 +1,12 @@ package com.en_circle.slt.plugin.swank; +import com.intellij.openapi.project.Project; + import java.math.BigInteger; public abstract class SlimeRequest { - public abstract SwankPacket createPacket(BigInteger requestId); + public abstract SwankPacket createPacket(BigInteger requestId, Project project); public BigInteger getRequestId() { return null; diff --git a/src/main/java/com/en_circle/slt/plugin/swank/SwankPacket.java b/src/main/java/com/en_circle/slt/plugin/swank/SwankPacket.java index e46b9f7..b96b8c6 100644 --- a/src/main/java/com/en_circle/slt/plugin/swank/SwankPacket.java +++ b/src/main/java/com/en_circle/slt/plugin/swank/SwankPacket.java @@ -39,88 +39,112 @@ public static SwankPacket rpcWriteString(String sexpression) { return new SwankPacket(formatted); } - public static SwankPacket sltEval(String sexpression, BigInteger continuation) { - return sltEval(sexpression, ":CL-USER", continuation); + public static SwankPacket sltEval(String sexpression, String breakpoints, BigInteger continuation) { + return sltEval(sexpression, breakpoints, ":CL-USER", continuation); } - public static SwankPacket sltEval(String sexpression, String packageName, BigInteger continuation) { - return sltEval(sexpression, packageName, "T", continuation); + public static SwankPacket sltEval(String sexpression, String breakpoints, String packageName, BigInteger continuation) { + return sltEval(sexpression, breakpoints, packageName, "T", continuation); } - public static SwankPacket sltEval(String sexpression, String packageName, String thread, BigInteger continuation) { + public static SwankPacket sltEval(String sexpression, String breakpoints, + String packageName, String thread, BigInteger continuation) { + String evalExpression = sexpression; + if (breakpoints != null) { + evalExpression = String.format("(progn (slt-core::with-breakpoints \"%s\") %s)", breakpoints, sexpression); + } packageName = StringUtils.replace(packageName, "\\", "\\\\"); packageName = StringUtils.replace(packageName, "\"", "\\\""); - sexpression = StringUtils.replace(sexpression, "\\", "\\\\"); - sexpression = StringUtils.replace(sexpression, "\"", "\\\""); + evalExpression = StringUtils.replace(evalExpression, "\\", "\\\\"); + evalExpression = StringUtils.replace(evalExpression, "\"", "\\\""); String formatted = String.format("(:emacs-rex (swank:slt-eval \"%s\") \":%s\" %s %s)", - sexpression, packageName, thread, continuation); + evalExpression, packageName, thread, continuation); return new SwankPacket(formatted); } - public static SwankPacket evalInFrame(String sexpression, BigInteger frame, String packageName, BigInteger thread, BigInteger continuation) { + public static SwankPacket evalInFrame(String sexpression, String breakpoints, BigInteger frame, String packageName, BigInteger thread, BigInteger continuation) { + String evalExpression = sexpression; + if (breakpoints != null) { + evalExpression = String.format("(progn (slt-core::with-breakpoints \"%s\") %s)", breakpoints, sexpression); + } packageName = StringUtils.replace(packageName, "\\", "\\\\"); packageName = StringUtils.replace(packageName, "\"", "\\\""); - sexpression = StringUtils.replace(sexpression, "\\", "\\\\"); - sexpression = StringUtils.replace(sexpression, "\"", "\\\""); + evalExpression = StringUtils.replace(evalExpression, "\\", "\\\\"); + evalExpression = StringUtils.replace(evalExpression, "\"", "\\\""); String formatted = String.format("(:emacs-rex (swank:eval-string-in-frame \"%s\" %s \"%s\") \":%s\" %s %s)", - sexpression, frame, packageName, packageName, thread, continuation); + evalExpression, frame, packageName, packageName, thread, continuation); return new SwankPacket(formatted); } - public static SwankPacket evalRegion(String region, BigInteger continuation) { - return evalRegion(region, ":CL-USER", "T", continuation); + public static SwankPacket evalRegion(String region, String breakpoints, BigInteger continuation) { + return evalRegion(region, breakpoints, ":CL-USER", "T", continuation); } - public static SwankPacket evalRegion(String region, String packageName, BigInteger continuation) { - return evalRegion(region, packageName, "T", continuation); + public static SwankPacket evalRegion(String region, String breakpoints, String packageName, BigInteger continuation) { + return evalRegion(region, breakpoints, packageName, "T", continuation); } - public static SwankPacket evalRegion(String region, String packageName, String thread, BigInteger continuation) { + public static SwankPacket evalRegion(String region, String breakpoints, String packageName, String thread, BigInteger continuation) { + String evalRegion = region; + if (breakpoints != null) { + evalRegion = String.format("(progn (slt-core::with-breakpoints \"%s\") %s)", breakpoints, region); + } packageName = StringUtils.replace(packageName, "\\", "\\\\"); packageName = StringUtils.replace(packageName, "\"", "\\\""); - region = StringUtils.replace(region, "\\", "\\\\"); - region = StringUtils.replace(region, "\"", "\\\""); + evalRegion = StringUtils.replace(evalRegion, "\\", "\\\\"); + evalRegion = StringUtils.replace(evalRegion, "\"", "\\\""); String formatted = String.format("(:emacs-rex (swank:interactive-eval-region \"%s\") \":%s\" %s %s)", - region, packageName, thread, continuation); + evalRegion, packageName, thread, continuation); return new SwankPacket(formatted); } - public static SwankPacket swankEvalAndGrab(String sexpression, BigInteger continuation) { - return swankEvalAndGrab(sexpression, ":CL-USER", continuation); + public static SwankPacket swankEvalAndGrab(String sexpression, String breakpoints, BigInteger continuation) { + return swankEvalAndGrab(sexpression, breakpoints, ":CL-USER", continuation); } - public static SwankPacket swankEvalAndGrab(String sexpression, String packageName, BigInteger continuation) { - return swankEvalAndGrab(sexpression, packageName, "T", continuation); + public static SwankPacket swankEvalAndGrab(String sexpression, String breakpoints, String packageName, BigInteger continuation) { + return swankEvalAndGrab(sexpression, breakpoints, packageName, "T", continuation); } - public static SwankPacket swankEvalAndGrab(String sexpression, String packageName, String thread, BigInteger continuation) { + public static SwankPacket swankEvalAndGrab(String sexpression, String breakpoints, String packageName, String thread, BigInteger continuation) { + String evalExpression = sexpression; + if (breakpoints != null) { + evalExpression = String.format("(progn (slt-core::with-breakpoints \"%s\") %s)", breakpoints, sexpression); + } packageName = StringUtils.replace(packageName, "\\", "\\\\"); packageName = StringUtils.replace(packageName, "\"", "\\\""); - sexpression = StringUtils.replace(sexpression, "\\", "\\\\"); - sexpression = StringUtils.replace(sexpression, "\"", "\\\""); + evalExpression = StringUtils.replace(evalExpression, "\\", "\\\\"); + evalExpression = StringUtils.replace(evalExpression, "\"", "\\\""); String formatted = String.format("(:emacs-rex (swank:eval-and-grab-output \"%s\") \":%s\" %s %s)", - sexpression, packageName, thread, continuation); + evalExpression, packageName, thread, continuation); return new SwankPacket(formatted); } - public static SwankPacket swankEvalRegion(String code, String filename, int bufferPosition, BigInteger continuation) { - return swankEvalRegion(code, filename, bufferPosition, ":CL-USER", continuation); + public static SwankPacket swankEvalRegion(String code, String breakpoints, String filename, int bufferPosition, BigInteger continuation) { + return swankEvalRegion(code, breakpoints, filename, bufferPosition, ":CL-USER", continuation); } - public static SwankPacket swankEvalRegion(String code, String filename, int bufferPosition, String packageName, BigInteger continuation) { - return swankEvalRegion(code, filename, bufferPosition, packageName, "T", continuation); + public static SwankPacket swankEvalRegion(String code, String breakpoints, String filename, int bufferPosition, String packageName, BigInteger continuation) { + return swankEvalRegion(code, breakpoints, filename, bufferPosition, packageName, "T", continuation); } - public static SwankPacket swankEvalRegion(String code, String filename, int bufferPosition, + public static SwankPacket swankEvalRegion(String code, String breakpoints, String filename, int bufferPosition, String packageName, String thread, BigInteger continuation) { + if (breakpoints == null) { + breakpoints = "NIL"; + } else { + breakpoints = "\"" + breakpoints + "\""; + } packageName = StringUtils.replace(packageName, "\\", "\\\\"); packageName = StringUtils.replace(packageName, "\"", "\\\""); code = StringUtils.replace(code, "\\", "\\\\"); code = StringUtils.replace(code, "\"", "\\\""); + breakpoints = StringUtils.replace(breakpoints, "\\", "\\\\"); + breakpoints = StringUtils.replace(breakpoints, "\"", "\\\""); filename = StringUtils.replace(filename, "\\", "\\\\"); filename = StringUtils.replace(filename, "\"", "\\\""); - String formatted = String.format("(:emacs-rex (swank:compile-string-region-slt \"%s\" \"%s\" %s \"%s\" :%s) \":%s\" %s %s)", - code, filename, bufferPosition, filename, packageName, packageName, thread, continuation); + String formatted = String.format("(:emacs-rex (swank:compile-string-region-slt \"%s\" %s \"%s\" %s \"%s\" :%s) \":%s\" %s %s)", + code, breakpoints, filename, bufferPosition, filename, packageName, packageName, thread, continuation); return new SwankPacket(formatted); } @@ -220,21 +244,26 @@ public static SwankPacket stepperNext(BigInteger threadId, String packageName, B return new SwankPacket(formatted); } - public static SwankPacket loadFile(String file, BigInteger continuation) { - return loadFile(file, "CL-USER", continuation); + public static SwankPacket loadFile(String file, String breakpoints, BigInteger continuation) { + return loadFile(file, breakpoints, "CL-USER", continuation); } - public static SwankPacket loadFile(String file, String packageName, BigInteger continuation) { - return loadFile(file, packageName, "T", continuation); + public static SwankPacket loadFile(String file, String breakpoints, String packageName, BigInteger continuation) { + return loadFile(file, breakpoints, packageName, "T", continuation); } - public static SwankPacket loadFile(String file, String packageName, String thread, BigInteger continuation) { + public static SwankPacket loadFile(String file, String breakpoints, String packageName, String thread, BigInteger continuation) { + if (breakpoints == null) { + breakpoints = "NIL"; + } else { + breakpoints = "\"" + breakpoints + "\""; + } packageName = StringUtils.replace(packageName, "\\", "\\\\"); packageName = StringUtils.replace(packageName, "\"", "\\\""); file = StringUtils.replace(file, "\\", "\\\\"); file = StringUtils.replace(file, "\"", "\\\""); - String formatted = String.format("(:emacs-rex (swank:load-file \"%s\") \":%s\" %s %s)", - file, packageName, thread, continuation); + String formatted = String.format("(:emacs-rex (swank:load-file-breakpoints \"%s\" %s) \":%s\" %s %s)", + file, breakpoints, packageName, thread, continuation); return new SwankPacket(formatted); } diff --git a/src/main/java/com/en_circle/slt/plugin/swank/requests/Argslist.java b/src/main/java/com/en_circle/slt/plugin/swank/requests/Argslist.java index 76b4a75..f2e30a0 100644 --- a/src/main/java/com/en_circle/slt/plugin/swank/requests/Argslist.java +++ b/src/main/java/com/en_circle/slt/plugin/swank/requests/Argslist.java @@ -5,6 +5,7 @@ import com.en_circle.slt.plugin.params.LispArgslist; import com.en_circle.slt.plugin.swank.SlimeRequest; import com.en_circle.slt.plugin.swank.SwankPacket; +import com.intellij.openapi.project.Project; import java.math.BigInteger; @@ -25,7 +26,7 @@ private Argslist(String symbol, String packageName, Callback callback) { } @Override - public SwankPacket createPacket(BigInteger requestId) { + public SwankPacket createPacket(BigInteger requestId, Project project) { return SwankPacket.argslist(symbol, packageName, requestId); } diff --git a/src/main/java/com/en_circle/slt/plugin/swank/requests/CompleteSearch.java b/src/main/java/com/en_circle/slt/plugin/swank/requests/CompleteSearch.java index c0e3f33..864e7ed 100644 --- a/src/main/java/com/en_circle/slt/plugin/swank/requests/CompleteSearch.java +++ b/src/main/java/com/en_circle/slt/plugin/swank/requests/CompleteSearch.java @@ -5,6 +5,7 @@ import com.en_circle.slt.plugin.lisp.lisp.LispSymbol; import com.en_circle.slt.plugin.swank.SlimeRequest; import com.en_circle.slt.plugin.swank.SwankPacket; +import com.intellij.openapi.project.Project; import java.math.BigInteger; @@ -29,7 +30,7 @@ private CompleteSearch(String prefix, SearchFilter searchFilter, Callback callba } @Override - public SwankPacket createPacket(BigInteger requestId) { + public SwankPacket createPacket(BigInteger requestId, Project project) { return SwankPacket.completeSearch(prefix, searchFilter == null ? "NIL" : searchFilter.str, requestId); } diff --git a/src/main/java/com/en_circle/slt/plugin/swank/requests/Eval.java b/src/main/java/com/en_circle/slt/plugin/swank/requests/Eval.java index 1eeb394..08913c4 100644 --- a/src/main/java/com/en_circle/slt/plugin/swank/requests/Eval.java +++ b/src/main/java/com/en_circle/slt/plugin/swank/requests/Eval.java @@ -3,27 +3,39 @@ import com.en_circle.slt.plugin.lisp.lisp.LispContainer; import com.en_circle.slt.plugin.lisp.lisp.LispString; import com.en_circle.slt.plugin.lisp.lisp.LispSymbol; +import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService; import com.en_circle.slt.plugin.swank.SlimeRequest; import com.en_circle.slt.plugin.swank.SwankPacket; +import com.intellij.openapi.project.Project; import java.math.BigInteger; public class Eval extends SlimeRequest { + public static SlimeRequest eval(String code, String module, boolean includeDebugger, Callback callback) { + return new Eval(code, module, includeDebugger, callback); + } + public static SlimeRequest eval(String code, String module, Callback callback) { - return new Eval(code, module, callback); + return eval(code, module, true, callback); + } + + public static SlimeRequest eval(String code, boolean includeDebugger, Callback callback) { + return eval(code, "cl-user", includeDebugger, callback); } public static SlimeRequest eval(String code, Callback callback) { - return new Eval(code, "cl-user", callback); + return eval(code, true, callback); } protected final Callback callback; + protected final boolean includeDebugger; protected final String module; protected final String code; - protected Eval(String code, String module, Callback callback) { + protected Eval(String code, String module, boolean includeDebugger, Callback callback) { this.callback = callback; + this.includeDebugger = includeDebugger; this.module = module; this.code = code; } @@ -42,8 +54,9 @@ private boolean isOk(LispContainer data) { } @Override - public SwankPacket createPacket(BigInteger requestId) { - return SwankPacket.sltEval(code, module, requestId); + public SwankPacket createPacket(BigInteger requestId, Project project) { + return SwankPacket.sltEval(code, includeDebugger ? LispEnvironmentService.getInstance(project) + .getBreakpointsForInstall() : null, module, requestId); } public interface Callback { diff --git a/src/main/java/com/en_circle/slt/plugin/swank/requests/EvalAndGrab.java b/src/main/java/com/en_circle/slt/plugin/swank/requests/EvalAndGrab.java index 19925f7..0db7f8b 100644 --- a/src/main/java/com/en_circle/slt/plugin/swank/requests/EvalAndGrab.java +++ b/src/main/java/com/en_circle/slt/plugin/swank/requests/EvalAndGrab.java @@ -4,8 +4,10 @@ import com.en_circle.slt.plugin.lisp.lisp.LispElement; import com.en_circle.slt.plugin.lisp.lisp.LispString; import com.en_circle.slt.plugin.lisp.lisp.LispSymbol; +import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService; import com.en_circle.slt.plugin.swank.SlimeRequest; import com.en_circle.slt.plugin.swank.SwankPacket; +import com.intellij.openapi.project.Project; import org.apache.commons.lang3.StringUtils; import java.math.BigInteger; @@ -14,24 +16,26 @@ public class EvalAndGrab extends SlimeRequest { - public static SlimeRequest eval(String code, String module, boolean parse, Callback callback) { - return new EvalAndGrab(code, module, parse, callback); + public static SlimeRequest eval(String code, String module, boolean includeDebugger, boolean parse, Callback callback) { + return new EvalAndGrab(code, module, includeDebugger, parse, callback); } - public static SlimeRequest eval(String code, boolean parse, Callback callback) { - return new EvalAndGrab(code, "cl-user", parse, callback); + public static SlimeRequest eval(String code, boolean includeDebugger, boolean parse, Callback callback) { + return new EvalAndGrab(code, "cl-user", includeDebugger, parse, callback); } protected final Callback callback; protected final String module; protected final String code; + protected final boolean includeDebugger; protected final boolean parse; - protected EvalAndGrab(String code, String module, boolean parse, Callback callback) { + protected EvalAndGrab(String code, String module, boolean includeDebugger, boolean parse, Callback callback) { this.callback = callback; this.module = module; this.code = code; this.parse = parse; + this.includeDebugger = includeDebugger; } public void processReply(LispContainer data, Function> parser) { @@ -56,8 +60,10 @@ private boolean isOk(LispContainer data) { } @Override - public SwankPacket createPacket(BigInteger requestId) { - return SwankPacket.swankEvalAndGrab(code, module, requestId); + public SwankPacket createPacket(BigInteger requestId, Project project) { + return SwankPacket.swankEvalAndGrab(code, + includeDebugger ? LispEnvironmentService.getInstance(project).getBreakpointsForInstall() : null, + module, requestId); } public interface Callback { diff --git a/src/main/java/com/en_circle/slt/plugin/swank/requests/EvalFromVirtualFile.java b/src/main/java/com/en_circle/slt/plugin/swank/requests/EvalFromVirtualFile.java index 60acaf0..87fe1aa 100644 --- a/src/main/java/com/en_circle/slt/plugin/swank/requests/EvalFromVirtualFile.java +++ b/src/main/java/com/en_circle/slt/plugin/swank/requests/EvalFromVirtualFile.java @@ -1,7 +1,9 @@ package com.en_circle.slt.plugin.swank.requests; +import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService; import com.en_circle.slt.plugin.swank.SlimeRequest; import com.en_circle.slt.plugin.swank.SwankPacket; +import com.intellij.openapi.project.Project; import java.math.BigInteger; @@ -34,8 +36,9 @@ protected EvalFromVirtualFile(String code, String module, String filename, int b } @Override - public SwankPacket createPacket(BigInteger requestId) { - return SwankPacket.swankEvalRegion(code, filename, bufferPosition, module, requestId); + public SwankPacket createPacket(BigInteger requestId, Project project) { + return SwankPacket.swankEvalRegion(code, LispEnvironmentService.getInstance(project).getBreakpointsForInstall(), + filename, bufferPosition, module, requestId); } public interface Callback { diff --git a/src/main/java/com/en_circle/slt/plugin/swank/requests/EvalStringInFrameEval.java b/src/main/java/com/en_circle/slt/plugin/swank/requests/EvalStringInFrameEval.java index bc9367e..168dd2f 100644 --- a/src/main/java/com/en_circle/slt/plugin/swank/requests/EvalStringInFrameEval.java +++ b/src/main/java/com/en_circle/slt/plugin/swank/requests/EvalStringInFrameEval.java @@ -1,7 +1,9 @@ package com.en_circle.slt.plugin.swank.requests; +import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService; import com.en_circle.slt.plugin.swank.SlimeRequest; import com.en_circle.slt.plugin.swank.SwankPacket; +import com.intellij.openapi.project.Project; import java.math.BigInteger; @@ -15,15 +17,16 @@ public static SlimeRequest evalInFrame(String code, BigInteger frame, BigInteger protected final BigInteger thread; protected EvalStringInFrameEval(String code, BigInteger frame, BigInteger thread, String module, Callback callback) { - super(code, module, callback); + super(code, module, true, callback); this.frame = frame; this.thread = thread; } @Override - public SwankPacket createPacket(BigInteger requestId) { - return SwankPacket.evalInFrame(code, frame, module, thread, requestId); + public SwankPacket createPacket(BigInteger requestId, Project project) { + return SwankPacket.evalInFrame(code, LispEnvironmentService.getInstance(project) + .getBreakpointsForInstall(), frame, module, thread, requestId); } diff --git a/src/main/java/com/en_circle/slt/plugin/swank/requests/FrameLocalsAndCatchTags.java b/src/main/java/com/en_circle/slt/plugin/swank/requests/FrameLocalsAndCatchTags.java index 4898e52..2a95cb4 100644 --- a/src/main/java/com/en_circle/slt/plugin/swank/requests/FrameLocalsAndCatchTags.java +++ b/src/main/java/com/en_circle/slt/plugin/swank/requests/FrameLocalsAndCatchTags.java @@ -5,6 +5,7 @@ import com.en_circle.slt.plugin.lisp.lisp.LispSymbol; import com.en_circle.slt.plugin.swank.SlimeRequest; import com.en_circle.slt.plugin.swank.SwankPacket; +import com.intellij.openapi.project.Project; import java.math.BigInteger; @@ -43,7 +44,7 @@ private boolean isOk(LispContainer data) { } @Override - public SwankPacket createPacket(BigInteger requestId) { + public SwankPacket createPacket(BigInteger requestId, Project project) { return SwankPacket.frameLocals(frame, threadId, module, requestId); } diff --git a/src/main/java/com/en_circle/slt/plugin/swank/requests/InspectFrameVar.java b/src/main/java/com/en_circle/slt/plugin/swank/requests/InspectFrameVar.java index 99e32f6..d67abc9 100644 --- a/src/main/java/com/en_circle/slt/plugin/swank/requests/InspectFrameVar.java +++ b/src/main/java/com/en_circle/slt/plugin/swank/requests/InspectFrameVar.java @@ -5,6 +5,7 @@ import com.en_circle.slt.plugin.lisp.lisp.LispSymbol; import com.en_circle.slt.plugin.swank.SlimeRequest; import com.en_circle.slt.plugin.swank.SwankPacket; +import com.intellij.openapi.project.Project; import java.math.BigInteger; @@ -45,7 +46,7 @@ private boolean isOk(LispContainer data) { } @Override - public SwankPacket createPacket(BigInteger requestId) { + public SwankPacket createPacket(BigInteger requestId, Project project) { return SwankPacket.inspectLocal(ix, frame, threadId, module, requestId); } diff --git a/src/main/java/com/en_circle/slt/plugin/swank/requests/InspectNth.java b/src/main/java/com/en_circle/slt/plugin/swank/requests/InspectNth.java index cebee54..3b7f8d3 100644 --- a/src/main/java/com/en_circle/slt/plugin/swank/requests/InspectNth.java +++ b/src/main/java/com/en_circle/slt/plugin/swank/requests/InspectNth.java @@ -5,6 +5,7 @@ import com.en_circle.slt.plugin.lisp.lisp.LispSymbol; import com.en_circle.slt.plugin.swank.SlimeRequest; import com.en_circle.slt.plugin.swank.SwankPacket; +import com.intellij.openapi.project.Project; import java.math.BigInteger; @@ -43,7 +44,7 @@ private boolean isOk(LispContainer data) { } @Override - public SwankPacket createPacket(BigInteger requestId) { + public SwankPacket createPacket(BigInteger requestId, Project project) { return SwankPacket.frameInspectNth(ix, threadId, module, requestId); } diff --git a/src/main/java/com/en_circle/slt/plugin/swank/requests/InspectorAction.java b/src/main/java/com/en_circle/slt/plugin/swank/requests/InspectorAction.java index 4d56a68..d2caef1 100644 --- a/src/main/java/com/en_circle/slt/plugin/swank/requests/InspectorAction.java +++ b/src/main/java/com/en_circle/slt/plugin/swank/requests/InspectorAction.java @@ -5,6 +5,7 @@ import com.en_circle.slt.plugin.lisp.lisp.LispSymbol; import com.en_circle.slt.plugin.swank.SlimeRequest; import com.en_circle.slt.plugin.swank.SwankPacket; +import com.intellij.openapi.project.Project; import java.math.BigInteger; @@ -43,7 +44,7 @@ private boolean isOk(LispContainer data) { } @Override - public SwankPacket createPacket(BigInteger requestId) { + public SwankPacket createPacket(BigInteger requestId, Project project) { switch (actionType) { case GO_BACK: diff --git a/src/main/java/com/en_circle/slt/plugin/swank/requests/InvokeNthRestart.java b/src/main/java/com/en_circle/slt/plugin/swank/requests/InvokeNthRestart.java index 348b511..062e778 100644 --- a/src/main/java/com/en_circle/slt/plugin/swank/requests/InvokeNthRestart.java +++ b/src/main/java/com/en_circle/slt/plugin/swank/requests/InvokeNthRestart.java @@ -4,6 +4,7 @@ import com.en_circle.slt.plugin.lisp.lisp.LispSymbol; import com.en_circle.slt.plugin.swank.SlimeRequest; import com.en_circle.slt.plugin.swank.SwankPacket; +import com.intellij.openapi.project.Project; import java.math.BigInteger; @@ -42,7 +43,7 @@ private boolean isOk(LispContainer data) { } @Override - public SwankPacket createPacket(BigInteger requestId) { + public SwankPacket createPacket(BigInteger requestId, Project project) { return SwankPacket.invokeNthRestart(restart, nestLevel, arg, args, threadId, requestId); } diff --git a/src/main/java/com/en_circle/slt/plugin/swank/requests/KillThread.java b/src/main/java/com/en_circle/slt/plugin/swank/requests/KillThread.java index 2b29bef..7b8b0fc 100644 --- a/src/main/java/com/en_circle/slt/plugin/swank/requests/KillThread.java +++ b/src/main/java/com/en_circle/slt/plugin/swank/requests/KillThread.java @@ -4,6 +4,7 @@ import com.en_circle.slt.plugin.lisp.lisp.LispSymbol; import com.en_circle.slt.plugin.swank.SlimeRequest; import com.en_circle.slt.plugin.swank.SwankPacket; +import com.intellij.openapi.project.Project; import java.math.BigInteger; @@ -34,7 +35,7 @@ private boolean isOk(LispContainer data) { } @Override - public SwankPacket createPacket(BigInteger requestId) { + public SwankPacket createPacket(BigInteger requestId, Project project) { return SwankPacket.killThread(id, requestId); } diff --git a/src/main/java/com/en_circle/slt/plugin/swank/requests/ListThreads.java b/src/main/java/com/en_circle/slt/plugin/swank/requests/ListThreads.java index f461e9c..e26898b 100644 --- a/src/main/java/com/en_circle/slt/plugin/swank/requests/ListThreads.java +++ b/src/main/java/com/en_circle/slt/plugin/swank/requests/ListThreads.java @@ -5,6 +5,7 @@ import com.en_circle.slt.plugin.swank.SlimeRequest; import com.en_circle.slt.plugin.swank.SwankPacket; import com.en_circle.slt.plugin.swank.components.ThreadInfo; +import com.intellij.openapi.project.Project; import java.math.BigInteger; @@ -21,7 +22,7 @@ private ListThreads(Callback callback) { } @Override - public SwankPacket createPacket(BigInteger requestId) { + public SwankPacket createPacket(BigInteger requestId, Project project) { return SwankPacket.dumpThreads(requestId); } diff --git a/src/main/java/com/en_circle/slt/plugin/swank/requests/LoadFile.java b/src/main/java/com/en_circle/slt/plugin/swank/requests/LoadFile.java index 2b41011..4d203b0 100644 --- a/src/main/java/com/en_circle/slt/plugin/swank/requests/LoadFile.java +++ b/src/main/java/com/en_circle/slt/plugin/swank/requests/LoadFile.java @@ -1,7 +1,9 @@ package com.en_circle.slt.plugin.swank.requests; +import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService; import com.en_circle.slt.plugin.swank.SlimeRequest; import com.en_circle.slt.plugin.swank.SwankPacket; +import com.intellij.openapi.project.Project; import java.math.BigInteger; @@ -18,7 +20,8 @@ public LoadFile(String file) { } @Override - public SwankPacket createPacket(BigInteger requestId) { - return SwankPacket.loadFile(file, requestId); + public SwankPacket createPacket(BigInteger requestId, Project project) { + return SwankPacket.loadFile(file, LispEnvironmentService.getInstance(project).getBreakpointsForInstall(), + requestId); } } diff --git a/src/main/java/com/en_circle/slt/plugin/swank/requests/MacroexpandAll.java b/src/main/java/com/en_circle/slt/plugin/swank/requests/MacroexpandAll.java index 489486f..0c8563e 100644 --- a/src/main/java/com/en_circle/slt/plugin/swank/requests/MacroexpandAll.java +++ b/src/main/java/com/en_circle/slt/plugin/swank/requests/MacroexpandAll.java @@ -5,6 +5,7 @@ import com.en_circle.slt.plugin.lisp.lisp.LispSymbol; import com.en_circle.slt.plugin.swank.SlimeRequest; import com.en_circle.slt.plugin.swank.SwankPacket; +import com.intellij.openapi.project.Project; import java.math.BigInteger; @@ -37,7 +38,7 @@ private boolean isOk(LispContainer data) { } @Override - public SwankPacket createPacket(BigInteger requestId) { + public SwankPacket createPacket(BigInteger requestId, Project project) { return SwankPacket.macroexpand(form, module, requestId); } diff --git a/src/main/java/com/en_circle/slt/plugin/swank/requests/SimpleCompletion.java b/src/main/java/com/en_circle/slt/plugin/swank/requests/SimpleCompletion.java index a0ead5f..484b99e 100644 --- a/src/main/java/com/en_circle/slt/plugin/swank/requests/SimpleCompletion.java +++ b/src/main/java/com/en_circle/slt/plugin/swank/requests/SimpleCompletion.java @@ -5,6 +5,7 @@ import com.en_circle.slt.plugin.lisp.lisp.LispSymbol; import com.en_circle.slt.plugin.swank.SlimeRequest; import com.en_circle.slt.plugin.swank.SwankPacket; +import com.intellij.openapi.project.Project; import java.math.BigInteger; @@ -39,7 +40,7 @@ private boolean isOk(LispContainer data) { } @Override - public SwankPacket createPacket(BigInteger requestId) { + public SwankPacket createPacket(BigInteger requestId, Project project) { return SwankPacket.simpleCompletion(prefix, packageName, module, requestId); } diff --git a/src/main/java/com/en_circle/slt/plugin/swank/requests/StepperAction.java b/src/main/java/com/en_circle/slt/plugin/swank/requests/StepperAction.java index edd028c..e5ca8a0 100644 --- a/src/main/java/com/en_circle/slt/plugin/swank/requests/StepperAction.java +++ b/src/main/java/com/en_circle/slt/plugin/swank/requests/StepperAction.java @@ -5,6 +5,7 @@ import com.en_circle.slt.plugin.lisp.lisp.LispSymbol; import com.en_circle.slt.plugin.swank.SlimeRequest; import com.en_circle.slt.plugin.swank.SwankPacket; +import com.intellij.openapi.project.Project; import java.math.BigInteger; @@ -43,7 +44,7 @@ private boolean isOk(LispContainer data) { } @Override - public SwankPacket createPacket(BigInteger requestId) { + public SwankPacket createPacket(BigInteger requestId, Project project) { switch (actionType) { case ENABLE: return SwankPacket.activateStepping(threadId, module, requestId); diff --git a/src/main/java/com/en_circle/slt/plugin/swank/requests/SuspendThread.java b/src/main/java/com/en_circle/slt/plugin/swank/requests/SuspendThread.java index 3fafb94..1bddfaa 100644 --- a/src/main/java/com/en_circle/slt/plugin/swank/requests/SuspendThread.java +++ b/src/main/java/com/en_circle/slt/plugin/swank/requests/SuspendThread.java @@ -4,6 +4,7 @@ import com.en_circle.slt.plugin.lisp.lisp.LispSymbol; import com.en_circle.slt.plugin.swank.SlimeRequest; import com.en_circle.slt.plugin.swank.SwankPacket; +import com.intellij.openapi.project.Project; import java.math.BigInteger; @@ -34,7 +35,7 @@ private boolean isOk(LispContainer data) { } @Override - public SwankPacket createPacket(BigInteger requestId) { + public SwankPacket createPacket(BigInteger requestId, Project project) { return SwankPacket.breakThread(id, requestId); } diff --git a/src/main/java/com/en_circle/slt/plugin/swank/requests/ThrowToToplevel.java b/src/main/java/com/en_circle/slt/plugin/swank/requests/ThrowToToplevel.java index 807e99d..2c0016b 100644 --- a/src/main/java/com/en_circle/slt/plugin/swank/requests/ThrowToToplevel.java +++ b/src/main/java/com/en_circle/slt/plugin/swank/requests/ThrowToToplevel.java @@ -2,6 +2,7 @@ import com.en_circle.slt.plugin.swank.SlimeRequest; import com.en_circle.slt.plugin.swank.SwankPacket; +import com.intellij.openapi.project.Project; import java.math.BigInteger; @@ -18,7 +19,7 @@ public ThrowToToplevel(BigInteger threadId) { } @Override - public SwankPacket createPacket(BigInteger requestId) { + public SwankPacket createPacket(BigInteger requestId, Project project) { return SwankPacket.throwToToplevel(threadId, requestId); } } diff --git a/src/main/java/com/en_circle/slt/plugin/swank/requests/Xrefs.java b/src/main/java/com/en_circle/slt/plugin/swank/requests/Xrefs.java index 492a856..4295ef7 100644 --- a/src/main/java/com/en_circle/slt/plugin/swank/requests/Xrefs.java +++ b/src/main/java/com/en_circle/slt/plugin/swank/requests/Xrefs.java @@ -5,6 +5,7 @@ import com.en_circle.slt.plugin.lisp.lisp.LispSymbol; import com.en_circle.slt.plugin.swank.SlimeRequest; import com.en_circle.slt.plugin.swank.SwankPacket; +import com.intellij.openapi.project.Project; import java.math.BigInteger; import java.util.Arrays; @@ -50,7 +51,7 @@ private boolean isOk(LispContainer data) { } @Override - public SwankPacket createPacket(BigInteger requestId) { + public SwankPacket createPacket(BigInteger requestId, Project project) { return SwankPacket.xrefs(xrefTypeList.stream().map(XrefType::getName).collect(Collectors.joining(" ")), name, packageName, requestId); } diff --git a/src/main/java/com/en_circle/slt/plugin/ui/PackageSelectorComponent.java b/src/main/java/com/en_circle/slt/plugin/ui/PackageSelectorComponent.java index 0582552..02fa27b 100644 --- a/src/main/java/com/en_circle/slt/plugin/ui/PackageSelectorComponent.java +++ b/src/main/java/com/en_circle/slt/plugin/ui/PackageSelectorComponent.java @@ -60,7 +60,7 @@ public ActionToolbar getActionToolbar() { public void refresh() { try { - LispEnvironmentService.getInstance(project).sendToLisp(EvalAndGrab.eval("(slt-core:list-package-names)", true, (result, stdout, parsed) -> { + LispEnvironmentService.getInstance(project).sendToLisp(EvalAndGrab.eval("(slt-core:list-package-names)", false, true, (result, stdout, parsed) -> { resolvePackages(parsed); }), false); } catch (Exception e) { diff --git a/src/main/java/com/en_circle/slt/plugin/ui/SltGeneralLog.java b/src/main/java/com/en_circle/slt/plugin/ui/SltGeneralLog.java index 9f65720..a0be34a 100644 --- a/src/main/java/com/en_circle/slt/plugin/ui/SltGeneralLog.java +++ b/src/main/java/com/en_circle/slt/plugin/ui/SltGeneralLog.java @@ -10,7 +10,6 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; import com.intellij.ui.tabs.TabInfo; -import org.apache.commons.lang3.StringUtils; import javax.swing.*; import java.awt.*; @@ -78,13 +77,11 @@ public String getTitle() { @Override public void logRequest(String request) { - request = StringUtils.truncate(request, 0, 4069); consoleView.print("\n\n" + request, ConsoleViewContentType.LOG_ERROR_OUTPUT); } @Override public void logResponse(String response) { - response = StringUtils.truncate(response, 0, 4069); consoleView.print("\n\n" + response, ConsoleViewContentType.LOG_INFO_OUTPUT); } diff --git a/src/main/java/com/en_circle/slt/plugin/ui/debug/SltBreakpointProperties.java b/src/main/java/com/en_circle/slt/plugin/ui/debug/SltBreakpointProperties.java index 1b95f68..c2ad630 100644 --- a/src/main/java/com/en_circle/slt/plugin/ui/debug/SltBreakpointProperties.java +++ b/src/main/java/com/en_circle/slt/plugin/ui/debug/SltBreakpointProperties.java @@ -1,22 +1,14 @@ package com.en_circle.slt.plugin.ui.debug; -import com.en_circle.slt.plugin.ui.debug.SltSymbolBreakpointType.SymbolType; -import com.intellij.openapi.components.State; -import com.intellij.openapi.components.Storage; import com.intellij.util.xmlb.XmlSerializerUtil; import com.intellij.xdebugger.breakpoints.XBreakpointProperties; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -@State( - name = "SltBreakpointProperties", - storages = @Storage("SltBreakpoints.xml") -) public class SltBreakpointProperties extends XBreakpointProperties { public String symbolName; public String packageName; - public SymbolType symbolType; public int offset; public String file; diff --git a/src/main/java/com/en_circle/slt/plugin/ui/debug/SltDebuggerImpl.java b/src/main/java/com/en_circle/slt/plugin/ui/debug/SltDebuggerImpl.java index 61d5b20..317da43 100644 --- a/src/main/java/com/en_circle/slt/plugin/ui/debug/SltDebuggerImpl.java +++ b/src/main/java/com/en_circle/slt/plugin/ui/debug/SltDebuggerImpl.java @@ -2,8 +2,8 @@ import com.en_circle.slt.plugin.SltBundle; import com.en_circle.slt.plugin.SltUIConstants; -import com.en_circle.slt.plugin.lisp.lisp.LispElement; import com.en_circle.slt.plugin.environment.LispFeatures; +import com.en_circle.slt.plugin.lisp.lisp.LispElement; import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService; import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService.LispEnvironmentState; import com.en_circle.slt.plugin.swank.debug.SltDebugAction; @@ -96,14 +96,16 @@ public void redraw(SltDebugInfo debugInfo) { JPanel content = new JPanel(new BorderLayout()); this.content.add(content, BorderLayout.CENTER); - DefaultActionGroup controlGroup = new DefaultActionGroup(); - controlGroup.add(new StepActivate()); -// controlGroup.add(new StepIntoAction()); -// controlGroup.add(new StepNextAction()); -// controlGroup.add(new StepOutAction()); - ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar("SltProcessWindowWrapEvent", controlGroup, false); - toolbar.setTargetComponent(this.content); - this.content.add(toolbar.getComponent(), BorderLayout.WEST); + if (LispEnvironmentService.getInstance(parent.getProject()).hasFeature(LispFeatures.BREAKPOINT_STEPPING)) { + DefaultActionGroup controlGroup = new DefaultActionGroup(); + controlGroup.add(new StepActivate()); + controlGroup.add(new StepIntoAction()); + controlGroup.add(new StepNextAction()); + controlGroup.add(new StepOutAction()); + ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar("SltProcessWindowWrapEvent", controlGroup, false); + toolbar.setTargetComponent(this.content); + this.content.add(toolbar.getComponent(), BorderLayout.WEST); + } JBSplitter splitter = new JBSplitter(false); splitter.setProportion(0.5f); @@ -268,8 +270,8 @@ private void enableStepping() { .sendToLisp(StepperAction.action(ActionType.ENABLE, lastDebug.getThreadId(), result -> ApplicationManager.getApplication().invokeLater(() -> {}))); } catch (Exception e) { - log.warn(SltBundle.message("slt.error.sbclstart"), e); - Messages.showErrorDialog(parent.getProject(), e.getMessage(), SltBundle.message("slt.ui.errors.sbcl.start")); + log.warn(SltBundle.message("slt.error.start"), e); + Messages.showErrorDialog(parent.getProject(), e.getMessage(), SltBundle.message("slt.ui.errors.lisp.start")); } } @@ -277,10 +279,10 @@ private void enableStepping(Runnable onEnable) { try { LispEnvironmentService.getInstance(parent.getProject()) .sendToLisp(StepperAction.action(ActionType.ENABLE, lastDebug.getThreadId(), result -> - ApplicationManager.getApplication().invokeLater(onEnable::run))); + ApplicationManager.getApplication().invokeLater(onEnable))); } catch (Exception e) { - log.warn(SltBundle.message("slt.error.sbclstart"), e); - Messages.showErrorDialog(parent.getProject(), e.getMessage(), SltBundle.message("slt.ui.errors.sbcl.start")); + log.warn(SltBundle.message("slt.error.start"), e); + Messages.showErrorDialog(parent.getProject(), e.getMessage(), SltBundle.message("slt.ui.errors.lisp.start")); } } @@ -290,8 +292,8 @@ private void stepIn() { .sendToLisp(StepperAction.action(ActionType.IN, lastDebug.getThreadId(), result -> ApplicationManager.getApplication().invokeLater(() -> processResult(result)))); } catch (Exception e) { - log.warn(SltBundle.message("slt.error.sbclstart"), e); - Messages.showErrorDialog(parent.getProject(), e.getMessage(), SltBundle.message("slt.ui.errors.sbcl.start")); + log.warn(SltBundle.message("slt.error.start"), e); + Messages.showErrorDialog(parent.getProject(), e.getMessage(), SltBundle.message("slt.ui.errors.lisp.start")); } } @@ -301,8 +303,8 @@ private void stepOut() { .sendToLisp(StepperAction.action(ActionType.OUT, lastDebug.getThreadId(), result -> ApplicationManager.getApplication().invokeLater(() -> processResult(result)))); } catch (Exception e) { - log.warn(SltBundle.message("slt.error.sbclstart"), e); - Messages.showErrorDialog(parent.getProject(), e.getMessage(), SltBundle.message("slt.ui.errors.sbcl.start")); + log.warn(SltBundle.message("slt.error.start"), e); + Messages.showErrorDialog(parent.getProject(), e.getMessage(), SltBundle.message("slt.ui.errors.lisp.start")); } } @@ -312,8 +314,8 @@ private void stepNext() { .sendToLisp(StepperAction.action(ActionType.NEXT, lastDebug.getThreadId(), result -> ApplicationManager.getApplication().invokeLater(() -> processResult(result)))); } catch (Exception e) { - log.warn(SltBundle.message("slt.error.sbclstart"), e); - Messages.showErrorDialog(parent.getProject(), e.getMessage(), SltBundle.message("slt.ui.errors.sbcl.start")); + log.warn(SltBundle.message("slt.error.start"), e); + Messages.showErrorDialog(parent.getProject(), e.getMessage(), SltBundle.message("slt.ui.errors.lisp.start")); } } diff --git a/src/main/java/com/en_circle/slt/plugin/ui/debug/SltSymbolBreakpointType.java b/src/main/java/com/en_circle/slt/plugin/ui/debug/SltSymbolBreakpointType.java index f78d51d..bb19d6c 100644 --- a/src/main/java/com/en_circle/slt/plugin/ui/debug/SltSymbolBreakpointType.java +++ b/src/main/java/com/en_circle/slt/plugin/ui/debug/SltSymbolBreakpointType.java @@ -44,6 +44,12 @@ public boolean canPutAt(@NotNull VirtualFile file, int line, @NotNull Project pr return null; } + @Override + @Nullable + public SltBreakpointProperties createProperties() { + return new SltBreakpointProperties(); + } + private List getPossiblePsiDebugSymbols(VirtualFile file, int line, Project project) { List infoList = new ArrayList<>(); @@ -80,29 +86,9 @@ private List getPossiblePsiDebugSymbols(VirtualFile file, int if ("defun".equalsIgnoreCase(head.getName())) { if (sexprs.size() > 1) { - addIfSymbol(sexprs.get(1), infoList, offset, endoffset, SymbolType.FUNCTION, head.getName()); - } - } else if ("defgeneric".equalsIgnoreCase(head.getName())) { - if (sexprs.size() > 1) { - addIfSymbol(sexprs.get(1), infoList, offset, endoffset, SymbolType.METHOD, head.getName()); - } - } else if ("defmacro".equalsIgnoreCase(head.getName())) { - if (sexprs.size() > 1) { - addIfSymbol(sexprs.get(1), infoList, offset, endoffset, SymbolType.MACRO, head.getName()); + addIfSymbol(sexprs.get(1), infoList, offset, endoffset, head.getName()); } } - // TODO -// } else if ("defmethod".equalsIgnoreCase(head.getName())) { -// // TODO: handle method special stuff to get tracing working correctly and not just on defgeneric level -// if (sexprs.size() > 1) { -// addIfSymbol(sexprs.get(1), infoList, offset, endoffset, SymbolType.METHOD, head.getName()); -// if (sexprs.size() > 2) { -// addIfSymbol(sexprs.get(2), infoList, offset, endoffset, SymbolType.METHOD, head.getName()); -// } -// } -// } - - // TODO: add defclass accessor/setter/getter into the mix! } } list = PsiTreeUtil.getParentOfType(list, LispList.class); @@ -117,7 +103,7 @@ private List getPossiblePsiDebugSymbols(VirtualFile file, int return infoList; } - private void addIfSymbol(LispSexpr sexpr, List infoList, int offset, int endoffset, SymbolType symbolType, + private void addIfSymbol(LispSexpr sexpr, List infoList, int offset, int endoffset, String headName) { if (sexpr.getDatum() != null) { if (sexpr.getDatum().getCompoundSymbol() != null) { @@ -125,7 +111,7 @@ private void addIfSymbol(LispSexpr sexpr, List infoList, int int soffset = symbol.getTextOffset(); if (soffset >= offset && soffset <= endoffset) { String packageName = LispParserUtil.getPackage(symbol); - SltBreakpointInfo info = new SltBreakpointInfo(symbol, packageName, symbolType); + SltBreakpointInfo info = new SltBreakpointInfo(symbol, packageName); infoList.add(info); } } @@ -161,12 +147,10 @@ private class SltBreakpointInfo extends XLineBreakpointVariant { private final LispSymbol element; private final String packageName; - private final SymbolType symbolType; - private SltBreakpointInfo(@NotNull LispSymbol element, String packageName, SymbolType symbolType) { + private SltBreakpointInfo(@NotNull LispSymbol element, String packageName) { this.element = element; this.packageName = packageName; - this.symbolType = symbolType; } @Override @@ -176,11 +160,7 @@ private SltBreakpointInfo(@NotNull LispSymbol element, String packageName, Symbo @Override public @Nullable Icon getIcon() { - return switch (symbolType) { - case FUNCTION -> Nodes.Function; - case MACRO -> Nodes.Annotationtype; - case METHOD -> Nodes.Method; - }; + return Nodes.Function; } @Override @@ -194,7 +174,6 @@ private SltBreakpointInfo(@NotNull LispSymbol element, String packageName, Symbo properties.symbolName = element.getName(); properties.packageName = packageName; - properties.symbolType = symbolType; properties.file = element.getContainingFile().getVirtualFile().getUrl(); properties.offset = element.getTextOffset(); @@ -202,7 +181,4 @@ private SltBreakpointInfo(@NotNull LispSymbol element, String packageName, Symbo } } - public enum SymbolType { - FUNCTION, MACRO, METHOD - } } diff --git a/src/main/lisp/load.lisp b/src/main/lisp/load.lisp index 8e38b8f..1f14e64 100644 --- a/src/main/lisp/load.lisp +++ b/src/main/lisp/load.lisp @@ -1,6 +1,13 @@ (eval-when (:execute) (format T "SLT Interpret ~S~%" slt:+slt-interpret+)) +(defpackage :slt-core + (:use :slt :cl :swank) + (:export analyze-symbol analyze-symbols read-fix-packages list-package-names + initialize-or-get-debug-context debug-context debug-frame-variable register-variable + install-breakpoint uninstall-breakpoint uninstall-breakpoints with-breakpoints + )) + (defun portable-quit (&optional code) (declare (ignorable code)) ;; This group from "clocc-port/ext.lisp" diff --git a/src/main/lisp/slt/slt-abcl.lisp b/src/main/lisp/slt/slt-abcl.lisp index e1bd0df..cf73ae4 100644 --- a/src/main/lisp/slt/slt-abcl.lisp +++ b/src/main/lisp/slt/slt-abcl.lisp @@ -1,8 +1,16 @@ (in-package :slt-core) -(defun install-breakpoint (symbol) ) +(defun uninstall-breakpoints () (symbol) + NIL) -(defun uninstall-breakpoint (symbol) ) +(defun uninstall-breakpoint (name) + NIL) + +(defun install-breakpoint (function-name) + NIL) + +(defun with-breakpoints (symbols) + NIL) (defun specialp (test-sym) NIL) \ No newline at end of file diff --git a/src/main/lisp/slt/slt-allegro.lisp b/src/main/lisp/slt/slt-allegro.lisp index de06193..1ff74eb 100644 --- a/src/main/lisp/slt/slt-allegro.lisp +++ b/src/main/lisp/slt/slt-allegro.lisp @@ -1,4 +1,21 @@ (in-package :slt-core) +(defun uninstall-breakpoints () + (untrace)) + +(defun uninstall-breakpoint (name) + (eval `(untrace ,name))) + +(defun install-breakpoint (function-name) + (ignore-errors + (swank::with-buffer-syntax () + (swank::sldb-break-at-start (swank::read-from-string function-name))))) + +(defun with-breakpoints (function-names) + (when function-names + (uninstall-breakpoints) + (loop for function-name in (cl-utilities:split-sequence #\Space function-names) do + (install-breakpoint function-name)))) + (defun specialp (test-sym) (eq (system:variable-information test-sym) :special)) \ No newline at end of file diff --git a/src/main/lisp/slt/slt-ccl.lisp b/src/main/lisp/slt/slt-ccl.lisp index 6479ef7..0b53a13 100644 --- a/src/main/lisp/slt/slt-ccl.lisp +++ b/src/main/lisp/slt/slt-ccl.lisp @@ -1,10 +1,16 @@ (in-package :slt-core) -(defun install-breakpoint (symbol) - (ignore-errors (eval `(trace ,symbol :break-before T)) T)) +(defun uninstall-breakpoints () (symbol) + NIL) -(defun uninstall-breakpoint (symbol) - (ignore-errors (eval `(untrace ,symbol)) T)) +(defun uninstall-breakpoint (name) + NIL) + +(defun install-breakpoint (function-name) + NIL) + +(defun with-breakpoints (symbols) + NIL) (defun specialp (test-sym) (eq (ccl:variable-information test-sym) :special)) \ No newline at end of file diff --git a/src/main/lisp/slt/slt-cmucl.lisp b/src/main/lisp/slt/slt-cmucl.lisp index 25abcec..864eda2 100644 --- a/src/main/lisp/slt/slt-cmucl.lisp +++ b/src/main/lisp/slt/slt-cmucl.lisp @@ -1,4 +1,21 @@ (in-package :slt-core) +(defun uninstall-breakpoints () + (untrace)) + +(defun uninstall-breakpoint (name) + (eval `(untrace ,name))) + +(defun install-breakpoint (function-name) + (ignore-errors + (swank::with-buffer-syntax () + (swank::sldb-break-at-start (swank::read-from-string function-name))))) + +(defun with-breakpoints (function-names) + (when function-names + (uninstall-breakpoints) + (loop for function-name in (cl-utilities:split-sequence #\Space function-names) do + (install-breakpoint function-name)))) + (defun specialp (test-sym) (eq (extensions:variable-information test-sym) :special)) \ No newline at end of file diff --git a/src/main/lisp/slt/slt-sbcl.lisp b/src/main/lisp/slt/slt-sbcl.lisp index 2dbbb02..08d1f7c 100644 --- a/src/main/lisp/slt/slt-sbcl.lisp +++ b/src/main/lisp/slt/slt-sbcl.lisp @@ -1,10 +1,16 @@ (in-package :slt-core) -(defun install-breakpoint (symbol) - (ignore-errors (eval `(trace ,symbol :break-before T)) T)) +(defun uninstall-breakpoints () (symbol) + NIL) -(defun uninstall-breakpoint (symbol) - (ignore-errors (eval `(untrace ,symbol)) T)) +(defun uninstall-breakpoint (name) + NIL) + +(defun install-breakpoint (function-name) + NIL) + +(defun with-breakpoints (symbols) + NIL) (defun specialp (test-sym) (eq (sb-cltl2:variable-information test-sym) :special)) \ No newline at end of file diff --git a/src/main/lisp/slt/slt.lisp b/src/main/lisp/slt/slt.lisp index 7bad28b..2ee9d23 100644 --- a/src/main/lisp/slt/slt.lisp +++ b/src/main/lisp/slt/slt.lisp @@ -1,10 +1,3 @@ -(defpackage :slt-core - (:use :slt :cl :swank) - (:export analyze-symbol analyze-symbols read-fix-packages list-package-names - initialize-or-get-debug-context debug-context debug-frame-variable register-variable - install-breakpoint uninstall-breakpoint - )) - (when (eq slt:+slt-interpret+ :sbcl) (load (merge-pathnames "slt-sbcl.lisp" *load-truename*))) (when (eq slt:+slt-interpret+ :abcl) @@ -97,4 +90,4 @@ (defun list-package-names () (let ((packages (list-all-packages))) (loop for package in packages collect - (package-name package)))) \ No newline at end of file + (package-name package)))) diff --git a/src/main/lisp/swank/swank-allegro.lisp b/src/main/lisp/swank/swank-allegro.lisp index 9235404..adfc5b8 100644 --- a/src/main/lisp/swank/swank-allegro.lisp +++ b/src/main/lisp/swank/swank-allegro.lisp @@ -7,6 +7,11 @@ (in-package :swank/allegro) +(let ((old-function #'frame-source-location)) + (defimplementation frame-source-location (index) + (handler-case (funcall old-function index) + (error (e) NIL)))) + (in-package :swank) (defslimefun backtrace (start end) diff --git a/src/main/lisp/swank/swank-cmucl.lisp b/src/main/lisp/swank/swank-cmucl.lisp index 8ef5afb..f207a90 100644 --- a/src/main/lisp/swank/swank-cmucl.lisp +++ b/src/main/lisp/swank/swank-cmucl.lisp @@ -1,32 +1,42 @@ (in-package :swank/backend) +(defparameter *in-get-restart-function-args* NIL) +(defparameter *in-print-frame-call-place* NIL) + (defun get-restart-function-args (restart) - (handler-case - (progn - (let* - ((fnc (slot-value restart 'function)) - (header (kernel:get-type fnc))) - (cond ((= header vm:function-header-type) - (kernel:%function-arglist fnc)) - ((= header vm:closure-header-type) - NIL) - ((eval::interpreted-function-p fnc) - (kernel:%function-type fnc)) - (t NIL)))) - (error (e) - (declare (ignore e)) - NIL))) + (unless *in-get-restart-function-args* + (let ((*in-get-restart-function-args* T)) + (handler-case + (progn + (let* + ((fnc (slot-value restart 'function)) + (header (kernel:get-type fnc))) + (cond ((= header vm:function-header-type) + (kernel:%function-arglist fnc)) + ((= header vm:closure-header-type) + NIL) + ((eval::interpreted-function-p fnc) + (kernel:%function-type fnc)) + (t NIL)))) + (error (e) + (declare (ignore e)) + NIL))))) (in-package :swank/cmucl) +(defun foreign-frame-source-location (frame) + `(:error "no srcloc available")) + (in-package :swank) (defun print-frame-call-place (frame) - (handler-case - (di:debug-function-name (di:frame-debug-function (di::frame-real-frame frame))) - (error (e) - (declare (ignore e)) - "Unknown frame"))) + (if *in-print-frame-call-place* "Recursive frame" + (let ((*in-print-frame-call-place* T)) + (handler-case + (di:debug-function-name (di:frame-debug-function (di::frame-real-frame frame))) + (error (e) + (declare (ignore e)) + "Unknown frame"))))) (defslimefun backtrace (start end) (loop for frame in (compute-backtrace start end) diff --git a/src/main/lisp/swank/swank.lisp b/src/main/lisp/swank/swank.lisp index c2b6e58..3c96747 100644 --- a/src/main/lisp/swank/swank.lisp +++ b/src/main/lisp/swank/swank.lisp @@ -52,7 +52,9 @@ format suitable for Emacs." (princ restart stream))) (swank-backend::get-restart-function-args restart))))) -(defslimefun compile-string-region-slt (string buffer offset filename package) +(defslimefun compile-string-region-slt (string breakpoints buffer offset filename package) + (when breakpoints + (slt-core:with-breakpoints breakpoints)) (with-buffer-syntax () (collect-notes (lambda () @@ -111,7 +113,13 @@ format suitable for Emacs." (cond ((eq args :not-available) nil) (t args))))) +(defslimefun load-file-breakpoints (filename breakpoints) + (when breakpoints + (slt-core:with-breakpoints breakpoints)) + (to-string (load (filename-to-pathname filename)))) + (export 'slt-eval) (export 'compile-string-region-slt) (export 'find-reference-prefix) (export 'operator-arglist-list) +(export 'load-file-breakpoints) \ No newline at end of file diff --git a/src/main/resources/templates/en_US/initscript.allegro.cl b/src/main/resources/templates/en_US/initscript.allegro.cl index 9c61b3e..294a3bd 100644 --- a/src/main/resources/templates/en_US/initscript.allegro.cl +++ b/src/main/resources/templates/en_US/initscript.allegro.cl @@ -7,6 +7,7 @@ "Defines current slt interpret.") (load "~qlpath~") +(ql:quickload :cl-utilities) (ql:quickload :swank) (ql:quickload :eclector) diff --git a/src/main/resources/templates/en_US/initscript.cl b/src/main/resources/templates/en_US/initscript.cl index 79057a3..846bf5d 100644 --- a/src/main/resources/templates/en_US/initscript.cl +++ b/src/main/resources/templates/en_US/initscript.cl @@ -7,6 +7,7 @@ "Defines current slt interpret.") (load "~qlpath~") +(ql:quickload :cl-utilities) (ql:quickload :swank) (ql:quickload :eclector) diff --git a/src/main/resources/templates/en_US/verify.abcl.cl b/src/main/resources/templates/en_US/verify.abcl.cl index 90a07f0..1cf63c5 100644 --- a/src/main/resources/templates/en_US/verify.abcl.cl +++ b/src/main/resources/templates/en_US/verify.abcl.cl @@ -1,6 +1,7 @@ (ignore-errors (load "~qlpath~")) (ignore-errors + (ql:quickload :cl-utilities) (ql:quickload :swank) (ql:quickload :eclector) (format *error-output* "SltVerified~%")) \ No newline at end of file diff --git a/src/main/resources/templates/en_US/verify.cl b/src/main/resources/templates/en_US/verify.cl index 17db304..ba1644e 100644 --- a/src/main/resources/templates/en_US/verify.cl +++ b/src/main/resources/templates/en_US/verify.cl @@ -1,4 +1,5 @@ (load "~qlpath~") +(ql:quickload :cl-utilities) (ql:quickload :swank) (ql:quickload :eclector) diff --git a/src/test/java/SlimeTest.java b/src/test/java/SlimeTest.java index e5dcfd4..4ab5671 100644 --- a/src/test/java/SlimeTest.java +++ b/src/test/java/SlimeTest.java @@ -51,7 +51,7 @@ public void onReadError(Exception e) { } })) { sent.addAndGet(1); - client.swankSend(SwankPacket.sltEval("(+ + 5)", new BigInteger("3"))); + client.swankSend(SwankPacket.sltEval("(+ + 5)", null, new BigInteger("3"))); Awaitility.await() .atMost(10, TimeUnit.SECONDS) From dcf50df9009083f315852cd9dcac0dd45cd1d532 Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Fri, 10 Feb 2023 11:46:14 +0100 Subject: [PATCH 29/40] more fun with badges --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index 0e0f791..1d9b779 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # SLT - A Common Lisp Language Plugin for Jetbrains IDE lineup ![GitHub release (latest by date)](https://img.shields.io/github/v/release/Enerccio/SLT) +![GitHub Release Date](https://img.shields.io/github/release-date/Enerccio/SLT) +![OSS Lifecycle](https://img.shields.io/osslifecycle/Enerccio/SLT) ![GitHub](https://img.shields.io/github/license/Enerccio/SLT) ![GitHub lisp counter](https://img.shields.io/github/search/Enerccio/SLT/lisp) [![0.1.0](https://badgen.net/github/milestones/enerccio/SLT/1)](https://github.com/enerccio/SLT/milestone/1) @@ -9,9 +11,22 @@ [![0.4.0](https://badgen.net/github/milestones/enerccio/SLT/5)](https://github.com/enerccio/SLT/milestone/5) ![GitHub all releases](https://img.shields.io/github/downloads/Enerccio/SLT/total) ![GitHub last commit](https://img.shields.io/github/last-commit/Enerccio/SLT) +![GitHub commit activity](https://img.shields.io/github/commit-activity/m/Enerccio/SLT) ![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/Enerccio/SLT) +![Lines of code](https://img.shields.io/tokei/lines/github/Enerccio/SLT) +![GitHub top language](https://img.shields.io/github/languages/top/Enerccio/SLT) [![(want-to-support-me? T NIL)](https://img.shields.io/liberapay/receives/Enerccio.svg?logo=liberapay)](https://liberapay.com/Enerccio) +![Liberapay patrons](https://img.shields.io/liberapay/patrons/Enerccio) +![Maintenance](https://img.shields.io/maintenance/yes/2023) +![GitHub issues](https://img.shields.io/github/issues/Enerccio/SLT) +![GitHub branch checks state](https://img.shields.io/github/checks-status/Enerccio/SLT/master) + +![Subreddit subscribers](https://img.shields.io/reddit/subreddit-subscribers/SLT_IDE?style=social) +![GitHub forks](https://img.shields.io/github/forks/Enerccio/SLT?style=social) +![GitHub Repo stars](https://img.shields.io/github/stars/Enerccio/SLT?style=social) +![GitHub watchers](https://img.shields.io/github/watchers/Enerccio/SLT?style=social) [![Join the chat at https://gitter.im/SLT-Plugin/community](https://badges.gitter.im/SLT-Plugin/Lobby.svg)](https://gitter.im/SLT-Plugin/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +![GitHub Discussions](https://img.shields.io/github/discussions/Enerccio/SLT) ![Image](src/main/resources/logo/logo.svg) From 2dcc96c46b83dcbe53c87e6f131089f2181b3bf8 Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Fri, 10 Feb 2023 11:48:13 +0100 Subject: [PATCH 30/40] reddit link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1d9b779..e0d9188 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ ![GitHub issues](https://img.shields.io/github/issues/Enerccio/SLT) ![GitHub branch checks state](https://img.shields.io/github/checks-status/Enerccio/SLT/master) -![Subreddit subscribers](https://img.shields.io/reddit/subreddit-subscribers/SLT_IDE?style=social) +[![Subreddit subscribers](https://img.shields.io/reddit/subreddit-subscribers/SLT_IDE?style=social)](https://old.reddit.com/r/SLT_IDE/) ![GitHub forks](https://img.shields.io/github/forks/Enerccio/SLT?style=social) ![GitHub Repo stars](https://img.shields.io/github/stars/Enerccio/SLT?style=social) ![GitHub watchers](https://img.shields.io/github/watchers/Enerccio/SLT?style=social) From a20fd02d209626f944fe737dd2ab34c32dd4f686 Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Fri, 10 Feb 2023 11:53:39 +0100 Subject: [PATCH 31/40] update readme --- README.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e0d9188..f202927 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ **THIS PLUGIN IS EXPERIMENTAL and can crash at any time! Please report all bugs!** This plugin is providing support for Common Lisp for JetBrains IDEs. -Using modified SLIME/Swank protocol to communicate with lisp runtime providing +Using modified SLIME/Swank protocol to communicate with lisp interpret providing IDE capabilities for Common Lisp. # (Somewhat) Detailed Installation and Usage Guide @@ -47,9 +47,16 @@ https://github.com/Enerccio/SLT/wiki/User-Guide Optionally (see more - guide): -1) [Steel Bank Common Lisp](https://www.sbcl.org/) installed +1) One of the supported LISP Interprets installed: + * [Steel Bank Common Lisp](https://www.sbcl.org/) + * [Armed Bear Common Lisp](https://armedbear.common-lisp.dev/) + * [Clozure Common Lisp](https://ccl.clozure.com/) + * [Allegro CL](https://franz.com/products/allegro-common-lisp/) + * [CMUCL](https://www.cons.org/cmucl/) 2) [Quicklisp](https://www.quicklisp.org/beta/) installed +(Not all features work with all interprets, see [FEATURES.md](FEATURES.md) for more info!) + ## Getting started See https://github.com/Enerccio/SLT/wiki/User-Guide#plugin-installation From d16f84c172ede8e30732f09b0b299ab2b681078a Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Fri, 10 Feb 2023 11:54:12 +0100 Subject: [PATCH 32/40] changelog update --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 052d6de..8a72425 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - CCL Support - AllegroCL Support - CMUCL Support +- Breakpoint support ### Fixes From f469a1d45d27e145e180a7947da6125289549a5d Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Sat, 11 Feb 2023 03:13:42 -0800 Subject: [PATCH 33/40] #48 --- .../slt/plugin/lisp/impl/LispCommentImpl.java | 16 +++- .../slt/plugin/lisp/impl/LispStringImpl.java | 22 ++++-- .../slt/plugin/lisp/psi/LispComment.java | 6 +- .../slt/plugin/lisp/psi/LispString.java | 6 +- .../slt/plugin/lisp/psi/LispVisitor.java | 6 +- .../slt/plugin/SltSpellCheckingStrategy.java | 29 +++++++ .../com/en_circle/slt/plugin/lisp/Lisp.bnf | 10 ++- .../plugin/lisp/psi/LispCommentElement.java | 7 ++ .../lisp/psi/impl/LispCommentElementImpl.java | 19 +++++ .../plugin/lisp/psi/impl/LispPsiImplUtil.java | 76 +++++++++++++++---- .../spellcheck/LispCommentTokenizer.java | 30 ++++++++ .../spellcheck/LispStringTokenizer.java | 31 ++++++++ src/main/resources/META-INF/plugin.xml | 4 +- 13 files changed, 231 insertions(+), 31 deletions(-) create mode 100644 src/main/java/com/en_circle/slt/plugin/SltSpellCheckingStrategy.java create mode 100644 src/main/java/com/en_circle/slt/plugin/lisp/psi/LispCommentElement.java create mode 100644 src/main/java/com/en_circle/slt/plugin/lisp/psi/impl/LispCommentElementImpl.java create mode 100644 src/main/java/com/en_circle/slt/plugin/spellcheck/LispCommentTokenizer.java create mode 100644 src/main/java/com/en_circle/slt/plugin/spellcheck/LispStringTokenizer.java diff --git a/src/main/gen/com/en_circle/slt/plugin/lisp/impl/LispCommentImpl.java b/src/main/gen/com/en_circle/slt/plugin/lisp/impl/LispCommentImpl.java index dc392d6..e3f443c 100644 --- a/src/main/gen/com/en_circle/slt/plugin/lisp/impl/LispCommentImpl.java +++ b/src/main/gen/com/en_circle/slt/plugin/lisp/impl/LispCommentImpl.java @@ -3,12 +3,14 @@ import com.en_circle.slt.plugin.lisp.psi.LispComment; import com.en_circle.slt.plugin.lisp.psi.LispVisitor; -import com.intellij.extapi.psi.ASTWrapperPsiElement; +import com.en_circle.slt.plugin.lisp.psi.impl.LispCommentElementImpl; +import com.en_circle.slt.plugin.lisp.psi.impl.LispPsiImplUtil; import com.intellij.lang.ASTNode; +import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; import org.jetbrains.annotations.NotNull; -public class LispCommentImpl extends ASTWrapperPsiElement implements LispComment { +public class LispCommentImpl extends LispCommentElementImpl implements LispComment { public LispCommentImpl(@NotNull ASTNode node) { super(node); @@ -24,4 +26,14 @@ public void accept(@NotNull PsiElementVisitor visitor) { else super.accept(visitor); } + @Override + public String getName() { + return LispPsiImplUtil.getName(this); + } + + @Override + public PsiElement setName(String newName) { + return LispPsiImplUtil.setName(this, newName); + } + } diff --git a/src/main/gen/com/en_circle/slt/plugin/lisp/impl/LispStringImpl.java b/src/main/gen/com/en_circle/slt/plugin/lisp/impl/LispStringImpl.java index 75f66e4..3fc3685 100644 --- a/src/main/gen/com/en_circle/slt/plugin/lisp/impl/LispStringImpl.java +++ b/src/main/gen/com/en_circle/slt/plugin/lisp/impl/LispStringImpl.java @@ -1,16 +1,14 @@ // This is a generated file. Not intended for manual editing. package com.en_circle.slt.plugin.lisp.impl; -import java.util.List; -import org.jetbrains.annotations.*; +import com.en_circle.slt.plugin.lisp.psi.LispString; +import com.en_circle.slt.plugin.lisp.psi.LispVisitor; +import com.en_circle.slt.plugin.lisp.psi.impl.LispPsiImplUtil; +import com.intellij.extapi.psi.ASTWrapperPsiElement; import com.intellij.lang.ASTNode; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; -import com.intellij.psi.util.PsiTreeUtil; -import static com.en_circle.slt.plugin.lisp.psi.LispTypes.*; -import com.intellij.extapi.psi.ASTWrapperPsiElement; -import com.en_circle.slt.plugin.lisp.psi.*; -import com.en_circle.slt.plugin.lisp.psi.impl.LispPsiImplUtil; +import org.jetbrains.annotations.NotNull; public class LispStringImpl extends ASTWrapperPsiElement implements LispString { @@ -28,4 +26,14 @@ public void accept(@NotNull PsiElementVisitor visitor) { else super.accept(visitor); } + @Override + public String getName() { + return LispPsiImplUtil.getName(this); + } + + @Override + public PsiElement setName(String newName) { + return LispPsiImplUtil.setName(this, newName); + } + } diff --git a/src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispComment.java b/src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispComment.java index 3e93f9d..d5621fb 100644 --- a/src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispComment.java +++ b/src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispComment.java @@ -3,6 +3,10 @@ import com.intellij.psi.PsiElement; -public interface LispComment extends PsiElement { +public interface LispComment extends LispCommentElement { + + String getName(); + + PsiElement setName(String newName); } diff --git a/src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispString.java b/src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispString.java index 53fb439..e80aba0 100644 --- a/src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispString.java +++ b/src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispString.java @@ -1,10 +1,12 @@ // This is a generated file. Not intended for manual editing. package com.en_circle.slt.plugin.lisp.psi; -import java.util.List; -import org.jetbrains.annotations.*; import com.intellij.psi.PsiElement; public interface LispString extends PsiElement { + String getName(); + + PsiElement setName(String newName); + } diff --git a/src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispVisitor.java b/src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispVisitor.java index aa6b4ce..d07232a 100644 --- a/src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispVisitor.java +++ b/src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispVisitor.java @@ -16,7 +16,7 @@ public void visitBinaryNumber(@NotNull LispBinaryNumber o) { } public void visitComment(@NotNull LispComment o) { - visitPsiElement(o); + visitCommentElement(o); } public void visitCompoundSymbol(@NotNull LispCompoundSymbol o) { @@ -103,6 +103,10 @@ public void visitVector(@NotNull LispVector o) { visitPsiElement(o); } + public void visitCommentElement(@NotNull LispCommentElement o) { + visitPsiElement(o); + } + public void visitNamedElement(@NotNull LispNamedElement o) { visitPsiElement(o); } diff --git a/src/main/java/com/en_circle/slt/plugin/SltSpellCheckingStrategy.java b/src/main/java/com/en_circle/slt/plugin/SltSpellCheckingStrategy.java new file mode 100644 index 0000000..8b66d3a --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/SltSpellCheckingStrategy.java @@ -0,0 +1,29 @@ +package com.en_circle.slt.plugin; + +import com.en_circle.slt.plugin.lisp.psi.LispString; +import com.en_circle.slt.plugin.lisp.psi.LispTypes; +import com.en_circle.slt.plugin.spellcheck.LispCommentTokenizer; +import com.en_circle.slt.plugin.spellcheck.LispStringTokenizer; +import com.intellij.psi.PsiElement; +import com.intellij.spellchecker.tokenizer.SpellcheckingStrategy; +import com.intellij.spellchecker.tokenizer.Tokenizer; +import org.jetbrains.annotations.NotNull; + +public class SltSpellCheckingStrategy extends SpellcheckingStrategy { + private final LispStringTokenizer stringTokenizer = new LispStringTokenizer(); + private final LispCommentTokenizer commentTokenizer = new LispCommentTokenizer(); + + @Override + public @NotNull Tokenizer getTokenizer(PsiElement element) { + if (element instanceof LispString) { + return stringTokenizer; + } + if (element.getNode().getElementType() == LispTypes.BLOCK_COMMENT) { + return commentTokenizer; + } + if (element.getNode().getElementType() == LispTypes.LINE_COMMENT) { + return commentTokenizer; + } + return EMPTY_TOKENIZER; + } +} diff --git a/src/main/java/com/en_circle/slt/plugin/lisp/Lisp.bnf b/src/main/java/com/en_circle/slt/plugin/lisp/Lisp.bnf index 536781b..fb37aeb 100644 --- a/src/main/java/com/en_circle/slt/plugin/lisp/Lisp.bnf +++ b/src/main/java/com/en_circle/slt/plugin/lisp/Lisp.bnf @@ -22,7 +22,11 @@ toplevel ::= sexpr sexpr ::= (enhancement* datum) | comment -comment ::= LINE_COMMENT | BLOCK_COMMENT +comment ::= LINE_COMMENT | BLOCK_COMMENT { + mixin="com.en_circle.slt.plugin.lisp.psi.impl.LispCommentElementImpl" + implements="com.en_circle.slt.plugin.lisp.psi.LispCommentElement" + methods=[getName setName] +} enhancement ::= REFERENCE_SET | TEST_SUCCESS | UNQUOTE | UNQUOTE_SPLICE | BACKQUOTE | QUOTE | FUNCTION @@ -46,7 +50,9 @@ structure ::= STRUCTURE_TOKEN list list ::= LPAREN sexpr* RPAREN { pin = 2 recoverWhile=list_recovery } private list_recovery ::= !(sexpr | RPAREN) -string ::= STRING_TOKEN +string ::= STRING_TOKEN { + methods=[getName setName] +} number ::= binary_number | octal_number | hex_number | radix_number | integer | ratio | real diff --git a/src/main/java/com/en_circle/slt/plugin/lisp/psi/LispCommentElement.java b/src/main/java/com/en_circle/slt/plugin/lisp/psi/LispCommentElement.java new file mode 100644 index 0000000..ace48a8 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/lisp/psi/LispCommentElement.java @@ -0,0 +1,7 @@ +package com.en_circle.slt.plugin.lisp.psi; + +import com.intellij.psi.PsiComment; + +public interface LispCommentElement extends PsiComment { + +} diff --git a/src/main/java/com/en_circle/slt/plugin/lisp/psi/impl/LispCommentElementImpl.java b/src/main/java/com/en_circle/slt/plugin/lisp/psi/impl/LispCommentElementImpl.java new file mode 100644 index 0000000..56e7a8c --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/lisp/psi/impl/LispCommentElementImpl.java @@ -0,0 +1,19 @@ +package com.en_circle.slt.plugin.lisp.psi.impl; + +import com.en_circle.slt.plugin.lisp.psi.LispCommentElement; +import com.intellij.extapi.psi.ASTWrapperPsiElement; +import com.intellij.lang.ASTNode; +import com.intellij.psi.tree.IElementType; +import org.jetbrains.annotations.NotNull; + +public class LispCommentElementImpl extends ASTWrapperPsiElement implements LispCommentElement { + + public LispCommentElementImpl(@NotNull ASTNode node) { + super(node); + } + + @Override + public @NotNull IElementType getTokenType() { + return getNode().getElementType(); + } +} diff --git a/src/main/java/com/en_circle/slt/plugin/lisp/psi/impl/LispPsiImplUtil.java b/src/main/java/com/en_circle/slt/plugin/lisp/psi/impl/LispPsiImplUtil.java index e54889f..7f0f59c 100644 --- a/src/main/java/com/en_circle/slt/plugin/lisp/psi/impl/LispPsiImplUtil.java +++ b/src/main/java/com/en_circle/slt/plugin/lisp/psi/impl/LispPsiImplUtil.java @@ -2,9 +2,7 @@ import com.en_circle.slt.plugin.SltCommonLispFileType; import com.en_circle.slt.plugin.lisp.LispSymbolPresentation; -import com.en_circle.slt.plugin.lisp.psi.LispFile; -import com.en_circle.slt.plugin.lisp.psi.LispSymbol; -import com.en_circle.slt.plugin.lisp.psi.LispTypes; +import com.en_circle.slt.plugin.lisp.psi.*; import com.intellij.lang.ASTNode; import com.intellij.navigation.ItemPresentation; import com.intellij.openapi.project.Project; @@ -47,17 +45,6 @@ public static String getName(LispSymbol element) { return identifier.getText(); } - private static LispSymbol createSymbol(Project project, String name) { - LispFile file = createFile(project, name); - return PsiTreeUtil.findChildOfType(file, LispSymbol.class); - } - - private static LispFile createFile(Project project, String text) { - String name = "dummy.cl"; - return (LispFile) PsiFileFactory.getInstance(project).createFileFromText(name, SltCommonLispFileType.INSTANCE, - String.format("%s", text)); - } - public static PsiElement setName(LispSymbol element, String newName) { ASTNode identifier = element.getNode().findChildByType(LispTypes.SYMBOL_TOKEN); if (identifier != null) { @@ -69,6 +56,67 @@ public static PsiElement setName(LispSymbol element, String newName) { return element; } + public static String getName(LispComment element) { + return element.getText(); + } + + public static PsiElement setName(LispComment element, String newName) { + ASTNode commentLineNode = element.getNode().findChildByType(LispTypes.LINE_COMMENT); + if (commentLineNode != null) { + LispComment comment = createComment(element.getProject(), newName); + ASTNode newComment = comment.getNode().findChildByType(LispTypes.LINE_COMMENT); + assert newComment != null; + element.getNode().replaceChild(commentLineNode, newComment); + return element; + } + ASTNode commentBlockNode = element.getNode().findChildByType(LispTypes.BLOCK_COMMENT); + if (commentBlockNode != null) { + LispComment comment = createComment(element.getProject(), newName); + ASTNode newComment = comment.getNode().findChildByType(LispTypes.BLOCK_COMMENT); + assert newComment != null; + element.getNode().replaceChild(commentBlockNode, newComment); + return element; + } + return element; + } + + public static String getName(LispString element) { + return element.getText(); + } + + public static PsiElement setName(LispString element, String newName) { + ASTNode stringLineNode = element.getNode().findChildByType(LispTypes.STRING_TOKEN); + if (stringLineNode != null) { + LispComment comment = createComment(element.getProject(), newName); + ASTNode newString = comment.getNode().findChildByType(LispTypes.STRING_TOKEN); + assert newString != null; + element.getNode().replaceChild(stringLineNode, newString); + return element; + } + return element; + } + + private static LispSymbol createSymbol(Project project, String name) { + LispFile file = createFile(project, name); + return PsiTreeUtil.findChildOfType(file, LispSymbol.class); + } + + private static LispComment createComment(Project project, String name) { + LispFile file = createFile(project, name); + return PsiTreeUtil.findChildOfType(file, LispComment.class); + } + + private static LispString createString(Project project, String name) { + LispFile file = createFile(project, name); + return PsiTreeUtil.findChildOfType(file, LispString.class); + } + + private static LispFile createFile(Project project, String text) { + String name = "dummy.cl"; + return (LispFile) PsiFileFactory.getInstance(project).createFileFromText(name, SltCommonLispFileType.INSTANCE, + String.format("%s", text)); + } + public static PsiElement getNameIdentifier(LispSymbol element) { ASTNode identifier = element.getNode().findChildByType(LispTypes.SYMBOL_TOKEN); if (identifier != null) { diff --git a/src/main/java/com/en_circle/slt/plugin/spellcheck/LispCommentTokenizer.java b/src/main/java/com/en_circle/slt/plugin/spellcheck/LispCommentTokenizer.java new file mode 100644 index 0000000..0149f0e --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/spellcheck/LispCommentTokenizer.java @@ -0,0 +1,30 @@ +package com.en_circle.slt.plugin.spellcheck; + +import com.intellij.openapi.roots.ProjectFileIndex; +import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiElement; +import com.intellij.spellchecker.inspections.IdentifierSplitter; +import com.intellij.spellchecker.tokenizer.TokenConsumer; +import com.intellij.spellchecker.tokenizer.Tokenizer; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; + +public class LispCommentTokenizer extends Tokenizer { + + @Override + public void tokenize(@NotNull PsiElement element, TokenConsumer consumer) { + VirtualFile virtualFile = element.getContainingFile().getVirtualFile(); + ProjectFileIndex fileIndex = ProjectRootManager.getInstance(element.getProject()).getFileIndex(); + boolean isInSource = (virtualFile != null) && fileIndex.isInContent(virtualFile); + if (isInSource) { + String text = element.getText(); + if (StringUtils.isBlank(text)) + return; + consumer.consumeToken(element, text, false, 0, TextRange.create(0, text.length()), + IdentifierSplitter.getInstance()); + } + } + +} diff --git a/src/main/java/com/en_circle/slt/plugin/spellcheck/LispStringTokenizer.java b/src/main/java/com/en_circle/slt/plugin/spellcheck/LispStringTokenizer.java new file mode 100644 index 0000000..1864aea --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/spellcheck/LispStringTokenizer.java @@ -0,0 +1,31 @@ +package com.en_circle.slt.plugin.spellcheck; + +import com.en_circle.slt.plugin.lisp.psi.LispString; +import com.intellij.openapi.roots.ProjectFileIndex; +import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.spellchecker.inspections.IdentifierSplitter; +import com.intellij.spellchecker.tokenizer.TokenConsumer; +import com.intellij.spellchecker.tokenizer.Tokenizer; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; + +public class LispStringTokenizer extends Tokenizer { + + @Override + public void tokenize(@NotNull LispString element, TokenConsumer consumer) { + VirtualFile virtualFile = element.getContainingFile().getVirtualFile(); + ProjectFileIndex fileIndex = ProjectRootManager.getInstance(element.getProject()).getFileIndex(); + boolean isInSource = (virtualFile != null) && fileIndex.isInContent(virtualFile); + if (isInSource) { + String text = element.getText(); + if (StringUtils.isBlank(text)) + return; + + consumer.consumeToken(element, text, false, 0, TextRange.create(1, text.length()-1), + IdentifierSplitter.getInstance()); + } + } + +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 6261200..cc70839 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -101,6 +101,8 @@ + + @@ -112,8 +114,6 @@ - - From 2cdf5b2d6b8c9c53445425e99cd7198072c611fa Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Sat, 11 Feb 2023 03:29:00 -0800 Subject: [PATCH 34/40] features --- FEATURES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/FEATURES.md b/FEATURES.md index 784383e..48014a3 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -29,7 +29,7 @@ Unsupported and will not be supported implementations: | Autocomplete | ✅ | ✅ | ✅ | ✅ | ✅ | | Find References | ✅ | ❎ | ✅️ | ❎ | ❎ | | Function Arguments | ✅ | ✅ | ✅️ | ✅️ | ✅️ | -| Automatic Download - Windows | ✅ | ❓ | ❓ | ❎ | ❓ | +| Automatic Download - Windows | ✅ | ❓ | ❓ | ❓ | ❓ | ¹Only read-only inspector available @@ -44,4 +44,4 @@ arguments because fuck you that's why. ⁴Allegro CL restarts have correct arglists so actions work but for some reason all restarts from SWANK have arguments, event abort ones... -⁵No implementation in Slime supports this. \ No newline at end of file +⁵No implementation in Slime supports this, maybe I will work in custom solutions. \ No newline at end of file From 46362caa531901adc552f32056621e86ec785463 Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Sat, 11 Feb 2023 03:38:16 -0800 Subject: [PATCH 35/40] update --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index ec362d0..1bfa2b5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,6 +1,6 @@ plugins { id("java") - id("org.jetbrains.intellij") version "1.12.0" + id("org.jetbrains.intellij") version "1.13.0" } group = "com.en_circle.slt" From 936638c9ae548362472778ed670ee987a3dc33db Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Sat, 11 Feb 2023 04:19:15 -0800 Subject: [PATCH 36/40] #51 --- CHANGELOG.md | 2 + .../slt/plugin/SltFoldingBuilder.java | 82 +++++++++++++++++++ src/main/resources/META-INF/plugin.xml | 2 + 3 files changed, 86 insertions(+) create mode 100644 src/main/java/com/en_circle/slt/plugin/SltFoldingBuilder.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a72425..ac72b4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ - AllegroCL Support - CMUCL Support - Breakpoint support +- Spellchecker support +- Folding support ### Fixes diff --git a/src/main/java/com/en_circle/slt/plugin/SltFoldingBuilder.java b/src/main/java/com/en_circle/slt/plugin/SltFoldingBuilder.java new file mode 100644 index 0000000..1877901 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/SltFoldingBuilder.java @@ -0,0 +1,82 @@ +package com.en_circle.slt.plugin; + +import com.en_circle.slt.plugin.lisp.psi.LispList; +import com.en_circle.slt.plugin.lisp.psi.LispSexpr; +import com.en_circle.slt.plugin.lisp.psi.LispToplevel; +import com.intellij.lang.ASTNode; +import com.intellij.lang.folding.FoldingBuilderEx; +import com.intellij.lang.folding.FoldingDescriptor; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.FoldingGroup; +import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; +import com.intellij.psi.util.PsiTreeUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class SltFoldingBuilder extends FoldingBuilderEx implements DumbAware { + + @Override + public FoldingDescriptor @NotNull [] buildFoldRegions(@NotNull PsiElement root, @NotNull Document document, boolean quick) { + List descriptors = new ArrayList<>(); + + Collection toplevels = + PsiTreeUtil.findChildrenOfType(root, LispToplevel.class); + + for (LispToplevel toplevel : toplevels) { + FoldingGroup group = FoldingGroup.newGroup("SLTFoldTopLevel " + toplevel.hashCode()); + if (toplevel.getSexpr().getDatum() != null && toplevel.getSexpr().getDatum().getList() != null) { + if (toplevel.getTextRange().getLength() > 2) { + FoldingDescriptor descriptor = new FoldingDescriptor(toplevel.getNode(), + new TextRange(toplevel.getTextRange().getStartOffset() + 1, + toplevel.getTextRange().getEndOffset() - 1), group); + descriptors.add(descriptor); + } + } + } + + return descriptors.toArray(new FoldingDescriptor[0]); + } + + @Override + public @Nullable String getPlaceholderText(@NotNull ASTNode node) { + String text = "..."; + if (node.getPsi() instanceof LispToplevel toplevel) { + if (toplevel.getSexpr().getDatum() != null && toplevel.getSexpr().getDatum().getList() != null) { + LispList list = toplevel.getSexpr().getDatum().getList(); + List elements = list.getSexprList(); + if (elements.size() > 0) { + LispSexpr head = elements.get(0); + if (head.getDatum() != null && head.getDatum().getCompoundSymbol() != null) { + String headName = head.getText(); + text = headName; + + if (headName.toLowerCase().startsWith("def") && elements.size() > 1) { + text += " " + elements.get(1).getText(); + + if (headName.equalsIgnoreCase("defmethod") && elements.size() > 2) { + LispSexpr potentialArgs = elements.get(2); + if (potentialArgs.getDatum() != null && potentialArgs.getDatum().getCompoundSymbol() != null) { + text += " " + potentialArgs.getText(); + } + } + } + } + } + + return text; + } + } + return "..."; + } + + @Override + public boolean isCollapsedByDefault(@NotNull ASTNode node) { + return false; + } +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index cc70839..0014e47 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -103,6 +103,8 @@ + + From 4059ad282eb3f2f5f894f02f0315e08c1f7537c2 Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Sat, 11 Feb 2023 21:40:06 +0100 Subject: [PATCH 37/40] structure view --- CHANGELOG.md | 2 +- .../plugin/lisp/impl/LispToplevelImpl.java | 12 ++ .../slt/plugin/lisp/psi/LispToplevel.java | 9 +- .../slt/plugin/lisp/psi/LispVisitor.java | 7 +- .../plugin/SltCommonLispParserDefinition.java | 8 +- .../slt/plugin/SltFoldingBuilder.java | 29 +-- .../en_circle/slt/plugin/SltIconProvider.java | 12 ++ .../slt/plugin/SltStructureAwareNavbar.java | 67 +++++++ .../slt/plugin/SltStructureViewExtension.java | 24 +++ .../autocomplete/HeadCompletionProvider.java | 10 +- .../com/en_circle/slt/plugin/lisp/Lisp.bnf | 5 +- .../com/en_circle/slt/plugin/lisp/Lisp.flex | 2 +- .../slt/plugin/lisp/LispLexerUtils.java | 4 +- .../slt/plugin/lisp/LispParserUtil.java | 171 ++++++++++++++++++ .../lisp/LispPotentialSymbolPresentation.java | 2 +- .../plugin/lisp/LispSymbolPresentation.java | 14 +- .../plugin/lisp/psi/impl/LispPsiImplUtil.java | 25 +++ .../structure/LispStructureViewElement.java | 106 +++++++++++ .../structure/LispStructureViewModel.java | 38 ++++ src/main/resources/META-INF/plugin.xml | 9 +- 20 files changed, 502 insertions(+), 54 deletions(-) create mode 100644 src/main/java/com/en_circle/slt/plugin/SltStructureAwareNavbar.java create mode 100644 src/main/java/com/en_circle/slt/plugin/SltStructureViewExtension.java create mode 100644 src/main/java/com/en_circle/slt/plugin/structure/LispStructureViewElement.java create mode 100644 src/main/java/com/en_circle/slt/plugin/structure/LispStructureViewModel.java diff --git a/CHANGELOG.md b/CHANGELOG.md index ac72b4c..c770541 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ - CMUCL Support - Breakpoint support - Spellchecker support -- Folding support +- Folding support, structure support, navigation bar ### Fixes diff --git a/src/main/gen/com/en_circle/slt/plugin/lisp/impl/LispToplevelImpl.java b/src/main/gen/com/en_circle/slt/plugin/lisp/impl/LispToplevelImpl.java index f456461..b97a804 100644 --- a/src/main/gen/com/en_circle/slt/plugin/lisp/impl/LispToplevelImpl.java +++ b/src/main/gen/com/en_circle/slt/plugin/lisp/impl/LispToplevelImpl.java @@ -4,8 +4,10 @@ import com.en_circle.slt.plugin.lisp.psi.LispSexpr; import com.en_circle.slt.plugin.lisp.psi.LispToplevel; import com.en_circle.slt.plugin.lisp.psi.LispVisitor; +import com.en_circle.slt.plugin.lisp.psi.impl.LispPsiImplUtil; import com.intellij.extapi.psi.ASTWrapperPsiElement; import com.intellij.lang.ASTNode; +import com.intellij.navigation.ItemPresentation; import com.intellij.psi.PsiElementVisitor; import org.jetbrains.annotations.NotNull; @@ -31,4 +33,14 @@ public LispSexpr getSexpr() { return findNotNullChildByClass(LispSexpr.class); } + @Override + public String getName() { + return LispPsiImplUtil.getName(this); + } + + @Override + public ItemPresentation getPresentation() { + return LispPsiImplUtil.getPresentation(this); + } + } diff --git a/src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispToplevel.java b/src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispToplevel.java index 0f37497..b97bb8f 100644 --- a/src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispToplevel.java +++ b/src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispToplevel.java @@ -1,12 +1,17 @@ // This is a generated file. Not intended for manual editing. package com.en_circle.slt.plugin.lisp.psi; -import com.intellij.psi.PsiElement; +import com.intellij.navigation.ItemPresentation; +import com.intellij.psi.NavigatablePsiElement; import org.jetbrains.annotations.NotNull; -public interface LispToplevel extends PsiElement { +public interface LispToplevel extends NavigatablePsiElement { @NotNull LispSexpr getSexpr(); + String getName(); + + ItemPresentation getPresentation(); + } diff --git a/src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispVisitor.java b/src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispVisitor.java index d07232a..08b96fe 100644 --- a/src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispVisitor.java +++ b/src/main/gen/com/en_circle/slt/plugin/lisp/psi/LispVisitor.java @@ -1,6 +1,7 @@ // This is a generated file. Not intended for manual editing. package com.en_circle.slt.plugin.lisp.psi; +import com.intellij.psi.NavigatablePsiElement; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; import org.jetbrains.annotations.NotNull; @@ -96,7 +97,7 @@ public void visitTested(@NotNull LispTested o) { } public void visitToplevel(@NotNull LispToplevel o) { - visitPsiElement(o); + visitNavigatablePsiElement(o); } public void visitVector(@NotNull LispVector o) { @@ -111,6 +112,10 @@ public void visitNamedElement(@NotNull LispNamedElement o) { visitPsiElement(o); } + public void visitNavigatablePsiElement(@NotNull NavigatablePsiElement o) { + visitElement(o); + } + public void visitPsiElement(@NotNull PsiElement o) { visitElement(o); } diff --git a/src/main/java/com/en_circle/slt/plugin/SltCommonLispParserDefinition.java b/src/main/java/com/en_circle/slt/plugin/SltCommonLispParserDefinition.java index a02f5c2..fd0373f 100644 --- a/src/main/java/com/en_circle/slt/plugin/SltCommonLispParserDefinition.java +++ b/src/main/java/com/en_circle/slt/plugin/SltCommonLispParserDefinition.java @@ -19,9 +19,13 @@ public class SltCommonLispParserDefinition implements ParserDefinition { public static final IFileElementType FILE = new IFileElementType(SltCommonLispLanguage.INSTANCE); - public static TokenSet STRINGS = TokenSet.create(LispTypes.STRING); + public static final TokenSet STRINGS = TokenSet.create(LispTypes.STRING_TOKEN); - public static TokenSet COMMENTS = TokenSet.create(LispTypes.COMMENT); + public static final TokenSet COMMENTS = TokenSet.create(LispTypes.LINE_COMMENT, LispTypes.BLOCK_COMMENT); + public static final TokenSet SYMBOLS = TokenSet.create(LispTypes.SYMBOL_TOKEN); + public static final TokenSet LITERALS = TokenSet.create(LispTypes.STRING_TOKEN, LispTypes.BIT_ARRAY, + LispTypes.BINARY_NUMBER_TOKEN, LispTypes.HEX_NUMBER_TOKEN, LispTypes.RADIX_NUMBER_TOKEN, + LispTypes.REAL_NUMBER, LispTypes.INTEGER_NUMBER, LispTypes.RATIO_NUMBER); @Override diff --git a/src/main/java/com/en_circle/slt/plugin/SltFoldingBuilder.java b/src/main/java/com/en_circle/slt/plugin/SltFoldingBuilder.java index 1877901..b6622c6 100644 --- a/src/main/java/com/en_circle/slt/plugin/SltFoldingBuilder.java +++ b/src/main/java/com/en_circle/slt/plugin/SltFoldingBuilder.java @@ -1,7 +1,6 @@ package com.en_circle.slt.plugin; -import com.en_circle.slt.plugin.lisp.psi.LispList; -import com.en_circle.slt.plugin.lisp.psi.LispSexpr; +import com.en_circle.slt.plugin.lisp.LispParserUtil; import com.en_circle.slt.plugin.lisp.psi.LispToplevel; import com.intellij.lang.ASTNode; import com.intellij.lang.folding.FoldingBuilderEx; @@ -45,32 +44,8 @@ public class SltFoldingBuilder extends FoldingBuilderEx implements DumbAware { @Override public @Nullable String getPlaceholderText(@NotNull ASTNode node) { - String text = "..."; if (node.getPsi() instanceof LispToplevel toplevel) { - if (toplevel.getSexpr().getDatum() != null && toplevel.getSexpr().getDatum().getList() != null) { - LispList list = toplevel.getSexpr().getDatum().getList(); - List elements = list.getSexprList(); - if (elements.size() > 0) { - LispSexpr head = elements.get(0); - if (head.getDatum() != null && head.getDatum().getCompoundSymbol() != null) { - String headName = head.getText(); - text = headName; - - if (headName.toLowerCase().startsWith("def") && elements.size() > 1) { - text += " " + elements.get(1).getText(); - - if (headName.equalsIgnoreCase("defmethod") && elements.size() > 2) { - LispSexpr potentialArgs = elements.get(2); - if (potentialArgs.getDatum() != null && potentialArgs.getDatum().getCompoundSymbol() != null) { - text += " " + potentialArgs.getText(); - } - } - } - } - } - - return text; - } + return LispParserUtil.determineTopLevelType(toplevel.getSexpr()).getShortForm(); } return "..."; } diff --git a/src/main/java/com/en_circle/slt/plugin/SltIconProvider.java b/src/main/java/com/en_circle/slt/plugin/SltIconProvider.java index 63f52ab..dd8e986 100644 --- a/src/main/java/com/en_circle/slt/plugin/SltIconProvider.java +++ b/src/main/java/com/en_circle/slt/plugin/SltIconProvider.java @@ -1,5 +1,6 @@ package com.en_circle.slt.plugin; +import com.intellij.icons.AllIcons.Nodes; import com.intellij.openapi.util.IconLoader; import javax.swing.*; @@ -9,6 +10,17 @@ public class SltIconProvider { public static final Icon file = IconLoader.getIcon("/icons/fileicon.svg", SltIconProvider.class); public static final Icon sbcl = IconLoader.getIcon("/icons/sbcl.png", SltIconProvider.class); + public static final Icon MACRO = Nodes.Template; + public static final Icon FUNCTION = Nodes.Function; + public static final Icon METHOD = Nodes.Method; + public static final Icon LAMBDA = Nodes.Lambda; + public static final Icon CONSTANT = Nodes.Constant; + public static final Icon SPECIAL = Nodes.Gvariable; + public static final Icon CLASS = Nodes.Class; + public static final Icon PACKAGE = Nodes.Package; + public static final Icon STRUCTURE = Nodes.Static; + public static final Icon TYPE = Nodes.Type; + public static Icon getFileIcon() { return file; } diff --git a/src/main/java/com/en_circle/slt/plugin/SltStructureAwareNavbar.java b/src/main/java/com/en_circle/slt/plugin/SltStructureAwareNavbar.java new file mode 100644 index 0000000..3679d28 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/SltStructureAwareNavbar.java @@ -0,0 +1,67 @@ +package com.en_circle.slt.plugin; + +import com.en_circle.slt.plugin.lisp.LispParserUtil; +import com.en_circle.slt.plugin.lisp.LispParserUtil.LispSexpressionInfo; +import com.en_circle.slt.plugin.lisp.LispParserUtil.SexpressionType; +import com.en_circle.slt.plugin.lisp.psi.LispFile; +import com.en_circle.slt.plugin.lisp.psi.LispSexpr; +import com.en_circle.slt.plugin.lisp.psi.LispToplevel; +import com.intellij.ide.navigationToolbar.StructureAwareNavBarModelExtension; +import com.intellij.lang.Language; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; + +public class SltStructureAwareNavbar extends StructureAwareNavBarModelExtension { + + @NotNull + @Override + protected Language getLanguage() { + return SltCommonLispLanguage.INSTANCE; + } + + @Override + public @Nullable Icon getIcon(Object object) { + if (object instanceof LispFile) { + return SltIconProvider.getFileIcon(); + } + if (object instanceof LispToplevel toplevel) { + LispSexpressionInfo info = LispParserUtil.determineTopLevelType(toplevel.getSexpr()); + if (info.getType() != SexpressionType.EXPRESSION) { + return info.getIcon(); + } + return SltIconProvider.getFileIcon(); + } + if (object instanceof LispSexpr sexpr) { + LispSexpressionInfo info = LispParserUtil.determineTopLevelType(sexpr); + if (info.getType() != SexpressionType.EXPRESSION) { + return info.getIcon(); + } + return SltIconProvider.getFileIcon(); + } + return null; + } + + @Override + public @Nullable String getPresentableText(Object object) { + if (object instanceof LispFile file) { + return file.getName(); + } + if (object instanceof LispToplevel toplevel) { + LispSexpressionInfo info = LispParserUtil.determineTopLevelType(toplevel.getSexpr()); + if (info.getType() != SexpressionType.EXPRESSION) { + return info.getLongForm(); + } + return toplevel.getContainingFile().getName(); + } + if (object instanceof LispSexpr sexpr) { + LispSexpressionInfo info = LispParserUtil.determineTopLevelType(sexpr); + if (info.getType() != SexpressionType.EXPRESSION) { + return info.getLongForm(); + } + return sexpr.getContainingFile().getName(); + } + return null; + } +} diff --git a/src/main/java/com/en_circle/slt/plugin/SltStructureViewExtension.java b/src/main/java/com/en_circle/slt/plugin/SltStructureViewExtension.java new file mode 100644 index 0000000..6305c2b --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/SltStructureViewExtension.java @@ -0,0 +1,24 @@ +package com.en_circle.slt.plugin; + +import com.en_circle.slt.plugin.structure.LispStructureViewModel; +import com.intellij.ide.structureView.StructureViewBuilder; +import com.intellij.ide.structureView.StructureViewModel; +import com.intellij.ide.structureView.TreeBasedStructureViewBuilder; +import com.intellij.lang.PsiStructureViewFactory; +import com.intellij.openapi.editor.Editor; +import com.intellij.psi.PsiFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class SltStructureViewExtension implements PsiStructureViewFactory { + + @Override + public @Nullable StructureViewBuilder getStructureViewBuilder(@NotNull PsiFile psiFile) { + return new TreeBasedStructureViewBuilder() { + @Override + public @NotNull StructureViewModel createStructureViewModel(@Nullable Editor editor) { + return new LispStructureViewModel(psiFile, editor); + } + }; + } +} diff --git a/src/main/java/com/en_circle/slt/plugin/autocomplete/HeadCompletionProvider.java b/src/main/java/com/en_circle/slt/plugin/autocomplete/HeadCompletionProvider.java index e8fbfff..4ed6d26 100644 --- a/src/main/java/com/en_circle/slt/plugin/autocomplete/HeadCompletionProvider.java +++ b/src/main/java/com/en_circle/slt/plugin/autocomplete/HeadCompletionProvider.java @@ -1,5 +1,6 @@ package com.en_circle.slt.plugin.autocomplete; +import com.en_circle.slt.plugin.SltIconProvider; import com.en_circle.slt.plugin.SymbolState; import com.en_circle.slt.plugin.SymbolState.SymbolBinding; import com.en_circle.slt.plugin.environment.LispFeatures; @@ -15,7 +16,6 @@ import com.intellij.codeInsight.completion.CompletionProvider; import com.intellij.codeInsight.completion.CompletionResultSet; import com.intellij.codeInsight.lookup.LookupElementBuilder; -import com.intellij.icons.AllIcons.Nodes; import com.intellij.openapi.project.Project; import com.intellij.util.ProcessingContext; import org.jetbrains.annotations.NotNull; @@ -45,13 +45,13 @@ protected void addCompletions(@NotNull CompletionParameters parameters, @NotNull .refreshSymbolFromServer(null, str.getValue()); LookupElementBuilder builder = LookupElementBuilder.create(str.getValue()); if (state.binding == SymbolBinding.MACRO) { - builder = builder.withIcon(Nodes.Template); + builder = builder.withIcon(SltIconProvider.MACRO); } else if (state.binding == SymbolBinding.FUNCTION) { - builder = builder.withIcon(Nodes.Function); + builder = builder.withIcon(SltIconProvider.FUNCTION); } else if (state.binding == SymbolBinding.METHOD) { - builder = builder.withIcon(Nodes.Method); + builder = builder.withIcon(SltIconProvider.METHOD); } else { - builder = builder.withIcon(Nodes.Lambda); + builder = builder.withIcon(SltIconProvider.LAMBDA); } builders.add(builder); } diff --git a/src/main/java/com/en_circle/slt/plugin/lisp/Lisp.bnf b/src/main/java/com/en_circle/slt/plugin/lisp/Lisp.bnf index fb37aeb..8107a8d 100644 --- a/src/main/java/com/en_circle/slt/plugin/lisp/Lisp.bnf +++ b/src/main/java/com/en_circle/slt/plugin/lisp/Lisp.bnf @@ -18,7 +18,10 @@ lispFile ::= toplevel* -toplevel ::= sexpr +toplevel ::= sexpr { + implements="com.intellij.psi.NavigatablePsiElement" + methods=[getName getPresentation] +} sexpr ::= (enhancement* datum) | comment diff --git a/src/main/java/com/en_circle/slt/plugin/lisp/Lisp.flex b/src/main/java/com/en_circle/slt/plugin/lisp/Lisp.flex index e84321c..5d08820 100644 --- a/src/main/java/com/en_circle/slt/plugin/lisp/Lisp.flex +++ b/src/main/java/com/en_circle/slt/plugin/lisp/Lisp.flex @@ -49,7 +49,7 @@ IElementType processBuffer(boolean unget) { %state STEP9 %state STEP9ESCAPE -// keep in since with LispLexerUtils! +// keep in sinc with LispLexerUtils! WHITESPACE_CHARACTER=[\r\n\t\ \x0c\x0a] CONSTITUENT_CHARACTER=[!$%&*+\-\./0-9:<=>?@A-Za-z\[\]\^_{}~] TERMINATING_MACRO_CHAR=[\"'\(\),;`] diff --git a/src/main/java/com/en_circle/slt/plugin/lisp/LispLexerUtils.java b/src/main/java/com/en_circle/slt/plugin/lisp/LispLexerUtils.java index d09b031..9ba6f25 100644 --- a/src/main/java/com/en_circle/slt/plugin/lisp/LispLexerUtils.java +++ b/src/main/java/com/en_circle/slt/plugin/lisp/LispLexerUtils.java @@ -11,8 +11,8 @@ public class LispLexerUtils { - // keep in sycn with flex! - private static final Pattern whitespace = Pattern.compile("[\\r\\n\\t \\x0c\\x0a]"); + // keep in sync with flex! + public static final Pattern whitespace = Pattern.compile("[\\r\\n\\t \\x0c\\x0a]"); public static IElementType processToken(LispNumberLexerAdapter lexer, String token) { if (StringUtils.containsOnly(token, ".")) { diff --git a/src/main/java/com/en_circle/slt/plugin/lisp/LispParserUtil.java b/src/main/java/com/en_circle/slt/plugin/lisp/LispParserUtil.java index 67fa17b..4b08b15 100644 --- a/src/main/java/com/en_circle/slt/plugin/lisp/LispParserUtil.java +++ b/src/main/java/com/en_circle/slt/plugin/lisp/LispParserUtil.java @@ -1,5 +1,6 @@ package com.en_circle.slt.plugin.lisp; +import com.en_circle.slt.plugin.SltIconProvider; import com.en_circle.slt.plugin.lisp.lisp.LispUtils; import com.en_circle.slt.plugin.lisp.psi.*; import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService; @@ -13,8 +14,10 @@ import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.containers.Stack; +import javax.swing.*; import java.util.List; import java.util.function.Function; +import java.util.regex.Pattern; public class LispParserUtil extends GeneratedParserUtilBase { @@ -326,7 +329,175 @@ public static QuoteState getQuoteStateUnfinished(PsiElement o) { return quoteState; } + public static LispSexpressionInfo determineTopLevelType(LispSexpr sexpr) { + LispSexpressionInfo info = new LispSexpressionInfo(); + info.type = SexpressionType.EXPRESSION; + info.shortForm = "..."; + + if (sexpr.getDatum() != null && sexpr.getDatum().getList() != null) { + LispList list = sexpr.getDatum().getList(); + List elements = list.getSexprList(); + if (elements.size() > 0) { + LispSexpr head = elements.get(0); + if (head.getDatum() != null && head.getDatum().getCompoundSymbol() != null) { + String headName = head.getText().toLowerCase(); + headName = getSymbolName(headName); + info.shortForm = headName; + if ("defclass".equals(headName)) { + info.type = SexpressionType.DEFCLASS; + info.icon = SltIconProvider.CLASS; + String secondElement = getSecondElement(elements); + info.shortForm += secondElement; + info.identification = secondElement.trim(); + } else if ("defconstant".equals(headName)) { + info.type = SexpressionType.DEFCONSTANT; + info.icon = SltIconProvider.CONSTANT; + String secondElement = getSecondElement(elements); + info.shortForm += secondElement; + info.identification = secondElement.trim(); + } else if ("defgeneric".equals(headName)) { + info.type = SexpressionType.DEFGENERIC; + info.icon = SltIconProvider.METHOD; + String secondElement = getSecondElement(elements); + info.shortForm += secondElement; + info.identification = secondElement.trim(); + info.longForm = getLongForm(elements, 2); + } else if ("defmacro".equals(headName)) { + info.type = SexpressionType.DEFMACRO; + info.icon = SltIconProvider.MACRO; + String secondElement = getSecondElement(elements); + info.shortForm += secondElement; + info.identification = secondElement.trim(); + info.longForm = getLongForm(elements, 2); + } else if ("defmethod".equals(headName)) { + info.type = SexpressionType.DEFMETHOD; + info.icon = SltIconProvider.METHOD; + String secondElement = getSecondElement(elements); + info.shortForm += secondElement; + info.identification = secondElement.trim(); + if (elements.size() > 2) { + LispSexpr potentialArgs = elements.get(2); + if (potentialArgs.getDatum() != null && potentialArgs.getDatum().getCompoundSymbol() != null) { + info.shortForm += " " + potentialArgs.getText(); + info.identification = potentialArgs.getText(); + info.longForm = getLongForm(elements, 3); + } else { + info.longForm = getLongForm(elements, 2); + } + } + } else if ("defpackage".equals(headName)) { + info.type = SexpressionType.DEFPACKAGE; + info.icon = SltIconProvider.PACKAGE; + String secondElement = getSecondElement(elements); + info.shortForm += secondElement; + info.identification = secondElement.trim(); + } else if ("defsetf".equals(headName)) { + info.type = SexpressionType.DEFSETF; + info.icon = SltIconProvider.FUNCTION; + String secondElement = getSecondElement(elements); + info.shortForm += secondElement; + info.identification = secondElement.trim(); + } else if ("defstruct".equals(headName)) { + info.type = SexpressionType.DEFSTRUCT; + info.icon = SltIconProvider.STRUCTURE; + String secondElement = getSecondElement(elements); + info.shortForm += secondElement; + info.identification = secondElement.trim(); + } else if ("deftype".equals(headName)) { + info.type = SexpressionType.DEFTYPE; + info.icon = SltIconProvider.STRUCTURE; + String secondElement = getSecondElement(elements); + info.shortForm += secondElement; + info.identification = secondElement.trim(); + info.longForm = getLongForm(elements, 2); + } else if ("defun".equals(headName)) { + info.type = SexpressionType.DEFUN; + info.icon = SltIconProvider.FUNCTION; + String secondElement = getSecondElement(elements); + info.shortForm += secondElement; + info.identification = secondElement.trim(); + info.longForm = getLongForm(elements, 2); + } else if ("defparameter".equals(headName)) { + info.type = SexpressionType.DEFPARAMETER; + info.icon = SltIconProvider.SPECIAL; + String secondElement = getSecondElement(elements); + info.shortForm += secondElement; + info.identification = secondElement.trim(); + } else if ("defvar".equals(headName)) { + info.type = SexpressionType.DEFVAR; + info.icon = SltIconProvider.SPECIAL; + String secondElement = getSecondElement(elements); + info.shortForm += secondElement; + info.identification = secondElement.trim(); + } + } + } + } + + return info; + } + + private static String getLongForm(List elements, int i) { + if (elements.size() > i) { + String text = elements.get(i).getText(); + return text.replaceAll(LispLexerUtils.whitespace.pattern() + "+", " "); + } + return null; + } + + private static String getSymbolName(String symbol) { + if (symbol.contains("::")) { + return symbol.split(Pattern.quote("::"))[1]; + } else if (symbol.contains(":")) { + return symbol.split(Pattern.quote(":"))[1]; + } + return symbol; + } + + private static String getSecondElement(List elements) { + if (elements.size() > 1) { + return " " + elements.get(1).getText(); + } + return ""; + } + public enum QuoteState { BACKQUOTE, QUOTE, UNQUOTE, UNQUOTE_SPLICE, NO_STATE, ERROR_STATE } + + public enum SexpressionType { + EXPRESSION, + DEFCLASS, DEFCONSTANT, DEFGENERIC, + DEFMACRO, DEFMETHOD, DEFPACKAGE, DEFPARAMETER, DEFSETF, DEFSTRUCT, + DEFTYPE, DEFUN, DEFVAR + } + + public static class LispSexpressionInfo { + + private SexpressionType type; + private String shortForm; + private String identification; + private String longForm; + private Icon icon; + + public SexpressionType getType() { + return type; + } + + public String getShortForm() { + return shortForm == null ? "" : shortForm; + } + + public Icon getIcon() { + return icon; + } + + public String getIdentification() { + return identification; + } + + public String getLongForm() { + return identification + (longForm == null ? "" : ": " + longForm); + } + } } diff --git a/src/main/java/com/en_circle/slt/plugin/lisp/LispPotentialSymbolPresentation.java b/src/main/java/com/en_circle/slt/plugin/lisp/LispPotentialSymbolPresentation.java index 7b7a3ec..e028523 100644 --- a/src/main/java/com/en_circle/slt/plugin/lisp/LispPotentialSymbolPresentation.java +++ b/src/main/java/com/en_circle/slt/plugin/lisp/LispPotentialSymbolPresentation.java @@ -16,7 +16,7 @@ public class LispPotentialSymbolPresentation implements ItemPresentation { private final Project project; - private PsiFile file; + private final PsiFile file; private final String symbol; private final String packageName; private final int offset; diff --git a/src/main/java/com/en_circle/slt/plugin/lisp/LispSymbolPresentation.java b/src/main/java/com/en_circle/slt/plugin/lisp/LispSymbolPresentation.java index 38b0169..332c241 100644 --- a/src/main/java/com/en_circle/slt/plugin/lisp/LispSymbolPresentation.java +++ b/src/main/java/com/en_circle/slt/plugin/lisp/LispSymbolPresentation.java @@ -1,8 +1,8 @@ package com.en_circle.slt.plugin.lisp; +import com.en_circle.slt.plugin.SltIconProvider; import com.en_circle.slt.plugin.SymbolState; import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService; -import com.intellij.icons.AllIcons.Nodes; import com.intellij.navigation.ItemPresentation; import com.intellij.openapi.editor.Document; import com.intellij.openapi.util.NlsSafe; @@ -53,12 +53,12 @@ private SymbolState refreshState() { return switch (state.binding) { case NONE, SPECIAL_FORM -> null; - case FUNCTION -> Nodes.Function; - case MACRO -> Nodes.Annotationtype; - case CONSTANT, KEYWORD -> Nodes.Constant; - case SPECIAL_VARIABLE -> Nodes.Gvariable; - case CLASS -> Nodes.Class; - case METHOD -> Nodes.Method; + case FUNCTION -> SltIconProvider.FUNCTION; + case MACRO -> SltIconProvider.MACRO; + case CONSTANT, KEYWORD -> SltIconProvider.CONSTANT; + case SPECIAL_VARIABLE -> SltIconProvider.SPECIAL; + case CLASS -> SltIconProvider.CLASS; + case METHOD -> SltIconProvider.METHOD; }; } } diff --git a/src/main/java/com/en_circle/slt/plugin/lisp/psi/impl/LispPsiImplUtil.java b/src/main/java/com/en_circle/slt/plugin/lisp/psi/impl/LispPsiImplUtil.java index 7f0f59c..ab88855 100644 --- a/src/main/java/com/en_circle/slt/plugin/lisp/psi/impl/LispPsiImplUtil.java +++ b/src/main/java/com/en_circle/slt/plugin/lisp/psi/impl/LispPsiImplUtil.java @@ -1,17 +1,23 @@ package com.en_circle.slt.plugin.lisp.psi.impl; import com.en_circle.slt.plugin.SltCommonLispFileType; +import com.en_circle.slt.plugin.lisp.LispParserUtil; +import com.en_circle.slt.plugin.lisp.LispParserUtil.LispSexpressionInfo; import com.en_circle.slt.plugin.lisp.LispSymbolPresentation; import com.en_circle.slt.plugin.lisp.psi.*; import com.intellij.lang.ASTNode; import com.intellij.navigation.ItemPresentation; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.NlsSafe; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFileFactory; import com.intellij.psi.PsiReference; import com.intellij.psi.PsiReferenceService.Hints; import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry; import com.intellij.psi.util.PsiTreeUtil; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; public class LispPsiImplUtil { @@ -60,6 +66,10 @@ public static String getName(LispComment element) { return element.getText(); } + public static String getName(LispToplevel element) { + return LispParserUtil.determineTopLevelType(element.getSexpr()).getShortForm(); + } + public static PsiElement setName(LispComment element, String newName) { ASTNode commentLineNode = element.getNode().findChildByType(LispTypes.LINE_COMMENT); if (commentLineNode != null) { @@ -134,4 +144,19 @@ public static ItemPresentation getPresentation(LispSymbol symbol) { return new LispSymbolPresentation(symbol); } + public static ItemPresentation getPresentation(LispToplevel toplevel) { + LispSexpressionInfo type = LispParserUtil.determineTopLevelType(toplevel.getSexpr()); + return new ItemPresentation() { + @Override + public @NlsSafe @Nullable String getPresentableText() { + return type.getShortForm(); + } + + @Override + public @Nullable Icon getIcon(boolean unused) { + return type.getIcon(); + } + }; + } + } \ No newline at end of file diff --git a/src/main/java/com/en_circle/slt/plugin/structure/LispStructureViewElement.java b/src/main/java/com/en_circle/slt/plugin/structure/LispStructureViewElement.java new file mode 100644 index 0000000..e3c8f55 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/structure/LispStructureViewElement.java @@ -0,0 +1,106 @@ +package com.en_circle.slt.plugin.structure; + +import com.en_circle.slt.plugin.SltIconProvider; +import com.en_circle.slt.plugin.lisp.LispParserUtil; +import com.en_circle.slt.plugin.lisp.LispParserUtil.LispSexpressionInfo; +import com.en_circle.slt.plugin.lisp.LispParserUtil.SexpressionType; +import com.en_circle.slt.plugin.lisp.psi.LispFile; +import com.en_circle.slt.plugin.lisp.psi.LispToplevel; +import com.intellij.ide.structureView.StructureViewTreeElement; +import com.intellij.ide.util.treeView.smartTree.SortableTreeElement; +import com.intellij.ide.util.treeView.smartTree.TreeElement; +import com.intellij.navigation.ItemPresentation; +import com.intellij.openapi.util.NlsSafe; +import com.intellij.psi.NavigatablePsiElement; +import com.intellij.psi.util.PsiTreeUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.ArrayList; +import java.util.List; + +public class LispStructureViewElement implements StructureViewTreeElement, SortableTreeElement { + + private final NavigatablePsiElement psiElement; + private final LispSexpressionInfo sexpressionInfo; + private final ItemPresentation presentation; + + public LispStructureViewElement(NavigatablePsiElement psiElement, LispSexpressionInfo sexpressionInfo) { + this.psiElement = psiElement; + this.sexpressionInfo = sexpressionInfo; + presentation = new ItemPresentation() { + @Override + public @NlsSafe @Nullable String getPresentableText() { + if (psiElement instanceof LispFile file) { + return file.getName(); + } else if (sexpressionInfo != null) { + return sexpressionInfo.getLongForm(); + } + return null; + } + + @Override + public @Nullable Icon getIcon(boolean unused) { + if (psiElement instanceof LispFile) { + return SltIconProvider.getFileIcon(); + } else if (sexpressionInfo != null) { + return sexpressionInfo.getIcon(); + } + return null; + } + }; + } + + @Override + public Object getValue() { + return psiElement; + } + + @Override + public @NotNull String getAlphaSortKey() { + if (sexpressionInfo != null) { + return sexpressionInfo.getIdentification(); + } + return ""; + } + + @Override + public @NotNull ItemPresentation getPresentation() { + return presentation; + } + + @Override + public TreeElement @NotNull [] getChildren() { + if (psiElement instanceof LispFile) { + List elementList = new ArrayList<>(); + LispToplevel[] toplevels = PsiTreeUtil.getChildrenOfType(psiElement, LispToplevel.class); + if (toplevels != null) { + for (LispToplevel toplevel : toplevels) { + LispSexpressionInfo sexpressionInfo = LispParserUtil.determineTopLevelType(toplevel.getSexpr()); + if (sexpressionInfo.getType() != SexpressionType.EXPRESSION) { + elementList.add(new LispStructureViewElement(toplevel, sexpressionInfo)); + } + } + } + + return elementList.toArray(new TreeElement[0]); + } + return EMPTY_ARRAY; + } + + @Override + public void navigate(boolean requestFocus) { + psiElement.navigate(requestFocus); + } + + @Override + public boolean canNavigate() { + return psiElement.canNavigate(); + } + + @Override + public boolean canNavigateToSource() { + return psiElement.canNavigateToSource(); + } +} diff --git a/src/main/java/com/en_circle/slt/plugin/structure/LispStructureViewModel.java b/src/main/java/com/en_circle/slt/plugin/structure/LispStructureViewModel.java new file mode 100644 index 0000000..31483a8 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/structure/LispStructureViewModel.java @@ -0,0 +1,38 @@ +package com.en_circle.slt.plugin.structure; + +import com.en_circle.slt.plugin.lisp.psi.LispToplevel; +import com.intellij.ide.structureView.StructureViewModel; +import com.intellij.ide.structureView.StructureViewModelBase; +import com.intellij.ide.structureView.StructureViewTreeElement; +import com.intellij.ide.util.treeView.smartTree.Sorter; +import com.intellij.openapi.editor.Editor; +import com.intellij.psi.PsiFile; +import org.jetbrains.annotations.NotNull; + +public class LispStructureViewModel extends StructureViewModelBase implements + StructureViewModel.ElementInfoProvider { + + public LispStructureViewModel(PsiFile psiFile, Editor editor) { + super(psiFile, editor, new LispStructureViewElement(psiFile, null)); + } + + @NotNull + public Sorter @NotNull [] getSorters() { + return new Sorter[]{Sorter.ALPHA_SORTER}; + } + + @Override + public boolean isAlwaysLeaf(StructureViewTreeElement element) { + return element.getValue() instanceof LispToplevel; + } + + @Override + public boolean isAlwaysShowsPlus(StructureViewTreeElement element) { + return false; + } + + @Override + protected Class @NotNull [] getSuitableClasses() { + return new Class[]{ LispToplevel.class }; + } +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 0014e47..4e17997 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -105,17 +105,18 @@ - - - - + + + + From e4fb9a8b346932c6d6d8a360736ab532cebcbe2e Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Sat, 11 Feb 2023 21:47:23 +0100 Subject: [PATCH 38/40] plugin update --- FEATURES.md | 2 +- src/main/resources/META-INF/plugin.xml | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/FEATURES.md b/FEATURES.md index 48014a3..4f2148e 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -29,7 +29,7 @@ Unsupported and will not be supported implementations: | Autocomplete | ✅ | ✅ | ✅ | ✅ | ✅ | | Find References | ✅ | ❎ | ✅️ | ❎ | ❎ | | Function Arguments | ✅ | ✅ | ✅️ | ✅️ | ✅️ | -| Automatic Download - Windows | ✅ | ❓ | ❓ | ❓ | ❓ | +| Automatic Download - Windows | ✅ | ❎ | ❓ | ❓ | ❓ | ¹Only read-only inspector available diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 4e17997..32ff9b6 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -168,17 +168,17 @@ 0.3.1 New features:

-

- Thread list with actions
-- Showing function parameters with ctrl+p

- -

0.3.0 New features:

- -

- Current package at editor cursor widget
-- Automatic Indentation
-- SDK support, Automatic download for Windows users
-- References
-- Global class/symbol search

+

0.4.0 New features:

+

- Support for multiple lisp interprets
+- ABCL Support
+- CCL Support
+- AllegroCL Support
+- CMUCL Support
+- Breakpoint support
+- Spellchecker support
+- Folding support, structure support, navigation bar

+ +See CHANGELOG.md for more details ]]>
\ No newline at end of file From 4c68ce191b46d77ce73dc930b597f3964c6d65d9 Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Sat, 11 Feb 2023 21:49:56 +0100 Subject: [PATCH 39/40] prepare for build --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 87b2c79..d0179b6 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,4 @@ bin/ .idea /.run/Build Release.run.xml +/temporary/ From 320790b23cdc151cff284abe49267d032e64ff50 Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Sat, 11 Feb 2023 21:51:59 +0100 Subject: [PATCH 40/40] prepare for build --- CHANGELOG.md | 2 +- README.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c770541..3b15b04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.4.0 +## 0.4.0 230211 ### Added diff --git a/README.md b/README.md index f202927..6e430e5 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ [![0.2.0](https://badgen.net/github/milestones/enerccio/SLT/2)](https://github.com/enerccio/SLT/milestone/2) [![0.3.0](https://badgen.net/github/milestones/enerccio/SLT/4)](https://github.com/enerccio/SLT/milestone/4) [![0.4.0](https://badgen.net/github/milestones/enerccio/SLT/5)](https://github.com/enerccio/SLT/milestone/5) +[![0.5.0](https://badgen.net/github/milestones/enerccio/SLT/6)](https://github.com/enerccio/SLT/milestone/6) ![GitHub all releases](https://img.shields.io/github/downloads/Enerccio/SLT/total) ![GitHub last commit](https://img.shields.io/github/last-commit/Enerccio/SLT) ![GitHub commit activity](https://img.shields.io/github/commit-activity/m/Enerccio/SLT)