1 package com.jetbrains.edu.learning.core;
3 import com.intellij.ide.SaveAndSyncHandler;
4 import com.intellij.openapi.actionSystem.AnActionEvent;
5 import com.intellij.openapi.actionSystem.Presentation;
6 import com.intellij.openapi.application.ApplicationManager;
7 import com.intellij.openapi.application.Result;
8 import com.intellij.openapi.command.CommandProcessor;
9 import com.intellij.openapi.command.UndoConfirmationPolicy;
10 import com.intellij.openapi.command.WriteCommandAction;
11 import com.intellij.openapi.command.undo.UndoManager;
12 import com.intellij.openapi.command.undo.UndoableAction;
13 import com.intellij.openapi.diagnostic.Logger;
14 import com.intellij.openapi.editor.Document;
15 import com.intellij.openapi.fileEditor.FileDocumentManager;
16 import com.intellij.openapi.project.Project;
17 import com.intellij.openapi.util.Pair;
18 import com.intellij.openapi.util.TextRange;
19 import com.intellij.openapi.vfs.VfsUtilCore;
20 import com.intellij.openapi.vfs.VirtualFile;
21 import com.intellij.openapi.vfs.VirtualFileManager;
22 import com.intellij.psi.PsiDirectory;
23 import com.intellij.util.containers.ContainerUtil;
24 import com.jetbrains.edu.learning.StudyUtils;
25 import com.jetbrains.edu.learning.courseFormat.*;
26 import org.jetbrains.annotations.NonNls;
27 import org.jetbrains.annotations.NotNull;
28 import org.jetbrains.annotations.Nullable;
30 import javax.imageio.ImageIO;
31 import java.io.FileOutputStream;
32 import java.io.IOException;
33 import java.io.PrintWriter;
36 public class EduUtils {
40 private static final Logger LOG = Logger.getInstance(EduUtils.class.getName());
42 public static final Comparator<StudyItem> INDEX_COMPARATOR = (o1, o2) -> o1.getIndex() - o2.getIndex();
44 public static void enableAction(@NotNull final AnActionEvent event, boolean isEnable) {
45 final Presentation presentation = event.getPresentation();
46 presentation.setVisible(isEnable);
47 presentation.setEnabled(isEnable);
51 * Gets number index in directory names like "task1", "lesson2"
53 * @param fullName full name of directory
54 * @param logicalName part of name without index
55 * @return index of object
57 public static int getIndex(@NotNull final String fullName, @NotNull final String logicalName) {
58 if (!fullName.startsWith(logicalName)) {
62 return Integer.parseInt(fullName.substring(logicalName.length())) - 1;
64 catch (NumberFormatException e) {
69 public static boolean indexIsValid(int index, Collection collection) {
70 int size = collection.size();
71 return index >= 0 && index < size;
74 @SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
76 public static VirtualFile flushWindows(@NotNull final TaskFile taskFile, @NotNull final VirtualFile file) {
77 final VirtualFile taskDir = file.getParent();
78 VirtualFile fileWindows = null;
79 final Document document = FileDocumentManager.getInstance().getDocument(file);
80 if (document == null) {
81 LOG.debug("Couldn't flush windows");
84 if (taskDir != null) {
85 final String name = file.getNameWithoutExtension() + EduNames.WINDOWS_POSTFIX;
86 deleteWindowsFile(taskDir, name);
87 PrintWriter printWriter = null;
89 fileWindows = taskDir.createChildData(taskFile, name);
90 printWriter = new PrintWriter(new FileOutputStream(fileWindows.getPath()));
91 for (AnswerPlaceholder answerPlaceholder : taskFile.getActivePlaceholders()) {
92 int length = answerPlaceholder.getRealLength();
93 int start = answerPlaceholder.getOffset();
94 final String windowDescription = document.getText(new TextRange(start, start + length));
95 printWriter.println("#educational_plugin_window = " + windowDescription);
97 ApplicationManager.getApplication().runWriteAction(() -> FileDocumentManager.getInstance().saveDocument(document));
99 catch (IOException e) {
103 if (printWriter != null) {
112 public static void synchronize() {
113 FileDocumentManager.getInstance().saveAllDocuments();
114 SaveAndSyncHandler.getInstance().refreshOpenFiles();
115 VirtualFileManager.getInstance().refreshWithoutFileWatcher(true);
119 public static VirtualFile copyFile(Object requestor, VirtualFile toDir, VirtualFile file) {
120 Document document = FileDocumentManager.getInstance().getDocument(file);
121 if (document != null) {
122 FileDocumentManager.getInstance().saveDocument(document);
124 String name = file.getName();
126 VirtualFile userFile = toDir.findChild(name);
127 if (userFile != null) {
128 userFile.delete(requestor);
130 return VfsUtilCore.copyFile(requestor, file, toDir);
132 catch (IOException e) {
133 LOG.info("Failed to create file " + name + " in folder " + toDir.getPath(), e);
139 public static Pair<VirtualFile, TaskFile> createStudentFile(Object requestor,
141 VirtualFile answerFile,
142 VirtualFile parentDir,
144 int toSubtaskIndex) {
146 VirtualFile studentFile = copyFile(requestor, parentDir, answerFile);
147 if (studentFile == null) {
150 Document studentDocument = FileDocumentManager.getInstance().getDocument(studentFile);
151 if (studentDocument == null) {
155 task = StudyUtils.getTaskForFile(project, answerFile);
161 TaskFile taskFile = task.getTaskFile(answerFile.getName());
162 if (taskFile == null) {
165 EduDocumentListener listener = new EduDocumentListener(taskFile, false);
166 studentDocument.addDocumentListener(listener);
167 taskFile.setTrackLengths(false);
168 for (AnswerPlaceholder placeholder : taskFile.getAnswerPlaceholders()) {
169 int fromSubtask = task.getActiveSubtaskIndex();
170 placeholder.switchSubtask(project, studentDocument, fromSubtask, toSubtaskIndex);
172 for (AnswerPlaceholder placeholder : taskFile.getAnswerPlaceholders()) {
173 replaceWithTaskText(project, studentDocument, placeholder, toSubtaskIndex);
175 taskFile.setTrackChanges(true);
176 studentDocument.removeDocumentListener(listener);
177 return Pair.create(studentFile, taskFile);
180 private static void replaceWithTaskText(Project project, Document studentDocument, AnswerPlaceholder placeholder, int toSubtaskIndex) {
181 AnswerPlaceholderSubtaskInfo info = placeholder.getSubtaskInfos().get(toSubtaskIndex);
185 String replacementText;
186 if (Collections.min(placeholder.getSubtaskInfos().keySet()) == toSubtaskIndex) {
187 replacementText = info.getPlaceholderText();
190 Integer max = Collections.max(ContainerUtil.filter(placeholder.getSubtaskInfos().keySet(), i -> i < toSubtaskIndex));
191 replacementText = placeholder.getSubtaskInfos().get(max).getPossibleAnswer();
193 replaceAnswerPlaceholder(project, studentDocument, placeholder, placeholder.getVisibleLength(toSubtaskIndex), replacementText);
196 public static void replaceAnswerPlaceholder(@NotNull final Project project,
197 @NotNull final Document document,
198 @NotNull final AnswerPlaceholder answerPlaceholder,
200 String replacementText) {
201 final int offset = answerPlaceholder.getOffset();
202 CommandProcessor.getInstance().executeCommand(project, () -> ApplicationManager.getApplication().runWriteAction(() -> {
203 document.replaceString(offset, offset + length, replacementText);
204 FileDocumentManager.getInstance().saveDocument(document);
205 }), "Replace Answer Placeholders", "Replace Answer Placeholders");
208 public static void deleteWindowDescriptions(@NotNull final Task task, @NotNull final VirtualFile taskDir) {
209 for (Map.Entry<String, TaskFile> entry : task.getTaskFiles().entrySet()) {
210 String name = entry.getKey();
211 VirtualFile virtualFile = taskDir.findChild(name);
212 if (virtualFile == null) {
215 String windowsFileName = virtualFile.getNameWithoutExtension() + EduNames.WINDOWS_POSTFIX;
216 deleteWindowsFile(taskDir, windowsFileName);
220 private static void deleteWindowsFile(@NotNull final VirtualFile taskDir, @NotNull final String name) {
221 final VirtualFile fileWindows = taskDir.findChild(name);
222 if (fileWindows != null && fileWindows.exists()) {
223 ApplicationManager.getApplication().runWriteAction(() -> {
225 fileWindows.delete(taskDir);
227 catch (IOException e) {
228 LOG.warn("Tried to delete non existed _windows file");
235 public static Task getTask(@NotNull final PsiDirectory directory, @NotNull final Course course) {
236 PsiDirectory lessonDir = directory.getParent();
237 if (lessonDir == null) {
240 Lesson lesson = course.getLesson(lessonDir.getName());
241 if (lesson == null) {
244 return lesson.getTask(directory.getName());
247 public static boolean isImage(String fileName) {
248 final String[] readerFormatNames = ImageIO.getReaderFormatNames();
249 for (@NonNls String format : readerFormatNames) {
250 final String ext = format.toLowerCase();
251 if (fileName.endsWith(ext)) {
258 public static void runUndoableAction(Project project, String name, UndoableAction action) {
259 runUndoableAction(project, name, action, UndoConfirmationPolicy.DO_NOT_REQUEST_CONFIRMATION);
262 public static void runUndoableAction(Project project, String name, UndoableAction action, UndoConfirmationPolicy confirmationPolicy) {
263 new WriteCommandAction(project, name) {
264 protected void run(@NotNull final Result result) throws Throwable {
266 UndoManager.getInstance(project).undoableActionPerformed(action);
270 protected UndoConfirmationPolicy getUndoConfirmationPolicy() {
271 return confirmationPolicy;