mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-147495 New Java scratch should have a class declaration and a caret inside main method
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
*/
|
||||
package com.intellij.ide.actions;
|
||||
|
||||
import com.intellij.ide.scratch.ScratchFileCreationHelper;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author gregsh
|
||||
*/
|
||||
public class JavaScratchFileCreationHelper extends ScratchFileCreationHelper {
|
||||
|
||||
@Override
|
||||
public boolean prepareText(@NotNull Project project, @NotNull Context context, @NotNull DataContext dataContext) {
|
||||
String caretMarker = "CARET_MARKER";
|
||||
if (context.text == "") {
|
||||
String text = reformat(
|
||||
project, context.language,
|
||||
"class Scratch { public static void main (String[] args) {\n" + caretMarker + "\n} }");
|
||||
context.caretOffset = text.indexOf(caretMarker);
|
||||
context.text = text.substring(0, context.caretOffset) + text.substring(context.caretOffset + caretMarker.length());
|
||||
return true;
|
||||
}
|
||||
//todo add required import statements from dataContext if available
|
||||
PsiFile psi = parseHeader(project, context.language, context.text);
|
||||
SyntaxTraverser<PsiElement> s = SyntaxTraverser.psiTraverser();
|
||||
if (s.withRoot(psi).traverse().filter(PsiClass.class).first() != null ||
|
||||
s.withRoot(psi).traverse().filter(PsiErrorElement.class).first() == null) {
|
||||
return true;
|
||||
}
|
||||
psi = parseHeader(project, context.language, "class Scratch {\n" + context.text + "\n}");
|
||||
if (s.withRoot(psi).traverse().filter(PsiMethod.class).first() != null) {
|
||||
String text = reformat(
|
||||
project, context.language,
|
||||
"class Scratch {\n" + caretMarker + "\n}");
|
||||
context.caretOffset = text.indexOf(caretMarker);
|
||||
context.text = text.substring(0, context.caretOffset) +
|
||||
StringUtil.trim(context.text) +
|
||||
text.substring(context.caretOffset + caretMarker.length());
|
||||
return true;
|
||||
}
|
||||
|
||||
psi = parseHeader(project, context.language, "class Scratch {\n" + context.text + "\n}");
|
||||
if (s.withRoot(psi).traverse().filter(PsiMember.class).first() != null) {
|
||||
String text = reformat(project, context.language,
|
||||
"class Scratch { public static void main (String[] args) {\n" + caretMarker + "\n} }");
|
||||
context.caretOffset = text.indexOf(caretMarker);
|
||||
context.text = text.substring(0, context.caretOffset) +
|
||||
StringUtil.trim(context.text) +
|
||||
text.substring(context.caretOffset + caretMarker.length());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -26,8 +26,8 @@ import com.intellij.lang.injection.InjectedLanguageManager;
|
||||
import com.intellij.openapi.actionSystem.*;
|
||||
import com.intellij.openapi.editor.Caret;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
|
||||
import com.intellij.openapi.fileTypes.InternalFileType;
|
||||
import com.intellij.openapi.fileTypes.LanguageFileType;
|
||||
import com.intellij.openapi.project.DumbAwareAction;
|
||||
import com.intellij.openapi.project.Project;
|
||||
@@ -35,7 +35,10 @@ import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiErrorElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.SyntaxTraverser;
|
||||
import com.intellij.ui.LayeredIcon;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.Function;
|
||||
@@ -57,6 +60,14 @@ import static com.intellij.openapi.util.Conditions.notNull;
|
||||
*/
|
||||
public class ScratchFileActions {
|
||||
|
||||
private static int ourCurrentBuffer = 0;
|
||||
|
||||
private static int nextBufferIndex() {
|
||||
ourCurrentBuffer = (ourCurrentBuffer % Registry.intValue("ide.scratch.buffers")) + 1;
|
||||
return ourCurrentBuffer;
|
||||
}
|
||||
|
||||
|
||||
public static class NewFileAction extends DumbAwareAction {
|
||||
private static final Icon ICON = LayeredIcon.create(AllIcons.FileTypes.Text, AllIcons.Actions.Scratch);
|
||||
|
||||
@@ -67,8 +78,7 @@ public class ScratchFileActions {
|
||||
private final String myActionText;
|
||||
|
||||
public NewFileAction() {
|
||||
final Presentation templatePresentation = getTemplatePresentation();
|
||||
templatePresentation.setIcon(ICON);
|
||||
getTemplatePresentation().setIcon(ICON);
|
||||
// A hacky way for customizing text in IDEs without File->New-> submenu
|
||||
myActionText = (isIdeWithoutNewSubmenu() ? "New " : "") + ActionsBundle.actionText(ACTION_ID);
|
||||
}
|
||||
@@ -88,21 +98,18 @@ public class ScratchFileActions {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
final Project project = e.getProject();
|
||||
Project project = e.getProject();
|
||||
if (project == null) return;
|
||||
|
||||
PsiFile file = e.getData(CommonDataKeys.PSI_FILE);
|
||||
Editor editor = e.getData(CommonDataKeys.EDITOR);
|
||||
|
||||
String eventText = getSelectionText(editor);
|
||||
if (eventText == null) {
|
||||
eventText = e.getData(PlatformDataKeys.PREDEFINED_TEXT);
|
||||
}
|
||||
final String text = StringUtil.notNullize(eventText);
|
||||
Language language = text.isEmpty() ? null : detectLanguageFromSelection(project, editor, file, text);
|
||||
Consumer<Language> consumer = language1 -> doCreateNewScratch(project, false, language1, text);
|
||||
if (language != null) {
|
||||
consumer.consume(language);
|
||||
ScratchFileCreationHelper.Context context = createContext(project, e);
|
||||
Consumer<Language> consumer = l -> {
|
||||
context.language = l;
|
||||
ScratchFileCreationHelper.EXTENSION.forLanguage(context.language).prepareText(
|
||||
project, context, DataContext.EMPTY_CONTEXT);
|
||||
doCreateNewScratch(project, context);
|
||||
};
|
||||
if (context.language != null) {
|
||||
consumer.consume(context.language);
|
||||
}
|
||||
else {
|
||||
LRUPopupBuilder.forFileLanguages(project, null, consumer).showCenteredInCurrentWindow(project);
|
||||
@@ -126,20 +133,6 @@ public class ScratchFileActions {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Language detectLanguageFromSelection(Project project, Editor editor, PsiFile file, String text) {
|
||||
Language language = getLanguageFromCaret(project, editor, file);
|
||||
FileType fileType = LanguageUtil.getLanguageFileType(language);
|
||||
if (fileType == null) return null;
|
||||
|
||||
CharSequence fileSnippet = text.subSequence(0, Math.min(text.length(), 10 * 1024));
|
||||
PsiFileFactory fileFactory = PsiFileFactory.getInstance(file.getProject());
|
||||
PsiFile psiFile = fileFactory.createFileFromText("a." + fileType.getDefaultExtension(), language, fileSnippet);
|
||||
PsiErrorElement firstError = SyntaxTraverser.psiTraverser(psiFile).traverse().filter(PsiErrorElement.class).first();
|
||||
// heuristics: first error must not be right under the file PSI
|
||||
return firstError == null || firstError.getParent() != psiFile ? language : null;
|
||||
}
|
||||
|
||||
public static class NewBufferAction extends DumbAwareAction {
|
||||
|
||||
@Override
|
||||
@@ -152,34 +145,65 @@ public class ScratchFileActions {
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
Project project = e.getProject();
|
||||
if (project == null) return;
|
||||
|
||||
PsiFile file = e.getData(CommonDataKeys.PSI_FILE);
|
||||
Editor editor = e.getData(CommonDataKeys.EDITOR);
|
||||
|
||||
String text = StringUtil.notNullize(getSelectionText(editor));
|
||||
Language language = text.isEmpty() ? null : detectLanguageFromSelection(project, editor, file, text);
|
||||
|
||||
doCreateNewScratch(project, true, ObjectUtils.notNull(language, StdLanguages.TEXT), text);
|
||||
ScratchFileCreationHelper.Context context = createContext(project, e);
|
||||
context.filePrefix = "buffer";
|
||||
context.createOption = ScratchFileService.Option.create_if_missing;
|
||||
context.fileCounter = ScratchFileActions::nextBufferIndex;
|
||||
if (context.language == null) context.language = StdLanguages.TEXT;
|
||||
doCreateNewScratch(project, context);
|
||||
}
|
||||
}
|
||||
|
||||
static void doCreateNewScratch(@NotNull Project project, boolean buffer, @NotNull Language language, @NotNull String text) {
|
||||
@NotNull
|
||||
static ScratchFileCreationHelper.Context createContext(@NotNull Project project, @NotNull AnActionEvent e) {
|
||||
PsiFile file = e.getData(CommonDataKeys.PSI_FILE);
|
||||
Editor editor = e.getData(CommonDataKeys.EDITOR);
|
||||
|
||||
ScratchFileCreationHelper.Context context = new ScratchFileCreationHelper.Context();
|
||||
context.text = StringUtil.notNullize(getSelectionText(editor));
|
||||
if (!context.text.isEmpty()) {
|
||||
context.language = getLanguageFromCaret(project, editor, file);
|
||||
checkLanguageAndTryToFixText(project, context, e.getDataContext());
|
||||
}
|
||||
else {
|
||||
context.text = StringUtil.notNullize(e.getData(PlatformDataKeys.PREDEFINED_TEXT));
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
static void doCreateNewScratch(@NotNull Project project, @NotNull ScratchFileCreationHelper.Context context) {
|
||||
FeatureUsageTracker.getInstance().triggerFeatureUsed("scratch");
|
||||
Language language = ObjectUtils.notNull(context.language);
|
||||
if (context.fileExtension == null) {
|
||||
LanguageFileType fileType = language.getAssociatedFileType();
|
||||
context.fileExtension = fileType == null ? "" : fileType.getDefaultExtension();
|
||||
}
|
||||
ScratchFileCreationHelper.EXTENSION.forLanguage(language).beforeCreate(project, context);
|
||||
|
||||
LanguageFileType fileType = language.getAssociatedFileType();
|
||||
String ext = buffer || fileType == null? "" : fileType.getDefaultExtension();
|
||||
String fileName = buffer ? "buffer" + nextBufferIndex() : "scratch";
|
||||
ScratchFileService.Option option = buffer ? ScratchFileService.Option.create_if_missing : ScratchFileService.Option.create_new_always;
|
||||
VirtualFile f = ScratchRootType.getInstance().createScratchFile(project, PathUtil.makeFileName(fileName, ext), language, text, option);
|
||||
String fileName = PathUtil.makeFileName(ObjectUtils.notNull(context.filePrefix, "scratch") +
|
||||
(context.fileCounter != null ? context.fileCounter.create() : ""),
|
||||
context.fileExtension);
|
||||
VirtualFile f = ScratchRootType.getInstance().createScratchFile(
|
||||
project, fileName, language, context.text, context.createOption);
|
||||
if (f != null) {
|
||||
FileEditorManager.getInstance(project).openFile(f, true);
|
||||
new OpenFileDescriptor(project, f, context.caretOffset).navigate(true);
|
||||
}
|
||||
}
|
||||
|
||||
private static int ourCurrentBuffer = 0;
|
||||
private static int nextBufferIndex() {
|
||||
ourCurrentBuffer = (ourCurrentBuffer % Registry.intValue("ide.scratch.buffers")) + 1;
|
||||
return ourCurrentBuffer;
|
||||
private static void checkLanguageAndTryToFixText(@NotNull Project project,
|
||||
@NotNull ScratchFileCreationHelper.Context context,
|
||||
@NotNull DataContext dataContext) {
|
||||
if (context.language == null) return;
|
||||
ScratchFileCreationHelper handler = ScratchFileCreationHelper.EXTENSION.forLanguage(context.language);
|
||||
if (handler.prepareText(project, context, dataContext)) return;
|
||||
|
||||
PsiFile psiFile = ScratchFileCreationHelper.parseHeader(project, context.language, context.text);
|
||||
PsiErrorElement firstError = SyntaxTraverser.psiTraverser(psiFile).traverse().filter(PsiErrorElement.class).first();
|
||||
// heuristics: first error must not be right under the file PSI
|
||||
// otherwise let the user choose the language manually
|
||||
if (firstError != null && firstError.getParent() == psiFile) {
|
||||
context.language = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -197,7 +221,11 @@ public class ScratchFileActions {
|
||||
int offset = caret.getOffset();
|
||||
PsiElement element = InjectedLanguageManager.getInstance(project).findInjectedElementAt(psiFile, offset);
|
||||
PsiFile file = element != null ? element.getContainingFile() : psiFile;
|
||||
return file.getLanguage();
|
||||
Language language = file.getLanguage();
|
||||
if (language == StdLanguages.TEXT && file.getFileType() instanceof InternalFileType) {
|
||||
return StdLanguages.XML;
|
||||
}
|
||||
return language;
|
||||
}
|
||||
|
||||
public static class LanguageAction extends DumbAwareAction {
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
*/
|
||||
package com.intellij.ide.scratch;
|
||||
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.lang.LanguageExtension;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.command.WriteCommandAction;
|
||||
import com.intellij.openapi.fileTypes.LanguageFileType;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.Factory;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiFileFactory;
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager;
|
||||
import com.intellij.util.PathUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author gregsh
|
||||
*/
|
||||
public abstract class ScratchFileCreationHelper {
|
||||
public static final LanguageExtension<ScratchFileCreationHelper> EXTENSION = new LanguageExtension<>(
|
||||
"com.intellij.scratch.creationHelper", new ScratchFileCreationHelper() {
|
||||
});
|
||||
|
||||
|
||||
public boolean prepareText(@NotNull Project project, @NotNull Context context, @NotNull DataContext dataContext) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void beforeCreate(@NotNull Project project, @NotNull Context context) {
|
||||
}
|
||||
|
||||
public static class Context {
|
||||
@NotNull
|
||||
public String text = "";
|
||||
public Language language;
|
||||
public int caretOffset;
|
||||
|
||||
public String filePrefix;
|
||||
public Factory<Integer> fileCounter;
|
||||
public String fileExtension;
|
||||
|
||||
public ScratchFileService.Option createOption = ScratchFileService.Option.create_new_always;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PsiFile parseHeader(@NotNull Project project,
|
||||
@NotNull Language language,
|
||||
@NotNull String text) {
|
||||
LanguageFileType fileType = language.getAssociatedFileType();
|
||||
CharSequence fileSnippet = StringUtil.first(text, 10 * 1024, false);
|
||||
PsiFileFactory fileFactory = PsiFileFactory.getInstance(project);
|
||||
return fileFactory.createFileFromText(
|
||||
PathUtil.makeFileName("a", fileType == null ? "" : fileType.getDefaultExtension()),
|
||||
language, fileSnippet);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String reformat(@NotNull Project project,
|
||||
@NotNull Language language,
|
||||
@NotNull String text) {
|
||||
return WriteCommandAction.runWriteCommandAction(project, (Computable<String>)() -> {
|
||||
PsiFile psi = parseHeader(project, language, text);
|
||||
if (psi != null) CodeStyleManager.getInstance(project).reformat(psi);
|
||||
return psi == null ? text : psi.getText();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -895,6 +895,9 @@
|
||||
<extensionPoint name="inspectionElementsMerger" interface="com.intellij.codeInspection.ex.InspectionElementsMerger"/>
|
||||
|
||||
<extensionPoint name="scratch.rootType" interface="com.intellij.ide.scratch.RootType"/>
|
||||
<extensionPoint name="scratch.creationHelper" beanClass="com.intellij.lang.LanguageExtensionPoint">
|
||||
<with attribute="implementationClass" implements="com.intellij.ide.scratch.ScratchFileCreationHelper"/>
|
||||
</extensionPoint>
|
||||
|
||||
<extensionPoint name="packageDependencies.visitor" beanClass="com.intellij.lang.LanguageExtensionPoint">
|
||||
<with attribute="implementationClass" implements="com.intellij.packageDependencies.DependencyVisitorFactory"/>
|
||||
|
||||
@@ -341,6 +341,7 @@
|
||||
<projectConfigurable groupId="build" groupWeight="120" instance="com.intellij.jarRepository.settings.RemoteRepositoriesConfigurable" order="last"/>
|
||||
|
||||
<scratch.rootType implementation="com.intellij.execution.jshell.JShellRootType"/>
|
||||
<scratch.creationHelper language="JAVA" implementationClass="com.intellij.ide.actions.JavaScratchFileCreationHelper" />
|
||||
|
||||
<intentionAction>
|
||||
<className>com.intellij.testIntegration.createTest.GenerateMissedTestsAction</className>
|
||||
|
||||
Reference in New Issue
Block a user