From da7213ef242fadf4e3bd1aede9e3033059f55729 Mon Sep 17 00:00:00 2001 From: Peter Vanusanik Date: Mon, 8 May 2023 12:08:42 +0200 Subject: [PATCH] #70 --- CHANGELOG.md | 5 +- README.md | 19 +-- .../slt/plugin/SltDocumentationProvider.java | 24 +--- .../plugin/actions/Macroexpand1Action.java | 29 +++++ .../slt/plugin/actions/MacroexpandAction.java | 29 +++++ .../plugin/actions/MacroexpandActionBase.java | 122 ++++++++++++++++++ .../plugin/actions/MacroexpandAllAction.java | 29 +++++ .../slt/plugin/actions/MacroexpandGroup.java | 42 ++++++ .../services/lisp/LispEnvironmentService.java | 3 - .../lisp/LispEnvironmentServiceImpl.java | 22 +--- .../SltLispEnvironmentMacroExpandCache.java | 90 ------------- .../slt/plugin/swank/SlimeListener.java | 8 ++ .../slt/plugin/swank/SwankPacket.java | 28 ++++ .../plugin/swank/requests/Macroexpand.java | 49 +++++++ .../plugin/swank/requests/Macroexpand1.java | 49 +++++++ .../plugin/swank/requests/MacroexpandAll.java | 2 +- src/main/resources/META-INF/plugin.xml | 44 +++++-- .../resources/messages/SltBundle.properties | 7 +- 18 files changed, 444 insertions(+), 157 deletions(-) create mode 100644 src/main/java/com/en_circle/slt/plugin/actions/Macroexpand1Action.java create mode 100644 src/main/java/com/en_circle/slt/plugin/actions/MacroexpandAction.java create mode 100644 src/main/java/com/en_circle/slt/plugin/actions/MacroexpandActionBase.java create mode 100644 src/main/java/com/en_circle/slt/plugin/actions/MacroexpandAllAction.java create mode 100644 src/main/java/com/en_circle/slt/plugin/actions/MacroexpandGroup.java delete mode 100644 src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltLispEnvironmentMacroExpandCache.java create mode 100644 src/main/java/com/en_circle/slt/plugin/swank/requests/Macroexpand.java create mode 100644 src/main/java/com/en_circle/slt/plugin/swank/requests/Macroexpand1.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ddc7a5..bf32300 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.5.1 +## 0.5.1 230508 ### Added - Symbol inspection @@ -6,6 +6,9 @@ - More browser options - Evaluate top level action +### Changes +- Macroexpand is now action and menu, no longer automatic! + ### Fixes - Added symbols with no file into all search places diff --git a/README.md b/README.md index 7ea13a6..779aeb4 100644 --- a/README.md +++ b/README.md @@ -95,26 +95,21 @@ You can also open this as a project in Intellij Idea. * [x] Breakpoints * [x] Documentation * [x] Hyperspec intergration -* [x] Macro expand in documentation - * Macro expand requires you to hover element twice for now +* [x] Macro expand (all, 1, normal) * [x] Find function by symbol name * [x] Search for symbols * [x] Back references +* [x] Rainbow braces * [ ] Refactoring * [ ] List of quicklisp installed packages / ASDF packages * [ ] List of modified top level forms that are yet to be evaluated * [ ] Actually make an IDE, ie just plugin with dependencies as one application, not a plugin - -### Far futures / possible goals - -* [x] SDK Support - * not a true SDK because that is only available in Intellij and not in (for instance) PyCharm, thus -this is implemented manually. - * [x] Download SBCL and quicklisp for user +* [x] SDK Support + * not a true SDK because that is only available in Intellij and not in (for instance) PyCharm, thus + this is implemented manually. + * [x] Download SBCL and quicklisp for user * [x] Automatic download of lisp interpret and quicklisp -* [x] Different lisp interpreter support -* [ ] Remote connections to interpreters -* [ ] Rewrite everything into ABCL just for purity’s sake lol +* [x] Different lisp interpreter support ## License 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 e8c2530..bed54ec 100644 --- a/src/main/java/com/en_circle/slt/plugin/SltDocumentationProvider.java +++ b/src/main/java/com/en_circle/slt/plugin/SltDocumentationProvider.java @@ -1,9 +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; import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService; import com.intellij.lang.documentation.AbstractDocumentationProvider; @@ -68,7 +66,7 @@ public class SltDocumentationProvider extends AbstractDocumentationProvider { String text = element.getText(); String packageName = LispParserUtil.getPackage(element); SymbolState state = LispEnvironmentService.getInstance(element.getProject()).refreshSymbolFromServer(packageName, text); - return asHtml(state, packageName, element); + return asHtml(state, element); } } return null; @@ -82,7 +80,7 @@ private PsiElement decideOnElement(PsiElement element, PsiElement originalElemen return originalElement; } - private String asHtml(SymbolState state, String packageName, PsiElement element) { + private String asHtml(SymbolState state, PsiElement element) { HtmlBuilder builder = new HtmlBuilder(); if (LispEnvironmentService.getInstance(element.getProject()).hasFeature(LispFeatures.DOCUMENTATION)) { String documentation = StringUtils.replace(StringUtils.replace(escape(state.documentation), " ", " "), @@ -91,24 +89,6 @@ private String asHtml(SymbolState state, String packageName, PsiElement element) HtmlChunk.raw(documentation)); } - 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(escape(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"))); - } - } - } - String doc = builder.toString(); return StringUtils.isBlank(doc) ? null : doc; } diff --git a/src/main/java/com/en_circle/slt/plugin/actions/Macroexpand1Action.java b/src/main/java/com/en_circle/slt/plugin/actions/Macroexpand1Action.java new file mode 100644 index 0000000..8c500c3 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/actions/Macroexpand1Action.java @@ -0,0 +1,29 @@ +package com.en_circle.slt.plugin.actions; + +import com.en_circle.slt.plugin.SltBundle; +import com.en_circle.slt.plugin.lisp.lisp.LispString; +import com.en_circle.slt.plugin.lisp.lisp.LispUtils; +import com.en_circle.slt.plugin.lisp.psi.LispList; +import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService; +import com.en_circle.slt.plugin.swank.requests.Macroexpand1; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Messages; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Macroexpand1Action extends MacroexpandActionBase { + private static final Logger log = LoggerFactory.getLogger(Macroexpand1Action.class); + + @Override + protected void macroexpand(Project project, LispList form, String packageName, MacroexpandCallback callback) { + try { + LispEnvironmentService service = LispEnvironmentService.getInstance(project); + service.sendToLisp(new Macroexpand1(form.getText(), packageName, result -> { + callback.showMacroexpand(LispUtils.unescape(((LispString) result).getValue())); + }), true); + } catch (Exception e) { + log.warn(SltBundle.message("slt.error.start"), e); + Messages.showErrorDialog(project, e.getMessage(), SltBundle.message("slt.ui.errors.lisp.start")); + } + } +} diff --git a/src/main/java/com/en_circle/slt/plugin/actions/MacroexpandAction.java b/src/main/java/com/en_circle/slt/plugin/actions/MacroexpandAction.java new file mode 100644 index 0000000..a32e3c6 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/actions/MacroexpandAction.java @@ -0,0 +1,29 @@ +package com.en_circle.slt.plugin.actions; + +import com.en_circle.slt.plugin.SltBundle; +import com.en_circle.slt.plugin.lisp.lisp.LispString; +import com.en_circle.slt.plugin.lisp.lisp.LispUtils; +import com.en_circle.slt.plugin.lisp.psi.LispList; +import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService; +import com.en_circle.slt.plugin.swank.requests.Macroexpand; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Messages; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MacroexpandAction extends MacroexpandActionBase { + private static final Logger log = LoggerFactory.getLogger(MacroexpandAction.class); + + @Override + protected void macroexpand(Project project, LispList form, String packageName, MacroexpandCallback callback) { + try { + LispEnvironmentService service = LispEnvironmentService.getInstance(project); + service.sendToLisp(new Macroexpand(form.getText(), packageName, result -> { + callback.showMacroexpand(LispUtils.unescape(((LispString) result).getValue())); + }), true); + } catch (Exception e) { + log.warn(SltBundle.message("slt.error.start"), e); + Messages.showErrorDialog(project, e.getMessage(), SltBundle.message("slt.ui.errors.lisp.start")); + } + } +} diff --git a/src/main/java/com/en_circle/slt/plugin/actions/MacroexpandActionBase.java b/src/main/java/com/en_circle/slt/plugin/actions/MacroexpandActionBase.java new file mode 100644 index 0000000..129d82f --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/actions/MacroexpandActionBase.java @@ -0,0 +1,122 @@ +package com.en_circle.slt.plugin.actions; + +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.lisp.LispParserUtil; +import com.en_circle.slt.plugin.lisp.psi.LispList; +import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService; +import com.intellij.openapi.actionSystem.ActionUpdateThread; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.ex.EditorEx; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.popup.JBPopup; +import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.util.text.HtmlBuilder; +import com.intellij.openapi.util.text.HtmlChunk; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import org.apache.commons.lang.StringEscapeUtils; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import javax.swing.text.html.HTMLEditorKit; +import java.awt.*; +import java.util.Objects; + +public abstract class MacroexpandActionBase extends AnAction { + + protected abstract void macroexpand(Project project, LispList form, String packageName, MacroexpandCallback callback); + + @Override + public void actionPerformed(@NotNull AnActionEvent event) { + EditorEx editor = (EditorEx) event.getData(CommonDataKeys.EDITOR); + LispList list = null; + + if (editor != null) { + PsiDocumentManager psiMgr = PsiDocumentManager.getInstance(Objects.requireNonNull(editor.getProject())); + psiMgr.commitDocument(editor.getDocument()); + PsiFile psiFile = psiMgr.getPsiFile(editor.getDocument()); + if (psiFile != null) { + int caretOffset = editor.getExpectedCaretOffset(); + list = findList(psiFile, caretOffset, editor.getDocument()); + if (list != null) { + editor.getSelectionModel().setSelection(list.getTextOffset(), list.getTextOffset() + list.getTextLength()); + } + } + + if (list != null) { + int offset = list.getTextOffset(); + String packageName = LispParserUtil.getPackage(psiFile, offset); + macroexpand(editor.getProject(), list, packageName, text -> SwingUtilities.invokeLater(() -> { + HtmlBuilder builder = new HtmlBuilder(); + String macroExpand = StringUtils.replace(StringUtils.replace(StringEscapeUtils.escapeHtml(text), " ", " "), + "\n", HtmlChunk.br().toString()); + builder.append(HtmlChunk.raw(macroExpand)); + + JEditorPane editorPane = new JEditorPane("text/html", ""); + editorPane.setEditorKit(new HTMLEditorKit()); + editorPane.setText(builder.toString()); + + JPanel textPanel = new JPanel(new BorderLayout()); + textPanel.add(editorPane, BorderLayout.CENTER); + + JBPopup popup = JBPopupFactory.getInstance() + .createComponentPopupBuilder(textPanel, null) + .setProject(editor.getProject()) + .setTitle(SltBundle.message("slt.documentation.macroexpand")) + .setShowBorder(true) + .setMovable(true) + .setFocusable(true) + .setRequestFocus(true) + .createPopup(); + popup.showInBestPositionFor(editor.getDataContext()); + })); + } + } + } + + protected LispList findList(PsiFile psiFile, int caretOffset, Document document) { + PsiElement element = psiFile.findElementAt(caretOffset); + if (element != null) { + PsiElement parent = element; + while (parent != null) { + parent = parent.getParent(); + if (parent instanceof LispList) { + return (LispList) parent; + } + } + } + return null; + } + + @Override + public void update(@NotNull AnActionEvent event) { + event.getPresentation().setEnabledAndVisible(false); + Editor editor = event.getData(CommonDataKeys.EDITOR); + 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(LispEnvironmentService.getInstance(event.getProject()) + .hasFeature(LispFeatures.MACROEXPAND)); + } + } + } + + @Override + public @NotNull ActionUpdateThread getActionUpdateThread() { + return ActionUpdateThread.BGT; + } + + protected interface MacroexpandCallback { + + void showMacroexpand(String text); + + } +} diff --git a/src/main/java/com/en_circle/slt/plugin/actions/MacroexpandAllAction.java b/src/main/java/com/en_circle/slt/plugin/actions/MacroexpandAllAction.java new file mode 100644 index 0000000..c10ae54 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/actions/MacroexpandAllAction.java @@ -0,0 +1,29 @@ +package com.en_circle.slt.plugin.actions; + +import com.en_circle.slt.plugin.SltBundle; +import com.en_circle.slt.plugin.lisp.lisp.LispString; +import com.en_circle.slt.plugin.lisp.lisp.LispUtils; +import com.en_circle.slt.plugin.lisp.psi.LispList; +import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService; +import com.en_circle.slt.plugin.swank.requests.MacroexpandAll; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Messages; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MacroexpandAllAction extends MacroexpandActionBase { + private static final Logger log = LoggerFactory.getLogger(MacroexpandAllAction.class); + + @Override + protected void macroexpand(Project project, LispList form, String packageName, MacroexpandCallback callback) { + try { + LispEnvironmentService service = LispEnvironmentService.getInstance(project); + service.sendToLisp(new MacroexpandAll(form.getText(), packageName, result -> { + callback.showMacroexpand(LispUtils.unescape(((LispString) result).getValue())); + }), true); + } catch (Exception e) { + log.warn(SltBundle.message("slt.error.start"), e); + Messages.showErrorDialog(project, e.getMessage(), SltBundle.message("slt.ui.errors.lisp.start")); + } + } +} diff --git a/src/main/java/com/en_circle/slt/plugin/actions/MacroexpandGroup.java b/src/main/java/com/en_circle/slt/plugin/actions/MacroexpandGroup.java new file mode 100644 index 0000000..2f40169 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/actions/MacroexpandGroup.java @@ -0,0 +1,42 @@ +package com.en_circle.slt.plugin.actions; + +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.intellij.openapi.actionSystem.ActionUpdateThread; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.editor.Editor; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiFile; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +public class MacroexpandGroup extends DefaultActionGroup { + + public MacroexpandGroup() { + super(() -> SltBundle.message("action.slt.actions.macroexpand.group.text"), true); + } + + @Override + public void update(@NotNull AnActionEvent event) { + event.getPresentation().setEnabledAndVisible(false); + Editor editor = event.getData(CommonDataKeys.EDITOR); + 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(LispEnvironmentService.getInstance(event.getProject()) + .hasFeature(LispFeatures.MACROEXPAND)); + } + } + } + + @Override + public @NotNull ActionUpdateThread getActionUpdateThread() { + return ActionUpdateThread.BGT; + } + +} 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 c9400f1..e860bec 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,7 +5,6 @@ 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; -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.services.lisp.components.SltLispEnvironmentSymbolCache.BatchedSymbolRefreshAction; import com.en_circle.slt.plugin.swank.SlimeListener.DebugInterface; @@ -55,8 +54,6 @@ static LispEnvironmentService getInstance(Project project) { SltLispEnvironment getEnvironment(); - String macroexpand(LispList form, String packageName); - void updateIndentation(LispElement element); Integer calculateOffset(PsiElement element, PsiFile file, boolean wasAfter, String text, int offset, String packageOverride); 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 d6c69a9..dfba7ec 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 @@ -8,11 +8,13 @@ import com.en_circle.slt.plugin.environment.SltLispEnvironmentConfiguration; 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.psi.LispList; 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.*; +import com.en_circle.slt.plugin.services.lisp.components.SltBreakpoint; +import com.en_circle.slt.plugin.services.lisp.components.SltBreakpointContainer; +import com.en_circle.slt.plugin.services.lisp.components.SltIndentationContainer; +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.swank.SlimeListener; import com.en_circle.slt.plugin.swank.SlimeListener.DebugInterface; @@ -63,7 +65,6 @@ public class LispEnvironmentServiceImpl implements LispEnvironmentService { private final Project project; private final SltIndentationContainer indentationContainer; private final SltLispEnvironmentSymbolCache symbolCache; - private final SltLispEnvironmentMacroExpandCache macroExpandCache; private final SltBreakpointContainer breakpointContainer; public LispEnvironmentServiceImpl(Project project) { @@ -71,7 +72,6 @@ public LispEnvironmentServiceImpl(Project project) { indentationContainer = new SltIndentationContainer(); indentationContainer.init(project); symbolCache = new SltLispEnvironmentSymbolCache(project); - macroExpandCache = new SltLispEnvironmentMacroExpandCache(); breakpointContainer = new SltBreakpointContainer(project); symbolCache.start(); @@ -330,23 +330,9 @@ public SltLispEnvironment getEnvironment() { return environment; } - @Override - public String macroexpand(LispList form, String packageName) { - try { - return macroExpandCache.macroexpand(form, packageName); - } catch (Exception e) { - log.error(e.getMessage()); - log.debug(e.getMessage(), e); - } - return null; - } - @Override public void updateIndentation(LispElement element) { indentationContainer.update((LispContainer) element); - - // also clear macro cache since we get this hit on macro update - macroExpandCache.clear(); } @Override diff --git a/src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltLispEnvironmentMacroExpandCache.java b/src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltLispEnvironmentMacroExpandCache.java deleted file mode 100644 index 599affb..0000000 --- a/src/main/java/com/en_circle/slt/plugin/services/lisp/components/SltLispEnvironmentMacroExpandCache.java +++ /dev/null @@ -1,90 +0,0 @@ -package com.en_circle.slt.plugin.services.lisp.components; - -import com.en_circle.slt.plugin.lisp.lisp.LispString; -import com.en_circle.slt.plugin.lisp.lisp.LispUtils; -import com.en_circle.slt.plugin.lisp.psi.LispList; -import com.en_circle.slt.plugin.services.lisp.LispEnvironmentService; -import com.en_circle.slt.plugin.swank.requests.MacroexpandAll; -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiFile; -import org.jetbrains.annotations.NotNull; - -import java.util.Objects; - -public class SltLispEnvironmentMacroExpandCache { - - private final LoadingCache expandedMacros; - - public SltLispEnvironmentMacroExpandCache() { - CacheLoader loader = new CacheLoader<>() { - @Override - public MacroExpandEntry load(@NotNull SltLispEnvironmentMacroExpandCache.MacroExpandEntry key) throws Exception { - LispEnvironmentService.getInstance(key.project).sendToLisp( - MacroexpandAll.macroexpand(key.form, key.packageName, result -> { - key.evaluated = LispUtils.unescape(((LispString) result).getValue()); - })); - return key; - } - }; - expandedMacros = CacheBuilder.newBuilder() - .maximumSize(512) - .build(loader); - } - - - public String macroexpand(LispList form, String packageName) throws Exception { - MacroExpandEntry entry = new MacroExpandEntry(); - entry.form = form.getNode().getText(); - PsiFile file = form.getContainingFile(); - if (file != null) { - entry.virtualFile = file.getVirtualFile(); - entry.modification = file.getModificationStamp(); - entry.packageName = packageName; - entry.project = form.getProject(); - MacroExpandEntry cachedEntry = expandedMacros.get(entry); - if (cachedEntry.modification < entry.modification) { - expandedMacros.refresh(cachedEntry); - } - return expandedMacros.get(entry).evaluated; - } - return null; - } - - public void clear() { - expandedMacros.invalidateAll(); - } - - private static class MacroExpandEntry { - - public Project project; - private String form; - private String packageName; - private String evaluated; - private VirtualFile virtualFile; - private long modification; - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - MacroExpandEntry entry = (MacroExpandEntry) o; - - if (!Objects.equals(form, entry.form)) return false; - if (!Objects.equals(packageName, entry.packageName)) return false; - return Objects.equals(virtualFile, entry.virtualFile); - } - - @Override - public int hashCode() { - int result = form != null ? form.hashCode() : 0; - result = 31 * result + (packageName != null ? packageName.hashCode() : 0); - result = 31 * result + (virtualFile != null ? virtualFile.hashCode() : 0); - return result; - } - } -} 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 cfc4647..d624734 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 @@ -155,6 +155,14 @@ private void processReturn(LispContainer reply) { macroexpandAll.processReply((LispContainer) reply.getItems().get(1)); } + if (request instanceof Macroexpand macroexpand) { + macroexpand.processReply((LispContainer) reply.getItems().get(1)); + } + + if (request instanceof Macroexpand1 macroexpand1) { + macroexpand1.processReply((LispContainer) reply.getItems().get(1)); + } + if (request instanceof SimpleCompletion simpleCompletion) { simpleCompletion.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 52de57f..5f3cb0c 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 @@ -277,11 +277,39 @@ public static SwankPacket loadFile(String file, String breakpoints, String packa return new SwankPacket(formatted); } + public static SwankPacket macroexpand1(String form, String packageName, BigInteger continuation) { + return macroexpand1(form, "T", packageName, continuation); + } + + public static SwankPacket macroexpand1(String form, String threadId, String packageName, BigInteger continuation) { + form = StringUtils.replace(form, "\\", "\\\\"); + form = StringUtils.replace(form, "\"", "\\\""); + packageName = StringUtils.replace(packageName, "\\", "\\\\"); + packageName = StringUtils.replace(packageName, "\"", "\\\""); + String formatted = String.format("(:emacs-rex (swank:swank-macroexpand-1 \"%s\") \":%s\" %s %s)", + form, packageName, threadId, continuation); + return new SwankPacket(formatted); + } + public static SwankPacket macroexpand(String form, String packageName, BigInteger continuation) { return macroexpand(form, "T", packageName, continuation); } public static SwankPacket macroexpand(String form, String threadId, String packageName, BigInteger continuation) { + form = StringUtils.replace(form, "\\", "\\\\"); + form = StringUtils.replace(form, "\"", "\\\""); + packageName = StringUtils.replace(packageName, "\\", "\\\\"); + packageName = StringUtils.replace(packageName, "\"", "\\\""); + String formatted = String.format("(:emacs-rex (swank:swank-macroexpand \"%s\") \":%s\" %s %s)", + form, packageName, threadId, continuation); + return new SwankPacket(formatted); + } + + public static SwankPacket macroexpandAll(String form, String packageName, BigInteger continuation) { + return macroexpandAll(form, "T", packageName, continuation); + } + + public static SwankPacket macroexpandAll(String form, String threadId, String packageName, BigInteger continuation) { form = StringUtils.replace(form, "\\", "\\\\"); form = StringUtils.replace(form, "\"", "\\\""); packageName = StringUtils.replace(packageName, "\\", "\\\\"); diff --git a/src/main/java/com/en_circle/slt/plugin/swank/requests/Macroexpand.java b/src/main/java/com/en_circle/slt/plugin/swank/requests/Macroexpand.java new file mode 100644 index 0000000..3e8db6a --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/swank/requests/Macroexpand.java @@ -0,0 +1,49 @@ +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 com.intellij.openapi.project.Project; + +import java.math.BigInteger; + +public class Macroexpand extends SlimeRequest { + + public static SlimeRequest macroexpand(String form, String module, Callback callback) { + return new Macroexpand(form, module, callback); + } + + private final Callback callback; + private final String form; + private final String module; + + public Macroexpand(String form, String module, Callback callback) { + this.form = form; + this.module = module; + this.callback = callback; + } + + 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, Project project) { + return SwankPacket.macroexpand(form, module, requestId); + } + + public interface Callback { + void onResult(LispElement result); + } + +} diff --git a/src/main/java/com/en_circle/slt/plugin/swank/requests/Macroexpand1.java b/src/main/java/com/en_circle/slt/plugin/swank/requests/Macroexpand1.java new file mode 100644 index 0000000..4716558 --- /dev/null +++ b/src/main/java/com/en_circle/slt/plugin/swank/requests/Macroexpand1.java @@ -0,0 +1,49 @@ +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 com.intellij.openapi.project.Project; + +import java.math.BigInteger; + +public class Macroexpand1 extends SlimeRequest { + + public static SlimeRequest macroexpand(String form, String module, Callback callback) { + return new Macroexpand1(form, module, callback); + } + + private final Callback callback; + private final String form; + private final String module; + + public Macroexpand1(String form, String module, Callback callback) { + this.form = form; + this.module = module; + this.callback = callback; + } + + 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, Project project) { + return SwankPacket.macroexpand1(form, module, requestId); + } + + public interface Callback { + void onResult(LispElement result); + } + +} 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 0c8563e..c7d3033 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 @@ -39,7 +39,7 @@ private boolean isOk(LispContainer data) { @Override public SwankPacket createPacket(BigInteger requestId, Project project) { - return SwankPacket.macroexpand(form, module, requestId); + return SwankPacket.macroexpandAll(form, module, requestId); } public interface Callback { diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index d018fb7..edb5dd8 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -145,17 +145,42 @@ + + + + + + + + + + + + + + + + + + + - + - + @@ -209,13 +234,16 @@ 0.5.0 New features:


-

Hyperspec embedded in the tool window (requires internet connection to see obviously)


+

0.5.1 New features:


+

Symbol inspection


+

Rainbow braces option


+

More CLHS browser options


+

Evaluate top level action


+
+

0.5.1 Changes and fixes:


+

Macroexpand is now action and menu, no longer automatic!


+

Added symbols with no file into all search places



-

0.5.0 Changes and fixes:


-

Support for definitions under other expressions


-

SltPlainTextSymbolCompletionContributor - to be used with git and such


- See CHANGELOG.md for more details ]]>
diff --git a/src/main/resources/messages/SltBundle.properties b/src/main/resources/messages/SltBundle.properties index 62c71bd..e4f22f6 100644 --- a/src/main/resources/messages/SltBundle.properties +++ b/src/main/resources/messages/SltBundle.properties @@ -23,9 +23,8 @@ slt.documentation.types.constant=Constant Form slt.documentation.types.specvariable=Special Variable slt.documentation.types.keyword=Keyword slt.documentation.types.class=Class -slt.documentation.macroexpand=Macro expansion: +slt.documentation.macroexpand=Macro Expansion: slt.documentation.types.method=Method -slt.documentation.macroexpand.generating=Generating macro expansion, hover again to see it # UI slt.ui.errors.lisp.start=Failed to Start Lisp @@ -226,6 +225,10 @@ slt.ui.statusbar.name=Current Package slt.ui.statusbar.info=Current Package: # Actions +action.slt.actions.macroexpand.group.text=Macroexpand +action.slt.actions.macroexpand.all.text=Macroexpand All +action.slt.actions.macroexpand.one.text=Macroexpand 1 +action.slt.actions.macroexpand.normal.text=Macroexpand action.slt.actions.eval.toplevel.text=Evaluate Top Level Expression action.slt.actions.eval.region.text=Evaluate Selected Region action.slt.actions.eval.previous.text=Evaluate Previous S-Expression