IDEA-126952 Scratches should have an extension

This commit is contained in:
Gregory.Shrago
2016-03-21 17:58:26 +03:00
parent ba5149249c
commit c932c993e5
3 changed files with 113 additions and 9 deletions
@@ -19,12 +19,19 @@ import com.intellij.ide.util.PropertiesComponent;
import com.intellij.lang.Language;
import com.intellij.lang.LanguageUtil;
import com.intellij.lang.PerFileMappings;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.command.undo.BasicUndoableAction;
import com.intellij.openapi.command.undo.UndoManager;
import com.intellij.openapi.command.undo.UnexpectedUndoException;
import com.intellij.openapi.fileTypes.LanguageFileType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.*;
import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.ReadonlyStatusHandler;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Consumer;
@@ -38,10 +45,9 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.util.Collections;
import java.util.Comparator;
import java.io.IOException;
import java.util.*;
import java.util.List;
import java.util.Map;
/**
* @author gregsh
@@ -62,13 +68,23 @@ public abstract class LRUPopupBuilder<T> {
private JBIterable<T> myExtraItems = JBIterable.empty();
@NotNull
public static ListPopup forFileLanguages(@NotNull Project project, @NotNull final Iterable<VirtualFile> files, @NotNull final PerFileMappings<Language> mappings) {
public static ListPopup forFileLanguages(@NotNull Project project, @NotNull Iterable<VirtualFile> files, @NotNull final PerFileMappings<Language> mappings) {
final VirtualFile[] filesCopy = VfsUtilCore.toVirtualFileArray(JBIterable.from(files).toList());
Arrays.sort(filesCopy, new Comparator<VirtualFile>() {
@Override
public int compare(VirtualFile o1, VirtualFile o2) {
return StringUtil.compare(o1.getName(), o2.getName(), !o1.getFileSystem().isCaseSensitive());
}
});
return forFileLanguages(project, null, new Consumer<Language>() {
@Override
public void consume(Language t) {
for (VirtualFile file : files) {
mappings.setMapping(file, t);
}
public void consume(final Language t) {
new WriteCommandAction(project, "Change Language") {
@Override
protected void run(@NotNull Result result) throws Throwable {
changeLanguageWithUndo(project, t, filesCopy, mappings);
}
}.execute();
}
});
}
@@ -260,4 +276,47 @@ public abstract class LRUPopupBuilder<T> {
return getClass().getName() + "/" + myTitle;
}
private static void changeLanguageWithUndo(@NotNull Project project,
@NotNull final Language t,
@NotNull final VirtualFile[] sortedFiles,
@NotNull final PerFileMappings<Language> mappings) throws UnexpectedUndoException {
ReadonlyStatusHandler.OperationStatus status = ReadonlyStatusHandler.getInstance(project).ensureFilesWritable(sortedFiles);
if (status.hasReadonlyFiles()) return;
final Set<VirtualFile> matchedExtensions = ContainerUtil.newLinkedHashSet();
final Map<VirtualFile, Language> oldMapping = ContainerUtil.newHashMap();
for (VirtualFile file : sortedFiles) {
oldMapping.put(file, mappings.getMapping(file));
if (ScratchUtil.hasMatchingExtension(project, file)) {
matchedExtensions.add(file);
}
}
BasicUndoableAction action = new BasicUndoableAction(sortedFiles) {
@Override
public void undo() throws UnexpectedUndoException {
for (VirtualFile file : sortedFiles) {
mappings.setMapping(file, oldMapping.get(file));
}
}
@Override
public void redo() throws UnexpectedUndoException {
for (VirtualFile file : sortedFiles) {
mappings.setMapping(file, t);
}
}
};
action.redo();
UndoManager.getInstance(project).undoableActionPerformed(action);
for (VirtualFile file : matchedExtensions) {
try {
ScratchUtil.updateFileExtension(project, file);
}
catch (IOException ignored) {
}
}
}
}
@@ -26,6 +26,7 @@ import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.editor.Caret;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileTypes.LanguageFileType;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
@@ -37,6 +38,7 @@ import com.intellij.psi.PsiFile;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
import com.intellij.util.ObjectUtils;
import com.intellij.util.PathUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.JBIterable;
import org.jetbrains.annotations.NotNull;
@@ -111,9 +113,11 @@ public class ScratchFileActions {
static void doCreateNewScratch(@NotNull Project project, boolean buffer, @NotNull Language language, @NotNull String text) {
FeatureUsageTracker.getInstance().triggerFeatureUsed("scratch");
LanguageFileType fileType = language.getAssociatedFileType();
String ext = buffer || fileType == null? "" : fileType.getDefaultExtension();
String fileName = buffer ? "buffer" + nextBufferIndex() : "scratch";
ScratchFileService.Option option = buffer ? ScratchFileService.Option.create_if_missing : ScratchFileService.Option.create_new_always;
VirtualFile f = ScratchRootType.getInstance().createScratchFile(project, fileName, language, text, option);
VirtualFile f = ScratchRootType.getInstance().createScratchFile(project, PathUtil.makeFileName(fileName, ext), language, text, option);
if (f != null) {
FileEditorManager.getInstance(project).openFile(f, true);
}
@@ -15,9 +15,21 @@
*/
package com.intellij.ide.scratch;
import com.intellij.lang.Language;
import com.intellij.lang.LanguageUtil;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.PathUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
/**
* @author gregsh
*/
@@ -33,4 +45,33 @@ public class ScratchUtil {
public static boolean isScratch(@Nullable VirtualFile file) {
return file != null && file.getFileType() == ScratchFileType.INSTANCE;
}
public static void updateFileExtension(@NotNull Project project, @Nullable VirtualFile file) throws IOException {
ApplicationManager.getApplication().assertWriteAccessAllowed();
if (CommandProcessor.getInstance().getCurrentCommand() == null) {
throw new AssertionError("command required");
}
if (file == null) return;
Language language = LanguageUtil.getLanguageForPsi(project, file);
String extension = file.getExtension();
FileType expected = extension == null ? null : FileTypeManager.getInstance().getFileTypeByExtension(extension);
FileType actual = language == null ? null : language.getAssociatedFileType();
if (expected == actual || actual == null) return;
String ext = actual.getDefaultExtension();
if (StringUtil.isEmpty(ext)) return;
String newName = PathUtil.makeFileName(file.getNameWithoutExtension(), ext);
VirtualFile parent = file.getParent();
newName = parent != null && parent.findChild(newName) != null ? PathUtil.makeFileName(file.getName(), ext) : newName;
file.rename(ScratchUtil.class, newName);
}
public static boolean hasMatchingExtension(@NotNull Project project, @NotNull VirtualFile file) {
String extension = file.getExtension();
Language language = LanguageUtil.getLanguageForPsi(project, file);
FileType expected = extension == null ? null : FileTypeManager.getInstance().getFileTypeByExtension(extension);
FileType actual = language == null ? null : language.getAssociatedFileType();
return expected == actual && actual != null;
}
}