├── settings.gradle ├── README ├── lib └── chelper.jar ├── resources ├── name │ └── admitriev │ │ └── jhelper │ │ ├── icons │ │ ├── copy.png │ │ ├── task.png │ │ ├── delete.png │ │ ├── newTask.png │ │ ├── speed.png │ │ ├── editTests.png │ │ ├── settings.png │ │ └── parseContest.png │ │ └── templates │ │ ├── task.template │ │ ├── submission.template │ │ └── run.template └── META-INF │ └── plugin.xml ├── gradle └── wrapper │ └── gradle-wrapper.properties ├── src └── name │ └── admitriev │ └── jhelper │ ├── exceptions │ ├── JHelperException.java │ └── NotificationException.java │ ├── ui │ ├── Notificator.java │ ├── AddTaskDialog.java │ ├── ParseListModel.java │ ├── RelativeFileChooserDescriptor.java │ ├── FileSelector.java │ ├── StreamConfigurationPanel.java │ ├── ConfigurationDialog.java │ ├── TaskSettingsComponent.java │ ├── UIUtils.java │ ├── ParseDialog.java │ └── EditTestsDialog.java │ ├── parsing │ └── Receiver.java │ ├── IDEUtils.java │ ├── actions │ ├── ConfigureAction.java │ ├── ParseContestAction.java │ ├── BaseAction.java │ ├── AddTaskAction.java │ ├── EditTestsAction.java │ ├── ProcessAction.java │ ├── CopySourceAction.java │ └── DeleteTaskAction.java │ ├── configuration │ ├── TaskConfigurationType.java │ ├── TaskConfigurationExecutionTarget.java │ ├── TaskConfigurationTargetProvider.java │ ├── TaskRunner.java │ └── TaskConfiguration.java │ ├── generation │ ├── IncludesProcessor.java │ ├── DeletionMarkingVisitor.java │ ├── TemplatesUtils.java │ ├── FileUtils.java │ └── CodeGenerationUtils.java │ ├── task │ ├── TaskData.java │ └── TaskUtils.java │ ├── network │ └── SimpleHttpServer.java │ └── components │ ├── Configurator.java │ ├── AutoSwitcher.java │ └── ChromeParser.java ├── .gitignore ├── gradlew.bat ├── gradlew └── LICENSE /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'JHelper' 2 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | CHelper for C++. 2 | 3 | Sport programming tool. 4 | -------------------------------------------------------------------------------- /lib/chelper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexeyDmitriev/JHelper/HEAD/lib/chelper.jar -------------------------------------------------------------------------------- /resources/name/admitriev/jhelper/icons/copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexeyDmitriev/JHelper/HEAD/resources/name/admitriev/jhelper/icons/copy.png -------------------------------------------------------------------------------- /resources/name/admitriev/jhelper/icons/task.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexeyDmitriev/JHelper/HEAD/resources/name/admitriev/jhelper/icons/task.png -------------------------------------------------------------------------------- /resources/name/admitriev/jhelper/icons/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexeyDmitriev/JHelper/HEAD/resources/name/admitriev/jhelper/icons/delete.png -------------------------------------------------------------------------------- /resources/name/admitriev/jhelper/icons/newTask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexeyDmitriev/JHelper/HEAD/resources/name/admitriev/jhelper/icons/newTask.png -------------------------------------------------------------------------------- /resources/name/admitriev/jhelper/icons/speed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexeyDmitriev/JHelper/HEAD/resources/name/admitriev/jhelper/icons/speed.png -------------------------------------------------------------------------------- /resources/name/admitriev/jhelper/icons/editTests.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexeyDmitriev/JHelper/HEAD/resources/name/admitriev/jhelper/icons/editTests.png -------------------------------------------------------------------------------- /resources/name/admitriev/jhelper/icons/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexeyDmitriev/JHelper/HEAD/resources/name/admitriev/jhelper/icons/settings.png -------------------------------------------------------------------------------- /resources/name/admitriev/jhelper/icons/parseContest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexeyDmitriev/JHelper/HEAD/resources/name/admitriev/jhelper/icons/parseContest.png -------------------------------------------------------------------------------- /resources/name/admitriev/jhelper/templates/task.template: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | class %ClassName% { 4 | public: 5 | void solve(std::istream& in, std::ostream& out) { 6 | 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /resources/name/admitriev/jhelper/templates/submission.template: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | %Code% 5 | 6 | int main() { 7 | %ClassName% solver; 8 | %Input% 9 | %Output% 10 | %SolverCall% 11 | return 0; 12 | } 13 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed May 27 16:37:44 IST 2020 2 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1-all.zip 3 | distributionBase=GRADLE_USER_HOME 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/exceptions/JHelperException.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.exceptions; 2 | 3 | /** 4 | * An exception representing a problem of JHelper. 5 | */ 6 | public class JHelperException extends RuntimeException { 7 | 8 | public JHelperException(String message, Throwable cause) { 9 | super(message, cause); 10 | } 11 | 12 | public JHelperException(String message) { 13 | super(message); 14 | } 15 | 16 | @Override 17 | public String getMessage() { 18 | return "Please, report that bug to issue tracker on https://github.com/AlexeyDmitriev/JHelper . Attach your code and this stack trace. " 19 | + super.getMessage(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/ui/Notificator.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.ui; 2 | 3 | import com.intellij.notification.NotificationGroup; 4 | import com.intellij.notification.NotificationGroupManager; 5 | import com.intellij.notification.NotificationType; 6 | 7 | public class Notificator { 8 | private static final NotificationGroup GROUP = NotificationGroupManager.getInstance().getNotificationGroup("JHelper"); 9 | 10 | private Notificator() { 11 | } 12 | 13 | public static void showNotification(String title, String content, NotificationType notificationType) { 14 | GROUP.createNotification(title, content, notificationType, null).notify(null); 15 | } 16 | 17 | public static void showNotification(String content, NotificationType notificationType) { 18 | showNotification("", content, notificationType); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/parsing/Receiver.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.parsing; 2 | 3 | import net.egork.chelper.parser.Description; 4 | import net.egork.chelper.parser.DescriptionReceiver; 5 | 6 | import java.util.Collection; 7 | 8 | /** 9 | * A class for receiving available contests and problems from parser asynchronously 10 | */ 11 | public abstract class Receiver implements DescriptionReceiver { 12 | private boolean stopped = false; 13 | 14 | @Override 15 | public boolean isStopped() { 16 | return stopped; 17 | } 18 | 19 | public void stop() { 20 | stopped = true; 21 | } 22 | 23 | public static class Empty extends Receiver { 24 | public Empty() { 25 | stop(); 26 | } 27 | 28 | @Override 29 | public void receiveDescriptions(Collection descriptions) { 30 | 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/ui/AddTaskDialog.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.ui; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.openapi.ui.DialogWrapper; 5 | import name.admitriev.jhelper.task.TaskData; 6 | import org.jetbrains.annotations.NotNull; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | import javax.swing.*; 10 | 11 | public class AddTaskDialog extends DialogWrapper { 12 | private TaskSettingsComponent component; 13 | 14 | public AddTaskDialog(@NotNull Project project) { 15 | super(project); 16 | setTitle("Add Task"); 17 | component = new TaskSettingsComponent( 18 | project, 19 | true, 20 | this::pack 21 | ); 22 | init(); 23 | } 24 | 25 | @Nullable 26 | @Override 27 | protected JComponent createCenterPanel() { 28 | return component; 29 | } 30 | 31 | public TaskData getTask() { 32 | return component.getTask(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/IDEUtils.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper; 2 | 3 | import com.intellij.execution.ExecutionTarget; 4 | import com.intellij.execution.ExecutionTargetManager; 5 | import com.intellij.execution.RunManager; 6 | import com.intellij.execution.RunnerAndConfigurationSettings; 7 | import com.intellij.openapi.project.Project; 8 | import com.jetbrains.cidr.cpp.cmake.workspace.CMakeWorkspace; 9 | 10 | public class IDEUtils { 11 | private IDEUtils() { 12 | } 13 | 14 | public static void reloadProject(Project project) { 15 | CMakeWorkspace.getInstance(project).scheduleReload(true); 16 | } 17 | 18 | public static void chooseConfigurationAndTarget( 19 | Project project, 20 | RunnerAndConfigurationSettings runConfiguration, 21 | ExecutionTarget target 22 | ) { 23 | RunManager.getInstance(project).setSelectedConfiguration(runConfiguration); 24 | ExecutionTargetManager.getInstance(project).setActiveTarget(target); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/actions/ConfigureAction.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.actions; 2 | 3 | import com.intellij.openapi.actionSystem.AnActionEvent; 4 | import com.intellij.openapi.project.Project; 5 | import name.admitriev.jhelper.components.Configurator; 6 | import name.admitriev.jhelper.exceptions.NotificationException; 7 | import name.admitriev.jhelper.ui.ConfigurationDialog; 8 | 9 | public class ConfigureAction extends BaseAction { 10 | @Override 11 | public void performAction(AnActionEvent e) { 12 | Project project = e.getProject(); 13 | if (project == null) { 14 | throw new NotificationException("No project found", "Are you in any project?"); 15 | } 16 | 17 | Configurator configurator = project.getService(Configurator.class); 18 | Configurator.State configuration = configurator.getState(); 19 | 20 | ConfigurationDialog x = new ConfigurationDialog(project, configuration); 21 | x.show(); 22 | if (x.isOK()) { 23 | configurator.loadState(x.getConfiguration()); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/ui/ParseListModel.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.ui; 2 | 3 | import javax.swing.*; 4 | import java.util.ArrayList; 5 | import java.util.Collection; 6 | import java.util.List; 7 | 8 | 9 | /** 10 | * ListModel that allows adding an entire {@code Collection} 11 | */ 12 | public class ParseListModel extends AbstractListModel { 13 | private List list = new ArrayList<>(); 14 | 15 | @Override 16 | public int getSize() { 17 | return list.size(); 18 | } 19 | 20 | @Override 21 | public T getElementAt(int index) { 22 | return list.get(index); 23 | } 24 | 25 | public void removeAll() { 26 | int size = getSize(); 27 | if (size == 0) { 28 | return; 29 | } 30 | list.clear(); 31 | fireIntervalRemoved(this, 0, size - 1); 32 | } 33 | 34 | public void addAll(Collection collection) { 35 | if (collection.isEmpty()) { 36 | return; 37 | } 38 | int size = getSize(); 39 | list.addAll(collection); 40 | fireIntervalAdded(this, size, getSize() - 1); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/exceptions/NotificationException.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.exceptions; 2 | 3 | /** 4 | * An exception representing an error that should be shown to end user 5 | */ 6 | public class NotificationException extends RuntimeException { 7 | private final String title; 8 | private final String content; 9 | 10 | public NotificationException(String content) { 11 | this(content, (Throwable) null); 12 | } 13 | 14 | public NotificationException(String content, Throwable cause) { 15 | super(content, cause); 16 | title = ""; 17 | this.content = content; 18 | } 19 | 20 | public NotificationException(String title, String content) { 21 | this(title, content, null); 22 | } 23 | 24 | public NotificationException(String title, String content, Throwable cause) { 25 | super(title + ": " + content, cause); 26 | this.title = title; 27 | this.content = content; 28 | } 29 | 30 | public String getTitle() { 31 | return title; 32 | } 33 | 34 | public String getContent() { 35 | return content; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/configuration/TaskConfigurationType.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.configuration; 2 | 3 | import com.intellij.execution.configurations.RunConfiguration; 4 | import com.intellij.execution.configurations.SimpleConfigurationType; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.openapi.util.IconLoader; 7 | import com.intellij.openapi.util.NotNullLazyValue; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | public class TaskConfigurationType extends SimpleConfigurationType { 11 | public TaskConfigurationType() { 12 | super( 13 | "name.admitriev.jhelper.configuration.TaskConfigurationType", 14 | "Task", 15 | "Task for JHelper", 16 | NotNullLazyValue.lazy(() -> { 17 | return IconLoader.getIcon( 18 | "/name/admitriev/jhelper/icons/task.png", 19 | TaskConfigurationType.class 20 | ); 21 | }) 22 | ); 23 | } 24 | 25 | @NotNull 26 | @Override 27 | public RunConfiguration createTemplateConfiguration(@NotNull Project project) { 28 | return new TaskConfiguration(project, this); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/actions/ParseContestAction.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.actions; 2 | 3 | import com.intellij.openapi.actionSystem.AnActionEvent; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.psi.PsiElement; 6 | import com.jetbrains.cidr.lang.psi.OCFile; 7 | import name.admitriev.jhelper.IDEUtils; 8 | import name.admitriev.jhelper.task.TaskData; 9 | import name.admitriev.jhelper.task.TaskUtils; 10 | import name.admitriev.jhelper.ui.ParseDialog; 11 | import name.admitriev.jhelper.ui.UIUtils; 12 | 13 | public class ParseContestAction extends BaseAction { 14 | @Override 15 | protected void performAction(AnActionEvent e) { 16 | Project project = e.getProject(); 17 | ParseDialog dialog = new ParseDialog(project); 18 | dialog.show(); 19 | if (!dialog.isOK()) { 20 | return; 21 | } 22 | for (TaskData taskData : dialog.getResult()) { 23 | PsiElement generatedFile = TaskUtils.saveNewTask(taskData, project); 24 | UIUtils.openMethodInEditor(project, (OCFile) generatedFile, "solve"); 25 | } 26 | 27 | IDEUtils.reloadProject(project); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | 10 | # Packages # 11 | ############ 12 | # it's better to unpack these files and commit the raw source 13 | # git has its own built in compression methods 14 | *.7z 15 | *.dmg 16 | *.gz 17 | *.iso 18 | *.jar 19 | *.rar 20 | *.tar 21 | *.zip 22 | 23 | # Logs and databases # 24 | ###################### 25 | *.log 26 | 27 | # OS generated files # 28 | ###################### 29 | .DS_Store* 30 | ehthumbs.db 31 | Icon? 32 | Thumbs.db 33 | 34 | # Editor Files # 35 | ################ 36 | *~ 37 | *.swp 38 | 39 | # Gradle Files # 40 | ################ 41 | .gradle 42 | .m2 43 | 44 | # Build output directies 45 | /target 46 | */target 47 | /build 48 | */build 49 | 50 | # IntelliJ specific files/directories 51 | out 52 | .idea 53 | *.ipr 54 | *.iws 55 | *.iml 56 | atlassian-ide-plugin.xml 57 | 58 | # Eclipse specific files/directories 59 | .classpath 60 | .project 61 | .settings 62 | .metadata 63 | 64 | # NetBeans specific files/directories 65 | .nbattrs 66 | 67 | # Vscode 68 | .vscode 69 | bin 70 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/actions/BaseAction.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.actions; 2 | 3 | import com.intellij.notification.NotificationType; 4 | import com.intellij.openapi.actionSystem.AnAction; 5 | import com.intellij.openapi.actionSystem.AnActionEvent; 6 | import name.admitriev.jhelper.exceptions.JHelperException; 7 | import name.admitriev.jhelper.exceptions.NotificationException; 8 | import name.admitriev.jhelper.ui.Notificator; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | public abstract class BaseAction extends AnAction { 12 | @Override 13 | public void actionPerformed(@NotNull AnActionEvent e) { 14 | try { 15 | performAction(e); 16 | } 17 | catch (NotificationException exception) { 18 | Notificator.showNotification( 19 | exception.getTitle(), 20 | exception.getContent(), 21 | NotificationType.ERROR 22 | ); 23 | } 24 | catch (JHelperException exception) { 25 | Notificator.showNotification( 26 | "Please report this exception to our bug tracker", 27 | "You can see it again in the event log", 28 | NotificationType.ERROR 29 | ); 30 | throw exception; 31 | } 32 | } 33 | 34 | protected abstract void performAction(AnActionEvent e); 35 | } 36 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/actions/AddTaskAction.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.actions; 2 | 3 | import com.intellij.openapi.actionSystem.AnActionEvent; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.psi.PsiElement; 6 | import com.jetbrains.cidr.lang.psi.OCFile; 7 | import name.admitriev.jhelper.IDEUtils; 8 | import name.admitriev.jhelper.exceptions.NotificationException; 9 | import name.admitriev.jhelper.task.TaskData; 10 | import name.admitriev.jhelper.task.TaskUtils; 11 | import name.admitriev.jhelper.ui.AddTaskDialog; 12 | import name.admitriev.jhelper.ui.UIUtils; 13 | 14 | public class AddTaskAction extends BaseAction { 15 | 16 | @Override 17 | public void performAction(AnActionEvent e) { 18 | Project project = e.getProject(); 19 | if (project == null) { 20 | throw new NotificationException("No project found", "Are you in any project?"); 21 | } 22 | 23 | AddTaskDialog dialog = new AddTaskDialog(project); 24 | dialog.show(); 25 | if (!dialog.isOK()) { 26 | return; 27 | } 28 | TaskData task = dialog.getTask(); 29 | 30 | PsiElement generatedFile = TaskUtils.saveNewTask(task, project); 31 | 32 | UIUtils.openMethodInEditor(project, (OCFile) generatedFile, "solve"); 33 | 34 | IDEUtils.reloadProject(project); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/configuration/TaskConfigurationExecutionTarget.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.configuration; 2 | 3 | import com.intellij.execution.ExecutionTarget; 4 | import com.intellij.execution.configurations.RunConfiguration; 5 | import org.jetbrains.annotations.NotNull; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | import javax.swing.Icon; 9 | 10 | public class TaskConfigurationExecutionTarget extends ExecutionTarget { 11 | private final ExecutionTarget originalTarget; 12 | 13 | TaskConfigurationExecutionTarget(ExecutionTarget originalTarget) { 14 | this.originalTarget = originalTarget; 15 | } 16 | 17 | @Override 18 | public @NotNull String getId() { 19 | return "name.admitriev.jhelper.configuration.TaskConfigurationExecutionTarget" + originalTarget.getId(); 20 | } 21 | 22 | @Override 23 | public @NotNull String getDisplayName() { 24 | return originalTarget.getDisplayName(); 25 | } 26 | 27 | @Override 28 | public @Nullable Icon getIcon() { 29 | return originalTarget.getIcon(); 30 | } 31 | 32 | @Override 33 | public boolean canRun(@NotNull RunConfiguration runConfiguration) { 34 | return runConfiguration instanceof TaskConfiguration; 35 | } 36 | 37 | public ExecutionTarget getOriginalTarget() { 38 | return originalTarget; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/configuration/TaskConfigurationTargetProvider.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.configuration; 2 | 3 | import com.intellij.execution.ExecutionTarget; 4 | import com.intellij.execution.ExecutionTargetManager; 5 | import com.intellij.execution.ExecutionTargetProvider; 6 | import com.intellij.execution.RunnerAndConfigurationSettings; 7 | import com.intellij.execution.configurations.RunConfiguration; 8 | import com.intellij.openapi.project.Project; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | import java.util.ArrayList; 12 | import java.util.Collections; 13 | import java.util.List; 14 | 15 | public class TaskConfigurationTargetProvider extends ExecutionTargetProvider { 16 | @Override 17 | public @NotNull List getTargets( 18 | @NotNull Project project, 19 | @NotNull RunConfiguration configuration 20 | ) { 21 | if (!(configuration instanceof TaskConfiguration)) { 22 | return Collections.emptyList(); 23 | } 24 | RunnerAndConfigurationSettings testRunner = TaskRunner.getRunnerSettings(project); 25 | if (testRunner == null) { 26 | return Collections.emptyList(); 27 | } 28 | List runnerTargets = ExecutionTargetManager.getInstance(project).getTargetsFor(testRunner.getConfiguration()); 29 | List myTargets = new ArrayList<>(); 30 | for (ExecutionTarget target : runnerTargets) { 31 | myTargets.add(new TaskConfigurationExecutionTarget(target)); 32 | } 33 | return myTargets; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/actions/EditTestsAction.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.actions; 2 | 3 | import com.intellij.execution.RunnerAndConfigurationSettings; 4 | import com.intellij.execution.configurations.RunConfiguration; 5 | import com.intellij.execution.impl.RunManagerImpl; 6 | import com.intellij.openapi.actionSystem.AnActionEvent; 7 | import com.intellij.openapi.project.Project; 8 | import name.admitriev.jhelper.configuration.TaskConfiguration; 9 | import name.admitriev.jhelper.ui.EditTestsDialog; 10 | import net.egork.chelper.task.Test; 11 | 12 | public class EditTestsAction extends BaseAction { 13 | 14 | @Override 15 | protected void performAction(AnActionEvent e) { 16 | Project project = e.getProject(); 17 | RunnerAndConfigurationSettings selectedConfiguration = 18 | RunManagerImpl.getInstanceImpl(project).getSelectedConfiguration(); 19 | if (selectedConfiguration == null) { 20 | return; 21 | } 22 | RunConfiguration configuration = selectedConfiguration.getConfiguration(); 23 | if (configuration instanceof TaskConfiguration) { 24 | TaskConfiguration taskConfiguration = (TaskConfiguration) configuration; 25 | Test[] originalTests = taskConfiguration.getTests(); 26 | EditTestsDialog dialog = new EditTestsDialog(originalTests, project); 27 | dialog.show(); 28 | if (!dialog.isOK()) { 29 | return; 30 | } 31 | Test[] newTests = dialog.getTests(); 32 | taskConfiguration.setTests(newTests); 33 | 34 | // @todo: save configuration 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/ui/RelativeFileChooserDescriptor.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.ui; 2 | 3 | import com.intellij.openapi.fileChooser.FileChooserDescriptor; 4 | import com.intellij.openapi.vfs.VirtualFile; 5 | import name.admitriev.jhelper.generation.FileUtils; 6 | 7 | public class RelativeFileChooserDescriptor extends FileChooserDescriptor { 8 | private String basePath; 9 | 10 | private RelativeFileChooserDescriptor( 11 | VirtualFile baseDir, 12 | boolean chooseFiles, 13 | boolean chooseFolders 14 | ) { 15 | super(chooseFiles, chooseFolders, false, false, false, false); 16 | basePath = baseDir.getPath(); 17 | withShowHiddenFiles(true); 18 | 19 | setRoots(baseDir); 20 | } 21 | 22 | @Override 23 | public boolean isFileSelectable(VirtualFile file) { 24 | return super.isFileSelectable(file) && FileUtils.isChild( 25 | basePath, 26 | file.getPath() 27 | ); 28 | } 29 | 30 | @Override 31 | public boolean isFileVisible(VirtualFile file, boolean showHiddenFiles) { 32 | return super.isFileVisible(file, showHiddenFiles) && ( 33 | FileUtils.isChild(basePath, file.getPath()) || FileUtils.isChild(file.getPath(), basePath) 34 | ); 35 | } 36 | 37 | public static RelativeFileChooserDescriptor fileChooser(VirtualFile baseDir) { 38 | return new RelativeFileChooserDescriptor(baseDir, true, false); 39 | } 40 | 41 | public static RelativeFileChooserDescriptor directoryChooser(VirtualFile baseDir) { 42 | return new RelativeFileChooserDescriptor(baseDir, false, true); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/ui/FileSelector.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.ui; 2 | 3 | import com.intellij.openapi.fileChooser.FileChooserDescriptor; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.openapi.ui.TextBrowseFolderListener; 6 | import com.intellij.openapi.ui.TextFieldWithBrowseButton; 7 | import com.intellij.openapi.vfs.VirtualFile; 8 | import name.admitriev.jhelper.generation.FileUtils; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | import javax.swing.*; 12 | 13 | public class FileSelector extends TextFieldWithBrowseButton.NoPathCompletion { 14 | 15 | public FileSelector(Project project, String initialValue, FileChooserDescriptor descriptor) { 16 | super(new JTextField(initialValue)); 17 | addBrowseFolderListener( 18 | new RelativePathBrowseListener(descriptor, project) 19 | ); 20 | installPathCompletion(descriptor); 21 | } 22 | 23 | private static class RelativePathBrowseListener extends TextBrowseFolderListener { 24 | private final String basePath; 25 | 26 | private RelativePathBrowseListener(FileChooserDescriptor descriptor, Project project) { 27 | super(descriptor, project); 28 | basePath = project.getBasePath(); 29 | } 30 | 31 | @NotNull 32 | @Override 33 | protected String chosenFileToResultingText(@NotNull VirtualFile chosenFile) { 34 | return FileUtils.relativePath(basePath, chosenFile.getPath()); 35 | } 36 | 37 | @NotNull 38 | @Override 39 | protected String expandPath(@NotNull String path) { 40 | return basePath+ '/' + path; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/ui/StreamConfigurationPanel.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.ui; 2 | 3 | import com.intellij.openapi.ui.ComboBox; 4 | import net.egork.chelper.task.StreamConfiguration; 5 | import org.jdesktop.swingx.VerticalLayout; 6 | 7 | import javax.swing.*; 8 | 9 | /** 10 | * Panel for configuration input or output for Task. 11 | */ 12 | public class StreamConfigurationPanel extends JPanel { 13 | private ComboBox type; 14 | private JTextField fileName; 15 | 16 | public StreamConfigurationPanel( 17 | StreamConfiguration configuration, 18 | StreamConfiguration.StreamType[] allowedTypes, 19 | String defaultFileName, 20 | SizeChangedListener listener 21 | ) { 22 | super(new VerticalLayout()); 23 | type = new ComboBox<>(allowedTypes); 24 | type.setSelectedItem(configuration.type); 25 | type.addActionListener( 26 | e -> { 27 | fileName.setVisible(((StreamConfiguration.StreamType) type.getSelectedItem()).hasStringParameter); 28 | if (listener != null) { 29 | listener.sizeChanged(); 30 | } 31 | } 32 | ); 33 | fileName = new JTextField(configuration.type.hasStringParameter ? configuration.fileName : defaultFileName); 34 | fileName.setVisible(((StreamConfiguration.StreamType) type.getSelectedItem()).hasStringParameter); 35 | 36 | add(type); 37 | add(fileName); 38 | } 39 | 40 | public StreamConfiguration getStreamConfiguration() { 41 | return new StreamConfiguration((StreamConfiguration.StreamType) type.getSelectedItem(), fileName.getText()); 42 | } 43 | 44 | @FunctionalInterface 45 | public interface SizeChangedListener { 46 | void sizeChanged(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/actions/ProcessAction.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.actions; 2 | 3 | import com.intellij.openapi.actionSystem.AnActionEvent; 4 | import com.intellij.openapi.fileEditor.FileEditorManager; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.openapi.vfs.VirtualFile; 7 | import com.intellij.psi.PsiFile; 8 | import com.intellij.psi.PsiManager; 9 | import name.admitriev.jhelper.exceptions.NotificationException; 10 | import name.admitriev.jhelper.generation.FileUtils; 11 | import name.admitriev.jhelper.generation.IncludesProcessor; 12 | 13 | /** 14 | * @author egor@egork.net 15 | */ 16 | public class ProcessAction extends BaseAction { 17 | @Override 18 | protected void performAction(AnActionEvent e) { 19 | Project project = e.getProject(); 20 | if (project == null) { 21 | throw new NotificationException("No project found", "Are you in any project?"); 22 | } 23 | FileEditorManager manager = FileEditorManager.getInstance(project); 24 | if (manager == null) { 25 | throw new NotificationException("This is unexpected", "File editor manager is null"); 26 | } 27 | VirtualFile[] files = manager.getSelectedFiles(); 28 | if (files.length == 0) { 29 | throw new NotificationException("No file found", "Do you have opened file?"); 30 | } 31 | PsiFile file = PsiManager.getInstance(project).findFile(files[0]); 32 | if (file == null) { 33 | throw new NotificationException("This is unexpected", "No associated PsiFile"); 34 | } 35 | if (!FileUtils.isCppFile(file)) { 36 | throw new NotificationException("Not a cpp file", "Only cpp files are currently supported"); 37 | } 38 | String result = IncludesProcessor.process(file); 39 | FileUtils.writeToFile(file, result); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/generation/IncludesProcessor.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.generation; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.PsiFile; 5 | import com.jetbrains.cidr.lang.psi.OCFile; 6 | import com.jetbrains.cidr.lang.psi.OCIncludeDirective; 7 | import com.jetbrains.cidr.lang.psi.OCPragma; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | import java.util.HashSet; 11 | import java.util.Set; 12 | 13 | public class IncludesProcessor { 14 | private Set processedFiles = new HashSet<>(); 15 | @SuppressWarnings("StringBufferField") 16 | private StringBuilder result = new StringBuilder(); 17 | 18 | 19 | private IncludesProcessor() { 20 | } 21 | 22 | private void processFile(PsiFile file) { 23 | if (processedFiles.contains(file)) { 24 | return; 25 | } 26 | processedFiles.add(file); 27 | for (PsiElement element : file.getChildren()) { 28 | if (element instanceof OCIncludeDirective) { 29 | OCIncludeDirective include = (OCIncludeDirective) element; 30 | if (isInternalInclude(include)) { 31 | processFile(include.getIncludedFile()); 32 | } 33 | else { 34 | processAngleBracketsInclude(include); 35 | } 36 | continue; 37 | } 38 | if (element instanceof OCPragma) { 39 | OCPragma pragma = (OCPragma) element; 40 | if (pragma.getContent(true).first.equals("once")) { 41 | continue; 42 | } 43 | } 44 | result.append(element.getText()); 45 | } 46 | } 47 | 48 | private static boolean isInternalInclude(OCIncludeDirective include) { 49 | PsiFile file = include.getIncludedFile(); 50 | return file != null && ((OCFile) file).isInProjectSources(); 51 | } 52 | 53 | private void processAngleBracketsInclude(OCIncludeDirective include) { 54 | PsiFile file = include.getIncludedFile(); 55 | if (processedFiles.contains(file)) { 56 | return; 57 | } 58 | processedFiles.add(file); 59 | result.append(include.getText()); 60 | } 61 | 62 | public static @NotNull String process(PsiFile file) { 63 | IncludesProcessor processor = new IncludesProcessor(); 64 | processor.processFile(file); 65 | return processor.result.toString(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /resources/name/admitriev/jhelper/templates/run.template: -------------------------------------------------------------------------------- 1 | #include "%TaskFile%" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace jhelper { 12 | 13 | struct Test { 14 | std::string input; 15 | std::string output; 16 | bool active; 17 | bool has_output; 18 | }; 19 | 20 | bool check(std::string expected, std::string actual) { 21 | while(!expected.empty() && isspace(*--expected.end())) 22 | expected.erase(--expected.end()); 23 | while(!actual.empty() && isspace(*--actual.end())) 24 | actual.erase(--actual.end()); 25 | return expected == actual; 26 | } 27 | 28 | } // namespace jhelper 29 | 30 | int main() { 31 | std::vector tests = { 32 | %Tests% 33 | }; 34 | bool allOK = true; 35 | int testID = 0; 36 | std::cout << std::fixed; 37 | double maxTime = 0.0; 38 | for(const jhelper::Test& test: tests ) { 39 | std::cout << "Test #" << ++testID << std::endl; 40 | std::cout << "Input: \n" << test.input << std::endl; 41 | if (test.has_output) { 42 | std::cout << "Expected output: \n" << test.output << std::endl; 43 | } 44 | else { 45 | std::cout << "Expected output: \n" << "N/A" << std::endl; 46 | } 47 | if (test.active) { 48 | std::stringstream in(test.input); 49 | std::ostringstream out; 50 | std::clock_t start = std::clock(); 51 | %ClassName% solver; 52 | %SolverCall% 53 | std::clock_t finish = std::clock(); 54 | double currentTime = double(finish - start) / CLOCKS_PER_SEC; 55 | maxTime = std::max(currentTime, maxTime); 56 | std::cout << "Actual output: \n" << out.str() << std::endl; 57 | if (test.has_output) { 58 | bool result = jhelper::check(test.output, out.str()); 59 | allOK = allOK && result; 60 | std::cout << "Result: " << (result ? "OK" : "Wrong answer") << std::endl; 61 | } 62 | std::cout << "Time: " << currentTime << std::endl; 63 | } 64 | else { 65 | std::cout << "SKIPPED\n"; 66 | } 67 | 68 | std::cout << std::endl; 69 | 70 | } 71 | if(allOK) { 72 | std::cout << "All OK" << std::endl; 73 | } 74 | else { 75 | std::cout << "Some cases failed" << std::endl; 76 | } 77 | std::cout << "Maximal time: " << maxTime << "s." << std::endl; 78 | return 0; 79 | } 80 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/task/TaskData.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.task; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import name.admitriev.jhelper.components.Configurator; 5 | import net.egork.chelper.task.StreamConfiguration; 6 | import net.egork.chelper.task.Test; 7 | import net.egork.chelper.task.TestType; 8 | 9 | import java.util.Arrays; 10 | 11 | /** 12 | * Represent configuration of a task 13 | */ 14 | public class TaskData { 15 | private final String name; 16 | private final String className; 17 | private final String cppPath; 18 | private final StreamConfiguration input; 19 | private final StreamConfiguration output; 20 | private final TestType testType; 21 | private final Test[] tests; 22 | 23 | public TaskData( 24 | String name, 25 | String className, 26 | String cppPath, 27 | StreamConfiguration input, 28 | StreamConfiguration output, 29 | TestType testType, 30 | Test[] tests 31 | ) { 32 | this.input = input; 33 | this.output = output; 34 | this.name = name; 35 | this.className = className; 36 | this.cppPath = cppPath; 37 | this.testType = testType; 38 | this.tests = Arrays.copyOf(tests, tests.length); 39 | } 40 | 41 | public TaskData(TaskData task) { 42 | this(task.name, task.className, task.cppPath, task.input, task.output, task.testType, task.tests); 43 | } 44 | 45 | public String getName() { 46 | return name; 47 | } 48 | 49 | public String getClassName() { 50 | return className; 51 | } 52 | 53 | public String getCppPath() { 54 | return cppPath; 55 | } 56 | 57 | public StreamConfiguration getInput() { 58 | return input; 59 | } 60 | 61 | public StreamConfiguration getOutput() { 62 | return output; 63 | } 64 | 65 | public Test[] getTests() { 66 | return Arrays.copyOf(tests, tests.length); 67 | } 68 | 69 | public static TaskData emptyTaskData(Project project) { 70 | return new TaskData( 71 | "", 72 | "", 73 | String.format(defaultCppPathFormat(project), ""), 74 | StreamConfiguration.STANDARD, 75 | StreamConfiguration.STANDARD, 76 | TestType.SINGLE, 77 | new Test[0] 78 | ); 79 | } 80 | 81 | public static String defaultCppPathFormat(Project project) { 82 | Configurator configurator = project.getService(Configurator.class); 83 | Configurator.State configuration = configurator.getState(); 84 | String path = configuration.getTasksDirectory(); 85 | return path + "/%s.cpp"; 86 | } 87 | 88 | public TestType getTestType() { 89 | return testType; 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/network/SimpleHttpServer.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.network; 2 | 3 | import com.intellij.notification.NotificationType; 4 | import com.intellij.openapi.application.ApplicationManager; 5 | import com.intellij.openapi.application.ModalityState; 6 | import com.intellij.util.Consumer; 7 | import name.admitriev.jhelper.ui.Notificator; 8 | 9 | import java.io.BufferedReader; 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | import java.io.InputStreamReader; 13 | import java.net.ServerSocket; 14 | import java.net.Socket; 15 | import java.net.SocketAddress; 16 | 17 | /** 18 | * Simple HTTP Server. 19 | * Passes every request without headers to given Consumer 20 | */ 21 | public class SimpleHttpServer implements Runnable { 22 | private Consumer consumer; 23 | private ServerSocket serverSocket = null; 24 | 25 | public SimpleHttpServer(SocketAddress endpoint, Consumer consumer) throws IOException { 26 | serverSocket = new ServerSocket(); 27 | serverSocket.bind(endpoint); 28 | this.consumer = consumer; 29 | } 30 | 31 | @Override 32 | public void run() { 33 | while (true) { 34 | try { 35 | if (serverSocket.isClosed()) { 36 | return; 37 | } 38 | try (Socket socket = serverSocket.accept()) { 39 | InputStream inputStream = socket.getInputStream(); 40 | String request = readFromStream(inputStream); 41 | String[] strings = request.split("\n\n", 2); 42 | 43 | //ignore headers 44 | if (strings.length < 2) { 45 | Notificator.showNotification( 46 | "ChromeParser", 47 | "Got response without body. Ignore.", 48 | NotificationType.INFORMATION 49 | ); 50 | continue; 51 | } 52 | String text = strings[1]; 53 | 54 | ApplicationManager.getApplication().invokeLater( 55 | () -> consumer.consume(text), 56 | ModalityState.defaultModalityState() 57 | ); 58 | } 59 | } 60 | catch (IOException ignored) { 61 | } 62 | } 63 | } 64 | 65 | private static String readFromStream(InputStream inputStream) throws IOException { 66 | try (BufferedReader reader = new BufferedReader( 67 | new InputStreamReader(inputStream, "UTF-8") 68 | )) { 69 | StringBuilder builder = new StringBuilder(); 70 | String line; 71 | //noinspection NestedAssignment 72 | while ((line = reader.readLine()) != null) 73 | builder.append(line).append('\n'); 74 | return builder.toString(); 75 | } 76 | } 77 | 78 | public void stop() { 79 | try { 80 | serverSocket.close(); 81 | } 82 | catch (IOException ignored) { 83 | 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/actions/CopySourceAction.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.actions; 2 | 3 | import com.intellij.execution.RunManagerEx; 4 | import com.intellij.execution.RunnerAndConfigurationSettings; 5 | import com.intellij.execution.configurations.RunConfiguration; 6 | import com.intellij.notification.NotificationType; 7 | import com.intellij.openapi.actionSystem.AnActionEvent; 8 | import com.intellij.openapi.editor.Document; 9 | import com.intellij.openapi.fileEditor.FileDocumentManager; 10 | import com.intellij.openapi.project.Project; 11 | import com.intellij.openapi.vfs.VirtualFile; 12 | import name.admitriev.jhelper.components.Configurator; 13 | import name.admitriev.jhelper.configuration.TaskConfiguration; 14 | import name.admitriev.jhelper.exceptions.NotificationException; 15 | import name.admitriev.jhelper.generation.CodeGenerationUtils; 16 | import name.admitriev.jhelper.ui.Notificator; 17 | 18 | import java.awt.*; 19 | import java.awt.datatransfer.StringSelection; 20 | 21 | public class CopySourceAction extends BaseAction { 22 | @Override 23 | protected void performAction(AnActionEvent e) { 24 | Project project = e.getProject(); 25 | if (project == null) 26 | throw new NotificationException("No project found", "Are you in any project?"); 27 | 28 | Configurator configurator = project.getService(Configurator.class); 29 | Configurator.State configuration = configurator.getState(); 30 | 31 | RunManagerEx runManager = RunManagerEx.getInstanceEx(project); 32 | RunnerAndConfigurationSettings selectedConfiguration = runManager.getSelectedConfiguration(); 33 | if (selectedConfiguration == null) { 34 | return; 35 | } 36 | 37 | RunConfiguration runConfiguration = selectedConfiguration.getConfiguration(); 38 | if (!(runConfiguration instanceof TaskConfiguration)) { 39 | Notificator.showNotification( 40 | "Not a JHelper configuration", 41 | "You have to choose JHelper Task to copy", 42 | NotificationType.WARNING 43 | ); 44 | return; 45 | } 46 | 47 | CodeGenerationUtils.generateSubmissionFileForTask(project, (TaskConfiguration)runConfiguration); 48 | 49 | VirtualFile file = project.getBaseDir().findFileByRelativePath(configuration.getOutputFile()); 50 | if (file == null) 51 | throw new NotificationException("Couldn't find output file"); 52 | Document document = FileDocumentManager.getInstance().getDocument(file); 53 | if (document == null) 54 | throw new NotificationException("Couldn't open output file"); 55 | StringSelection selection = new StringSelection(document.getText()); 56 | Toolkit.getDefaultToolkit().getSystemClipboard().setContents(selection, selection); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/ui/ConfigurationDialog.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.ui; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.openapi.ui.DialogWrapper; 5 | import com.intellij.openapi.ui.LabeledComponent; 6 | import name.admitriev.jhelper.components.Configurator; 7 | import org.jdesktop.swingx.VerticalLayout; 8 | import org.jetbrains.annotations.NotNull; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | import javax.swing.*; 12 | 13 | public class ConfigurationDialog extends DialogWrapper { 14 | private final JComponent component; 15 | private JTextField author; 16 | private FileSelector tasksDirectory; 17 | private FileSelector outputFile; 18 | private FileSelector runFile; 19 | private JCheckBox codeEliminationOn; 20 | private JCheckBox codeReformattingOn; 21 | 22 | public ConfigurationDialog(@NotNull Project project, Configurator.State configuration) { 23 | super(project); 24 | setTitle("JHelper configuration for " + project.getName()); 25 | 26 | author = new JTextField(configuration.getAuthor()); 27 | 28 | tasksDirectory = new FileSelector( 29 | project, 30 | configuration.getTasksDirectory(), 31 | RelativeFileChooserDescriptor.directoryChooser(project.getBaseDir()) 32 | ); 33 | outputFile = new FileSelector( 34 | project, 35 | configuration.getOutputFile(), 36 | RelativeFileChooserDescriptor.fileChooser(project.getBaseDir()) 37 | ); 38 | runFile = new FileSelector( 39 | project, 40 | configuration.getRunFile(), 41 | RelativeFileChooserDescriptor.fileChooser(project.getBaseDir()) 42 | ); 43 | 44 | codeEliminationOn = new JCheckBox("Eliminate code?", configuration.isCodeEliminationOn()); 45 | codeReformattingOn = new JCheckBox("Reformat code?", configuration.isCodeReformattingOn()); 46 | 47 | JPanel panel = new JPanel(new VerticalLayout()); 48 | panel.add(LabeledComponent.create(author, "Author")); 49 | panel.add(LabeledComponent.create(tasksDirectory, "Tasks directory")); 50 | panel.add(LabeledComponent.create(outputFile, "Output file")); 51 | panel.add(LabeledComponent.create(runFile, "Run File")); 52 | panel.add(codeEliminationOn); 53 | panel.add(codeReformattingOn); 54 | 55 | component = panel; 56 | 57 | init(); 58 | } 59 | 60 | @Nullable 61 | @Override 62 | protected JComponent createCenterPanel() { 63 | return component; 64 | } 65 | 66 | public Configurator.State getConfiguration() { 67 | return new Configurator.State( 68 | author.getText(), 69 | tasksDirectory.getText(), 70 | outputFile.getText(), 71 | runFile.getText(), 72 | codeEliminationOn.isSelected(), 73 | codeReformattingOn.isSelected() 74 | ); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/actions/DeleteTaskAction.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.actions; 2 | 3 | import com.intellij.execution.RunManagerEx; 4 | import com.intellij.execution.RunnerAndConfigurationSettings; 5 | import com.intellij.execution.configurations.RunConfiguration; 6 | import com.intellij.notification.NotificationType; 7 | import com.intellij.openapi.actionSystem.AnActionEvent; 8 | import com.intellij.openapi.application.ApplicationManager; 9 | import com.intellij.openapi.project.Project; 10 | import com.intellij.openapi.vfs.VirtualFile; 11 | import name.admitriev.jhelper.configuration.TaskConfiguration; 12 | import name.admitriev.jhelper.exceptions.NotificationException; 13 | import name.admitriev.jhelper.ui.Notificator; 14 | 15 | import java.io.IOException; 16 | 17 | public class DeleteTaskAction extends BaseAction { 18 | @Override 19 | protected void performAction(AnActionEvent e) { 20 | Project project = e.getProject(); 21 | if (project == null) { 22 | throw new NotificationException("No project found", "Are you in any project?"); 23 | } 24 | 25 | RunManagerEx runManager = RunManagerEx.getInstanceEx(project); 26 | RunnerAndConfigurationSettings selectedConfiguration = runManager.getSelectedConfiguration(); 27 | if (selectedConfiguration == null) { 28 | return; 29 | } 30 | RunConfiguration configuration = selectedConfiguration.getConfiguration(); 31 | if (configuration instanceof TaskConfiguration) { 32 | removeFiles(project, (TaskConfiguration) configuration); 33 | runManager.removeConfiguration(selectedConfiguration); 34 | selectSomeTaskConfiguration(runManager); 35 | } 36 | else { 37 | Notificator.showNotification( 38 | "Not a JHelper configuration", 39 | "To delete a configuration you should choose it first", 40 | NotificationType.WARNING 41 | ); 42 | } 43 | } 44 | 45 | private void removeFiles(Project project, TaskConfiguration taskConfiguration) { 46 | String cppPath = taskConfiguration.getCppPath(); 47 | 48 | ApplicationManager.getApplication().runWriteAction( 49 | new Runnable() { 50 | @Override 51 | public void run() { 52 | VirtualFile classFile = project.getBaseDir().findFileByRelativePath(cppPath); 53 | if (classFile != null) { 54 | try { 55 | classFile.delete(this); 56 | } 57 | catch (IOException ignored) { 58 | Notificator.showNotification( 59 | "Couldn't delete class file", 60 | NotificationType.WARNING 61 | ); 62 | } 63 | } 64 | } 65 | } 66 | ); 67 | } 68 | 69 | private static void selectSomeTaskConfiguration(RunManagerEx runManager) { 70 | for (RunnerAndConfigurationSettings settings : runManager.getAllSettings()) { 71 | if (settings.getConfiguration() instanceof TaskConfiguration) { 72 | runManager.setSelectedConfiguration(settings); 73 | return; 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/ui/TaskSettingsComponent.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.ui; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.openapi.ui.ComboBox; 5 | import com.intellij.openapi.ui.LabeledComponent; 6 | import name.admitriev.jhelper.task.TaskData; 7 | import net.egork.chelper.task.StreamConfiguration; 8 | import net.egork.chelper.task.Test; 9 | import net.egork.chelper.task.TestType; 10 | import org.jdesktop.swingx.VerticalLayout; 11 | 12 | import javax.swing.*; 13 | 14 | /** 15 | * Panel for task configuration. 16 | */ 17 | public final class TaskSettingsComponent extends JPanel { 18 | private JTextField name = null; 19 | private JTextField className = null; 20 | private FileSelector cppPath = null; 21 | private StreamConfigurationPanel input = null; 22 | private StreamConfigurationPanel output = null; 23 | private ComboBox testType = null; 24 | private final boolean canChangeName; 25 | 26 | private Project project; 27 | 28 | private StreamConfigurationPanel.SizeChangedListener listener; 29 | 30 | public TaskSettingsComponent(Project project, boolean canChangeName) { 31 | this(project, canChangeName, null); 32 | } 33 | 34 | public TaskSettingsComponent(Project project, boolean canChangeName, StreamConfigurationPanel.SizeChangedListener listener) { 35 | super(new VerticalLayout()); 36 | this.project = project; 37 | this.listener = listener; 38 | this.canChangeName = canChangeName; 39 | 40 | setTaskData(TaskData.emptyTaskData(project)); 41 | } 42 | 43 | public void setTaskData(TaskData taskData) { 44 | removeAll(); 45 | name = new JTextField(taskData.getName()); 46 | name.setEnabled(canChangeName); 47 | 48 | className = new JTextField(taskData.getClassName()); 49 | cppPath = new FileSelector( 50 | project, 51 | taskData.getCppPath(), 52 | RelativeFileChooserDescriptor.fileChooser(project.getBaseDir()) 53 | ); 54 | input = new StreamConfigurationPanel( 55 | taskData.getInput(), 56 | StreamConfiguration.StreamType.values(), 57 | "input.txt", 58 | listener 59 | ); 60 | output = new StreamConfigurationPanel( 61 | taskData.getOutput(), 62 | StreamConfiguration.OUTPUT_TYPES, 63 | "output.txt", 64 | listener 65 | ); 66 | 67 | testType = new ComboBox<>(TestType.values()); 68 | testType.setSelectedItem(taskData.getTestType()); 69 | 70 | add(LabeledComponent.create(name, "Task name")); 71 | add(LabeledComponent.create(className, "Class name")); 72 | add(LabeledComponent.create(cppPath, "Path")); 73 | add(LabeledComponent.create(input, "Input")); 74 | add(LabeledComponent.create(output, "Output")); 75 | add(LabeledComponent.create(testType, "Test type")); 76 | 77 | UIUtils.mirrorFields(name, className); 78 | UIUtils.mirrorFields(name, cppPath.getTextField(), TaskData.defaultCppPathFormat(project)); 79 | } 80 | 81 | public TaskData getTask() { 82 | return new TaskData( 83 | name.getText(), 84 | className.getText(), 85 | cppPath.getText(), 86 | input.getStreamConfiguration(), 87 | output.getStreamConfiguration(), 88 | (TestType) testType.getSelectedItem(), 89 | new Test[0] 90 | ); 91 | } 92 | 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/components/Configurator.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.components; 2 | 3 | import com.intellij.openapi.components.PersistentStateComponent; 4 | import com.intellij.openapi.components.Service; 5 | import com.intellij.openapi.components.State; 6 | import com.intellij.openapi.components.Storage; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | 10 | @State(name = "Configurator", storages = @Storage("JHelper.xml")) 11 | @Service(Service.Level.PROJECT) 12 | public final class Configurator implements PersistentStateComponent { 13 | public Configurator() { 14 | state = new Configurator.State(); 15 | } 16 | 17 | @Override 18 | public @NotNull Configurator.State getState() { 19 | return state; 20 | } 21 | 22 | @SuppressWarnings("ParameterHidesMemberVariable") 23 | @Override 24 | public void loadState(@NotNull Configurator.State state) { 25 | this.state = state; 26 | } 27 | 28 | private Configurator.State state; 29 | 30 | public static class State { 31 | private String author; 32 | private String tasksDirectory; 33 | private String outputFile; 34 | private String runFile; 35 | private boolean codeEliminationOn; 36 | private boolean codeReformattingOn; 37 | 38 | public State( 39 | String author, 40 | String tasksDirectory, 41 | String outputFile, 42 | String runFile, 43 | boolean codeEliminationOn, 44 | boolean codeReformattingOn 45 | ) { 46 | this.author = author; 47 | this.tasksDirectory = tasksDirectory; 48 | this.outputFile = outputFile; 49 | this.runFile = runFile; 50 | this.codeEliminationOn = codeEliminationOn; 51 | this.codeReformattingOn = codeReformattingOn; 52 | } 53 | 54 | public State() { 55 | this("", "tasks", "output/main.cpp", "testrunner/main.cpp", false, false); 56 | } 57 | 58 | public String getAuthor() { 59 | return author; 60 | } 61 | 62 | public String getTasksDirectory() { 63 | return tasksDirectory; 64 | } 65 | 66 | public String getOutputFile() { 67 | return outputFile; 68 | } 69 | 70 | public String getRunFile() { 71 | return runFile; 72 | } 73 | 74 | public boolean isCodeEliminationOn() { 75 | return codeEliminationOn; 76 | } 77 | 78 | public boolean isCodeReformattingOn() { 79 | return codeReformattingOn; 80 | } 81 | 82 | @Deprecated 83 | public void setAuthor(String author) { 84 | this.author = author; 85 | } 86 | 87 | @Deprecated 88 | public void setTasksDirectory(String tasksDirectory) { 89 | this.tasksDirectory = tasksDirectory; 90 | } 91 | 92 | @Deprecated 93 | public void setOutputFile(String outputFile) { 94 | this.outputFile = outputFile; 95 | } 96 | 97 | @Deprecated 98 | public void setRunFile(String runFile) { 99 | this.runFile = runFile; 100 | } 101 | 102 | @Deprecated 103 | public void setCodeEliminationOn(boolean codeEliminationOn) { 104 | this.codeEliminationOn = codeEliminationOn; 105 | } 106 | 107 | @Deprecated 108 | public void setCodeReformattingOn(boolean codeReformattingOn) { 109 | this.codeReformattingOn = codeReformattingOn; 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/generation/DeletionMarkingVisitor.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.generation; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.PsiReference; 5 | import com.intellij.psi.search.SearchScope; 6 | import com.intellij.psi.search.searches.ReferencesSearch; 7 | import com.jetbrains.cidr.lang.psi.OCCppNamespace; 8 | import com.jetbrains.cidr.lang.psi.OCDeclaration; 9 | import com.jetbrains.cidr.lang.psi.OCDeclarator; 10 | import com.jetbrains.cidr.lang.psi.OCElement; 11 | import com.jetbrains.cidr.lang.psi.OCFunctionDefinition; 12 | import com.jetbrains.cidr.lang.psi.OCStructLike; 13 | import com.jetbrains.cidr.lang.psi.OCTypeElement; 14 | import com.jetbrains.cidr.lang.psi.impl.OCDefineDirectiveImpl; 15 | import com.jetbrains.cidr.lang.psi.visitors.OCVisitor; 16 | 17 | import java.util.Collection; 18 | 19 | public class DeletionMarkingVisitor extends OCVisitor { 20 | private final Collection toDelete; 21 | private SearchScope searchScope; 22 | 23 | public DeletionMarkingVisitor(Collection toDelete, SearchScope searchScope) { 24 | //noinspection AssignmentToCollectionOrArrayFieldFromParameter 25 | this.toDelete = toDelete; 26 | this.searchScope = searchScope; 27 | } 28 | 29 | private static boolean isParentFor(OCElement potentialParent, PsiElement potentialChild) { 30 | while (potentialChild != null) { 31 | //noinspection ObjectEquality 32 | if (potentialChild == potentialParent) { 33 | return true; 34 | } 35 | potentialChild = potentialChild.getParent(); 36 | } 37 | return false; 38 | } 39 | 40 | @Override 41 | public void visitFunctionDefinition(OCFunctionDefinition functionDefinition) { 42 | String name = functionDefinition.getName(); 43 | if ("main".equals(name)) { 44 | return; 45 | } 46 | if (name != null && name.startsWith("operator")) { 47 | // To workaround a fact that no usages found for operators 48 | return; 49 | } 50 | super.visitFunctionDefinition(functionDefinition); 51 | } 52 | 53 | private void removeIfNoReference(OCElement element) { 54 | for (PsiReference reference : ReferencesSearch.search(element, searchScope)) { 55 | PsiElement referenceElement = reference.getElement(); 56 | if (!isParentFor(element, referenceElement)) { 57 | return; 58 | } 59 | } 60 | toDelete.add(element); 61 | } 62 | 63 | @Override 64 | public void visitNamespace(OCCppNamespace namespace) { 65 | if (namespace.getChildren().length == 0) { 66 | toDelete.add(namespace); 67 | } 68 | else { 69 | namespace.acceptChildren(this); 70 | } 71 | } 72 | 73 | @Override 74 | public void visitDefineDirective(OCDefineDirectiveImpl directive) { 75 | removeIfNoReference(directive); 76 | } 77 | 78 | 79 | @Override 80 | public void visitDeclarator(OCDeclarator declarator) { 81 | removeIfNoReference(declarator); 82 | } 83 | 84 | @Override 85 | public void visitStructLike(OCStructLike structLike) { 86 | removeIfNoReference(structLike); 87 | structLike.acceptChildren(this); 88 | } 89 | 90 | @Override 91 | public void visitTypeElement(OCTypeElement typeElement) { 92 | typeElement.acceptChildren(this); 93 | } 94 | 95 | @Override 96 | public void visitDeclaration(OCDeclaration declaration) { 97 | declaration.acceptChildren(this); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 34 | 35 | @rem Find java.exe 36 | if defined JAVA_HOME goto findJavaFromJavaHome 37 | 38 | set JAVA_EXE=java.exe 39 | %JAVA_EXE% -version >NUL 2>&1 40 | if "%ERRORLEVEL%" == "0" goto init 41 | 42 | echo. 43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 44 | echo. 45 | echo Please set the JAVA_HOME variable in your environment to match the 46 | echo location of your Java installation. 47 | 48 | goto fail 49 | 50 | :findJavaFromJavaHome 51 | set JAVA_HOME=%JAVA_HOME:"=% 52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 53 | 54 | if exist "%JAVA_EXE%" goto init 55 | 56 | echo. 57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 58 | echo. 59 | echo Please set the JAVA_HOME variable in your environment to match the 60 | echo location of your Java installation. 61 | 62 | goto fail 63 | 64 | :init 65 | @rem Get command-line arguments, handling Windows variants 66 | 67 | if not "%OS%" == "Windows_NT" goto win9xME_args 68 | 69 | :win9xME_args 70 | @rem Slurp the command line arguments. 71 | set CMD_LINE_ARGS= 72 | set _SKIP=2 73 | 74 | :win9xME_args_slurp 75 | if "x%~1" == "x" goto execute 76 | 77 | set CMD_LINE_ARGS=%* 78 | 79 | :execute 80 | @rem Setup the command line 81 | 82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 83 | 84 | @rem Execute Gradle 85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 86 | 87 | :end 88 | @rem End local scope for the variables with windows NT shell 89 | if "%ERRORLEVEL%"=="0" goto mainEnd 90 | 91 | :fail 92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 93 | rem the _cmd.exe /c_ return code! 94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 95 | exit /b 1 96 | 97 | :mainEnd 98 | if "%OS%"=="Windows_NT" endlocal 99 | 100 | :omega 101 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/task/TaskUtils.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.task; 2 | 3 | import com.intellij.execution.RunManager; 4 | import com.intellij.execution.RunnerAndConfigurationSettings; 5 | import com.intellij.execution.configurations.ConfigurationFactory; 6 | import com.intellij.lang.Language; 7 | import com.intellij.openapi.application.ApplicationManager; 8 | import com.intellij.openapi.project.Project; 9 | import com.intellij.openapi.util.Computable; 10 | import com.intellij.openapi.vfs.VirtualFile; 11 | import com.intellij.psi.PsiDirectory; 12 | import com.intellij.psi.PsiElement; 13 | import com.intellij.psi.PsiFile; 14 | import com.intellij.psi.PsiFileFactory; 15 | import com.intellij.psi.PsiManager; 16 | import name.admitriev.jhelper.configuration.TaskConfiguration; 17 | import name.admitriev.jhelper.configuration.TaskConfigurationType; 18 | import name.admitriev.jhelper.exceptions.NotificationException; 19 | import name.admitriev.jhelper.generation.FileUtils; 20 | import name.admitriev.jhelper.generation.TemplatesUtils; 21 | 22 | public class TaskUtils { 23 | 24 | private TaskUtils() { 25 | } 26 | 27 | /** 28 | * Generates task file content depending on custom user template 29 | */ 30 | private static String getTaskContent(Project project, String className) { 31 | String template = TemplatesUtils.getTemplate(project, "task"); 32 | template = TemplatesUtils.replaceAll(template, TemplatesUtils.CLASS_NAME, className); 33 | return template; 34 | } 35 | 36 | public static PsiElement saveNewTask(TaskData taskData, Project project) { 37 | createConfigurationForTask(project, taskData); 38 | return generateCPP(project, taskData); 39 | } 40 | 41 | private static PsiElement generateCPP(Project project, TaskData taskData) { 42 | VirtualFile parent = FileUtils.findOrCreateByRelativePath(project.getBaseDir(), FileUtils.getDirectory(taskData.getCppPath())); 43 | PsiDirectory psiParent = PsiManager.getInstance(project).findDirectory(parent); 44 | if (psiParent == null) { 45 | throw new NotificationException("Couldn't open parent directory as PSI"); 46 | } 47 | 48 | Language objC = Language.findLanguageByID("ObjectiveC"); 49 | if (objC == null) { 50 | throw new NotificationException("Language not found"); 51 | } 52 | 53 | PsiFile file = PsiFileFactory.getInstance(project).createFileFromText( 54 | FileUtils.getFilename(taskData.getCppPath()), 55 | objC, 56 | getTaskContent(project, taskData.getClassName()) 57 | ); 58 | if (file == null) { 59 | throw new NotificationException("Couldn't generate file"); 60 | } 61 | return ApplicationManager.getApplication().runWriteAction( 62 | (Computable) () -> psiParent.add(file) 63 | ); 64 | 65 | } 66 | 67 | private static void createConfigurationForTask(Project project, TaskData taskData) { 68 | TaskConfigurationType configurationType = new TaskConfigurationType(); 69 | ConfigurationFactory factory = configurationType.getConfigurationFactories()[0]; 70 | 71 | RunManager manager = RunManager.getInstance(project); 72 | TaskConfiguration taskConfiguration = new TaskConfiguration( 73 | project, 74 | factory 75 | ); 76 | taskConfiguration.setFromTaskData(taskData); 77 | RunnerAndConfigurationSettings configuration = manager.createConfiguration( 78 | taskConfiguration, 79 | factory 80 | ); 81 | configuration.storeInDotIdeaFolder(); 82 | manager.addConfiguration(configuration); 83 | 84 | manager.setSelectedConfiguration(configuration); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/components/AutoSwitcher.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.components; 2 | 3 | import com.intellij.execution.RunManager; 4 | import com.intellij.execution.RunManagerListener; 5 | import com.intellij.execution.RunnerAndConfigurationSettings; 6 | import com.intellij.execution.configurations.RunConfiguration; 7 | import com.intellij.execution.impl.RunManagerImpl; 8 | import com.intellij.execution.impl.RunnerAndConfigurationSettingsImpl; 9 | import com.intellij.openapi.application.ApplicationManager; 10 | import com.intellij.openapi.application.ModalityState; 11 | import com.intellij.openapi.fileEditor.FileEditorManager; 12 | import com.intellij.openapi.fileEditor.FileEditorManagerEvent; 13 | import com.intellij.openapi.fileEditor.FileEditorManagerListener; 14 | import com.intellij.openapi.project.DumbService; 15 | import com.intellij.openapi.project.Project; 16 | import com.intellij.openapi.vfs.VirtualFile; 17 | import name.admitriev.jhelper.configuration.TaskConfiguration; 18 | import org.jetbrains.annotations.NotNull; 19 | 20 | public class AutoSwitcher implements RunManagerListener, FileEditorManagerListener { 21 | private final Project project; 22 | private boolean busy = false; 23 | 24 | public AutoSwitcher(Project project) { 25 | this.project = project; 26 | } 27 | 28 | @Override 29 | public void fileOpened(@NotNull FileEditorManager source, @NotNull VirtualFile file) { 30 | selectTask(file); 31 | } 32 | 33 | private void selectTask(VirtualFile file) { 34 | Runnable selectTaskRunnable = () -> { 35 | if (busy || file == null) { 36 | return; 37 | } 38 | RunManagerImpl runManager = RunManagerImpl.getInstanceImpl(project); 39 | RunnerAndConfigurationSettings oldConfiguration = runManager.getSelectedConfiguration(); 40 | if (oldConfiguration != null && !(oldConfiguration.getConfiguration() instanceof TaskConfiguration)) { 41 | return; 42 | } 43 | for (RunConfiguration configuration : runManager.getAllConfigurationsList()) { 44 | if (configuration instanceof TaskConfiguration) { 45 | TaskConfiguration task = (TaskConfiguration) configuration; 46 | String pathToClassFile = task.getCppPath(); 47 | VirtualFile expectedFie = project.getBaseDir().findFileByRelativePath(pathToClassFile); 48 | if (file.equals(expectedFie)) { 49 | busy = true; 50 | RunManager.getInstance(project).setSelectedConfiguration( 51 | new RunnerAndConfigurationSettingsImpl( 52 | runManager, 53 | configuration, 54 | false 55 | ) 56 | ); 57 | busy = false; 58 | return; 59 | } 60 | } 61 | } 62 | 63 | }; 64 | 65 | DumbService.getInstance(project).smartInvokeLater(selectTaskRunnable); 66 | } 67 | 68 | @Override 69 | public void selectionChanged(@NotNull FileEditorManagerEvent event) { 70 | selectTask(event.getNewFile()); 71 | } 72 | 73 | @Override 74 | public void runConfigurationSelected(RunnerAndConfigurationSettings selectedConfiguration) { 75 | if (selectedConfiguration == null) { 76 | return; 77 | } 78 | RunConfiguration configuration = selectedConfiguration.getConfiguration(); 79 | if (busy || !(configuration instanceof TaskConfiguration)) { 80 | return; 81 | } 82 | busy = true; 83 | String pathToClassFile = ((TaskConfiguration) configuration).getCppPath(); 84 | VirtualFile toOpen = project.getBaseDir().findFileByRelativePath(pathToClassFile); 85 | if (toOpen != null) { 86 | ApplicationManager.getApplication().invokeAndWait(() -> FileEditorManager.getInstance(project).openFile( 87 | toOpen, 88 | true 89 | ), ModalityState.NON_MODAL); 90 | } 91 | busy = false; 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/generation/TemplatesUtils.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.generation; 2 | 3 | import com.intellij.openapi.editor.Document; 4 | import com.intellij.openapi.fileEditor.FileDocumentManager; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.openapi.vfs.VirtualFile; 7 | import com.intellij.psi.PsiFile; 8 | import com.intellij.psi.PsiManager; 9 | import name.admitriev.jhelper.exceptions.JHelperException; 10 | import name.admitriev.jhelper.exceptions.NotificationException; 11 | 12 | import java.io.BufferedReader; 13 | import java.io.IOException; 14 | import java.io.InputStream; 15 | import java.io.InputStreamReader; 16 | import java.util.regex.Matcher; 17 | import java.util.regex.Pattern; 18 | 19 | /** 20 | * Utility class for customizing templates of code 21 | */ 22 | public class TemplatesUtils { 23 | 24 | public static final Pattern CLASS_NAME = Pattern.compile("%ClassName%", Pattern.LITERAL); 25 | public static final Pattern TASK_FILE = Pattern.compile("%TaskFile%", Pattern.LITERAL); 26 | public static final Pattern TESTS = Pattern.compile("%Tests%", Pattern.LITERAL); 27 | public static final Pattern SOLVER_CALL = Pattern.compile("%SolverCall%", Pattern.LITERAL); 28 | public static final Pattern INPUT = Pattern.compile("%Input%", Pattern.LITERAL); 29 | public static final Pattern OUTPUT = Pattern.compile("%Output%", Pattern.LITERAL); 30 | public static final Pattern CODE = Pattern.compile("%Code%", Pattern.LITERAL); 31 | 32 | private TemplatesUtils() { 33 | } 34 | 35 | public static String replaceAll(String text, Pattern pattern, String replacement) { 36 | return pattern.matcher(text).replaceAll(Matcher.quoteReplacement(replacement)); 37 | } 38 | 39 | public static String getTemplate(Project project, String name) { 40 | String filename = name + ".template"; 41 | VirtualFile file = project.getBaseDir().findFileByRelativePath(filename); 42 | if (file == null) { 43 | createTemplateFromDefault(project, name); 44 | file = project.getBaseDir().findFileByRelativePath(filename); 45 | if (file == null) { 46 | throw new JHelperException("Can't open template file(" + filename + ") after its creation"); 47 | } 48 | 49 | } 50 | Document document = FileDocumentManager.getInstance().getDocument(file); 51 | if (document == null) { 52 | throw new NotificationException("Couldn't find template \"" + name + '"'); 53 | } 54 | 55 | return document.getText(); 56 | } 57 | 58 | private static void createTemplateFromDefault(Project project, String name) { 59 | String filename = name + ".template"; 60 | VirtualFile file = FileUtils.findOrCreateByRelativePath(project.getBaseDir(), filename); 61 | PsiFile psiFile = PsiManager.getInstance(project).findFile(file); 62 | String defaultTemplate; 63 | try { 64 | defaultTemplate = getResourceContent("/name/admitriev/jhelper/templates/" + filename); 65 | } 66 | catch (IOException e) { 67 | throw new NotificationException("Couldn't open default template " + filename, e); 68 | } 69 | 70 | FileUtils.writeToFile(psiFile, defaultTemplate); 71 | } 72 | 73 | /** 74 | * Returns content of resource file (from resource folder) as a string. 75 | */ 76 | private static String getResourceContent(String name) throws IOException { 77 | try (InputStream stream = TemplatesUtils.class.getResourceAsStream(name)) { 78 | if (stream == null) { 79 | throw new IOException("Couldn't open a stream to resource " + name); 80 | } 81 | try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream, "UTF-8"))) { 82 | StringBuilder sb = new StringBuilder(); 83 | String line; 84 | //noinspection NestedAssignment 85 | while ((line = reader.readLine()) != null) { 86 | sb.append(line).append('\n'); 87 | } 88 | return sb.toString(); 89 | } 90 | } 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/ui/UIUtils.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.ui; 2 | 3 | import com.intellij.openapi.fileEditor.OpenFileDescriptor; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.openapi.util.Ref; 6 | import com.intellij.psi.PsiElement; 7 | import com.intellij.ui.DocumentAdapter; 8 | import com.jetbrains.cidr.lang.psi.OCBlockStatement; 9 | import com.jetbrains.cidr.lang.psi.OCFile; 10 | import com.jetbrains.cidr.lang.psi.OCFunctionDefinition; 11 | import com.jetbrains.cidr.lang.psi.visitors.OCRecursiveVisitor; 12 | import org.jetbrains.annotations.NotNull; 13 | 14 | import javax.swing.*; 15 | import javax.swing.event.DocumentEvent; 16 | import java.util.concurrent.atomic.AtomicBoolean; 17 | 18 | public class UIUtils { 19 | private UIUtils() { 20 | } 21 | 22 | /** 23 | * Make two fields change simultaneously until the second one ({@code copy}) changed manually. 24 | * Maintains equality of {@code String.format(format, main.getText())} and {@code copy.getText()} 25 | * 26 | * Does nothing if this equality is wrong when method is called. 27 | * 28 | * @param format format for {@link String#format}. Should contain exactly one format specifier equal to %s 29 | */ 30 | public static void mirrorFields(JTextField main, JTextField copy, String format) { 31 | if (!String.format(format, main.getText()).equals(copy.getText())) { 32 | // The copy is already changed. 33 | return; 34 | } 35 | AtomicBoolean changingFirst = new AtomicBoolean(false); 36 | AtomicBoolean secondChanged = new AtomicBoolean(false); 37 | main.getDocument().addDocumentListener( 38 | 39 | new DocumentAdapter() { 40 | 41 | @Override 42 | protected void textChanged(DocumentEvent e) { 43 | if (secondChanged.get()) { 44 | return; 45 | } 46 | while (!changingFirst.compareAndSet(false, true)) { 47 | // intentionally empty 48 | } 49 | 50 | copy.setText(String.format(format, main.getText())); 51 | 52 | changingFirst.set(false); 53 | } 54 | } 55 | ); 56 | 57 | copy.getDocument().addDocumentListener( 58 | new DocumentAdapter() { 59 | @Override 60 | protected void textChanged(DocumentEvent e) { 61 | if (!changingFirst.get()) { 62 | secondChanged.set(true); 63 | } 64 | } 65 | } 66 | ); 67 | } 68 | 69 | /** 70 | * Make two fields change simultaneously until the second one ({@code copy}) changed manually. 71 | * Maintains equality of {@code main.getText()} and {@code copy.getText()} 72 | * 73 | * Does nothing if this equality is wrong when method is called. 74 | */ 75 | public static void mirrorFields(JTextField main, JTextField copy) { 76 | mirrorFields(main, copy, "%s"); 77 | } 78 | 79 | /** 80 | * Finds method @{code methodName} in @{code file} and opens it in an editor. 81 | */ 82 | public static void openMethodInEditor(Project project, OCFile file, String methodName) { 83 | new OpenFileDescriptor( 84 | project, 85 | file.getVirtualFile(), 86 | findMethodBody(file, methodName).getTextOffset() 87 | ).navigate(true); 88 | } 89 | 90 | private static PsiElement findMethodBody(OCFile file, @NotNull String method) { 91 | Ref result = new Ref<>(); 92 | file.accept( 93 | new OCRecursiveVisitor() { 94 | @Override 95 | public void visitFunctionDefinition(OCFunctionDefinition ocFunctionDefinition) { 96 | if (method.equals(ocFunctionDefinition.getName())) { 97 | // continue recursion 98 | super.visitFunctionDefinition(ocFunctionDefinition); 99 | } 100 | } 101 | 102 | @Override 103 | public void visitBlockStatement(OCBlockStatement ocBlockStatement) { 104 | result.set(ocBlockStatement.getOpeningBrace().getNextSibling()); 105 | } 106 | } 107 | ); 108 | return result.get(); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /resources/META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | name.admitriev.jhelper 3 | JHelper 4 | _patched_ 5 | JHelper 6 | 7 | Sport programming tool to inline library code and provide testing framework. 8 | 9 | 10 | In 0.19 fixed configuration saving. 11 | You may need to resave your configuration. 12 | 13 | 14 | 15 | 16 | com.intellij.modules.cidr.lang 17 | com.intellij.clion 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 33 | 35 | 36 | 37 | 38 | 39 | name.admitriev.jhelper.components.ChromeParser 40 | 41 | 42 | 43 | 44 | 51 | 58 | 65 | 72 | 79 | 86 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/components/ChromeParser.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.components; 2 | 3 | import com.intellij.notification.NotificationType; 4 | import com.intellij.openapi.components.ProjectComponent; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.psi.PsiElement; 7 | import com.intellij.util.text.StringTokenizer; 8 | import com.jetbrains.cidr.lang.psi.OCFile; 9 | import name.admitriev.jhelper.IDEUtils; 10 | import name.admitriev.jhelper.network.SimpleHttpServer; 11 | import name.admitriev.jhelper.task.TaskData; 12 | import name.admitriev.jhelper.task.TaskUtils; 13 | import name.admitriev.jhelper.ui.Notificator; 14 | import name.admitriev.jhelper.ui.UIUtils; 15 | import net.egork.chelper.parser.*; 16 | import net.egork.chelper.task.Task; 17 | 18 | import java.io.IOException; 19 | import java.net.InetSocketAddress; 20 | import java.util.Collection; 21 | import java.util.Collections; 22 | import java.util.HashMap; 23 | import java.util.Map; 24 | 25 | /** 26 | * A Component to monitor request from CHelper Chrome Extension and parse them to Tasks 27 | */ 28 | public class ChromeParser implements ProjectComponent { 29 | private static final int PORT = 4243; 30 | private static final Map PARSERS; 31 | 32 | static { 33 | Map taskParsers = new HashMap<>(); 34 | taskParsers.put("yandex", new YandexParser()); 35 | taskParsers.put("codeforces", new CodeforcesParser()); 36 | taskParsers.put("hackerrank", new HackerRankParser()); 37 | taskParsers.put("facebook", new FacebookParser()); 38 | taskParsers.put("usaco", new UsacoParser()); 39 | taskParsers.put("gcj", new GCJParser()); 40 | taskParsers.put("bayan", new BayanParser()); 41 | taskParsers.put("kattis", new KattisParser()); 42 | taskParsers.put("codechef", new CodeChefParser()); 43 | taskParsers.put("hackerearth", new HackerEarthParser()); 44 | taskParsers.put("atcoder", new AtCoderParser()); 45 | taskParsers.put("csacademy", new CSAcademyParser()); 46 | taskParsers.put("new-gcj", new NewGCJParser()); 47 | taskParsers.put("json", new JSONParser()); 48 | PARSERS = Collections.unmodifiableMap(taskParsers); 49 | } 50 | 51 | private SimpleHttpServer server = null; 52 | private Project project; 53 | 54 | public ChromeParser(Project project) { 55 | this.project = project; 56 | } 57 | 58 | @Override 59 | public void projectOpened() { 60 | try { 61 | server = new SimpleHttpServer( 62 | new InetSocketAddress("localhost", PORT), 63 | request -> { 64 | StringTokenizer st = new StringTokenizer(request); 65 | String type = st.nextToken(); 66 | Parser parser = PARSERS.get(type); 67 | if (parser == null) { 68 | Notificator.showNotification( 69 | "Unknown parser", 70 | "Parser " + type + " unknown, request ignored", 71 | NotificationType.INFORMATION 72 | ); 73 | return; 74 | } 75 | String page = request.substring(st.getCurrentPosition()); 76 | Collection tasks = parser.parseTaskFromHTML(page); 77 | if (tasks.isEmpty()) { 78 | Notificator.showNotification( 79 | "Couldn't parse any task", 80 | "Maybe format changed?", 81 | NotificationType.WARNING 82 | ); 83 | } 84 | 85 | Configurator configurator = project.getService(Configurator.class); 86 | Configurator.State configuration = configurator.getState(); 87 | String path = configuration.getTasksDirectory(); 88 | for (Task rawTask : tasks) { 89 | TaskData task = new TaskData( 90 | rawTask.name, 91 | rawTask.taskClass, 92 | String.format("%s/%s.cpp", path, rawTask.taskClass), 93 | rawTask.input, 94 | rawTask.output, 95 | rawTask.testType, 96 | rawTask.tests 97 | ); 98 | PsiElement generatedFile = TaskUtils.saveNewTask(task, project); 99 | UIUtils.openMethodInEditor(project, (OCFile) generatedFile, "solve"); 100 | } 101 | 102 | IDEUtils.reloadProject(project); 103 | } 104 | ); 105 | 106 | new Thread(server, "ChromeParserThread").start(); 107 | } 108 | catch (IOException ignored) { 109 | Notificator.showNotification( 110 | "Could not create serverSocket for Chrome parser", 111 | "Probably another CHelper or JHelper project is running?", 112 | NotificationType.ERROR 113 | ); 114 | } 115 | } 116 | 117 | @Override 118 | public void projectClosed() { 119 | if (server != null) { 120 | server.stop(); 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/configuration/TaskRunner.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.configuration; 2 | 3 | import com.intellij.execution.ExecutionTarget; 4 | import com.intellij.execution.ProgramRunnerUtil; 5 | import com.intellij.execution.RunManager; 6 | import com.intellij.execution.RunnerAndConfigurationSettings; 7 | import com.intellij.execution.configurations.RunProfile; 8 | import com.intellij.execution.configurations.RunnerSettings; 9 | import com.intellij.execution.runners.ExecutionEnvironment; 10 | import com.intellij.execution.runners.ProgramRunner; 11 | import com.intellij.openapi.project.Project; 12 | import com.intellij.openapi.vfs.VirtualFile; 13 | import com.intellij.psi.PsiFile; 14 | import com.intellij.psi.PsiManager; 15 | import name.admitriev.jhelper.IDEUtils; 16 | import name.admitriev.jhelper.exceptions.NotificationException; 17 | import name.admitriev.jhelper.generation.CodeGenerationUtils; 18 | import org.jetbrains.annotations.NotNull; 19 | import org.jetbrains.annotations.Nullable; 20 | 21 | import java.util.List; 22 | 23 | /** 24 | * Class for Running TaskConfiguration 25 | * It isn't fully compliant with {@link ProgramRunner} Interface because {@link #execute} doesn't call {@link RunProfile#getState} 26 | * as described in IDEA DEV Confluence 27 | */ 28 | public class TaskRunner implements ProgramRunner { 29 | private static final String RUN_CONFIGURATION_NAME = "testrunner"; 30 | 31 | @NotNull 32 | @Override 33 | public String getRunnerId() { 34 | return "name.admitriev.jhelper.configuration.TaskRunner"; 35 | } 36 | 37 | @Override 38 | public boolean canRun(@NotNull String executorId, @NotNull RunProfile profile) { 39 | return profile instanceof TaskConfiguration; 40 | } 41 | 42 | /** 43 | * Runs specified TaskConfiguration: generates code and then runs output configuration. 44 | * 45 | * @throws ClassCastException if {@code environment.getRunProfile()} is not {@link TaskConfiguration}. 46 | * @see ExecutionEnvironment#getRunProfile() 47 | */ 48 | @Override 49 | public void execute(@NotNull ExecutionEnvironment environment) { 50 | Project project = environment.getProject(); 51 | 52 | TaskConfiguration taskConfiguration = (TaskConfiguration) environment.getRunProfile(); 53 | CodeGenerationUtils.generateSubmissionFileForTask(project, taskConfiguration); 54 | 55 | generateRunFileForTask(project, taskConfiguration); 56 | 57 | List allSettings = RunManager.getInstance(project).getAllSettings(); 58 | RunnerAndConfigurationSettings testRunnerSettings = null; 59 | for (RunnerAndConfigurationSettings configuration : allSettings) { 60 | if (configuration.getName().equals(RUN_CONFIGURATION_NAME)) { 61 | testRunnerSettings = configuration; 62 | } 63 | } 64 | if (testRunnerSettings == null) { 65 | throw new NotificationException( 66 | "No run configuration found", 67 | "It should be called (" + RUN_CONFIGURATION_NAME + ")" 68 | ); 69 | } 70 | 71 | ExecutionTarget originalExecutionTarget = environment.getExecutionTarget(); 72 | ExecutionTarget testRunnerExecutionTarget = ((TaskConfigurationExecutionTarget)originalExecutionTarget).getOriginalTarget(); 73 | RunnerAndConfigurationSettings originalSettings = environment.getRunnerAndConfigurationSettings(); 74 | 75 | IDEUtils.chooseConfigurationAndTarget(project, testRunnerSettings, testRunnerExecutionTarget); 76 | ProgramRunnerUtil.executeConfiguration(testRunnerSettings, environment.getExecutor()); 77 | 78 | IDEUtils.chooseConfigurationAndTarget(project, originalSettings, originalExecutionTarget); 79 | } 80 | 81 | @Nullable 82 | public static RunnerAndConfigurationSettings getRunnerSettings(@NotNull Project project) { 83 | return getSettingsByName(project, RUN_CONFIGURATION_NAME); 84 | } 85 | 86 | private static void generateRunFileForTask(Project project, TaskConfiguration taskConfiguration) { 87 | String pathToClassFile = taskConfiguration.getCppPath(); 88 | VirtualFile virtualFile = project.getBaseDir().findFileByRelativePath(pathToClassFile); 89 | if (virtualFile == null) { 90 | throw new NotificationException("Task file not found", "Seems your task is in inconsistent state"); 91 | } 92 | 93 | PsiFile psiFile = PsiManager.getInstance(project).findFile(virtualFile); 94 | if (psiFile == null) { 95 | throw new NotificationException("Couldn't get PSI file for input file"); 96 | } 97 | 98 | CodeGenerationUtils.generateRunFile(project, psiFile, taskConfiguration); 99 | } 100 | 101 | @Nullable 102 | private static RunnerAndConfigurationSettings getSettingsByName(@NotNull Project project, String name) { 103 | for (RunnerAndConfigurationSettings configuration : RunManager.getInstance(project).getAllSettings()) { 104 | if (configuration.getName().equals(name)) { 105 | return configuration; 106 | } 107 | } 108 | return null; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/generation/FileUtils.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.generation; 2 | 3 | import com.intellij.openapi.application.ApplicationManager; 4 | import com.intellij.openapi.command.WriteCommandAction; 5 | import com.intellij.openapi.editor.Document; 6 | import com.intellij.openapi.fileEditor.FileDocumentManager; 7 | import com.intellij.openapi.project.Project; 8 | import com.intellij.openapi.util.Computable; 9 | import com.intellij.openapi.util.text.StringUtil; 10 | import com.intellij.openapi.vfs.VirtualFile; 11 | import com.intellij.psi.PsiDocumentManager; 12 | import com.intellij.psi.PsiFile; 13 | import name.admitriev.jhelper.exceptions.NotificationException; 14 | import net.egork.chelper.util.OutputWriter; 15 | import org.jetbrains.annotations.NotNull; 16 | import org.jetbrains.annotations.Nullable; 17 | 18 | import java.io.IOException; 19 | 20 | public class FileUtils { 21 | private FileUtils() { 22 | } 23 | 24 | public static OutputWriter getOutputWriter(VirtualFile virtualFile, Object requestor) { 25 | try { 26 | return new OutputWriter(virtualFile.getOutputStream(requestor)); 27 | } 28 | catch (IOException e) { 29 | throw new NotificationException("Couldn't open virtual file to write", e); 30 | } 31 | } 32 | 33 | @Nullable 34 | private static VirtualFile findChild(VirtualFile file, @NotNull String child) { 35 | if (child.equals(".")) { 36 | return file; 37 | } 38 | if (child.equals("..")) { 39 | return file.getParent(); 40 | } 41 | return file.findChild(child); 42 | } 43 | 44 | public static VirtualFile findOrCreateByRelativePath(VirtualFile root, String localPath) { 45 | return ApplicationManager.getApplication().runWriteAction( 46 | new Computable() { 47 | @Override 48 | public VirtualFile compute() { 49 | String path = localPath; 50 | path = StringUtil.trimStart(path, "/"); 51 | if (path.isEmpty()) { 52 | return root; 53 | } 54 | int index = path.indexOf('/'); 55 | if (index < 0) { 56 | index = path.length(); 57 | } 58 | String name = path.substring(0, index); 59 | 60 | @Nullable VirtualFile child = findChild(root, name); 61 | if (child == null) { 62 | try { 63 | if (index == path.length()) { 64 | child = root.createChildData(this, name); 65 | } 66 | else { 67 | child = root.createChildDirectory(this, name); 68 | } 69 | } 70 | catch (IOException e) { 71 | throw new NotificationException( 72 | "Couldn't create directory: " + root.getPath() + '/' + name, 73 | e 74 | ); 75 | } 76 | } 77 | assert child != null; 78 | 79 | if (index < path.length()) { 80 | return findOrCreateByRelativePath(child, path.substring(index + 1)); 81 | } 82 | return child; 83 | } 84 | } 85 | ); 86 | } 87 | 88 | /** 89 | * Checks if given file is a C++ file. 90 | * In other words checks if code may be generated for that file 91 | */ 92 | public static boolean isCppFile(PsiFile file) { 93 | return file.getName().endsWith(".cpp"); 94 | } 95 | 96 | public static void writeToFile(PsiFile outputFile, String... strings) { 97 | Project project = outputFile.getProject(); 98 | Document document = PsiDocumentManager.getInstance(project).getDocument(outputFile); 99 | if (document == null) { 100 | throw new NotificationException("Couldn't open output file as document"); 101 | } 102 | 103 | WriteCommandAction.writeCommandAction(project).run( 104 | () -> { 105 | document.deleteString(0, document.getTextLength()); 106 | for (String string : strings) { 107 | document.insertString(document.getTextLength(), string); 108 | } 109 | FileDocumentManager.getInstance().saveDocument(document); 110 | PsiDocumentManager.getInstance(project).commitDocument(document); 111 | } 112 | ); 113 | } 114 | 115 | public static String relativePath(String parentPath, String childPath) { 116 | if (!parentPath.endsWith("/")) { 117 | parentPath += "/"; 118 | } 119 | if (!isChild(parentPath, childPath)) { 120 | throw new IllegalArgumentException("childPath should be inside a parentPath"); 121 | } 122 | // Minimum is needed for case when childPath = parentPath and there's no / at the end of childPath 123 | return childPath.substring(Math.min(parentPath.length(), childPath.length())); 124 | } 125 | 126 | public static boolean isChild(String parentPath, String childPath) { 127 | if (!parentPath.endsWith("/")) { 128 | parentPath += "/"; 129 | } 130 | if (!childPath.endsWith("/")) { 131 | childPath += "/"; 132 | } 133 | return childPath.startsWith(parentPath); 134 | } 135 | 136 | public static String getDirectory(String filePath) { 137 | int index = filePath.lastIndexOf('/'); 138 | if (index < 0) { 139 | return "."; 140 | } 141 | return filePath.substring(0, index + 1); 142 | } 143 | 144 | public static String getFilename(String filePath) { 145 | int index = filePath.lastIndexOf('/'); 146 | if (index < 0) { 147 | return filePath; 148 | } 149 | return filePath.substring(index + 1); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | # Determine the Java command to use to start the JVM. 86 | if [ -n "$JAVA_HOME" ] ; then 87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 88 | # IBM's JDK on AIX uses strange locations for the executables 89 | JAVACMD="$JAVA_HOME/jre/sh/java" 90 | else 91 | JAVACMD="$JAVA_HOME/bin/java" 92 | fi 93 | if [ ! -x "$JAVACMD" ] ; then 94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 95 | 96 | Please set the JAVA_HOME variable in your environment to match the 97 | location of your Java installation." 98 | fi 99 | else 100 | JAVACMD="java" 101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 102 | 103 | Please set the JAVA_HOME variable in your environment to match the 104 | location of your Java installation." 105 | fi 106 | 107 | # Increase the maximum file descriptors if we can. 108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 109 | MAX_FD_LIMIT=`ulimit -H -n` 110 | if [ $? -eq 0 ] ; then 111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 112 | MAX_FD="$MAX_FD_LIMIT" 113 | fi 114 | ulimit -n $MAX_FD 115 | if [ $? -ne 0 ] ; then 116 | warn "Could not set maximum file descriptor limit: $MAX_FD" 117 | fi 118 | else 119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 120 | fi 121 | fi 122 | 123 | # For Darwin, add options to specify how the application appears in the dock 124 | if $darwin; then 125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 126 | fi 127 | 128 | # For Cygwin or MSYS, switch paths to Windows format before running java 129 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 132 | JAVACMD=`cygpath --unix "$JAVACMD"` 133 | 134 | # We build the pattern for arguments to be converted via cygpath 135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 136 | SEP="" 137 | for dir in $ROOTDIRSRAW ; do 138 | ROOTDIRS="$ROOTDIRS$SEP$dir" 139 | SEP="|" 140 | done 141 | OURCYGPATTERN="(^($ROOTDIRS))" 142 | # Add a user-defined pattern to the cygpath arguments 143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 145 | fi 146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 147 | i=0 148 | for arg in "$@" ; do 149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 151 | 152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 154 | else 155 | eval `echo args$i`="\"$arg\"" 156 | fi 157 | i=`expr $i + 1` 158 | done 159 | case $i in 160 | 0) set -- ;; 161 | 1) set -- "$args0" ;; 162 | 2) set -- "$args0" "$args1" ;; 163 | 3) set -- "$args0" "$args1" "$args2" ;; 164 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 165 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 166 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 167 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 168 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 169 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 170 | esac 171 | fi 172 | 173 | # Escape application args 174 | save () { 175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 176 | echo " " 177 | } 178 | APP_ARGS=`save "$@"` 179 | 180 | # Collect all arguments for the java command, following the shell quoting and substitution rules 181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 182 | 183 | exec "$JAVACMD" "$@" 184 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/ui/ParseDialog.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.ui; 2 | 3 | import com.intellij.notification.NotificationType; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.openapi.ui.ComboBox; 6 | import com.intellij.openapi.ui.DialogWrapper; 7 | import com.intellij.openapi.ui.LabeledComponent; 8 | import com.intellij.ui.SimpleListCellRenderer; 9 | import com.intellij.ui.components.JBList; 10 | import com.intellij.ui.components.JBScrollPane; 11 | import name.admitriev.jhelper.components.Configurator; 12 | import name.admitriev.jhelper.parsing.Receiver; 13 | import name.admitriev.jhelper.task.TaskData; 14 | import net.egork.chelper.parser.Description; 15 | import net.egork.chelper.parser.Parser; 16 | import net.egork.chelper.parser.ParserTask; 17 | import net.egork.chelper.task.Task; 18 | import net.egork.chelper.task.TestType; 19 | import org.jdesktop.swingx.HorizontalLayout; 20 | import org.jdesktop.swingx.VerticalLayout; 21 | import org.jetbrains.annotations.Nullable; 22 | 23 | import javax.swing.*; 24 | import java.util.ArrayList; 25 | import java.util.Collection; 26 | import java.util.List; 27 | 28 | public class ParseDialog extends DialogWrapper { 29 | private JComponent component; 30 | 31 | private ComboBox parserComboBox; 32 | private ComboBox testType; 33 | 34 | private JBList contestList; 35 | private Receiver contestReceiver = new Receiver.Empty(); 36 | private ParseListModel contestModel = new ParseListModel<>(); 37 | 38 | private JBList problemList; 39 | private Receiver problemReceiver = new Receiver.Empty(); 40 | private ParseListModel problemModel = new ParseListModel<>(); 41 | 42 | private Project project; 43 | 44 | public ParseDialog(@Nullable Project project) { 45 | super(project); 46 | this.project = project; 47 | setTitle("Parse contest"); 48 | JPanel panel = new JPanel(new VerticalLayout()); 49 | 50 | parserComboBox = new ComboBox<>(Parser.PARSERS); 51 | parserComboBox.setRenderer( 52 | new SimpleListCellRenderer() { 53 | @Override 54 | public void customize(JList list, Parser parser, int index, boolean selected, boolean hasFocus) { 55 | setText(parser.getName()); 56 | setIcon(parser.getIcon()); 57 | } 58 | } 59 | ); 60 | parserComboBox.addActionListener( 61 | e -> refresh() 62 | ); 63 | 64 | testType = new ComboBox<>(TestType.values()); 65 | 66 | contestList = new JBList<>(contestModel); 67 | contestList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 68 | contestList.addListSelectionListener( 69 | e -> { 70 | problemReceiver.stop(); 71 | problemModel.removeAll(); 72 | 73 | Parser parser = (Parser) parserComboBox.getSelectedItem(); 74 | Description contest = contestList.getSelectedValue(); 75 | 76 | problemReceiver = generateProblemReceiver(); 77 | 78 | if (contest != null) { 79 | new ParserTask( 80 | contest.id, problemReceiver, parser 81 | ); 82 | } 83 | } 84 | ); 85 | 86 | problemList = new JBList<>(problemModel); 87 | problemList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); 88 | 89 | 90 | JPanel contestsTasksPanel = new JPanel(new HorizontalLayout()); 91 | contestsTasksPanel.add( 92 | new JBScrollPane( 93 | contestList, 94 | ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, 95 | ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER 96 | ) 97 | ); 98 | contestsTasksPanel.add( 99 | new JBScrollPane( 100 | problemList, 101 | ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, 102 | ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER 103 | ) 104 | ); 105 | 106 | panel.add(LabeledComponent.create(parserComboBox, "Parser")); 107 | panel.add(contestsTasksPanel); 108 | panel.add(LabeledComponent.create(testType, "Test type")); 109 | 110 | component = panel; 111 | 112 | init(); 113 | } 114 | 115 | private void refresh() { 116 | Parser parser = (Parser) parserComboBox.getSelectedItem(); 117 | Description chosenDescription = contestList.getSelectedValue(); 118 | contestReceiver.stop(); 119 | contestModel.removeAll(); 120 | 121 | contestReceiver = generateContestReceiver(chosenDescription); 122 | 123 | new ParserTask( 124 | null, contestReceiver, parser 125 | ); 126 | } 127 | 128 | private Receiver generateProblemReceiver() { 129 | return new Receiver() { 130 | @Override 131 | public void receiveDescriptions(Collection descriptions) { 132 | Receiver thisReceiver = this; 133 | SwingUtilities.invokeLater( 134 | () -> { 135 | //noinspection ObjectEquality 136 | if (problemReceiver != thisReceiver) { 137 | return; 138 | } 139 | boolean shouldMark = problemModel.getSize() == 0; 140 | problemModel.addAll(descriptions); 141 | if (shouldMark) { 142 | problemList.setSelectionInterval(0, problemModel.getSize() - 1); 143 | } 144 | } 145 | ); 146 | } 147 | }; 148 | } 149 | 150 | private Receiver generateContestReceiver(Description chosenDescription) { 151 | return new Receiver() { 152 | @Override 153 | public void receiveDescriptions(Collection descriptions) { 154 | Receiver thisReceiver = this; 155 | SwingUtilities.invokeLater( 156 | () -> { 157 | //noinspection ObjectEquality 158 | if (contestReceiver != thisReceiver) { 159 | return; 160 | } 161 | boolean shouldMark = contestModel.getSize() == 0; 162 | contestModel.addAll(descriptions); 163 | if (shouldMark) { 164 | for (Description contest : descriptions) { 165 | if (chosenDescription != null && chosenDescription.id.equals(contest.id)) { 166 | contestList.setSelectedValue(contest, true); 167 | return; 168 | } 169 | } 170 | if (contestModel.getSize() > 0) { 171 | contestList.setSelectedIndex(0); 172 | } 173 | } 174 | } 175 | ); 176 | } 177 | }; 178 | } 179 | 180 | @Nullable 181 | @Override 182 | protected JComponent createCenterPanel() { 183 | refresh(); 184 | return component; 185 | } 186 | 187 | public Collection getResult() { 188 | List list = new ArrayList<>(); 189 | List selectedTasks = problemList.getSelectedValuesList(); 190 | Parser parser = (Parser) parserComboBox.getSelectedItem(); 191 | 192 | Configurator configurator = project.getService(Configurator.class); 193 | Configurator.State configuration = configurator.getState(); 194 | 195 | String path = configuration.getTasksDirectory(); 196 | 197 | for (Object taskDescription : selectedTasks) { 198 | Description description = (Description) taskDescription; 199 | Task rawTask = parser.parseTask(description); 200 | if (rawTask == null) { 201 | Notificator.showNotification( 202 | "Unable to parse task " + description.description, 203 | "Connection problems or format change", 204 | NotificationType.ERROR 205 | ); 206 | continue; 207 | } 208 | TaskData myTask = new TaskData( 209 | rawTask.name, 210 | rawTask.taskClass, 211 | String.format("%s/%s.cpp", path, rawTask.taskClass), 212 | rawTask.input, 213 | rawTask.output, 214 | (TestType) testType.getSelectedItem(), 215 | rawTask.tests 216 | ); 217 | list.add(myTask); 218 | } 219 | return list; 220 | } 221 | } 222 | 223 | 224 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/configuration/TaskConfiguration.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.configuration; 2 | 3 | import com.intellij.execution.ExecutionTarget; 4 | import com.intellij.execution.Executor; 5 | import com.intellij.execution.configurations.ConfigurationFactory; 6 | import com.intellij.execution.configurations.RunConfiguration; 7 | import com.intellij.execution.configurations.RunConfigurationBase; 8 | import com.intellij.execution.configurations.RuntimeConfigurationException; 9 | import com.intellij.execution.runners.ExecutionEnvironment; 10 | import com.intellij.openapi.options.SettingsEditor; 11 | import com.intellij.openapi.project.Project; 12 | import com.jetbrains.cidr.execution.CidrCommandLineState; 13 | import name.admitriev.jhelper.exceptions.JHelperException; 14 | import name.admitriev.jhelper.task.TaskData; 15 | import name.admitriev.jhelper.ui.TaskSettingsComponent; 16 | import net.egork.chelper.task.StreamConfiguration; 17 | import net.egork.chelper.task.Test; 18 | import net.egork.chelper.task.TestType; 19 | import org.jdom.Element; 20 | import org.jetbrains.annotations.NotNull; 21 | import org.jetbrains.annotations.Nullable; 22 | 23 | import javax.swing.*; 24 | import java.util.Arrays; 25 | import java.util.List; 26 | import java.util.Objects; 27 | 28 | /** 29 | * Run Configuration for running JHelper tasks 30 | */ 31 | public class TaskConfiguration extends RunConfigurationBase { 32 | private String className; 33 | private String cppPath; 34 | private StreamConfiguration input; 35 | private StreamConfiguration output; 36 | private TestType testType; 37 | private Test[] tests; 38 | 39 | @Override 40 | public boolean canRunOn(@NotNull ExecutionTarget target) { 41 | return target instanceof TaskConfigurationExecutionTarget; 42 | } 43 | 44 | public TaskConfiguration(Project project, ConfigurationFactory factory) { 45 | super(project, factory, ""); 46 | className = ""; 47 | cppPath = ""; 48 | input = new StreamConfiguration(StreamConfiguration.StreamType.STANDARD); 49 | output = new StreamConfiguration(StreamConfiguration.StreamType.STANDARD); 50 | testType = TestType.SINGLE; 51 | tests = new Test[0]; 52 | } 53 | 54 | @NotNull 55 | @Override 56 | public SettingsEditor getConfigurationEditor() { 57 | return new SettingsEditor() { 58 | private TaskSettingsComponent component = new TaskSettingsComponent(getProject(), false); 59 | 60 | @Override 61 | protected void resetEditorFrom(@NotNull TaskConfiguration settings) { 62 | component.setTaskData(new TaskData( 63 | getName(), 64 | className, 65 | cppPath, 66 | input, 67 | output, 68 | testType, 69 | new Test[0] 70 | )); 71 | } 72 | 73 | @Override 74 | protected void applyEditorTo(@NotNull TaskConfiguration settings) { 75 | TaskData data = component.getTask(); 76 | settings.className = data.getClassName(); 77 | settings.cppPath = data.getCppPath(); 78 | settings.input = data.getInput(); 79 | settings.output = data.getOutput(); 80 | settings.testType = data.getTestType(); 81 | } 82 | 83 | @Override 84 | protected @NotNull JComponent createEditor() { 85 | return component; 86 | } 87 | }; 88 | } 89 | 90 | @Override 91 | public void checkConfiguration() throws RuntimeConfigurationException { 92 | } 93 | 94 | @Override 95 | public @Nullable CidrCommandLineState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment environment) { 96 | // RunConfiguration configuration = TaskRunner.getRunnerSettings(getProject()).getConfiguration(); 97 | // return new CidrCommandLineState(environment, new CMakeLauncher(environment, (CMakeAppRunConfiguration)configuration));th 98 | throw new JHelperException("This method is not expected to be used"); 99 | } 100 | 101 | @Override 102 | public TaskConfiguration clone() { 103 | TaskConfiguration newConfiguration = (TaskConfiguration) super.clone(); 104 | newConfiguration.className = className; 105 | newConfiguration.cppPath = cppPath; 106 | newConfiguration.input = input; 107 | newConfiguration.output = output; 108 | newConfiguration.testType = testType; 109 | newConfiguration.tests = tests.clone(); 110 | return newConfiguration; 111 | } 112 | 113 | private static StreamConfiguration readStreamConfiguration( 114 | Element element, 115 | String typeAttribute, 116 | String filenameAttribute 117 | ) { 118 | try { 119 | StreamConfiguration.StreamType inputType = StreamConfiguration.StreamType.valueOf( 120 | element.getAttribute(typeAttribute).getValue() 121 | ); 122 | if (inputType.hasStringParameter) { 123 | String filename = element.getAttributeValue(filenameAttribute); 124 | return new StreamConfiguration(inputType, filename); 125 | } 126 | else { 127 | return new StreamConfiguration(inputType); 128 | } 129 | } catch (RuntimeException ignored) { 130 | return StreamConfiguration.STANDARD; 131 | } 132 | } 133 | 134 | @Override 135 | public void readExternal(Element element) { 136 | super.readExternal(element); 137 | className = element.getAttributeValue("className", ""); 138 | cppPath = element.getAttributeValue("cppPath", ""); 139 | input = readStreamConfiguration(element, "inputPath", "inputFile"); 140 | output = readStreamConfiguration(element, "outputPath", "outputFile"); 141 | try { 142 | testType = TestType.valueOf(element.getAttributeValue("testType", "SINGLE")); 143 | } catch (IllegalArgumentException ignored) { 144 | testType = TestType.SINGLE; 145 | } 146 | 147 | List children = element.getChildren(); 148 | for (Element child : children) { 149 | if (Objects.equals(child.getName(), "tests")) { 150 | List testChildren = child.getChildren(); 151 | tests = new Test[testChildren.size()]; 152 | for (int i = 0; i < testChildren.size(); ++i) { 153 | tests[i] = readTest(testChildren.get(i)); 154 | } 155 | } 156 | } 157 | } 158 | 159 | private static Test readTest(Element element) { 160 | assert element.getName().equals("test"); 161 | String input = element.getAttributeValue("input"); 162 | String output = element.getAttributeValue("output"); 163 | boolean active = element.getAttributeValue("active").equals("true"); 164 | return new Test(input, output, 0, active); 165 | } 166 | 167 | @Override 168 | public void writeExternal(Element element) { 169 | element.setAttribute("className", className); 170 | element.setAttribute("cppPath", cppPath); 171 | element.setAttribute("inputType", String.valueOf(input.type.name())); 172 | if (input.fileName != null) { 173 | element.setAttribute("inputFile", input.fileName); 174 | } 175 | element.setAttribute("outputType", String.valueOf(output.type.name())); 176 | if (output.fileName != null) { 177 | element.setAttribute("outputFile", output.fileName); 178 | } 179 | element.setAttribute("testType", testType.name()); 180 | 181 | Element testsElements = new Element("tests"); 182 | for (Test test : tests) { 183 | Element testElement = new Element("test"); 184 | testElement.setAttribute("input", test.input); 185 | if (test.output != null) { 186 | testElement.setAttribute("output", test.output); 187 | } 188 | testElement.setAttribute("active", String.valueOf(test.active)); 189 | testsElements.addContent(testElement); 190 | } 191 | element.addContent(testsElements); 192 | 193 | super.writeExternal(element); 194 | } 195 | 196 | public void setFromTaskData(TaskData data) { 197 | setName(data.getName()); 198 | className = data.getClassName(); 199 | cppPath = data.getCppPath(); 200 | input = data.getInput(); 201 | output = data.getOutput(); 202 | testType = data.getTestType(); 203 | tests = data.getTests(); 204 | } 205 | 206 | public void setTests(Test[] tests) { 207 | this.tests = Arrays.copyOf(tests, tests.length); 208 | } 209 | 210 | public Test[] getTests() { 211 | return Arrays.copyOf(tests, tests.length); 212 | } 213 | 214 | public String getCppPath() { 215 | return cppPath; 216 | } 217 | 218 | public String getClassName() { 219 | return className; 220 | } 221 | 222 | public TestType getTestType() { 223 | return testType; 224 | } 225 | 226 | public StreamConfiguration getInput() { 227 | return input; 228 | } 229 | 230 | public StreamConfiguration getOutput() { 231 | return output; 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/ui/EditTestsDialog.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.ui; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.openapi.ui.DialogWrapper; 5 | import com.intellij.openapi.ui.LabeledComponent; 6 | import com.intellij.openapi.ui.VerticalFlowLayout; 7 | import com.intellij.ui.DocumentAdapter; 8 | import com.intellij.ui.components.JBList; 9 | import com.intellij.ui.components.JBScrollPane; 10 | import net.egork.chelper.task.Test; 11 | import net.egork.chelper.ui.VariableGridLayout; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | import javax.swing.*; 15 | import javax.swing.event.DocumentEvent; 16 | import java.awt.*; 17 | import java.util.ArrayList; 18 | import java.util.Arrays; 19 | import java.util.List; 20 | 21 | public class EditTestsDialog extends DialogWrapper { 22 | private static final int HEIGHT = new JLabel("Test").getPreferredSize().height; 23 | private static final double LIST_PANEL_FRACTION = 0.35; 24 | private static final Dimension PREFERRED_SIZE = new Dimension(600, 400); 25 | private static final int GRID_LAYOUT_GAP = 5; 26 | 27 | private List tests; 28 | 29 | private int currentTest; 30 | private JBList testList; 31 | private JTextArea input; 32 | private JTextArea output; 33 | private List checkBoxes = new ArrayList<>(); 34 | private JPanel checkBoxesPanel; 35 | private JCheckBox knowAnswer; 36 | private JPanel outputPanel; 37 | private boolean updating = false; 38 | 39 | private JComponent component; 40 | 41 | public EditTestsDialog(Test[] tests, Project project) { 42 | super(project); 43 | setTitle("Tests"); 44 | this.tests = new ArrayList<>(Arrays.asList(tests)); 45 | VariableGridLayout mainLayout = new VariableGridLayout(1, 2, GRID_LAYOUT_GAP, GRID_LAYOUT_GAP); 46 | mainLayout.setColFraction(0, LIST_PANEL_FRACTION); 47 | JPanel mainPanel = new JPanel(mainLayout); 48 | JPanel selectorAndButtonsPanel = new JPanel(new BorderLayout()); 49 | 50 | selectorAndButtonsPanel.add( 51 | LabeledComponent.create( 52 | new JBScrollPane( 53 | generateListPanel(), 54 | ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, 55 | ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER 56 | ), 57 | "Tests" 58 | ) 59 | ); 60 | 61 | selectorAndButtonsPanel.add(createButtonPanel(), BorderLayout.PAGE_END); 62 | mainPanel.add(selectorAndButtonsPanel); 63 | mainPanel.add(generateTestPanel()); 64 | 65 | mainPanel.setPreferredSize(PREFERRED_SIZE); 66 | component = mainPanel; 67 | setSelectedTest(Math.min(0, tests.length - 1)); 68 | 69 | init(); 70 | } 71 | 72 | private JPanel generateListPanel() { 73 | JPanel checkBoxesAndSelectorPanel = new JPanel(new BorderLayout()); 74 | checkBoxesPanel = new JPanel(new VerticalFlowLayout(VerticalFlowLayout.TOP, 0, 0, false, false)); 75 | for (Test test : tests) { 76 | JCheckBox checkBox = createCheckBox(test); 77 | checkBoxesPanel.add(checkBox); 78 | } 79 | checkBoxesAndSelectorPanel.add(checkBoxesPanel, BorderLayout.WEST); 80 | testList = new JBList<>(tests); 81 | testList.setFixedCellHeight(HEIGHT); 82 | testList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 83 | testList.setLayoutOrientation(JList.VERTICAL); 84 | testList.addListSelectionListener( 85 | e -> { 86 | if (updating) { 87 | return; 88 | } 89 | int index = testList.getSelectedIndex(); 90 | if (index >= 0 && index < testList.getItemsCount()) { 91 | saveCurrentTest(); 92 | setSelectedTest(index); 93 | } 94 | } 95 | ); 96 | checkBoxesAndSelectorPanel.add(testList, BorderLayout.CENTER); 97 | return checkBoxesAndSelectorPanel; 98 | } 99 | 100 | private JPanel generateTestPanel() { 101 | JPanel testPanel = new JPanel(new GridLayout(2, 1, GRID_LAYOUT_GAP, GRID_LAYOUT_GAP)); 102 | 103 | input = generateSavingTextArea(); 104 | JPanel inputPanel = LabeledComponent.create(new JBScrollPane(input), "Input"); 105 | 106 | output = generateSavingTextArea(); 107 | outputPanel = LabeledComponent.create(new JBScrollPane(output), "Output"); 108 | 109 | knowAnswer = new JCheckBox("Know answer?"); 110 | knowAnswer.addActionListener(e -> saveCurrentTest()); 111 | JPanel outputAndCheckBoxPanel = new JPanel(new BorderLayout()); 112 | outputAndCheckBoxPanel.add(knowAnswer, BorderLayout.NORTH); 113 | outputAndCheckBoxPanel.add(outputPanel, BorderLayout.CENTER); 114 | testPanel.add(inputPanel); 115 | testPanel.add(outputAndCheckBoxPanel); 116 | return testPanel; 117 | } 118 | 119 | private JTextArea generateSavingTextArea() { 120 | JTextArea result = new JTextArea(); 121 | result.setFont(Font.decode(Font.MONOSPACED)); 122 | result.getDocument().addDocumentListener( 123 | new DocumentAdapter() { 124 | @Override 125 | protected void textChanged(DocumentEvent e) { 126 | saveCurrentTest(); 127 | } 128 | } 129 | ); 130 | return result; 131 | } 132 | 133 | private JPanel createButtonPanel() { 134 | JPanel buttonsPanel = new JPanel(new GridLayout(2, 2)); 135 | JButton allButton = new JButton("All"); 136 | allButton.addActionListener( 137 | e -> { 138 | int index = 0; 139 | for (JCheckBox checkBox : checkBoxes) { 140 | checkBox.setSelected(true); 141 | tests.set( 142 | index, 143 | tests.get(index).setActive(true) 144 | ); 145 | index++; 146 | } 147 | setSelectedTest(currentTest); 148 | } 149 | ); 150 | buttonsPanel.add(allButton); 151 | JButton noneButton = new JButton("None"); 152 | noneButton.addActionListener( 153 | e -> { 154 | int index = 0; 155 | for (JCheckBox checkBox : checkBoxes) { 156 | checkBox.setSelected(false); 157 | tests.set( 158 | index, 159 | tests.get(index).setActive(false) 160 | ); 161 | index++; 162 | } 163 | setSelectedTest(currentTest); 164 | } 165 | ); 166 | buttonsPanel.add(noneButton); 167 | JButton newTestButton = new JButton("New"); 168 | newTestButton.addActionListener( 169 | e -> { 170 | saveCurrentTest(); 171 | int index = tests.size(); 172 | Test test = new Test("", "", index); 173 | tests.add(test); 174 | checkBoxesPanel.add(createCheckBox(test)); 175 | setSelectedTest(index); 176 | } 177 | ); 178 | buttonsPanel.add(newTestButton); 179 | JButton removeButton = new JButton("Remove"); 180 | removeButton.addActionListener( 181 | e -> { 182 | if (currentTest == -1) { 183 | return; 184 | } 185 | while (checkBoxes.size() > currentTest) { 186 | checkBoxesPanel.remove(checkBoxes.get(currentTest)); 187 | checkBoxes.remove(currentTest); 188 | } 189 | tests.remove(currentTest); 190 | int size = tests.size(); 191 | for (int i = currentTest; i < size; i++) { 192 | Test test = tests.get(i); 193 | test = new Test(test.input, test.output, i, test.active); 194 | tests.set(i, test); 195 | checkBoxesPanel.add(createCheckBox(test)); 196 | } 197 | if (currentTest < size) { 198 | setSelectedTest(currentTest); 199 | return; 200 | } 201 | if (size > 0) { 202 | setSelectedTest(0); 203 | return; 204 | } 205 | setSelectedTest(-1); 206 | } 207 | ); 208 | buttonsPanel.add(removeButton); 209 | return buttonsPanel; 210 | } 211 | 212 | private JCheckBox createCheckBox(Test test) { 213 | JCheckBox checkBox = new JCheckBox("", test.active); 214 | Dimension preferredSize = new Dimension(checkBox.getPreferredSize().width, HEIGHT); 215 | checkBox.setPreferredSize(preferredSize); 216 | checkBox.addActionListener( 217 | e -> { 218 | tests.set(test.index, tests.get(test.index).setActive(checkBox.isSelected())); 219 | setSelectedTest(currentTest); 220 | } 221 | ); 222 | checkBoxes.add(checkBox); 223 | return checkBox; 224 | } 225 | 226 | private void setSelectedTest(int index) { 227 | updating = true; 228 | currentTest = -1; 229 | if (index == -1) { 230 | input.setVisible(false); 231 | output.setVisible(false); 232 | } 233 | else { 234 | input.setVisible(true); 235 | output.setVisible(true); 236 | input.setText(tests.get(index).input); 237 | knowAnswer.setSelected(tests.get(index).output != null); 238 | output.setText(knowAnswer.isSelected() ? tests.get(index).output : ""); 239 | outputPanel.setVisible(knowAnswer.isSelected()); 240 | } 241 | currentTest = index; 242 | testList.setListData(tests.toArray(new Test[tests.size()])); 243 | if (testList.getSelectedIndex() != currentTest) { 244 | testList.setSelectedIndex(currentTest); 245 | } 246 | testList.repaint(); 247 | checkBoxesPanel.repaint(); 248 | updating = false; 249 | } 250 | 251 | private void saveCurrentTest() { 252 | if (currentTest == -1) { 253 | return; 254 | } 255 | tests.set( 256 | currentTest, 257 | new Test( 258 | input.getText(), 259 | knowAnswer.isSelected() ? output.getText() : null, 260 | currentTest, 261 | checkBoxes.get(currentTest).isSelected() 262 | ) 263 | ); 264 | outputPanel.setVisible(knowAnswer.isSelected()); 265 | } 266 | 267 | public Test[] getTests() { 268 | return tests.toArray(new Test[tests.size()]); 269 | } 270 | 271 | @Nullable 272 | @Override 273 | protected JComponent createCenterPanel() { 274 | return component; 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /src/name/admitriev/jhelper/generation/CodeGenerationUtils.java: -------------------------------------------------------------------------------- 1 | package name.admitriev.jhelper.generation; 2 | 3 | import com.intellij.codeInsight.actions.ReformatCodeProcessor; 4 | import com.intellij.openapi.command.WriteCommandAction; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.openapi.util.io.FileUtil; 7 | import com.intellij.openapi.vfs.VirtualFile; 8 | import com.intellij.psi.PsiElement; 9 | import com.intellij.psi.PsiFile; 10 | import com.intellij.psi.PsiManager; 11 | import com.intellij.psi.search.GlobalSearchScope; 12 | import com.intellij.psi.search.SearchScope; 13 | import name.admitriev.jhelper.components.Configurator; 14 | import name.admitriev.jhelper.configuration.TaskConfiguration; 15 | import name.admitriev.jhelper.exceptions.NotificationException; 16 | import net.egork.chelper.task.StreamConfiguration; 17 | import net.egork.chelper.task.Test; 18 | import net.egork.chelper.task.TestType; 19 | import org.jetbrains.annotations.NotNull; 20 | 21 | import java.util.ArrayList; 22 | import java.util.Collection; 23 | 24 | public class CodeGenerationUtils { 25 | private CodeGenerationUtils() { 26 | } 27 | 28 | /** 29 | * Generates main function for testing purposes. 30 | * 31 | * @param project Project to get configuration from 32 | */ 33 | public static void generateRunFile( 34 | @NotNull Project project, 35 | @NotNull PsiFile inputFile, 36 | @NotNull TaskConfiguration task 37 | ) { 38 | if (!FileUtils.isCppFile(inputFile)) { 39 | throw new NotificationException("Not a cpp file", "Only cpp files are currently supported"); 40 | } 41 | 42 | PsiFile psiOutputFile = getRunFile(project); 43 | 44 | FileUtils.writeToFile( 45 | psiOutputFile, 46 | generateRunFileContent( 47 | project, 48 | task, 49 | FileUtil.getRelativePath( 50 | psiOutputFile.getVirtualFile().getParent().getPath(), 51 | inputFile.getVirtualFile().getPath(), 52 | '/' 53 | ) 54 | ) 55 | ); 56 | } 57 | 58 | private static void generateSubmissionFile( 59 | @NotNull Project project, 60 | @NotNull PsiFile inputFile, 61 | @NotNull TaskConfiguration task 62 | ) { 63 | if (!FileUtils.isCppFile(inputFile)) { 64 | throw new NotificationException("Not a cpp file", "Only cpp files are currently supported"); 65 | } 66 | 67 | String result = IncludesProcessor.process(inputFile); 68 | PsiFile psiOutputFile = getOutputFile(project); 69 | 70 | FileUtils.writeToFile( 71 | psiOutputFile, 72 | authorComment(project), 73 | generateSubmissionFileContent(project, result, task) 74 | ); 75 | 76 | Configurator configurator = project.getService(Configurator.class); 77 | Configurator.State configuration = configurator.getState(); 78 | 79 | if (configuration.isCodeEliminationOn()) { 80 | removeUnusedCode(psiOutputFile); 81 | } 82 | 83 | if (configuration.isCodeReformattingOn()) { 84 | new ReformatCodeProcessor(psiOutputFile, false).run(); 85 | } 86 | } 87 | 88 | 89 | private static String generateRunFileContent(Project project, TaskConfiguration task, String path) { 90 | String template = TemplatesUtils.getTemplate(project, "run"); 91 | template = TemplatesUtils.replaceAll(template, TemplatesUtils.TASK_FILE, path); 92 | template = TemplatesUtils.replaceAll(template, TemplatesUtils.TESTS, generateTestDeclaration(task.getTests())); 93 | template = TemplatesUtils.replaceAll(template, TemplatesUtils.CLASS_NAME, task.getClassName()); 94 | template = TemplatesUtils.replaceAll( 95 | template, 96 | TemplatesUtils.SOLVER_CALL, 97 | generateSolverCall(task.getTestType()) 98 | ); 99 | return template; 100 | } 101 | 102 | private static @NotNull String generateTestDeclaration(Test[] tests) { 103 | StringBuilder result = new StringBuilder(); 104 | for (Test test : tests) { 105 | result.append( 106 | "{" + 107 | quote(test.input) + ", " + 108 | quote(test.output != null ? test.output : "") + ", " + 109 | Boolean.toString(test.active) + ", " + 110 | Boolean.toString(test.output != null) + 111 | "}," 112 | ); 113 | } 114 | return result.toString(); 115 | } 116 | 117 | private static CharSequence quote(String input) { 118 | StringBuilder sb = new StringBuilder(""); 119 | sb.append('"'); 120 | for (char c : input.toCharArray()) { 121 | if (c == '\n') { 122 | sb.append("\\n"); 123 | continue; 124 | } 125 | if (c == '"' || c == '\'' || c == '\\') { 126 | sb.append('\\'); 127 | } 128 | sb.append(c); 129 | } 130 | sb.append('"'); 131 | return sb; 132 | } 133 | 134 | private static String generateSubmissionFileContent(Project project, String code, TaskConfiguration task) { 135 | String template = TemplatesUtils.getTemplate(project, "submission"); 136 | if (task.getInput().type == StreamConfiguration.StreamType.LOCAL_REGEXP) { 137 | code = code + '\n' + generateFileNameGetter(); 138 | } 139 | template = TemplatesUtils.replaceAll(template, TemplatesUtils.CODE, code); 140 | template = TemplatesUtils.replaceAll(template, TemplatesUtils.CLASS_NAME, task.getClassName()); 141 | template = TemplatesUtils.replaceAll(template, TemplatesUtils.INPUT, getInputDeclaration(task)); 142 | template = TemplatesUtils.replaceAll(template, TemplatesUtils.OUTPUT, getOutputDeclaration(task)); 143 | template = TemplatesUtils.replaceAll( 144 | template, 145 | TemplatesUtils.SOLVER_CALL, 146 | generateSolverCall(task.getTestType()) 147 | ); 148 | return template; 149 | } 150 | 151 | private static String generateFileNameGetter() { 152 | return "#include \n" + 153 | "#include \n" + 154 | "#include \n" + 155 | "#include \n" + 156 | "#include \n" + 157 | "\n" + 158 | "std::string getLastFileName(const std::string& regexString) {\n" + 159 | "\tDIR* dir;\n" + 160 | "\tdirent* entry;\n" + 161 | "\tstd::string result = \"\";\n" + 162 | "\tint64_t resultModificationTime = 0;\n" + 163 | "\tstd::regex regex(regexString);\n" + 164 | "\tif ((dir = opendir (\".\")) != NULL) {\n" + 165 | "\t\twhile ((entry = readdir (dir)) != NULL) {\n" + 166 | "\t\t\tif (std::regex_match(entry->d_name, regex)) {\n" + 167 | "\t\t\t\tstruct stat buffer;\n" + 168 | "\t\t\t\tstat(entry->d_name, &buffer);\n" + 169 | "\t\t\t\tint64_t modificationTime = static_cast(buffer.st_mtimespec.tv_sec) * 1000000000 +\n" + 170 | "\t\t\t\t\t\tstatic_cast(buffer.st_mtimespec.tv_nsec);\n" + 171 | "\n" + 172 | "\t\t\t\tif (modificationTime > resultModificationTime) {\n" + 173 | "\t\t\t\t\tresultModificationTime = modificationTime;\n" + 174 | "\t\t\t\t\tresult = entry->d_name;\n" + 175 | "\t\t\t\t}\n" + 176 | "\t\t\t}\n" + 177 | "\t\t}\n" + 178 | "\t\tclosedir (dir);\n" + 179 | "\t} else {\n" + 180 | "\t\tthrow std::runtime_error(\"Couldn't open current directory\");\n" + 181 | "\t}\n" + 182 | "\tif (result.empty()) {\n" + 183 | "\t\tthrow std::runtime_error(\"No file found\");\n" + 184 | "\t}" + 185 | "\treturn result;\n" + 186 | "}"; 187 | } 188 | 189 | private static String generateSolverCall(TestType testType) { 190 | switch (testType) { 191 | case SINGLE: 192 | return "solver.solve(in, out);"; 193 | case MULTI_NUMBER: 194 | return "int n;\n" + 195 | "in >> n;\n" + 196 | "for(int i = 0; i < n; ++i) {\n" + 197 | "\tsolver.solve(in, out);\n" + 198 | "}\n"; 199 | case MULTI_EOF: 200 | return "while(in.good()) {\n" + 201 | "\tsolver.solve(in, out);\n" + 202 | "}\n"; 203 | default: 204 | throw new IllegalArgumentException("Unknown testType:" + testType); 205 | } 206 | } 207 | 208 | private static String getOutputDeclaration(TaskConfiguration task) { 209 | String outputFileName = task.getOutput().getFileName(task.getName(), ".out"); 210 | if (outputFileName == null) { 211 | return "std::ostream& out(std::cout);"; 212 | } 213 | else if (task.getOutput().type == StreamConfiguration.StreamType.LOCAL_REGEXP) { 214 | throw new NotificationException("Your task is in inconsistent state", "Can't output to local regexp"); 215 | } 216 | else { 217 | return "std::ofstream out(\"" + outputFileName + "\");"; 218 | } 219 | } 220 | 221 | private static String getInputDeclaration(TaskConfiguration task) { 222 | if (task.getInput().type == StreamConfiguration.StreamType.LOCAL_REGEXP) { 223 | return "std::ifstream in(getLastFileName(" + quote(task.getInput().fileName) + "));"; 224 | } 225 | else if (task.getInput().type == StreamConfiguration.StreamType.STANDARD) { 226 | return "std::istream& in(std::cin);"; 227 | } 228 | else { 229 | String inputFileName = task.getInput().getFileName(task.getName(), ".in"); 230 | return "std::ifstream in(\"" + inputFileName + "\");"; 231 | } 232 | } 233 | 234 | private static @NotNull PsiFile getOutputFile(Project project) { 235 | Configurator configurator = project.getService(Configurator.class); 236 | Configurator.State configuration = configurator.getState(); 237 | 238 | VirtualFile outputFile = project.getBaseDir().findFileByRelativePath(configuration.getOutputFile()); 239 | if (outputFile == null) { 240 | throw new NotificationException( 241 | "No output file found.", 242 | "You should configure output file to point to existing file" 243 | ); 244 | } 245 | 246 | PsiFile psiOutputFile = PsiManager.getInstance(project).findFile(outputFile); 247 | if (psiOutputFile == null) { 248 | throw new NotificationException("Couldn't open output file as PSI"); 249 | } 250 | return psiOutputFile; 251 | } 252 | 253 | 254 | @NotNull 255 | private static PsiFile getRunFile(Project project) { 256 | Configurator configurator = project.getService(Configurator.class); 257 | Configurator.State configuration = configurator.getState(); 258 | 259 | VirtualFile outputFile = project.getBaseDir().findFileByRelativePath(configuration.getRunFile()); 260 | if (outputFile == null) { 261 | throw new NotificationException( 262 | "No run file found.", 263 | "You should configure run file to point to existing file" 264 | ); 265 | } 266 | 267 | PsiFile psiOutputFile = PsiManager.getInstance(project).findFile(outputFile); 268 | if (psiOutputFile == null) { 269 | throw new NotificationException("Couldn't open run file as PSI"); 270 | } 271 | return psiOutputFile; 272 | } 273 | 274 | private static String authorComment(Project project) { 275 | Configurator configurator = project.getService(Configurator.class); 276 | Configurator.State configuration = configurator.getState(); 277 | 278 | return "/**\n" + 279 | " * code generated by JHelper\n" + 280 | " * More info: https://github.com/AlexeyDmitriev/JHelper\n" + 281 | " * @author " + configuration.getAuthor() + '\n' + 282 | " */\n\n"; 283 | } 284 | 285 | private static void removeUnusedCode(PsiFile file) { 286 | while (true) { 287 | Collection toDelete = new ArrayList<>(); 288 | Project project = file.getProject(); 289 | SearchScope scope = GlobalSearchScope.fileScope(project, file.getVirtualFile()); 290 | file.acceptChildren(new DeletionMarkingVisitor(toDelete, scope)); 291 | if (toDelete.isEmpty()) { 292 | break; 293 | } 294 | WriteCommandAction.writeCommandAction(project).run( 295 | () -> { 296 | for (PsiElement element : toDelete) { 297 | element.delete(); 298 | } 299 | } 300 | ); 301 | 302 | } 303 | } 304 | 305 | /** 306 | * Generates code for submission. 307 | * Adds main function, inlines all used code except standard library and puts it to output file from configuration 308 | * 309 | * @param project Project to get configuration from 310 | */ 311 | public static void generateSubmissionFileForTask( 312 | @NotNull Project project, 313 | @NotNull TaskConfiguration taskConfiguration 314 | ) { 315 | String pathToClassFile = taskConfiguration.getCppPath(); 316 | VirtualFile virtualFile = project.getBaseDir().findFileByRelativePath(pathToClassFile); 317 | if (virtualFile == null) { 318 | throw new NotificationException("Task file not found", "Seems your task is in inconsistent state"); 319 | } 320 | 321 | PsiFile psiFile = PsiManager.getInstance(project).findFile(virtualFile); 322 | if (psiFile == null) { 323 | throw new NotificationException("Couldn't get PSI file for input file"); 324 | } 325 | generateSubmissionFile(project, psiFile, taskConfiguration); 326 | } 327 | } 328 | --------------------------------------------------------------------------------