Java REPL support for JDK9 (project Kulla, JShell) (IDEA-161611)

This commit is contained in:
Eugene Zhuravlev
2017-07-04 20:11:12 +02:00
parent 7b4c9e0b8b
commit 1cf632c195
46 changed files with 3008 additions and 7 deletions
+1
View File
@@ -10,6 +10,7 @@
<file url="file://$PROJECT_DIR$/jps/jps-builders-6/src/org/jetbrains/jps/javac/OptimizedFileManager.java" />
<file url="file://$PROJECT_DIR$/jps/jps-builders-6/src/org/jetbrains/jps/javac/OptimizedFileManager17.java" />
<file url="file://$PROJECT_DIR$/java/java-tests/testSrc/com/intellij/index/IndexTestGenerator.scala" />
<file url="file://$PROJECT_DIR$/java/execution/jshell-frontend/src/com/intellij/execution/jshell/frontend/Main.java" />
</excludeFromCompile>
<resourceExtensions>
<entry name=".+\.(properties|gif|png|jpeg|jpg|xml|dtd|tld|xsd|ft|html|template)" />
+9
View File
@@ -0,0 +1,9 @@
<component name="libraryTable">
<library name="precompiled_jshell-frontend">
<CLASSES>
<root url="jar://$PROJECT_DIR$/lib/jshell-frontend.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>
+2
View File
@@ -157,6 +157,8 @@
<module fileurl="file://$PROJECT_DIR$/jps/model-impl/jps-model-tests.iml" filepath="$PROJECT_DIR$/jps/model-impl/jps-model-tests.iml" group="jps" />
<module fileurl="file://$PROJECT_DIR$/jps/model-serialization/jps-serialization-tests.iml" filepath="$PROJECT_DIR$/jps/model-serialization/jps-serialization-tests.iml" group="jps" />
<module fileurl="file://$PROJECT_DIR$/jps/standalone-builder/jps-standalone-builder.iml" filepath="$PROJECT_DIR$/jps/standalone-builder/jps-standalone-builder.iml" group="jps" />
<module fileurl="file://$PROJECT_DIR$/java/execution/jshell-frontend/jshell-frontend.iml" filepath="$PROJECT_DIR$/java/execution/jshell-frontend/jshell-frontend.iml" group="java" />
<module fileurl="file://$PROJECT_DIR$/java/execution/jshell-protocol/jshell-protocol.iml" filepath="$PROJECT_DIR$/java/execution/jshell-protocol/jshell-protocol.iml" group="java" />
<module fileurl="file://$PROJECT_DIR$/json/json.iml" filepath="$PROJECT_DIR$/json/json.iml" group="json" />
<module fileurl="file://$PROJECT_DIR$/json/tests/json-tests.iml" filepath="$PROJECT_DIR$/json/tests/json-tests.iml" group="json" />
<module fileurl="file://$PROJECT_DIR$/java/jsp-base-openapi/jsp-base-openapi.iml" filepath="$PROJECT_DIR$/java/jsp-base-openapi/jsp-base-openapi.iml" group="java" />
+2
View File
@@ -17,7 +17,9 @@
<orderEntry type="module" module-name="java-impl" />
<orderEntry type="module" module-name="java-indexing-api" />
<orderEntry type="module" module-name="smRunner" />
<orderEntry type="module" module-name="jshell-protocol" />
<orderEntry type="library" name="Coverage" level="project" />
<orderEntry type="library" scope="RUNTIME" name="precompiled_jshell-frontend" level="project" />
</component>
<component name="copyright">
<Base>
@@ -0,0 +1,64 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.execution.jshell;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
/**
* @author Eugene Zhuravlev
* Date: 06-Jun-17
*/
class DropJShellStateAction extends AnAction{
private static final Logger LOG = Logger.getInstance("#com.intellij.execution.jshell.ExecuteJShellAction");
private static final AnAction ourInstance = new DropJShellStateAction();
private DropJShellStateAction() {
super(AllIcons.Actions.Delete);
}
@Override
public void actionPerformed(AnActionEvent e) {
final Project project = e.getProject();
if (project == null) {
return;
}
final VirtualFile vFile = CommonDataKeys.VIRTUAL_FILE.getData(e.getDataContext());
if (vFile == null) {
return;
}
try {
final JShellHandler handler = JShellHandler.getAssociatedHandler(vFile);
if (handler != null) {
handler.toFront();
handler.dropState();
}
}
catch (Exception ex) {
LOG.info(ex);
}
}
public static AnAction getSharedInstance() {
return ourInstance;
}
}
@@ -0,0 +1,159 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.execution.jshell;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ex.util.EditorUtil;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.util.DocumentUtil;
import org.jetbrains.annotations.Nullable;
/**
* @author Eugene Zhuravlev
* Date: 06-Jun-17
*/
class ExecuteJShellAction extends AnAction{
private static final Logger LOG = Logger.getInstance("#com.intellij.execution.jshell.ExecuteJShellAction");
private static final AnAction ourInstance = new ExecuteJShellAction();
private ExecuteJShellAction() {
super(AllIcons.Toolwindows.ToolWindowRun);
}
@Override
public void actionPerformed(AnActionEvent e) {
final Project project = e.getProject();
if (project == null) {
return;
}
final Editor editor = CommonDataKeys.EDITOR.getData(e.getDataContext());
if (editor == null) {
return;
}
final VirtualFile vFile = CommonDataKeys.VIRTUAL_FILE.getData(e.getDataContext());
if (vFile == null) {
return;
}
FileDocumentManager.getInstance().saveAllDocuments();
final Document document = editor.getDocument();
final TextRange selectedRange = EditorUtil.getSelectionInAnyMode(editor);
String code = null;
if (selectedRange.isEmpty()) {
final PsiElement snippet = getSnippetFromContext(project, e);
if (snippet != null) {
code = snippet.getText();
}
}
else {
code = document.getText(selectedRange);
}
if (StringUtil.isEmptyOrSpaces(code)) {
JShellDiagnostic.notifyInfo("Nothing to execute", project);
return;
}
try {
JShellHandler handler = JShellHandler.getAssociatedHandler(vFile);
if (handler == null) {
handler = JShellHandler.create(project, vFile, e.getData(LangDataKeys.MODULE));
}
if (handler != null) {
handler.toFront();
handler.evaluate(code);
}
}
catch (Exception ex) {
JShellDiagnostic.notifyError(ex, project);
}
}
@Nullable
private static PsiElement getSnippetFromContext(Project project, AnActionEvent e) {
final Editor editor = e.getData(CommonDataKeys.EDITOR);
if (editor != null) {
final PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
if (file instanceof PsiJShellFile) {
final PsiElement context = getContextElement(file, editor.getDocument(), editor.getCaretModel().getOffset());
for (PsiElement element = context; element != null; element = element.getParent()) {
if (element instanceof PsiJShellImportHolder || element instanceof PsiJShellHolderMethod) {
return element;
}
if (element instanceof PsiMember && ((PsiMember)element).getContainingClass() instanceof PsiJShellRootClass) {
return element;
}
if (element instanceof PsiClass) {
final PsiClass containingClass = ((PsiClass)element).getContainingClass();
if (containingClass == null || containingClass instanceof PsiJShellRootClass) {
return element;
}
}
}
return file;
}
}
return null;
}
private static PsiElement getContextElement(PsiFile file, final Document doc, final int offset) {
final int begin = DocumentUtil.getLineStartOffset(offset, doc);
final int end = DocumentUtil.getLineEndOffset(offset, doc);
PsiElement result = null;
for (int off = begin; off <= end; off++) {
result = file.findElementAt(off);
if (result != null) {
break;
}
}
while (result instanceof PsiWhiteSpace) {
final PsiElement next = result.getNextSibling();
if (next == null || next.getTextOffset() > end) {
break;
}
result = next;
}
while (result != null) {
final PsiElement parent = result.getParent();
if (parent instanceof PsiJShellSyntheticElement) {
break;
}
result = parent;
}
return result;
}
public static AnAction getSharedInstance() {
return ourInstance;
}
}
@@ -0,0 +1,37 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.execution.jshell;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationType;
import com.intellij.openapi.project.Project;
/**
* @author Eugene Zhuravlev
* Date: 04-Jul-17
*/
public class JShellDiagnostic {
private static final String NOTIFICATION_GROUP = "JSHELL_NOTIFICATIONS";
private static final String TITLE = "JShell";
public static void notifyInfo(final String text, final Project project) {
new Notification(NOTIFICATION_GROUP, TITLE, text, NotificationType.INFORMATION).notify(project);
}
public static void notifyError(Exception ex, final Project project) {
new Notification(NOTIFICATION_GROUP, TITLE, ex.getMessage(), NotificationType.ERROR).notify(project);
}
}
@@ -0,0 +1,447 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.execution.jshell;
import com.intellij.execution.ExecutionManager;
import com.intellij.execution.Executor;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.execution.executors.DefaultRunExecutor;
import com.intellij.execution.impl.ConsoleState;
import com.intellij.execution.impl.ConsoleViewImpl;
import com.intellij.execution.impl.ConsoleViewRunningState;
import com.intellij.execution.jshell.protocol.*;
import com.intellij.execution.jshell.protocol.Event;
import com.intellij.execution.process.*;
import com.intellij.execution.ui.ConsoleViewContentType;
import com.intellij.execution.ui.RunContentDescriptor;
import com.intellij.execution.ui.actions.CloseAction;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.FileEditorManagerListener;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.JavaSdkType;
import com.intellij.openapi.projectRoots.JavaSdkVersion;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.OrderEnumerator;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.concurrency.SequentialTaskExecutor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.ide.PooledThreadExecutor;
import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
/**
* @author Eugene Zhuravlev
* Date: 01-Jun-17
*/
public class JShellHandler {
private static final Logger LOG = Logger.getInstance("#com.intellij.execution.jshell.JShellHandler");
public static final Key<JShellHandler> MARKER_KEY = Key.create("JShell console key");
private static final Charset ourCharset = StandardCharsets.UTF_8;
private static final Executor EXECUTOR = DefaultRunExecutor.getRunExecutorInstance();
private static final String JSHELL_FRONTEND_JAR = "jshell-frontend.jar";
private final Project myProject;
private final RunContentDescriptor myRunContent;
private final ConsoleViewImpl myConsoleView;
private final OSProcessHandler myProcess;
private final MessageReader<Response> myMessageReader;
private final MessageWriter<Request> myMessageWriter;
private final ExecutorService myTaskQueue = new SequentialTaskExecutor("JShell Command Queue", PooledThreadExecutor.INSTANCE);
private JShellHandler(@NotNull Project project,
RunContentDescriptor descriptor,
ConsoleViewImpl view,
VirtualFile contentFile,
OSProcessHandler handler) throws Exception {
myProject = project;
myRunContent = descriptor;
myConsoleView = view;
myProcess = handler;
final PipedInputStream is = new PipedInputStream();
final OutputStreamWriter readerSink = new OutputStreamWriter(new PipedOutputStream(is));
myMessageReader = new MessageReader<>(is, Response.class);
myMessageWriter = new MessageWriter<>(handler.getProcessInput(), Request.class);
handler.addProcessListener(new ProcessAdapter() {
@Override
public void onTextAvailable(ProcessEvent event, Key outputType) {
if (outputType == ProcessOutputTypes.STDOUT) {
try {
readerSink.write(event.getText());
readerSink.flush();
}
catch (IOException e) {
LOG.info(e);
}
}
else {
myConsoleView.print(event.getText(), outputType == ProcessOutputTypes.STDERR? ConsoleViewContentType.ERROR_OUTPUT : ConsoleViewContentType.SYSTEM_OUTPUT);
}
}
@Override
public void processTerminated(ProcessEvent event) {
if (getAssociatedHandler(contentFile) == JShellHandler.this) {
// process terminated either by closing file or by close action
contentFile.putUserData(MARKER_KEY, null);
}
}
});
project.getMessageBus().connect().subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, new FileEditorManagerListener() {
@Override
public void fileClosed(@NotNull FileEditorManager source, @NotNull VirtualFile file) {
if (file.equals(contentFile)) {
// if file was closed then kill process and hide console content
JShellHandler.this.stop();
}
}
});
contentFile.putUserData(MARKER_KEY, this);
view.attachToProcess(handler);
}
@Nullable
public static JShellHandler getAssociatedHandler(VirtualFile contentFile) {
return contentFile != null? contentFile.getUserData(MARKER_KEY) : null;
}
public static JShellHandler create(@NotNull final Project project, @NotNull final VirtualFile contentFile, @Nullable Module module) throws Exception{
final OSProcessHandler processHandler = launchProcess(project, module);
final String title = "JShell " + contentFile.getNameWithoutExtension();
final ConsoleViewImpl consoleView = new MyConsoleView(project);
final RunContentDescriptor descriptor = new RunContentDescriptor(consoleView, processHandler, new JPanel(new BorderLayout()), title);
final JShellHandler jshellHandler = new JShellHandler(project, descriptor, consoleView, contentFile, processHandler);
// must call getComponent before createConsoleActions()
final JComponent consoleViewComponent = consoleView.getComponent();
final DefaultActionGroup actionGroup = new DefaultActionGroup();
//actionGroup.add(new BuildAndRestartConsoleAction(module, project, defaultExecutor, descriptor, restarter(project, contentFile)));
//actionGroup.addSeparator();
actionGroup.addAll(consoleView.createConsoleActions());
actionGroup.add(new CloseAction(EXECUTOR, descriptor, project) {
@Override
public void actionPerformed(AnActionEvent e) {
jshellHandler.stop();
if (!processHandler.waitFor(10000)) {
processHandler.destroyProcess();
}
super.actionPerformed(e);
}
});
final ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, actionGroup, false);
toolbar.setTargetComponent(consoleViewComponent);
final JComponent ui = descriptor.getComponent();
ui.add(consoleViewComponent, BorderLayout.CENTER);
ui.add(toolbar.getComponent(), BorderLayout.WEST);
processHandler.startNotify();
ExecutionManager.getInstance(project).getContentManager().showRunContent(EXECUTOR, descriptor);
return jshellHandler;
}
// todo: do we need to include project's compiled classes into the classpath or libraries only?
// todo: if we include project classes, make sure they are compiled
private static OSProcessHandler launchProcess(@NotNull Project project, @Nullable Module module) throws Exception{
final Sdk sdk = module != null? ModuleRootManager.getInstance(module).getSdk() : ProjectRootManager.getInstance(project).getProjectSdk();
if (sdk == null || !(sdk.getSdkType() instanceof JavaSdkType)) {
throw new ExecException(
(sdk != null ? "Expected Java SDK" : " SDK is not configured") +
(module != null? " for module " + module.getName() : " for project " + project.getName())
);
}
final JavaSdkType javaSdkType = (JavaSdkType)sdk.getSdkType();
final String ver = sdk.getVersionString();
final JavaSdkVersion sdkVersion = ver == null? null : JavaSdkVersion.fromVersionString(ver);
if (sdkVersion == null) {
throw new ExecException("Cannot determine version for JDK " + sdk.getName() + ". Please re-configure the JDK.");
}
if (!sdkVersion.isAtLeast(JavaSdkVersion.JDK_1_9)) {
throw new ExecException("JDK version is " + sdkVersion.getDescription() + ". JDK 9 or higher is needed to run JShell.");
}
final String vmExePath = javaSdkType.getVMExecutablePath(sdk);
if (vmExePath == null) {
throw new ExecException("Cannot determine path to VM executable for JDK " + sdk.getName() + ". Please re-configure the JDK.");
}
final File executableFile = new File(vmExePath);
final String frontEndPath = findFrontEndLibrary();
if (frontEndPath == null) {
throw new ExecException("Library " + JSHELL_FRONTEND_JAR + " not found in IDE classpath");
}
final GeneralCommandLine cmdLine = new GeneralCommandLine();
cmdLine.setExePath(executableFile.getAbsolutePath());
cmdLine.setWorkDirectory(executableFile.getParent());
cmdLine.setCharset(ourCharset);
cmdLine.addParameter("--add-modules");
cmdLine.addParameter("java.xml.bind");
final StringBuilder launchCp = new StringBuilder().append(frontEndPath);
final String protocolJar = getLibPath(Endpoint.class);
if (protocolJar != null) {
launchCp.append(File.pathSeparator).append(protocolJar);
}
if (launchCp.length() > 0) {
cmdLine.addParameter("-classpath");
cmdLine.addParameter(launchCp.toString());
}
cmdLine.addParameter("com.intellij.execution.jshell.frontend.Main");
final Set<File> cp = new LinkedHashSet<>();
final Computable<OrderEnumerator> orderEnumerator = module != null ? () -> ModuleRootManager.getInstance(module).orderEntries()
: () -> ProjectRootManager.getInstance(project).orderEntries();
ApplicationManager.getApplication().runReadAction(() -> {
for (String s : orderEnumerator.compute().librariesOnly().recursively().withoutSdk().getPathsList().getPathList()) {
cp.add(new File(s));
}
});
cmdLine.addParameter("--class-path");
cmdLine.addParameter(StringUtil.join(cp, File.pathSeparator));
return new OSProcessHandler(cmdLine);
}
private static String findFrontEndLibrary() {
final String path = PathManager.getResourceRoot(JShellHandler.class.getClassLoader(), "/com/intellij/execution/jshell/frontend/Marker.class");
return path != null? path : JSHELL_FRONTEND_JAR;
}
private static String getLibPath(final Class<?> aClass) {
return PathManager.getResourceRoot(aClass, "/" + aClass.getName().replace('.', '/') + ".class");
}
public void stop() {
myProcess.destroyProcess(); // use force
ExecutionManager.getInstance(myProject).getContentManager().removeRunContent(EXECUTOR, myRunContent);
}
public void toFront() {
ExecutionManager.getInstance(myProject).getContentManager().toFrontRunContent(EXECUTOR, myRunContent);
}
@Nullable
public Future<Response> evaluate(@NotNull String code) {
return StringUtil.isEmptyOrSpaces(code) ? null : myTaskQueue.submit(() -> sendInput(new Request(nextUid(), Request.Command.EVAL, code)));
}
public void dropState() {
myTaskQueue.submit(() -> sendInput(new Request(nextUid(), Request.Command.DROP_STATE, null)));
}
private static String nextUid() {
return UUID.randomUUID().toString();
}
@Nullable
private Response sendInput(final Request request) {
final boolean alive = !myProcess.isProcessTerminating() && !myProcess.isProcessTerminated();
if (alive) {
myConsoleView.performWhenNoDeferredOutput(() -> {
try {
myMessageWriter.send(request);
}
catch (IOException e) {
LOG.info(e);
}
});
try {
final StringBuffer stdOut = new StringBuffer();
final Response response = myMessageReader.receive(unparsedText -> stdOut.append(unparsedText));
renderResponse(response, stdOut.toString());
return response;
}
catch (IOException e) {
LOG.info(e);
}
}
return null;
}
private void renderResponse(Response response, String stdOut) {
//myConsoleView.print("\n-------------------evaluation " + response.getUid() + "------------------------", ConsoleViewContentType.NORMAL_OUTPUT);
final List<Event> events = response.getEvents();
if (events != null) {
for (Event event : events) {
if (event.getCauseSnippet() == null) {
final String exception = event.getExceptionText();
if (!StringUtil.isEmptyOrSpaces(exception)) {
myConsoleView.print("\n" + exception, ConsoleViewContentType.SYSTEM_OUTPUT);
}
final String diagnostic = event.getDiagnostic();
if (!StringUtil.isEmptyOrSpaces(diagnostic)) {
myConsoleView.print("\n" + diagnostic, ConsoleViewContentType.SYSTEM_OUTPUT);
}
final String descr = getEventDescription(event);
if (!StringUtil.isEmptyOrSpaces(descr)) {
myConsoleView.print("\n" + descr, ConsoleViewContentType.SYSTEM_OUTPUT);
}
final CodeSnippet snippet = event.getSnippet();
final String value = snippet != null && !snippet.getSubKind().hasValue()? null : event.getValue();
if (value != null) {
myConsoleView.print(" = " + (value.isEmpty()? "\"\"" : value), ConsoleViewContentType.NORMAL_OUTPUT);
}
}
}
}
else {
myConsoleView.print("\nCompleted.", ConsoleViewContentType.SYSTEM_OUTPUT);
}
if (!StringUtil.isEmpty(stdOut)) {
//myConsoleView.print("\n-----evaluation output-----\n", ConsoleViewContentType.NORMAL_OUTPUT);
myConsoleView.print("\n", ConsoleViewContentType.NORMAL_OUTPUT);
// delegate unparsed text directly to console
if (!"\n".equals(stdOut) /*hack to ignore possible empty line before 'message-begin' merker*/) {
myConsoleView.print(stdOut, ConsoleViewContentType.NORMAL_OUTPUT);
}
}
}
private static String getEventDescription(Event event) {
final CodeSnippet snippet = event.getSnippet();
if (event.getCauseSnippet() != null || snippet == null) {
return "";
}
final CodeSnippet.Status status = event.getStatus();
final CodeSnippet.Kind kind = snippet.getKind();
final CodeSnippet.SubKind subKind = snippet.getSubKind();
String presentation = snippet.getPresentation();
if (presentation == null || subKind == CodeSnippet.SubKind.TEMP_VAR_EXPRESSION_SUBKIND) {
presentation = StringUtil.trim(snippet.getCodeText());
}
String actionLabel;
if (event.getPreviousStatus() == CodeSnippet.Status.NONEXISTENT && status.isDefined()) {
if (subKind == CodeSnippet.SubKind.VAR_DECLARATION_WITH_INITIALIZER_SUBKIND ||
/*subKind == CodeSnippet.SubKind.TEMP_VAR_EXPRESSION_SUBKIND ||*/
!subKind.isExecutable()) {
actionLabel = "Defined";
}
else {
actionLabel = "";
}
}
else if (status == CodeSnippet.Status.REJECTED){
actionLabel = "Rejected";
}
else if (status == CodeSnippet.Status.DROPPED) {
actionLabel = "Dropped";
}
else if (status == CodeSnippet.Status.OVERWRITTEN) {
actionLabel = "Overwritten";
}
else {
actionLabel = "";
}
String kindLabel;
if (kind == CodeSnippet.Kind.TYPE_DECL) {
if (subKind == CodeSnippet.SubKind.INTERFACE_SUBKIND) {
kindLabel = "interface";
}
else if (subKind == CodeSnippet.SubKind.ENUM_SUBKIND) {
kindLabel = "enum";
}
else if (subKind == CodeSnippet.SubKind.ANNOTATION_TYPE_SUBKIND) {
kindLabel = "annotation";
}
else {
kindLabel = "class";
}
}
else if (kind == CodeSnippet.Kind.VAR){
kindLabel = subKind == CodeSnippet.SubKind.TEMP_VAR_EXPRESSION_SUBKIND ? ""/*"temp var"*/ : "field";
}
else if (kind == CodeSnippet.Kind.METHOD) {
kindLabel = "method";
}
else if (kind == CodeSnippet.Kind.IMPORT) {
kindLabel = subKind == CodeSnippet.SubKind.STATIC_IMPORT_ON_DEMAND_SUBKIND || subKind == CodeSnippet.SubKind.SINGLE_STATIC_IMPORT_SUBKIND ? "static import" : "import";
}
else {
kindLabel = "";
}
final StringBuilder descr = new StringBuilder();
descr.append(actionLabel);
if (!actionLabel.isEmpty()) {
descr.append(" ");
}
if (!kindLabel.isEmpty()) {
descr.append(kindLabel).append(" ");
}
descr.append(presentation);
return descr.toString();
}
private static class MyConsoleView extends ConsoleViewImpl {
public MyConsoleView(Project project) {
super(project, GlobalSearchScope.allScope(project), true, new ConsoleState.NotStartedStated() {
@NotNull
@Override
public ConsoleState attachTo(@NotNull ConsoleViewImpl console, ProcessHandler processHandler) {
// do not automatically display all the text that is sent/recieved between processes
// the ootput from console will be formatted and sent to console view
return new ConsoleViewRunningState(console, processHandler, this, false, false);
}
}, true);
}
}
private static final class ExecException extends Exception {
public ExecException(String message) {
super(message);
}
public ExecException(String message, Throwable cause) {
super(message, cause);
}
@Override
public synchronized Throwable fillInStackTrace() {
return this;
}
}
}
@@ -0,0 +1,65 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.execution.jshell;
import com.intellij.execution.console.ConsoleRootType;
import com.intellij.ide.highlighter.JShellFileType;
import com.intellij.openapi.actionSystem.CommonShortcuts;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.TextEditor;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
/**
* @author Eugene Zhuravlev
* Date: 09-May-17
*/
public final class JShellRootType extends ConsoleRootType {
public static final String CONTENT_ID = "jshell_console";
public JShellRootType() {
super("jshell", "JShell Console");
}
@NotNull
public static JShellRootType getInstance() {
return findByClass(JShellRootType.class);
}
@NotNull
@Override
public String getDefaultFileExtension() {
return JShellFileType.DEFAULT_EXTENSION;
}
@NotNull
@Override
public String getContentPathName(@NotNull String id) {
assert id == CONTENT_ID;
return CONTENT_ID;
}
@Override
public void fileOpened(@NotNull final VirtualFile file, @NotNull FileEditorManager source) {
for (FileEditor fileEditor : source.getAllEditors(file)) {
if (fileEditor instanceof TextEditor) {
ExecuteJShellAction.getSharedInstance().registerCustomShortcutSet(CommonShortcuts.CTRL_ENTER, fileEditor.getComponent());
}
}
}
}
@@ -0,0 +1,61 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.execution.jshell;
import com.intellij.execution.console.ConsoleHistoryController;
import com.intellij.ide.scratch.ScratchFileService;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
/**
* @author Eugene Zhuravlev
* Date: 09-May-17
*/
public class LaunchJShellConsoleAction extends AnAction{
public LaunchJShellConsoleAction() {
}
@Override
public void actionPerformed(AnActionEvent e) {
final Project project = e.getProject();
if (project == null) {
return;
}
final VirtualFile contentFile = ConsoleHistoryController.getContentFile(
JShellRootType.getInstance(),
JShellRootType.CONTENT_ID,
ScratchFileService.Option.create_new_always
);
assert contentFile != null;
try {
FileEditorManager.getInstance(project).openFile(contentFile, true);
JShellHandler.create(project, contentFile, e.getData(LangDataKeys.MODULE));
}
catch (Exception ex) {
JShellDiagnostic.notifyError(ex, project);
}
}
@Override
public void update(AnActionEvent e) {
super.update(e);
}
}
@@ -0,0 +1,62 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.execution.jshell;
import com.intellij.ide.scratch.RootType;
import com.intellij.ide.scratch.ScratchFileService;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.ActionToolbar;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.editor.impl.EditorHeaderComponent;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.EditorNotifications;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
/**
* @author Eugene Zhuravlev
* Date: 06-Jun-17
*/
public class SnippetEditorDecorator extends EditorNotifications.Provider<JComponent>{
public static final Key<JComponent> CONTEXT_KEY = Key.create("jshell.editor.toolbar");
@NotNull
@Override
public Key<JComponent> getKey() {
return CONTEXT_KEY;
}
@Nullable
@Override
public JComponent createNotificationPanel(@NotNull VirtualFile file, @NotNull FileEditor fileEditor) {
final RootType root = ScratchFileService.getInstance().getRootType(file);
if ((root instanceof JShellRootType)) {
final DefaultActionGroup actions = new DefaultActionGroup(ExecuteJShellAction.getSharedInstance(), DropJShellStateAction.getSharedInstance());
final ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar("JShellSnippetEditor", actions, true);
final EditorHeaderComponent header = new EditorHeaderComponent();
header.add(toolbar.getComponent());
return header;
}
return null;
}
}
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="jshell-protocol" />
</component>
</module>
@@ -0,0 +1,200 @@
package com.intellij.execution.jshell.frontend;
import com.intellij.execution.jshell.protocol.*;
import jdk.jshell.*;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.function.Consumer;
/**
* @author Eugene Zhuravlev
* Date: 12-Jun-17
*
* @noinspection UseOfSystemOutOrSystemErr
*/
public class Main {
private static final String ARG_CLASSPATH = "--class-path";
private static final Consumer<String> NULL_CONSUMER = s -> {};
//private static Request createTestRequest() {
// return new Request(UUID.randomUUID().toString(), Request.Command.EVAL, "int a = 77;\n" +
// "int b = a + 3");
//}
public static void main(String[] args) throws Exception {
final MessageReader<Request> reader = new MessageReader<>(new BufferedInputStream(System.in), Request.class);
final MessageWriter<Response> writer = new MessageWriter<>(new BufferedOutputStream(System.out), Response.class);
try (JShell shell = JShell.create()) {
configureJShell(args, shell);
while (true) {
final Request request = reader.receive(NULL_CONSUMER);
if (request == null) {
break;
}
final Request.Command command = request.getCommand();
if (command == Request.Command.EXIT) {
return;
}
final Response response = new Response();
response.setUid(request.getUid());
try {
if (command == Request.Command.DROP_STATE) {
shell.snippets().forEach(snippet -> exportEvents(shell, shell.drop(snippet), response));
}
else if (command == Request.Command.EVAL) {
final List<String> toEval = new ArrayList<>();
String input = request.getCodeText();
do {
final SourceCodeAnalysis.CompletionInfo info = shell.sourceCodeAnalysis().analyzeCompletion(input);
final SourceCodeAnalysis.Completeness completeness = info.completeness();
if (completeness.isComplete()) {
toEval.add(completeness == SourceCodeAnalysis.Completeness.COMPLETE_WITH_SEMI ? info.source() + ";" : info.source());
}
else if (completeness != SourceCodeAnalysis.Completeness.EMPTY){
// we try to evaluate even the snippets containing errors so that those errors will be displayed
toEval.add(info.source());
}
input = info.remaining();
}
while (input != null && !input.isEmpty());
for (String inputElement : toEval) {
exportEvents(shell, shell.eval(inputElement), response);
}
}
}
finally {
writer.send(response);
}
}
}
catch (Throwable e) {
e.printStackTrace();
}
finally {
System.out.println("\nJShell terminated.");
}
}
private static void exportEvents(JShell shell, List<SnippetEvent> events, Response response) {
for (SnippetEvent event : events) {
final Snippet.Status status = event.status();
final Snippet snippet = event.snippet();
final Event e = new Event(
createCodeSnippet(snippet),
createCodeSnippet(event.causeSnippet()),
convertEnum(status, CodeSnippet.Status.class),
convertEnum(event.previousStatus(), CodeSnippet.Status.class),
event.value()
);
//noinspection ThrowableNotThrown
final JShellException exception = event.exception();
if (exception != null) {
e.setExceptionText(exception.getMessage());
}
if (status == Snippet.Status.RECOVERABLE_DEFINED || status == Snippet.Status.RECOVERABLE_NOT_DEFINED || status == Snippet.Status.REJECTED) {
final StringBuilder buf = new StringBuilder();
shell.diagnostics(snippet).forEach(diagnostic -> {
final String message = diagnostic.getMessage(Locale.US);
if (message != null && !message.isEmpty()) {
if (buf.length() > 0){
buf.append("\n");
}
if (diagnostic.isError()) {
buf.append("ERROR: ");
}
buf.append(message);
}
});
if (buf.length() > 0) {
e.setDiagnostic(buf.toString());
}
}
response.addEvent(e);
}
}
private static void configureJShell(String[] args, JShell shell) {
// todo: add more parameters if needed
boolean cpFound = false;
for (String arg : args) {
if (ARG_CLASSPATH.equals(arg)) {
cpFound = true;
}
else {
if (cpFound) {
cpFound = false;
for (String path : arg.split(File.pathSeparator)) {
shell.addToClasspath(path);
}
}
}
}
}
private static CodeSnippet createCodeSnippet(Snippet snippet) {
return snippet == null ? null : new CodeSnippet(
snippet.id(),
convertEnum(snippet.kind(), CodeSnippet.Kind.class),
convertEnum(snippet.subKind(), CodeSnippet.SubKind.class),
snippet.source(),
createPresentation(snippet)
);
}
private static String createPresentation(Snippet snippet) {
if (snippet instanceof ExpressionSnippet) {
final ExpressionSnippet expr = (ExpressionSnippet)snippet;
return expr.typeName() + " " + expr.name();
}
if (snippet instanceof ImportSnippet) {
return ((ImportSnippet)snippet).fullname();
}
if (snippet instanceof MethodSnippet) {
final MethodSnippet methodSnippet = (MethodSnippet)snippet;
final StringBuilder buf = new StringBuilder(getReturnType(methodSnippet));
if (buf.length() > 0) {
buf.append(" ");
}
return buf.append(methodSnippet.name()).append("(").append(methodSnippet.parameterTypes()).append(")").toString();
}
if (snippet instanceof TypeDeclSnippet) {
return ((TypeDeclSnippet)snippet).name();
}
if (snippet instanceof VarSnippet) {
final VarSnippet varSnippet = (VarSnippet)snippet;
return varSnippet.typeName() + " " + varSnippet.name();
}
if (snippet instanceof PersistentSnippet) {
return ((PersistentSnippet)snippet).name();
}
return null;
}
private static String getReturnType(MethodSnippet methodSnippet) {
final String sig = methodSnippet.signature();
final int idx = sig == null? -1 : sig.lastIndexOf(")");
return idx > 0? sig.substring(idx + 1) : "";
}
private static <TI extends Enum<TI>, TO extends Enum<TO>> TO convertEnum(Enum<TI> from, Class<TO> toEnumOfClass) {
if (from != null) {
try {
return Enum.valueOf(toEnumOfClass, from.name());
}
catch (IllegalArgumentException ignored) {
}
}
return Enum.valueOf(toEnumOfClass, "UNKNOWN");
}
}
@@ -0,0 +1,8 @@
package com.intellij.execution.jshell.frontend;
/**
* @author Eugene Zhuravlev
* Date: 13-Jun-17
*/
public interface Marker {
}
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/testStrc" isTestSource="true" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" scope="TEST" name="JUnit4" level="project" />
</component>
</module>
@@ -0,0 +1,200 @@
package com.intellij.execution.jshell.protocol;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlEnum;
import javax.xml.bind.annotation.XmlType;
/**
* @author Eugene Zhuravlev
* Date: 14-Jun-17
*/
@XmlType
public class CodeSnippet {
@XmlEnum
public enum Status {
VALID(true, true),
RECOVERABLE_DEFINED(true, true),
RECOVERABLE_NOT_DEFINED(true, false),
DROPPED(false, false),
OVERWRITTEN(false, false),
REJECTED(false, false),
NONEXISTENT(false, false),
UNKNOWN (false, false);
private final boolean myIsActive;
private final boolean myIsDefined;
Status(boolean isActive, boolean isDefined) {
this.myIsActive = isActive;
this.myIsDefined = isDefined;
}
public boolean isActive() {
return myIsActive;
}
public boolean isDefined() {
return myIsDefined;
}
}
@XmlEnum
public enum Kind {
IMPORT(true),
TYPE_DECL(true),
METHOD(true),
VAR(true),
EXPRESSION(false),
STATEMENT(false),
ERRONEOUS(false),
UNKNOWN(false);
private final boolean isPersistent;
Kind(boolean isPersistent) {
this.isPersistent = isPersistent;
}
public boolean isPersistent() {
return isPersistent;
}
}
@XmlEnum
public enum SubKind {
SINGLE_TYPE_IMPORT_SUBKIND(Kind.IMPORT),
TYPE_IMPORT_ON_DEMAND_SUBKIND(Kind.IMPORT),
SINGLE_STATIC_IMPORT_SUBKIND(Kind.IMPORT),
STATIC_IMPORT_ON_DEMAND_SUBKIND(Kind.IMPORT),
CLASS_SUBKIND(Kind.TYPE_DECL),
INTERFACE_SUBKIND(Kind.TYPE_DECL),
ENUM_SUBKIND(Kind.TYPE_DECL),
ANNOTATION_TYPE_SUBKIND(Kind.TYPE_DECL),
METHOD_SUBKIND(Kind.METHOD),
VAR_DECLARATION_SUBKIND(Kind.VAR),
VAR_DECLARATION_WITH_INITIALIZER_SUBKIND(Kind.VAR, true, true),
TEMP_VAR_EXPRESSION_SUBKIND(Kind.VAR, true, true),
VAR_VALUE_SUBKIND(Kind.EXPRESSION, true, true),
ASSIGNMENT_SUBKIND(Kind.EXPRESSION, true, true),
OTHER_EXPRESSION_SUBKIND(Kind.EXPRESSION, true, true),
STATEMENT_SUBKIND(Kind.STATEMENT, true, false),
UNKNOWN_SUBKIND(Kind.ERRONEOUS, false, false);
private final boolean isExecutable;
private final boolean hasValue;
private final Kind kind;
SubKind(Kind kind) {
this.kind = kind;
this.isExecutable = false;
this.hasValue = false;
}
SubKind(Kind kind, boolean isExecutable, boolean hasValue) {
this.kind = kind;
this.isExecutable = isExecutable;
this.hasValue = hasValue;
}
public boolean isExecutable() {
return isExecutable;
}
public boolean hasValue() {
return hasValue;
}
public Kind kind() {
return kind;
}
}
private String myId;
private Kind myKind;
private SubKind mySubKind;
private String myCodeText;
private String myPresentation;
public CodeSnippet() {
}
public CodeSnippet(String id, Kind kind, SubKind subKind, String codeText, String presentation) {
myId = id;
myKind = kind;
mySubKind = subKind;
myCodeText = codeText;
myPresentation = presentation;
}
public String getId() {
return myId;
}
@XmlAttribute
public void setId(String id) {
myId = id;
}
public Kind getKind() {
return myKind;
}
@XmlAttribute
public void setKind(Kind kind) {
myKind = kind;
}
public SubKind getSubKind() {
return mySubKind;
}
@XmlAttribute
public void setSubKind(SubKind subKind) {
mySubKind = subKind;
}
public String getCodeText() {
return myCodeText;
}
@XmlElement
public void setCodeText(String codeText) {
myCodeText = codeText;
}
public String getPresentation() {
return myPresentation;
}
@XmlElement
public void setPresentation(String presentation) {
myPresentation = presentation;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CodeSnippet snippet = (CodeSnippet)o;
if (myId != null ? !myId.equals(snippet.myId) : snippet.myId != null) return false;
if (myKind != snippet.myKind) return false;
if (mySubKind != snippet.mySubKind) return false;
if (myCodeText != null ? !myCodeText.equals(snippet.myCodeText) : snippet.myCodeText != null) return false;
if (myPresentation != null ? !myPresentation.equals(snippet.myPresentation) : snippet.myPresentation != null) return false;
return true;
}
@Override
public int hashCode() {
int result = myId != null ? myId.hashCode() : 0;
result = 31 * result + (myKind != null ? myKind.hashCode() : 0);
result = 31 * result + (mySubKind != null ? mySubKind.hashCode() : 0);
result = 31 * result + (myCodeText != null ? myCodeText.hashCode() : 0);
result = 31 * result + (myPresentation != null ? myPresentation.hashCode() : 0);
return result;
}
}
@@ -0,0 +1,10 @@
package com.intellij.execution.jshell.protocol;
/**
* @author Eugene Zhuravlev
* Date: 12-Jun-17
*/
public class Endpoint {
public static final String MSG_BEGIN = "__#begin#__";
public static final String MSG_END = "__#end#__";
}
@@ -0,0 +1,97 @@
package com.intellij.execution.jshell.protocol;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
/**
* @author Eugene Zhuravlev
* Date: 12-Jun-17
*/
@XmlRootElement
public class Event {
private CodeSnippet myCauseSnippet;
private CodeSnippet mySnippet;
private CodeSnippet.Status myPreviousStatus;
private CodeSnippet.Status myStatus;
private String myValue;
private String myExceptionText;
private String myDiagnostic;
public Event() {
}
public Event(CodeSnippet snippet, CodeSnippet causeSnippet,
CodeSnippet.Status status, CodeSnippet.Status previousStatus,
String value) {
myCauseSnippet = causeSnippet;
mySnippet = snippet;
myPreviousStatus = previousStatus;
myStatus = status;
myValue = value;
}
public CodeSnippet.Status getPreviousStatus() {
return myPreviousStatus;
}
@XmlElement
public void setPreviousStatus(CodeSnippet.Status previousStatus) {
myPreviousStatus = previousStatus;
}
public CodeSnippet.Status getStatus() {
return myStatus;
}
@XmlElement
public void setStatus(CodeSnippet.Status status) {
myStatus = status;
}
public String getValue() {
return myValue;
}
@XmlElement
public void setValue(String value) {
myValue = value;
}
public CodeSnippet getCauseSnippet() {
return myCauseSnippet;
}
@XmlElement
public void setCauseSnippet(CodeSnippet causeSnippet) {
myCauseSnippet = causeSnippet;
}
public CodeSnippet getSnippet() {
return mySnippet;
}
@XmlElement
public void setSnippet(CodeSnippet snippet) {
mySnippet = snippet;
}
public String getExceptionText() {
return myExceptionText;
}
@XmlElement
public void setExceptionText(String exceptionText) {
myExceptionText = exceptionText;
}
public String getDiagnostic() {
return myDiagnostic;
}
@XmlElement
public void setDiagnostic(String diagnostic) {
myDiagnostic = diagnostic;
}
}
@@ -0,0 +1,27 @@
package com.intellij.execution.jshell.protocol;
import javax.xml.bind.annotation.XmlAttribute;
/**
* @author Eugene Zhuravlev
* Date: 12-Jun-17
*/
public abstract class Message {
private String myUid;
public Message() {
}
public Message(String uid) {
myUid = uid;
}
public String getUid() {
return myUid;
}
@XmlAttribute
public void setUid(String uid) {
myUid = uid;
}
}
@@ -0,0 +1,46 @@
package com.intellij.execution.jshell.protocol;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import java.io.*;
import java.util.function.Consumer;
/**
* @author Eugene Zhuravlev
* Date: 12-Jun-17
*/
public class MessageReader<T> extends Endpoint {
private final BufferedReader myIn;
private final JAXBContext myContext;
public MessageReader(InputStream input, Class<T> msgType) throws Exception {
myIn = new BufferedReader(new InputStreamReader(input));
myContext = JAXBContext.newInstance(msgType);
}
public T receive(final Consumer<String> unparsedOutputSink) throws IOException {
while (true) {
String line = myIn.readLine();
if (line == null) {
return null;
}
if (MSG_BEGIN.equals(line)) {
final StringBuilder buf = new StringBuilder();
for (String body = myIn.readLine(); !MSG_END.equals(body.trim()); body = myIn.readLine()) {
buf.append(body).append("\n");
}
try {
//noinspection unchecked
return (T)myContext.createUnmarshaller().unmarshal(new StringReader(buf.toString()));
}
catch (JAXBException e) {
throw new IOException(e);
}
}
else {
unparsedOutputSink.accept(line + "\n");
}
}
}
}
@@ -0,0 +1,41 @@
package com.intellij.execution.jshell.protocol;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
/**
* @author Eugene Zhuravlev
* Date: 12-Jun-17
*/
public class MessageWriter<T extends Message> extends Endpoint {
private final BufferedWriter myOut;
private final JAXBContext myContext;
public MessageWriter(OutputStream output, Class<T> msgType) throws Exception {
myOut = new BufferedWriter(new OutputStreamWriter(output));
myContext = JAXBContext.newInstance(msgType);
}
public void send(T message) throws IOException {
try {
myOut.newLine();
myOut.write(MSG_BEGIN);
myOut.newLine();
myContext.createMarshaller().marshal(message, myOut);
}
catch (JAXBException e) {
throw new IOException(e);
}
finally {
myOut.newLine();
myOut.write(MSG_END);
myOut.newLine();
myOut.flush();
}
}
}
@@ -0,0 +1,47 @@
package com.intellij.execution.jshell.protocol;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlEnum;
import javax.xml.bind.annotation.XmlRootElement;
/**
* @author Eugene Zhuravlev
* Date: 12-Jun-17
*/
@XmlRootElement
public class Request extends Message{
private Command myCommand;
private String myCodeText;
@XmlEnum
public enum Command{
EVAL, DROP_STATE, EXIT
}
public Request() {
}
public Request(String uid, Command cmd, String codeText) {
super(uid);
myCommand = cmd;
myCodeText = codeText;
}
public Command getCommand() {
return myCommand;
}
@XmlElement
public void setCommand(Command command) {
myCommand = command;
}
public String getCodeText() {
return myCodeText;
}
@XmlElement
public void setCodeText(String codeText) {
myCodeText = codeText;
}
}
@@ -0,0 +1,44 @@
package com.intellij.execution.jshell.protocol;
import com.sun.xml.internal.txw2.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @author Eugene Zhuravlev
* Date: 12-Jun-17
*/
@XmlRootElement
public class Response extends Message{
private List<Event> myEvents;
public Response() {
}
public Response(String uid, Event... events) {
super(uid);
Collections.addAll(myEvents = new ArrayList<>(), events);
}
@XmlElement
public List<Event> getEvents() {
return myEvents;
}
@XmlElement
public void setEvents(List<Event> events) {
myEvents = events;
}
public void addEvent(Event event) {
List<Event> events = myEvents;
if (events == null) {
events = new ArrayList<>();
myEvents = events;
}
events.add(event);
}
}
@@ -0,0 +1,61 @@
package com.intellij.execution.jshell.protocol;
import junit.framework.TestCase;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.UUID;
/**
* @author Eugene Zhuravlev
* Date: 12-Jun-17
*/
public class JShellMessageMarshallingTest extends TestCase {
private static final Event[] EMPTY_EVENT_ARRAY = new Event[0];
public void testSendReceive() throws Exception {
final PipedInputStream clientIn = new PipedInputStream();
final PipedOutputStream serverOut = new PipedOutputStream(clientIn);
final PipedInputStream serverIn = new PipedInputStream();
final PipedOutputStream clientOut = new PipedOutputStream(serverIn);
final MessageWriter<Request> clientWriter = new MessageWriter<>(clientOut, Request.class);
final MessageReader<Request> serverReader = new MessageReader<>(serverIn, Request.class);
final MessageWriter<Response> serverWriter = new MessageWriter<>(serverOut, Response.class);
final MessageReader<Response> clientReader = new MessageReader<>(clientIn, Response.class);
final Request request = new Request(UUID.randomUUID().toString(), Request.Command.EVAL,
"System.out.println(\"Hello, World!\");\n int var = 7 + 7;");
clientWriter.send(request);
final Request receivedRequest = serverReader.receive(s -> {});
assertEquals(request.getUid(), receivedRequest.getUid());
assertEquals(request.getCodeText(), receivedRequest.getCodeText());
final CodeSnippet snippet = new CodeSnippet("code-snippet-id", CodeSnippet.Kind.EXPRESSION, CodeSnippet.SubKind.OTHER_EXPRESSION_SUBKIND, "a+b", "expression:a+b");
final Event event1 = new Event(null, null, CodeSnippet.Status.UNKNOWN, CodeSnippet.Status.NONEXISTENT, null);
event1.setExceptionText("some exception");
event1.setDiagnostic("error diagnostic");
final Event event2 = new Event(snippet, null, CodeSnippet.Status.VALID, CodeSnippet.Status.NONEXISTENT, "14");
final Response response = new Response(request.getUid(), event1, event2);
serverWriter.send(response);
final Response receivedResponse = clientReader.receive(s -> {});
assertEquals(response.getUid(), receivedResponse.getUid());
final Event[] events = response.getEvents().toArray(EMPTY_EVENT_ARRAY);
final Event[] receivedEvents = receivedResponse.getEvents().toArray(EMPTY_EVENT_ARRAY);
assertEquals(events.length, receivedEvents.length);
for (int i = 0; i < events.length; i++) {
final Event expectedEvent = events[i];
final Event receivedEvent = receivedEvents[i];
assertEquals(expectedEvent.getSnippet(), receivedEvent.getSnippet());
assertEquals(expectedEvent.getCauseSnippet(), receivedEvent.getCauseSnippet());
assertEquals(expectedEvent.getStatus(), receivedEvent.getStatus());
assertEquals(expectedEvent.getPreviousStatus(), receivedEvent.getPreviousStatus());
assertEquals(expectedEvent.getValue(), receivedEvent.getValue());
assertEquals(expectedEvent.getExceptionText(), receivedEvent.getExceptionText());
}
}
}
@@ -15,15 +15,21 @@
*/
package com.intellij.openapi.fileTypes.impl;
import com.intellij.ide.highlighter.*;
import com.intellij.ide.highlighter.JShellFileType;
import com.intellij.ide.highlighter.JavaClassFileType;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.FileTypeConsumer;
import com.intellij.openapi.fileTypes.FileTypeFactory;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
public class JavaFileTypeFactory extends FileTypeFactory {
@Override
public void createFileTypes(@NotNull final FileTypeConsumer consumer) {
consumer.consume(JavaClassFileType.INSTANCE, "class");
consumer.consume(JavaFileType.INSTANCE, "java");
for (FileType ft : Arrays.asList(JavaClassFileType.INSTANCE, JavaFileType.INSTANCE, JShellFileType.INSTANCE)) {
consumer.consume(ft, ft.getDefaultExtension());
}
}
}
@@ -0,0 +1,33 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.lang.java;
import com.intellij.lang.Language;
import org.jetbrains.annotations.NotNull;
public class JShellLanguage extends Language {
public static final JShellLanguage INSTANCE = new JShellLanguage();
private JShellLanguage() {
super(JavaLanguage.INSTANCE, "JShellLanguage");
}
@NotNull
@Override
public String getDisplayName() {
return "JShell Snippet";
}
}
@@ -0,0 +1,22 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi;
import java.util.Collection;
public interface PsiJShellFile extends PsiFile {
Collection<PsiJShellImportHolder> getSnippets();
}
@@ -0,0 +1,29 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi;
import org.jetbrains.annotations.NotNull;
/**
* @author Eugene Zhuravlev
* Date: 21-Jun-17
*/
public interface PsiJShellHolderMethod extends PsiMethod, PsiJShellSyntheticElement{
PsiJShellHolderMethod[] EMPTY_ARRAY = new PsiJShellHolderMethod[0];
@NotNull
PsiElement[] getStatements();
}
@@ -0,0 +1,26 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi;
/**
* @author Eugene Zhuravlev
* Date: 21-Jun-17
*/
public interface PsiJShellImportHolder extends PsiElement, PsiJShellSyntheticElement{
PsiJShellImportHolder[] EMPTY_ARRAY = new PsiJShellImportHolder[0];
PsiImportStatement getImportStatement();
}
@@ -0,0 +1,24 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi;
/**
* @author Eugene Zhuravlev
* Date: 24-Jun-17
*/
public interface PsiJShellRootClass extends PsiSyntheticClass, PsiJShellSyntheticElement{
PsiJShellImportHolder[] getSnippets();
}
@@ -0,0 +1,23 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi;
/**
* @author Eugene Zhuravlev
* Date: 21-Jun-17
*/
public interface PsiJShellSyntheticElement extends PsiElement, SyntheticElement{
}
@@ -0,0 +1,63 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.ide.highlighter;
import com.intellij.icons.AllIcons;
import com.intellij.ide.IdeBundle;
import com.intellij.lang.java.JShellLanguage;
import com.intellij.openapi.fileTypes.LanguageFileType;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
public class JShellFileType extends LanguageFileType {
@NonNls public static final String DEFAULT_EXTENSION = "snippet";
@NonNls public static final String DOT_DEFAULT_EXTENSION = "." + DEFAULT_EXTENSION;
public static final JShellFileType INSTANCE = new JShellFileType();
private JShellFileType() {
super(JShellLanguage.INSTANCE);
}
@Override
@NotNull
public String getName() {
return "JSHELL";
}
@Override
@NotNull
public String getDescription() {
return IdeBundle.message("filetype.description.jshell");
}
@Override
@NotNull
public String getDefaultExtension() {
return DEFAULT_EXTENSION;
}
@Override
public Icon getIcon() {
return AllIcons.FileTypes.Java; // todo: a dedicated icon?
}
@Override
public boolean isJVMDebuggingSupported() {
return false;
}
}
@@ -0,0 +1,77 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.lang.java;
import com.intellij.lang.ASTNode;
import com.intellij.lang.PsiBuilder;
import com.intellij.lang.PsiParser;
import com.intellij.lang.java.parser.JShellParser;
import com.intellij.lang.java.parser.JavaParserUtil;
import com.intellij.openapi.project.Project;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.FileViewProvider;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.impl.source.JShellFileImpl;
import com.intellij.psi.impl.source.tree.IJShellElementType;
import com.intellij.psi.impl.source.tree.JShellElementType;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.IFileElementType;
import org.jetbrains.annotations.NotNull;
/**
* @author Eugene Zhuravlev
* Date: 21-Jun-17
*/
public class JShellParserDefinition extends JavaParserDefinition{
private static final PsiParser PARSER = new PsiParser() {
@NotNull
@Override
public ASTNode parse(@NotNull IElementType rootElement, @NotNull PsiBuilder builder) {
JavaParserUtil.setLanguageLevel(builder, LanguageLevel.HIGHEST);
final PsiBuilder.Marker r = builder.mark();
JShellParser.INSTANCE.getFileParser().parse(builder);
r.done(rootElement);
return builder.getTreeBuilt();
}
};
@Override
public PsiFile createFile(FileViewProvider viewProvider) {
return new JShellFileImpl(viewProvider);
}
@Override
public IFileElementType getFileNodeType() {
return JShellElementType.FILE;
}
@NotNull
@Override
public PsiElement createElement(ASTNode node) {
final IElementType type = node.getElementType();
if (type instanceof IJShellElementType) {
return ((IJShellElementType)type).createPsi(node);
}
return super.createElement(node);
}
@NotNull
@Override
public PsiParser createParser(Project project) {
return PARSER;
}
}
@@ -144,7 +144,7 @@ public class FileParser {
}
@NotNull
private Pair<PsiBuilder.Marker, Boolean> parseImportList(PsiBuilder builder, Predicate<PsiBuilder> stopper) {
protected Pair<PsiBuilder.Marker, Boolean> parseImportList(PsiBuilder builder, Predicate<PsiBuilder> stopper) {
PsiBuilder.Marker list = builder.mark();
boolean isEmpty = true;
@@ -189,7 +189,7 @@ public class FileParser {
}
@Nullable
private PsiBuilder.Marker parseImportStatement(PsiBuilder builder) {
protected PsiBuilder.Marker parseImportStatement(PsiBuilder builder) {
if (builder.getTokenType() != JavaTokenType.IMPORT_KEYWORD) return null;
final PsiBuilder.Marker statement = builder.mark();
@@ -0,0 +1,125 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.lang.java.parser;
import com.intellij.codeInsight.daemon.JavaErrorMessages;
import com.intellij.lang.LighterASTNode;
import com.intellij.lang.PsiBuilder;
import com.intellij.openapi.util.Condition;
import com.intellij.psi.impl.source.tree.JShellElementType;
import com.intellij.psi.impl.source.tree.JavaElementType;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* @author Eugene Zhuravlev
* Date: 21-Jun-17
*/
public class JShellParser extends JavaParser {
public static final JShellParser INSTANCE = new JShellParser();
private static final Set<IElementType> IMPORT = Collections.singleton(JavaElementType.IMPORT_STATEMENT);
private static final HashSet<IElementType> TOP_LEVEL_DECLARATIONS = new HashSet<>(Arrays.asList(
JavaElementType.FIELD, JavaElementType.METHOD, JavaElementType.CLASS
));
private final FileParser myJShellFileParser = new FileParser(JShellParser.this) {
@Override
public void parse(@NotNull PsiBuilder builder) {
while (!builder.eof()) {
PsiBuilder.Marker wrapper = builder.mark();
IElementType wrapperType = null;
PsiBuilder.Marker marker = parseImportStatement(builder);
if (isParsed(marker, builder, tokenType -> IMPORT.contains(tokenType))) {
wrapperType = JShellElementType.IMPORT_HOLDER;
}
else {
revert(marker);
marker = getExpressionParser().parse(builder);
if (marker != null) {
wrapperType = JShellElementType.STATEMENTS_HOLDER;
}
else {
marker = getStatementParser().parseStatement(builder);
if (isParsed(marker, builder, tokenType-> !JavaElementType.DECLARATION_STATEMENT.equals(tokenType))) {
wrapperType = JShellElementType.STATEMENTS_HOLDER;
}
else {
revert(marker);
marker = getDeclarationParser().parse(builder, DeclarationParser.Context.CLASS);
if (isParsed(marker, builder, tokenType -> TOP_LEVEL_DECLARATIONS.contains(tokenType))) {
wrapper.drop(); // don't need wrapper for top-level declaration
wrapper = null;
}
else {
revert(marker);
marker = null;
}
}
}
}
if (marker == null) {
if (wrapper != null) {
wrapper.drop();
}
break;
}
if (wrapper != null) {
wrapper.done(wrapperType);
}
}
if (!builder.eof()) {
builder.mark().error(JavaErrorMessages.message("unexpected.token"));
while (!builder.eof()) {
builder.advanceLexer();
}
}
}
};
private static boolean isParsed(@Nullable PsiBuilder.Marker parsedMarker, PsiBuilder builder, final Condition<IElementType> cond) {
if (parsedMarker == null) {
return false;
}
final LighterASTNode lastDone = builder.getLatestDoneMarker();
if (lastDone == null) {
return false;
}
return cond.value(lastDone.getTokenType());
}
private static void revert(PsiBuilder.Marker parsedMarker) {
if (parsedMarker != null) {
parsedMarker.rollbackTo();
}
}
@NotNull
@Override
public FileParser getFileParser() {
return myJShellFileParser;
}
}
@@ -0,0 +1,72 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.impl.source;
import com.intellij.ide.highlighter.JShellFileType;
import com.intellij.lang.Language;
import com.intellij.lang.java.JShellLanguage;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.tree.JShellElementType;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.List;
/**
* @author Eugene Zhuravlev
* Date: 28-Jul-15
*/
public class JShellFileImpl extends PsiJavaFileBaseImpl implements PsiJShellFile {
public JShellFileImpl(FileViewProvider viewProvider) {
super(JShellElementType.FILE, JShellElementType.ROOT_CLASS, viewProvider);
}
@Override
public boolean processDeclarations(@NotNull PsiScopeProcessor processor, @NotNull ResolveState state, PsiElement lastParent, @NotNull PsiElement place) {
return super.processDeclarations(processor, state, lastParent, place);
}
@NotNull
@Override
public Language getLanguage() {
return JShellLanguage.INSTANCE;
}
@NotNull
public FileType getFileType() {
return JShellFileType.INSTANCE;
}
public boolean isPhysical() {
return getViewProvider().isPhysical();
}
@Override
public Collection<PsiJShellImportHolder> getSnippets() {
final List<PsiJShellImportHolder> result = new SmartList<>();
PsiElement child = getFirstChild();
while (child != null) {
if (child instanceof PsiJShellImportHolder) {
result.add((PsiJShellImportHolder)child);
}
child = child.getNextSibling();
}
return result;
}
}
@@ -0,0 +1,43 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.impl.source;
import com.intellij.psi.*;
import com.intellij.psi.augment.PsiAugmentProvider;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.Set;
/**
* @author Eugene Zhuravlev
* Date: 28-Jun-17
*/
public class JShellPsiAugmentProvider extends PsiAugmentProvider{
private static final Set<String> JSHELL_FIELD_MODIFIERS = Collections.unmodifiableSet(ContainerUtil.newHashSet(PsiModifier.PUBLIC, PsiModifier.STATIC));
@NotNull
@Override
protected Set<String> transformModifiers(@NotNull PsiModifierList modifierList, @NotNull Set<String> modifiers) {
// enforce permanent field modifiers for all variables declared at top-level
return isInsideJShellField(modifierList)? JSHELL_FIELD_MODIFIERS : modifiers;
}
private static boolean isInsideJShellField(PsiElement element) {
final PsiElement parent = element.getParent();
return parent instanceof PsiField && parent.getParent() instanceof PsiJShellRootClass;
}
}
@@ -0,0 +1,240 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.impl.source;
import com.intellij.extapi.psi.ASTWrapperPsiElement;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiSuperMethodImplUtil;
import com.intellij.psi.impl.light.LightModifierList;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.util.MethodSignature;
import com.intellij.psi.util.MethodSignatureBackedByPsiMethod;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
/**
* @author Eugene Zhuravlev
* Date: 21-Jun-17
*/
public class PsiJShellHolderMethodImpl extends ASTWrapperPsiElement implements PsiJShellHolderMethod {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.PsiJShellHolderMethodImpl");
private final String myName;
private PsiParameterList myParameterList;
private PsiReferenceList myThrowsList;
public PsiJShellHolderMethodImpl(@NotNull ASTNode node, int index) {
super(node);
myName = "_$$jshell_holder_method$$" + index;
}
@NotNull
@Override
public PsiElement[] getStatements() {
List<PsiElement> result = null;
for (PsiElement child = getFirstChild(); child != null; child = child.getNextSibling()) {
if (child instanceof PsiStatement || child instanceof PsiExpression) {
if (result == null) {
result = new SmartList<>();
}
result.add(child);
}
}
return result == null? PsiElement.EMPTY_ARRAY : result.toArray(PsiElement.EMPTY_ARRAY);
}
@NotNull
@Override
public String getName() {
return myName;
}
@Nullable
@Override
public PsiType getReturnType() {
return PsiType.VOID;
}
@Nullable
@Override
public PsiTypeElement getReturnTypeElement() {
return null;
}
@NotNull
@Override
public PsiParameterList getParameterList() {
if (myParameterList != null) {
return myParameterList;
}
try {
PsiElementFactory elementFactory = JavaPsiFacade.getInstance(getProject()).getElementFactory();
myParameterList = elementFactory.createParameterList(ArrayUtil.EMPTY_STRING_ARRAY, PsiType.EMPTY_ARRAY);
return myParameterList;
}
catch (IncorrectOperationException e) {
LOG.error(e);
return null;
}
}
@NotNull
@Override
public PsiReferenceList getThrowsList() {
if (myThrowsList != null) {
return myThrowsList;
}
final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(getProject()).getElementFactory();
try {
myThrowsList = elementFactory.createReferenceList(new PsiJavaCodeReferenceElement[]{
elementFactory.createFQClassNameReferenceElement("java.lang.Throwable", getResolveScope())
});
return myThrowsList;
}
catch (IncorrectOperationException e) {
LOG.error(e);
return null;
}
}
@Nullable
@Override
public PsiCodeBlock getBody() {
return (PsiCodeBlock)getFirstChild();
}
@Override
public boolean isConstructor() {
return false;
}
@Override
public boolean isVarArgs() {
return false;
}
@NotNull
@Override
public MethodSignature getSignature(@NotNull PsiSubstitutor substitutor) {
return MethodSignatureBackedByPsiMethod.create(this, PsiSubstitutor.EMPTY);
}
@Nullable
@Override
public PsiIdentifier getNameIdentifier() {
return null;
}
@NotNull
@Override
public PsiMethod[] findSuperMethods() {
return PsiMethod.EMPTY_ARRAY;
}
@NotNull
@Override
public PsiMethod[] findSuperMethods(boolean checkAccess) {
return PsiMethod.EMPTY_ARRAY;
}
@NotNull
@Override
public PsiMethod[] findSuperMethods(PsiClass parentClass) {
return PsiMethod.EMPTY_ARRAY;
}
@NotNull
@Override
public List<MethodSignatureBackedByPsiMethod> findSuperMethodSignaturesIncludingStatic(boolean checkAccess) {
return Collections.emptyList();
}
@Nullable
@Override
public PsiMethod findDeepestSuperMethod() {
return null;
}
@NotNull
@Override
public PsiMethod[] findDeepestSuperMethods() {
return PsiMethod.EMPTY_ARRAY;
}
@NotNull
@Override
public PsiModifierList getModifierList() {
return new LightModifierList(getManager());
}
@Override
public PsiElement setName(@NotNull String name) throws IncorrectOperationException {
throw new IncorrectOperationException("Can't change name of JShell holder method");
}
@NotNull
@Override
public HierarchicalMethodSignature getHierarchicalMethodSignature() {
return PsiSuperMethodImplUtil.getHierarchicalMethodSignature(this);
}
@Override
public boolean isDeprecated() {
return false;
}
@Nullable
@Override
public PsiDocComment getDocComment() {
return null;
}
@Override
public boolean hasTypeParameters() {
return false;
}
@Nullable
@Override
public PsiTypeParameterList getTypeParameterList() {
return null;
}
@NotNull
@Override
public PsiTypeParameter[] getTypeParameters() {
return PsiTypeParameter.EMPTY_ARRAY;
}
@Nullable
@Override
public PsiClass getContainingClass() {
return (PsiClass)getParent();
}
@Override
public boolean hasModifierProperty(@NotNull String name) {
return PsiModifier.PUBLIC.equals(name) || PsiModifier.STATIC.equals(name) || PsiModifier.FINAL.equals(name);
}
}
@@ -0,0 +1,60 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.impl.source;
import com.intellij.extapi.psi.ASTWrapperPsiElement;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiImportStatement;
import com.intellij.psi.PsiJShellImportHolder;
import com.intellij.psi.ResolveState;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.scope.util.PsiScopesUtil;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
/**
* @author Eugene Zhuravlev
* Date: 21-Jun-17
* according to JShell spec, a snippet must correspond to one of the following JLS syntax productions:
Expression
Statement
ClassDeclaration
InterfaceDeclaration
MethodDeclaration
FieldDeclaration
ImportDeclaration
*/
public class PsiJShellImportHolderImpl extends ASTWrapperPsiElement implements PsiJShellImportHolder {
public PsiJShellImportHolderImpl(@NotNull ASTNode node) {
super(node);
}
@Override
public boolean processDeclarations(@NotNull PsiScopeProcessor processor, @NotNull ResolveState state, PsiElement lastParent, @NotNull PsiElement place) {
final PsiImportStatement importStatement = getImportStatement();
if (importStatement != null) {
processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, this);
return processor.execute(importStatement, state);
}
return PsiScopesUtil.walkChildrenScopes(this, processor, state, lastParent, place);
}
@Override
public PsiImportStatement getImportStatement() {
return PsiTreeUtil.getChildOfType(this, PsiImportStatement.class);
}
}
@@ -0,0 +1,330 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.impl.source;
import com.intellij.extapi.psi.ASTWrapperPsiElement;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.Pair;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.impl.InheritanceImplUtil;
import com.intellij.psi.impl.PsiClassImplUtil;
import com.intellij.psi.impl.PsiSuperMethodImplUtil;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
/**
* @author Eugene Zhuravlev
* Date: 21-Jun-17
* according to JShell spec, a snippet must correspond to one of the following JLS syntax productions:
Expression
Statement
ClassDeclaration
InterfaceDeclaration
MethodDeclaration
FieldDeclaration
ImportDeclaration
*/
public class PsiJShellRootClassImpl extends ASTWrapperPsiElement implements PsiJShellRootClass {
private String myName;
private String myQName;
public PsiJShellRootClassImpl(ASTNode node, int index) {
super(node);
myName = "$$jshell_root_class$$" + index;
myQName = "REPL." + myName;
}
@Override
public PsiJShellImportHolder[] getSnippets() {
return findChildren(PsiJShellImportHolder.class, PsiJShellImportHolder.EMPTY_ARRAY);
}
@Override
public boolean processDeclarations(@NotNull PsiScopeProcessor processor, @NotNull ResolveState state, PsiElement lastParent, @NotNull PsiElement place) {
final LanguageLevel level = PsiUtil.getLanguageLevel(place);
return PsiClassImplUtil.processDeclarationsInClass(this, processor, state, null, lastParent, place, level, false);
}
@NotNull
@Override
public String getName() {
return myName;
}
@Nullable
@Override
public String getQualifiedName() {
return myQName;
}
@Override
public boolean isInterface() {
return false;
}
@Override
public boolean isAnnotationType() {
return false;
}
@Override
public boolean isEnum() {
return false;
}
@Nullable
@Override
public PsiReferenceList getExtendsList() {
return null;
}
@Nullable
@Override
public PsiReferenceList getImplementsList() {
return null;
}
@NotNull
@Override
public PsiClassType[] getExtendsListTypes() {
return PsiClassType.EMPTY_ARRAY;
}
@NotNull
@Override
public PsiClassType[] getImplementsListTypes() {
return PsiClassType.EMPTY_ARRAY;
}
@Nullable
@Override
public PsiClass getSuperClass() {
return null;
}
@NotNull
@Override
public PsiClass[] getInterfaces() {
return PsiClass.EMPTY_ARRAY;
}
@NotNull
@Override
public PsiClass[] getSupers() {
return PsiClass.EMPTY_ARRAY;
}
@NotNull
@Override
public PsiClassType[] getSuperTypes() {
return PsiClassType.EMPTY_ARRAY;
}
@NotNull
@Override
public PsiField[] getFields() {
return findChildren(PsiField.class, PsiField.EMPTY_ARRAY);
}
@NotNull
@Override
public PsiMethod[] getMethods() {
return findChildren(PsiMethod.class, PsiMethod.EMPTY_ARRAY);
}
@NotNull
@Override
public PsiMethod[] getConstructors() {
return PsiMethod.EMPTY_ARRAY;
}
@NotNull
@Override
public PsiClass[] getInnerClasses() {
return findChildren(PsiClass.class, PsiClass.EMPTY_ARRAY);
}
@NotNull
@Override
public PsiClassInitializer[] getInitializers() {
return findChildren(PsiClassInitializer.class, PsiClassInitializer.EMPTY_ARRAY);
}
@NotNull
@Override
public PsiField[] getAllFields() {
return PsiClassImplUtil.getAllFields(this);
}
@NotNull
@Override
public PsiMethod[] getAllMethods() {
return PsiClassImplUtil.getAllMethods(this);
}
@NotNull
@Override
public PsiClass[] getAllInnerClasses() {
return PsiClassImplUtil.getAllInnerClasses(this);
}
@Nullable
@Override
public PsiField findFieldByName(String name, boolean checkBases) {
return PsiClassImplUtil.findFieldByName(this, name, checkBases);
}
@Nullable
@Override
public PsiMethod findMethodBySignature(PsiMethod patternMethod, boolean checkBases) {
return PsiClassImplUtil.findMethodBySignature(this, patternMethod, checkBases);
}
@NotNull
@Override
public PsiMethod[] findMethodsBySignature(PsiMethod patternMethod, boolean checkBases) {
return PsiClassImplUtil.findMethodsBySignature(this, patternMethod, checkBases);
}
@NotNull
@Override
public PsiMethod[] findMethodsByName(String name, boolean checkBases) {
return PsiClassImplUtil.findMethodsByName(this, name, checkBases);
}
@NotNull
@Override
public List<Pair<PsiMethod, PsiSubstitutor>> findMethodsAndTheirSubstitutorsByName(String name, boolean checkBases) {
return PsiClassImplUtil.findMethodsAndTheirSubstitutorsByName(this, name, checkBases);
}
@NotNull
@Override
public List<Pair<PsiMethod, PsiSubstitutor>> getAllMethodsAndTheirSubstitutors() {
return PsiClassImplUtil.getAllWithSubstitutorsByMap(this, PsiClassImplUtil.MemberType.METHOD);
}
@Nullable
@Override
public PsiClass findInnerClassByName(String name, boolean checkBases) {
return PsiClassImplUtil.findInnerByName(this, name, checkBases);
}
@Nullable
@Override
public PsiIdentifier getNameIdentifier() {
return null;
}
@Override
public PsiElement getScope() {
return getContainingFile();
}
@Override
public boolean isInheritor(@NotNull PsiClass baseClass, boolean checkDeep) {
return InheritanceImplUtil.isInheritor(this, baseClass, checkDeep);
}
@Override
public boolean isInheritorDeep(PsiClass baseClass, @Nullable PsiClass classToByPass) {
return InheritanceImplUtil.isInheritorDeep(this, baseClass, classToByPass);
}
@Nullable
@Override
public PsiClass getContainingClass() {
return null;
}
@NotNull
@Override
public Collection<HierarchicalMethodSignature> getVisibleSignatures() {
return PsiSuperMethodImplUtil.getVisibleSignatures(this);
}
@Override
public PsiElement setName(@NotNull String name) throws IncorrectOperationException {
return null;
}
@Override
public boolean isDeprecated() {
return false;
}
@Nullable
@Override
public PsiDocComment getDocComment() {
return null;
}
@Override
public boolean hasTypeParameters() {
return false;
}
@Nullable
@Override
public PsiTypeParameterList getTypeParameterList() {
return null;
}
@NotNull
@Override
public PsiTypeParameter[] getTypeParameters() {
return PsiTypeParameter.EMPTY_ARRAY;
}
@Nullable
@Override
public PsiModifierList getModifierList() {
return null;
}
@Override
public boolean hasModifierProperty(@NotNull String name) {
return PsiModifier.PUBLIC.equals(name) || PsiModifier.FINAL.equals(name);
}
@Nullable
@Override
public PsiJavaToken getLBrace() {
return null;
}
@Nullable
@Override
public PsiJavaToken getRBrace() {
return null;
}
@NotNull
private <T extends PsiElement> T[] findChildren(final Class<T> memberClass, final T[] emptyArray) {
final T[] members = PsiTreeUtil.getChildrenOfType(this, memberClass);
return members != null ? members : emptyArray;
}
}
@@ -0,0 +1,35 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.impl.source.tree;
import com.intellij.lang.ASTNode;
import com.intellij.lang.java.JShellLanguage;
import com.intellij.psi.PsiElement;
import com.intellij.psi.tree.ILazyParseableElementType;
import org.jetbrains.annotations.NotNull;
/**
* @author Eugene Zhuravlev
* Date: 24-Jun-17
*/
public abstract class IJShellElementType extends ILazyParseableElementType{
public IJShellElementType(@NotNull String debugName) {
super(debugName, JShellLanguage.INSTANCE);
}
public abstract PsiElement createPsi(ASTNode node);
}
@@ -0,0 +1,58 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.impl.source.tree;
import com.intellij.lang.ASTNode;
import com.intellij.lang.java.JShellLanguage;
import com.intellij.psi.PsiElement;
import com.intellij.psi.impl.source.PsiJShellHolderMethodImpl;
import com.intellij.psi.impl.source.PsiJShellImportHolderImpl;
import com.intellij.psi.impl.source.PsiJShellRootClassImpl;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.IFileElementType;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author Eugene Zhuravlev
* Date: 21-Jun-17
*/
public interface JShellElementType {
IFileElementType FILE = new IFileElementType("JSHELL_FILE", JShellLanguage.INSTANCE);
IElementType ROOT_CLASS = new IJShellElementType("JSHELL_ROOT_CLASS") {
private final AtomicInteger ourClassCounter = new AtomicInteger();
@Override
public PsiElement createPsi(ASTNode node) {
return new PsiJShellRootClassImpl(node, ourClassCounter.getAndIncrement());
}
};
IElementType STATEMENTS_HOLDER = new IJShellElementType("JSHELL_STATEMENTS_HOLDER") {
private final AtomicInteger ourMethodCounter = new AtomicInteger();
@Override
public PsiElement createPsi(ASTNode node) {
return new PsiJShellHolderMethodImpl(node, ourMethodCounter.getAndIncrement());
}
};
IElementType IMPORT_HOLDER = new IJShellElementType("JSHELL_IMPORT_HOLDER") {
@Override
public PsiElement createPsi(ASTNode node) {
return new PsiJShellImportHolderImpl(node);
}
};
}
Binary file not shown.
@@ -205,6 +205,7 @@ filetype.description.gui.designer.form=GUI Designer forms
filetype.description.html=HTML files
filetype.description.class=Java class files
filetype.description.java=Java source files
filetype.description.jshell=JShell snippet files
filetype.description.jspx=JSPx files
filetype.description.idea.module=Idea Module
filetype.description.jsp=Java Server Page files
@@ -353,6 +353,15 @@ public class PathManager {
return url != null ? extractRoot(url, path) : null;
}
/**
* Attempts to detect classpath entry which contains given resource.
*/
@Nullable
public static String getResourceRoot(@NotNull ClassLoader cl, String resourcePath) {
final URL url = cl.getResource(resourcePath);
return url != null ? extractRoot(url, resourcePath) : null;
}
/**
* Attempts to extract classpath entry part from passed URL.
*/
@@ -375,8 +384,8 @@ public class PathManager {
}
else if (URLUtil.JAR_PROTOCOL.equals(protocol)) {
Pair<String, String> paths = URLUtil.splitJarUrl(resourceURL.getFile());
if (paths != null) {
resultPath = paths.first;
if (paths != null && paths.first != null) {
resultPath = FileUtil.toSystemDependentName(paths.first);
}
}
+10
View File
@@ -361,6 +361,8 @@
<!--<expectedTypesProvider implementation="com.intellij.codeInsight.JavaExpectedTypesProvider"/>-->
<customPropertyScopeProvider implementation="com.intellij.psi.impl.search.SimpleAccessorScopeProvider"/>
<editorNotificationProvider implementation="com.intellij.execution.jshell.SnippetEditorDecorator"/>
<lang.psiAugmentProvider implementation="com.intellij.psi.impl.source.JShellPsiAugmentProvider"/>
<referencesSearch implementation="com.intellij.psi.impl.search.PsiAnnotationMethodReferencesSearcher"/>
<referencesSearch implementation="com.intellij.psi.impl.search.ConstructorReferencesSearcher"/>
@@ -1242,6 +1244,7 @@
</intentionAction>
<lang.parserDefinition language="JAVA" implementationClass="com.intellij.lang.java.JavaParserDefinition"/>
<lang.parserDefinition language="JShellLanguage" implementationClass="com.intellij.lang.java.JShellParserDefinition"/>
<lang.refactoringSupport language="JAVA" implementationClass="com.intellij.lang.java.JavaRefactoringSupportProvider"/>
<lang.refactoringSupport.classMembersRefactoringSupport language="JAVA" implementationClass="com.intellij.lang.java.JavaClassMembersRefactoringSupport"/>
@@ -1996,6 +1999,8 @@
<deadCode implementation="com.intellij.codeInspection.java19modules.Java9ModuleEntryPoint"/>
<scratch.rootType implementation="com.intellij.execution.jshell.JShellRootType"/>
<javaModuleSystem implementation="com.intellij.psi.impl.JavaPlatformModuleSystem"/>
</extensions>
@@ -2026,6 +2031,11 @@
<action id="MethodOverloadSwitchUp" class="com.intellij.codeInsight.editorActions.JavaMethodOverloadSwitchUpAction"/>
<action id="MethodOverloadSwitchDown" class="com.intellij.codeInsight.editorActions.JavaMethodOverloadSwitchDownAction" />
<action id="JShell.Console"
class="com.intellij.execution.jshell.LaunchJShellConsoleAction"
text="JShell Console..." description="Launch JShell Console">
<add-to-group group-id="ToolsMenu" anchor="last"/>
</action>
</actions>
</idea-plugin>