diff --git a/platform/core-api/src/com/intellij/openapi/vfs/encoding/EncodingProjectManager.java b/platform/core-api/src/com/intellij/openapi/vfs/encoding/EncodingProjectManager.java index bdaf73b487e0..f97c837d1c4d 100644 --- a/platform/core-api/src/com/intellij/openapi/vfs/encoding/EncodingProjectManager.java +++ b/platform/core-api/src/com/intellij/openapi/vfs/encoding/EncodingProjectManager.java @@ -3,7 +3,6 @@ package com.intellij.openapi.vfs.encoding; import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.nio.charset.Charset; @@ -31,12 +30,4 @@ public abstract class EncodingProjectManager extends EncodingManager { */ @Override public abstract void setDefaultCharsetName(@NotNull String name); - - /** - * Sets encoding by file / dir path. - * The target file or directory may not exist. - * @param fileOrDirPath target file / dir absolute path - * @param charset null to remove specific configuration. - */ - public abstract void setEncodingByPath(@NotNull final String fileOrDirPath, @Nullable final Charset charset); } diff --git a/platform/core-impl/src/com/intellij/core/CoreEncodingProjectManager.java b/platform/core-impl/src/com/intellij/core/CoreEncodingProjectManager.java index d150495c203f..df80566086b4 100644 --- a/platform/core-impl/src/com/intellij/core/CoreEncodingProjectManager.java +++ b/platform/core-impl/src/com/intellij/core/CoreEncodingProjectManager.java @@ -34,10 +34,6 @@ public class CoreEncodingProjectManager extends EncodingProjectManager { public void setEncoding(@Nullable VirtualFile virtualFileOrDir, @Nullable Charset charset) { } - @Override - public void setEncodingByPath(@NotNull String path, @Nullable Charset charset) { - } - @Override public boolean isNative2AsciiForPropertiesFiles() { return false; diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/encoding/EncodingProjectManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/vfs/encoding/EncodingProjectManagerImpl.java index 98542abae9b2..6d74b5f3ce97 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/encoding/EncodingProjectManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/encoding/EncodingProjectManagerImpl.java @@ -23,7 +23,10 @@ import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.startup.StartupActivity; import com.intellij.openapi.util.*; import com.intellij.openapi.util.text.StringUtilRt; -import com.intellij.openapi.vfs.*; +import com.intellij.openapi.vfs.CharsetToolkit; +import com.intellij.openapi.vfs.VfsUtilCore; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileVisitor; import com.intellij.openapi.vfs.impl.LightFilePointer; import com.intellij.openapi.vfs.newvfs.events.VFileContentChangeEvent; import com.intellij.openapi.vfs.newvfs.impl.VirtualFileSystemEntry; @@ -202,28 +205,6 @@ public final class EncodingProjectManagerImpl extends EncodingProjectManager imp return myModificationTracker; } - @Override - public void setEncodingByPath(@NotNull final String fileOrDirPath, @Nullable final Charset charset) { - final VirtualFile vf = LocalFileSystem.getInstance().findFileByPath(fileOrDirPath); - if (vf != null) { - setEncoding(vf, charset); - return; - } - - Charset oldCharset; - VirtualFilePointer pointer = VirtualFilePointerManager.getInstance().create(VfsUtilCore.pathToUrl(fileOrDirPath), this, null); - if (charset == null) { - oldCharset = myMapping.remove(pointer); - } - else { - oldCharset = myMapping.put(pointer, charset); - } - - if (!Comparing.equal(oldCharset, charset)) { - myModificationTracker.incModificationCount(); - } - } - @Override public void setEncoding(@Nullable final VirtualFile virtualFileOrDir, @Nullable final Charset charset) { Charset oldCharset; @@ -300,11 +281,18 @@ public final class EncodingProjectManagerImpl extends EncodingProjectManager imp .collect(Collectors.toMap(p -> p.getFirst(), p -> p.getSecond(), (c1, c2) -> c1)); } + /** + * @return readonly map of current mappings. to modify mappings use {@link #setPointerMapping(Map)} + */ + @NotNull + public Map getAllPointersMappings() { + return Collections.unmodifiableMap(myMapping); + } + public void setMapping(@NotNull Map mapping) { ApplicationManager.getApplication().assertIsWriteThread(); FileDocumentManager.getInstance().saveAllDocuments(); // consider all files as unmodified final Map newMap = new HashMap<>(mapping.size()); - final Map oldMap = new HashMap<>(myMapping); // ChangeFileEncodingAction should not start progress "reload files..." suppressReloadDuring(() -> { @@ -320,27 +308,47 @@ public final class EncodingProjectManagerImpl extends EncodingProjectManager imp if (!fileIndex.isInContent(virtualFile)) continue; VirtualFilePointer pointer = VirtualFilePointerManager.getInstance().create(virtualFile, this, null); - if (!virtualFile.isDirectory() && !Comparing.equal(charset, oldMap.get(pointer))) { - Document document; - byte[] bytes; - try { - document = FileDocumentManager.getInstance().getDocument(virtualFile); - if (document == null) throw new IOException(); - bytes = virtualFile.contentsToByteArray(); - } - catch (IOException e) { - continue; - } - // ask whether to reload/convert when in doubt - boolean changed = new ChangeFileEncodingAction().chosen(document, null, virtualFile, bytes, charset); - - if (!changed) continue; - } + if (!fileEncodingChanged(virtualFile, myMapping.get(pointer), charset)) continue; newMap.put(pointer, charset); } } }); + updateMapping(newMap); + } + + + public void setPointerMapping(@NotNull Map mapping) { + ApplicationManager.getApplication().assertIsWriteThread(); + FileDocumentManager.getInstance().saveAllDocuments(); // consider all files as unmodified + final Map newMap = new HashMap<>(mapping.size()); + + // ChangeFileEncodingAction should not start progress "reload files..." + suppressReloadDuring(() -> { + ProjectFileIndex fileIndex = ProjectRootManager.getInstance(myProject).getFileIndex(); + for (Map.Entry entry : mapping.entrySet()) { + VirtualFilePointer filePointer = entry.getKey(); + Charset charset = entry.getValue(); + if (charset == null) throw new IllegalArgumentException("Null charset for " + filePointer + "; mapping: " + mapping); + if (filePointer == null) { + myProjectCharset = charset; + } + else { + final VirtualFile virtualFile = filePointer.getFile(); + if (virtualFile != null) { + if (!fileIndex.isInContent(virtualFile) + || !fileEncodingChanged(virtualFile, myMapping.get(filePointer), charset)) continue; + } + newMap.put(filePointer, charset); + } + } + }); + + updateMapping(newMap); + } + + private void updateMapping(Map newMap) { + Map oldMap = new HashMap<>(myMapping); myMapping.clear(); myMapping.putAll(newMap); @@ -384,6 +392,26 @@ public final class EncodingProjectManagerImpl extends EncodingProjectManager imp myModificationTracker.incModificationCount(); } + private static boolean fileEncodingChanged(@NotNull VirtualFile virtualFile, + @Nullable Charset oldCharset, + @NotNull Charset newCharset) { + if (!virtualFile.isDirectory() && !Comparing.equal(newCharset, oldCharset)) { + Document document; + byte[] bytes; + try { + document = FileDocumentManager.getInstance().getDocument(virtualFile); + if (document == null) throw new IOException(); + bytes = virtualFile.contentsToByteArray(); + } + catch (IOException e) { + return false; + } + // ask whether to reload/convert when in doubt + return new ChangeFileEncodingAction().chosen(document, null, virtualFile, bytes, newCharset); + } + return true; + } + @NotNull private static Processor createChangeCharsetProcessor(@NotNull Project project) { return file -> { diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/configurers/MavenEncodingConfigurer.kt b/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/configurers/MavenEncodingConfigurer.kt index 7ff1cb8121cc..d88dac4e2cd4 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/configurers/MavenEncodingConfigurer.kt +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/configurers/MavenEncodingConfigurer.kt @@ -15,9 +15,19 @@ */ package org.jetbrains.idea.maven.importing.configurers +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.application.ReadAction +import com.intellij.openapi.components.service import com.intellij.openapi.module.Module import com.intellij.openapi.project.Project +import com.intellij.openapi.util.io.FileUtil +import com.intellij.openapi.vfs.LocalFileSystem +import com.intellij.openapi.vfs.VfsUtil.fileToUrl +import com.intellij.openapi.vfs.VfsUtilCore.urlToPath import com.intellij.openapi.vfs.encoding.EncodingProjectManager +import com.intellij.openapi.vfs.encoding.EncodingProjectManagerImpl +import com.intellij.openapi.vfs.pointers.VirtualFilePointer +import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager import org.jetbrains.idea.maven.project.MavenProject import org.jetbrains.idea.maven.utils.MavenLog import java.io.File @@ -29,26 +39,70 @@ import java.nio.charset.UnsupportedCharsetException */ class MavenEncodingConfigurer : MavenModuleConfigurer() { override fun configure(mavenProject: MavenProject, project: Project, module: Module) { - fillSourceEncoding(mavenProject, EncodingProjectManager.getInstance(project)) - fillResourceEncoding(project, mavenProject, EncodingProjectManager.getInstance(project)) + val encodingCollector = EncodingCollector(project) + + ReadAction.compute { + fillSourceEncoding(mavenProject, encodingCollector) + } + + ReadAction.compute { + fillResourceEncoding(project, mavenProject, encodingCollector) + } + + encodingCollector.applyCollectedInfo() } - private fun fillResourceEncoding(project: Project, - mavenProject: MavenProject, - projectManager: EncodingProjectManager) { - mavenProject.getResourceEncoding(project)?.let(this::getCharset)?.let { charset -> - mavenProject.resources.forEach { resource -> - projectManager.setEncodingByPath(File(resource.directory).absolutePath, charset) + class EncodingCollector(project: Project) { + private val newPointerMappings = LinkedHashMap() + private val oldPointerMappings = LinkedHashMap() + private val encodingManager = (EncodingProjectManager.getInstance(project) as EncodingProjectManagerImpl) + + fun processDir(directory: String, charset: Charset) { + val dirVfile = LocalFileSystem.getInstance().findFileByIoFile(File(directory)) + val pointer = if (dirVfile != null) { + service().create(dirVfile, encodingManager, null) + } else { + service().create(fileToUrl(File(directory).absoluteFile), encodingManager, null) + } + newPointerMappings[pointer] = charset + encodingManager.allPointersMappings.forEach { + val filePointer = it.key + if (FileUtil.isAncestor(directory, urlToPath(filePointer.url), false) + || newPointerMappings.containsKey(filePointer)) { + newPointerMappings[filePointer] = charset + oldPointerMappings.remove(filePointer) + } + else { + oldPointerMappings[filePointer] = it.value + } + } + } + + fun applyCollectedInfo() { + if (newPointerMappings.isEmpty()) { + return + } + + val pointerMapping = newPointerMappings + oldPointerMappings + + ApplicationManager.getApplication().invokeAndWait { + encodingManager.setPointerMapping(pointerMapping) } } } + private fun fillResourceEncoding(project: Project, + mavenProject: MavenProject, + encodingCollector: EncodingCollector) { + mavenProject.getResourceEncoding(project)?.let(this::getCharset)?.let { charset -> + mavenProject.resources.map { it.directory }.forEach { encodingCollector.processDir(it, charset) } + } + } + private fun fillSourceEncoding(mavenProject: MavenProject, - projectManager: EncodingProjectManager) { + encodingCollector: EncodingCollector) { mavenProject.sourceEncoding?.let(this::getCharset)?.let { charset -> - mavenProject.sources.forEach { directory -> - projectManager.setEncodingByPath(File(directory).absolutePath, charset) - } + mavenProject.sources.forEach { encodingCollector.processDir(it, charset) } } }