diff --git a/platform/core-impl/src/com/intellij/openapi/fileEditor/impl/FileDocumentManagerBase.java b/platform/core-impl/src/com/intellij/openapi/fileEditor/impl/FileDocumentManagerBase.java index 172e87dc198a..7c38086288b9 100644 --- a/platform/core-impl/src/com/intellij/openapi/fileEditor/impl/FileDocumentManagerBase.java +++ b/platform/core-impl/src/com/intellij/openapi/fileEditor/impl/FileDocumentManagerBase.java @@ -1,4 +1,4 @@ -// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.openapi.fileEditor.impl; import com.intellij.openapi.application.ApplicationManager; @@ -29,13 +29,13 @@ import java.util.Map; public abstract class FileDocumentManagerBase extends FileDocumentManager { public static final Key HARD_REF_TO_DOCUMENT_KEY = Key.create("HARD_REF_TO_DOCUMENT_KEY"); public static final Key TRACK_NON_PHYSICAL = Key.create("TRACK_NON_PHYSICAL"); + private static final Key FILE_KEY = Key.create("FILE_KEY"); private static final Key BIG_FILE_PREVIEW = Key.create("BIG_FILE_PREVIEW"); private static final Object lock = new Object(); @Override - @Nullable - public Document getDocument(@NotNull VirtualFile file) { + public @Nullable Document getDocument(@NotNull VirtualFile file) { ApplicationManager.getApplication().assertReadAccessAllowed(); DocumentEx document = (DocumentEx)getCachedDocument(file); if (document == null) { @@ -47,7 +47,7 @@ public abstract class FileDocumentManagerBase extends FileDocumentManager { CharSequence text = loadText(file, tooLarge); synchronized (lock) { document = (DocumentEx)getCachedDocument(file); - if (document != null) return document; // Double checking + if (document != null) return document; // double-checking document = (DocumentEx)createDocument(text, file); document.setModificationStamp(file.getModificationStamp()); @@ -78,8 +78,7 @@ public abstract class FileDocumentManagerBase extends FileDocumentManager { document.putUserData(BIG_FILE_PREVIEW, tooLarge ? Boolean.TRUE : null); } - @NotNull - private CharSequence loadText(@NotNull VirtualFile file, boolean tooLarge) { + private @NotNull CharSequence loadText(@NotNull VirtualFile file, boolean tooLarge) { if (file instanceof LightVirtualFile) { FileViewProvider vp = findCachedPsiInAnyProject(file); if (vp != null) { @@ -90,24 +89,26 @@ public abstract class FileDocumentManagerBase extends FileDocumentManager { return tooLarge ? LoadTextUtil.loadText(file, getPreviewCharCount(file)) : LoadTextUtil.loadText(file); } - @NotNull - protected abstract Document createDocument(@NotNull CharSequence text, @NotNull VirtualFile file); + protected abstract @NotNull Document createDocument(@NotNull CharSequence text, @NotNull VirtualFile file); @Override - @Nullable - public Document getCachedDocument(@NotNull VirtualFile file) { + public @Nullable Document getCachedDocument(@NotNull VirtualFile file) { Document hard = file.getUserData(HARD_REF_TO_DOCUMENT_KEY); return hard != null ? hard : getDocumentFromCache(file); } - // store file<->document association with hard references to avoid undesired gc. - // works for non-physical ViewProviders only to avoid memleaks. - // please do not use under the penalty of severe memory leaks and wild PSI inconsistencies. + /** + * Storing file<->document association with hard references to avoid undesired GCs. + * Works for non-physical ViewProviders only, to avoid memory leaks. + * Please do not use under the penalty of severe memory leaks and wild PSI inconsistencies. + */ @ApiStatus.Internal public static void registerDocument(@NotNull Document document, @NotNull VirtualFile virtualFile) { if (!(virtualFile instanceof LightVirtualFile) && !(virtualFile.getFileSystem() instanceof NonPhysicalFileSystem)) { - throw new IllegalArgumentException("Hard-coding file<->document association is permitted for non-physical files only (see FileViewProvider.isPhysical()) to avoid memleaks. virtualFile="+virtualFile); + throw new IllegalArgumentException( + "Hard-coding file<->document association is permitted for non-physical files only (see FileViewProvider.isPhysical())" + + " to avoid memory leaks. virtualFile=" + virtualFile); } synchronized (lock) { document.putUserData(FILE_KEY, virtualFile); @@ -116,10 +117,8 @@ public abstract class FileDocumentManagerBase extends FileDocumentManager { } @Override - @Nullable - public VirtualFile getFile(@NotNull Document document) { - if (document instanceof FrozenDocument) return null; - return document.getUserData(FILE_KEY); + public @Nullable VirtualFile getFile(@NotNull Document document) { + return document instanceof FrozenDocument ? null : document.getUserData(FILE_KEY); } @Override diff --git a/platform/core-impl/src/com/intellij/openapi/fileEditor/impl/LoadTextUtil.java b/platform/core-impl/src/com/intellij/openapi/fileEditor/impl/LoadTextUtil.java index 81a9ce53fe35..e5e9a11cfad4 100644 --- a/platform/core-impl/src/com/intellij/openapi/fileEditor/impl/LoadTextUtil.java +++ b/platform/core-impl/src/com/intellij/openapi/fileEditor/impl/LoadTextUtil.java @@ -16,8 +16,6 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.util.text.Strings; import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileEvent; -import com.intellij.openapi.vfs.encoding.EncodingManager; import com.intellij.openapi.vfs.encoding.EncodingRegistry; import com.intellij.testFramework.LightVirtualFile; import com.intellij.util.*; @@ -48,16 +46,15 @@ public final class LoadTextUtil { private LoadTextUtil() { } - @NotNull - private static ConvertResult convertLineSeparatorsToSlashN(@NotNull CharBuffer buffer) { + private static @NotNull ConvertResult convertLineSeparatorsToSlashN(@NotNull CharBuffer buffer) { int dst = 0; char prev = ' '; int crCount = 0; int lfCount = 0; int crlfCount = 0; - final int length = buffer.length(); - final char[] bufferArray = CharArrayUtil.fromSequenceWithoutCopying(buffer); + int length = buffer.length(); + char[] bufferArray = CharArrayUtil.fromSequenceWithoutCopying(buffer); for (int src = 0; src < length; src++) { char c = bufferArray != null ? bufferArray[src]:buffer.charAt(src); @@ -92,8 +89,7 @@ public final class LoadTextUtil { private static final char UNDEFINED_CHAR = 0xFDFF; - @NotNull - private static ConvertResult convertLineSeparatorsToSlashN(byte @NotNull [] charsAsBytes, int startOffset, int endOffset) { + private static @NotNull ConvertResult convertLineSeparatorsToSlashN(byte @NotNull [] charsAsBytes, int startOffset, int endOffset) { int lineBreak = findLineBreakOrWideChar(charsAsBytes, startOffset, endOffset); if (!BitUtil.isSet(lineBreak, CR) && !BitUtil.isSet(lineBreak, WIDE)) { // optimisation: if there is no CR in the file, no line separator conversion is necessary. we can re-use the passed byte buffer inplace @@ -141,8 +137,7 @@ public final class LoadTextUtil { return new ConvertResult(sequence, crCount, lfCount, crlfCount); } - @NotNull - private static ConvertResult convertWideCharacters(byte @NotNull [] charsAsBytes, int startOffset, int endOffset) { + private static @NotNull ConvertResult convertWideCharacters(byte @NotNull [] charsAsBytes, int startOffset, int endOffset) { // convert \r\n to \n, \r to \n, wide char to UNDEFINED_CHAR char prev = ' '; int crCount = 0; @@ -183,6 +178,7 @@ public final class LoadTextUtil { private static final int CR = 1; private static final int LF = 2; private static final int WIDE = 4; + @MagicConstant(flags = {CR, LF, WIDE}) private static int findLineBreakOrWideChar(byte @NotNull [] ints, int start, int end) { int flags = 0; @@ -195,10 +191,12 @@ public final class LoadTextUtil { return flags; } - // private fake charsets for files which have one-byte-for-ascii-characters encoding but contain seven bits characters only. used for optimization since we don't have to encode-decode bytes here. + // Private fake charsets for files having one-byte-for-ascii-characters encoding but containing only 7-bit characters. + // Used for optimization since we don't have to encode-decode bytes here. private static final Charset INTERNAL_SEVEN_BIT_UTF8 = new SevenBitCharset(StandardCharsets.UTF_8); private static final Charset INTERNAL_SEVEN_BIT_ISO_8859_1 = new SevenBitCharset(StandardCharsets.ISO_8859_1); private static final Charset INTERNAL_SEVEN_BIT_WIN_1251 = new SevenBitCharset(CharsetToolkit.WIN_1251_CHARSET); + private static class SevenBitCharset extends Charset { private final Charset myBaseCharset; @@ -238,13 +236,12 @@ public final class LoadTextUtil { } } - // guess from file type or content - @NotNull - private static DetectResult detectHardCharset(@NotNull VirtualFile virtualFile, + // guess from a file type or a content + private static @NotNull DetectResult detectHardCharset(@NotNull VirtualFile virtualFile, byte @NotNull [] internalBuffer, int length, @NotNull FileType fileType) { - String charsetName = fileType.getCharset(virtualFile, internalBuffer); // todo aaaaah + String charsetName = fileType.getCharset(virtualFile, internalBuffer); // todo[cdr] Charset charset = charsetName == null ? null : CharsetToolkit.forName(charsetName); DetectResult detectResult = guessFromContent(virtualFile, internalBuffer, length); Charset hardCodedCharset = charset == null ? detectResult.hardCodedCharset : charset; @@ -260,24 +257,21 @@ public final class LoadTextUtil { return new DetectResult(result, guessed, detectedBOM); } - @NotNull - public static Charset detectCharsetAndSetBOM(@NotNull VirtualFile virtualFile, byte @NotNull [] content, @NotNull FileType fileType) { + public static @NotNull Charset detectCharsetAndSetBOM(@NotNull VirtualFile virtualFile, byte @NotNull [] content, @NotNull FileType fileType) { Charset internalCharset = detectInternalCharsetAndSetBOM(virtualFile, content, content.length, true, fileType).hardCodedCharset; return internalCharset instanceof SevenBitCharset ? ((SevenBitCharset)internalCharset).myBaseCharset : internalCharset; } - @NotNull - private static Charset getDefaultCharsetFromEncodingManager(@NotNull VirtualFile virtualFile) { + private static @NotNull Charset getDefaultCharsetFromEncodingManager(@NotNull VirtualFile virtualFile) { Charset specifiedExplicitly = EncodingRegistry.getInstance().getEncoding(virtualFile, true); return ObjectUtils.notNull(specifiedExplicitly, EncodingRegistry.getInstance().getDefaultCharset()); } - @NotNull - private static DetectResult detectInternalCharsetAndSetBOM(@NotNull VirtualFile file, - byte @NotNull [] internalBuffer, - int length, - boolean saveBOM, - @NotNull FileType fileType) { + private static @NotNull DetectResult detectInternalCharsetAndSetBOM(@NotNull VirtualFile file, + byte @NotNull [] internalBuffer, + int length, + boolean saveBOM, + @NotNull FileType fileType) { DetectResult info = detectHardCharset(file, internalBuffer, length, fileType); Charset charset; @@ -314,19 +308,17 @@ public final class LoadTextUtil { } - @NotNull - public static DetectResult guessFromContent(@NotNull VirtualFile virtualFile, byte @NotNull [] content) { + public static @NotNull DetectResult guessFromContent(@NotNull VirtualFile virtualFile, byte @NotNull [] content) { return guessFromContent(virtualFile, content, content.length); } private static final boolean GUESS_UTF = Boolean.parseBoolean(System.getProperty("idea.guess.utf.encoding", "true")); - @NotNull - private static DetectResult guessFromContent(@NotNull VirtualFile virtualFile, byte @NotNull [] content, int length) { + private static @NotNull DetectResult guessFromContent(@NotNull VirtualFile virtualFile, byte @NotNull [] content, int length) { AutoDetectionReason detectedFromBytes = null; try { DetectResult info; if (GUESS_UTF) { - info = guessFromBytes(content, 0, length, getDefaultCharsetFromEncodingManager(virtualFile)); + info = guessFromBytes(content, length, getDefaultCharsetFromEncodingManager(virtualFile)); if (info.BOM != null) { detectedFromBytes = AutoDetectionReason.FROM_BOM; } @@ -344,11 +336,8 @@ public final class LoadTextUtil { } } - @NotNull - private static DetectResult guessFromBytes(byte @NotNull [] content, - int startOffset, int endOffset, - @NotNull Charset defaultCharset) { - if (startOffset == endOffset) { + private static @NotNull DetectResult guessFromBytes(byte @NotNull [] content, int endOffset, @NotNull Charset defaultCharset) { + if (endOffset == 0) { return new DetectResult(null, CharsetToolkit.GuessedEncoding.SEVEN_BIT, null); } CharsetToolkit toolkit = new CharsetToolkit(content, defaultCharset, true); @@ -357,46 +346,14 @@ public final class LoadTextUtil { byte[] bom = ObjectUtils.notNull(CharsetToolkit.getMandatoryBom(charset), CharsetToolkit.UTF8_BOM); return new DetectResult(charset, null, bom); } - CharsetToolkit.GuessedEncoding guessed = toolkit.guessFromContent(startOffset, endOffset); + CharsetToolkit.GuessedEncoding guessed = toolkit.guessFromContent(0, endOffset); if (guessed == CharsetToolkit.GuessedEncoding.VALID_UTF8) { return new DetectResult(StandardCharsets.UTF_8, CharsetToolkit.GuessedEncoding.VALID_UTF8, null); //UTF detected, ignore all directives } return new DetectResult(null, guessed, null); } - /** - * Tries to detect text in the {@code bytes} and call the {@code fileTextProcessor} with the text (if detected) or with null if not - */ - public static String getTextFromBytesOrNull(byte @NotNull [] bytes, int startOffset, int endOffset) { - Charset defaultCharset = EncodingManager.getInstance().getDefaultCharset(); - DetectResult info = guessFromBytes(bytes, startOffset, endOffset, defaultCharset); - Charset charset; - if (info.hardCodedCharset != null) { - charset = info.hardCodedCharset; - } - else { - switch (info.guessed) { - case SEVEN_BIT: - charset = StandardCharsets.US_ASCII; - break; - case VALID_UTF8: - charset = StandardCharsets.UTF_8; - break; - case INVALID_UTF8: - case BINARY: - // the charset was not detected so the file is likely binary - return null; - default: - throw new IllegalStateException(String.valueOf(info.guessed)); - } - } - byte[] bom = info.BOM; - ConvertResult result = convertBytes(bytes, Math.min(startOffset + (bom == null ? 0 : bom.length), endOffset), endOffset, charset); - return result.text.toString(); - } - - @NotNull - private static Pair.NonNull getOverriddenCharsetByBOM(byte @NotNull [] content, @NotNull Charset charset) { + private static @NotNull Pair.NonNull getOverriddenCharsetByBOM(byte @NotNull [] content, @NotNull Charset charset) { if (charset.name().contains(CharsetToolkit.UTF8) && CharsetToolkit.hasUTF8Bom(content)) { return Pair.createNonNull(charset, CharsetToolkit.UTF8_BOM); } @@ -420,16 +377,7 @@ public final class LoadTextUtil { } /** - * Overwrites file with text and sets modification stamp and time stamp to the specified values. - *

- * Normally you should not use this method. - * - * @param requestor any object to control who called this method. Note that - * it is considered to be an external change if {@code requestor} is {@code null}. - * See {@link VirtualFileEvent#getRequestor} - * @param newModificationStamp new modification stamp or -1 if no special value should be set @return {@code Writer} - * @throws IOException if an I/O error occurs - * @see VirtualFile#getModificationStamp() + * Normally, one should not use this method. */ public static void write(@Nullable Project project, @NotNull VirtualFile virtualFile, @@ -443,24 +391,22 @@ public final class LoadTextUtil { if (!charset.equals(existing)) { virtualFile.setCharset(charset); } - setDetectedFromBytesFlagBack(virtualFile, buffer); + restoreDetectedFromContentFlag(virtualFile, buffer); try (OutputStream stream = virtualFile.getOutputStream(requestor, newModificationStamp, -1)) { stream.write(buffer); } } - @NotNull - public static Pair.NonNull charsetForWriting(@Nullable Project project, - @NotNull VirtualFile virtualFile, - @NotNull String text, - @NotNull Charset existing) { + public static @NotNull Pair.NonNull charsetForWriting(@Nullable Project project, + @NotNull VirtualFile virtualFile, + @NotNull String text, + @NotNull Charset existing) { Charset specified = extractCharsetFromFileContent(project, virtualFile, text); Pair.NonNull chosen = chooseMostlyHarmlessCharset(existing, specified, text); Charset charset = chosen.first; - // in case of "UTF-16", OutputStreamWriter sometimes adds BOM on it's own. - // see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6800103 + // in the case of "UTF-16", OutputStreamWriter sometimes adds BOM on its own (see JDK-6800103) byte[] bom = virtualFile.getBOM(); Charset fromBom = bom == null ? null : CharsetToolkit.guessFromBOM(bom); if (fromBom != null && !fromBom.equals(charset)) { @@ -469,18 +415,17 @@ public final class LoadTextUtil { return chosen; } - private static void setDetectedFromBytesFlagBack(@NotNull VirtualFile virtualFile, byte @NotNull [] content) { + private static void restoreDetectedFromContentFlag(@NotNull VirtualFile virtualFile, byte @NotNull [] content) { if (virtualFile.getBOM() == null) { guessFromContent(virtualFile, content); } else { - // prevent file to be reloaded in other encoding after save with BOM + // prevent file to be reloaded in another encoding after save with BOM setCharsetAutoDetectionReason(virtualFile, AutoDetectionReason.FROM_BOM); } } - @NotNull - public static Pair.NonNull chooseMostlyHarmlessCharset(@NotNull Charset existing, @NotNull Charset specified, @NotNull String text) { + public static @NotNull Pair.NonNull chooseMostlyHarmlessCharset(@NotNull Charset existing, @NotNull Charset specified, @NotNull String text) { try { if (specified.equals(existing)) { return Pair.createNonNull(specified, text.getBytes(existing)); @@ -494,7 +439,7 @@ public final class LoadTextUtil { if (out != null) { return Pair.createNonNull(existing, out); //otherwise stick to the old encoding if it's ok } - return Pair.createNonNull(specified, text.getBytes(specified)); //if both are bad there is no difference + return Pair.createNonNull(specified, text.getBytes(specified)); //if both are bad, there is no difference } catch (RuntimeException e) { Charset defaultCharset = Charset.defaultCharset(); @@ -517,21 +462,18 @@ public final class LoadTextUtil { } } - @NotNull - public static Charset extractCharsetFromFileContent(@Nullable Project project, @NotNull VirtualFile virtualFile, @NotNull CharSequence text) { + public static @NotNull Charset extractCharsetFromFileContent(@Nullable Project project, @NotNull VirtualFile virtualFile, @NotNull CharSequence text) { return ObjectUtils.notNull(charsetFromContentOrNull(project, virtualFile, text), virtualFile.getCharset()); } - @Nullable("returns null if cannot determine from content") - public static Charset charsetFromContentOrNull(@Nullable Project project, @NotNull VirtualFile virtualFile, @NotNull CharSequence text) { + public static @Nullable("returns null if cannot determine from content") Charset charsetFromContentOrNull(@Nullable Project project, @NotNull VirtualFile virtualFile, @NotNull CharSequence text) { return CharsetUtil.extractCharsetFromFileContent(project, virtualFile, virtualFile.getFileType(), text); } - @NotNull - public static CharSequence loadText(@NotNull final VirtualFile file) { + public static @NotNull CharSequence loadText(@NotNull VirtualFile file) { FileType type = file.getFileType(); if (type.isBinary()) { - final BinaryFileDecompiler decompiler = BinaryFileTypeDecompilers.getInstance().forFileType(type); + BinaryFileDecompiler decompiler = BinaryFileTypeDecompilers.getInstance().forFileType(type); if (decompiler != null) { CharSequence text = decompiler.decompile(file); try { @@ -550,19 +492,13 @@ public final class LoadTextUtil { } /** - * Loads content of given virtual file. If limit is {@value UNLIMITED} then full CharSequence will be returned. Else CharSequence - * will be truncated by limit if it has bigger length. - * @param file Virtual file for content loading - * @param limit Maximum characters count or {@value UNLIMITED} * @throws IllegalArgumentException for binary files - * @return Full or truncated CharSequence with file content */ - @NotNull - public static CharSequence loadText(@NotNull final VirtualFile file, int limit) { + public static @NotNull CharSequence loadText(@NotNull VirtualFile file, int limit) { FileType type = file.getFileType(); - if (type.isBinary()) throw new IllegalArgumentException( - "Attempt to load truncated text for binary file: " + file.getPresentableUrl() + ". File type: " + type.getName() - ); + if (type.isBinary()) { + throw new IllegalArgumentException("Attempt to load truncated text for binary file: " + file.getPresentableUrl() + ". File type: " + type.getName()); + } if (file instanceof LightVirtualFile) { return limitCharSequence(((LightVirtualFile)file).getContent(), limit); @@ -572,38 +508,32 @@ public final class LoadTextUtil { throw new AssertionError("'" + file.getPresentableUrl() + "' is a directory"); } try { - byte[] bytes = limit == UNLIMITED ? file.contentsToByteArray() : - FileUtil.loadFirstAndClose(file.getInputStream(), limit); + byte[] bytes = limit == UNLIMITED ? file.contentsToByteArray() : FileUtil.loadFirstAndClose(file.getInputStream(), limit); return getTextByBinaryPresentation(bytes, file); } catch (IOException e) { - LOG.debug(e); return Strings.EMPTY_CHAR_SEQUENCE; } } - @NotNull - private static CharSequence limitCharSequence(@NotNull CharSequence sequence, int limit) { + private static @NotNull CharSequence limitCharSequence(@NotNull CharSequence sequence, int limit) { return limit == UNLIMITED ? sequence : sequence.subSequence(0, Math.min(limit, sequence.length())); } - @NotNull - public static CharSequence getTextByBinaryPresentation(final byte @NotNull [] bytes, @NotNull VirtualFile virtualFile) { + public static @NotNull CharSequence getTextByBinaryPresentation(byte @NotNull [] bytes, @NotNull VirtualFile virtualFile) { return getTextByBinaryPresentation(bytes, virtualFile, true, true); } - @NotNull - public static CharSequence getTextByBinaryPresentation(byte @NotNull [] bytes, - @NotNull VirtualFile virtualFile, - boolean saveDetectedSeparators, - boolean saveBOM) { + public static @NotNull CharSequence getTextByBinaryPresentation(byte @NotNull [] bytes, + @NotNull VirtualFile virtualFile, + boolean saveDetectedSeparators, + boolean saveBOM) { DetectResult info = detectInternalCharsetAndSetBOM(virtualFile, bytes, bytes.length, saveBOM, virtualFile.getFileType()); ConvertResult result = convertBytesAndSetSeparator(bytes, bytes.length, virtualFile, saveDetectedSeparators, info, info.hardCodedCharset); return result.text; } - @NotNull - static Set detectAllLineSeparators(@NotNull VirtualFile virtualFile) { + static @NotNull Set detectAllLineSeparators(@NotNull VirtualFile virtualFile) { byte[] bytes; try { bytes = virtualFile.contentsToByteArray(); @@ -618,8 +548,7 @@ public final class LoadTextUtil { } // written in push way to make sure no-one stores the CharSequence because it came from thread-local byte buffers which will be overwritten soon - @NotNull - public static FileType processTextFromBinaryPresentationOrNull(@NotNull ByteSequence bytes, + public static @NotNull FileType processTextFromBinaryPresentationOrNull(@NotNull ByteSequence bytes, @NotNull VirtualFile virtualFile, boolean saveDetectedSeparators, boolean saveBOM, @@ -631,7 +560,7 @@ public final class LoadTextUtil { CharsetToolkit.GuessedEncoding guessed = detectResult.guessed; CharSequence toProcess; if (internalCharset == null || guessed == CharsetToolkit.GuessedEncoding.BINARY || guessed == CharsetToolkit.GuessedEncoding.INVALID_UTF8) { - // the charset was not detected so the file is likely binary + // the charset was not detected, so the file is likely binary toProcess = null; } else { @@ -641,13 +570,12 @@ public final class LoadTextUtil { return fileTextProcessor.fun(toProcess); } - @NotNull - private static ConvertResult convertBytesAndSetSeparator(byte @NotNull [] bytes, - int length, - @NotNull VirtualFile virtualFile, - boolean saveDetectedSeparators, - @NotNull DetectResult info, - @NotNull Charset internalCharset) { + private static @NotNull ConvertResult convertBytesAndSetSeparator(byte @NotNull [] bytes, + int length, + @NotNull VirtualFile virtualFile, + boolean saveDetectedSeparators, + @NotNull DetectResult info, + @NotNull Charset internalCharset) { byte[] bom = info.BOM; int BOMEndOffset = Math.min(length, bom == null ? 0 : bom.length); ConvertResult result = convertBytes(bytes, BOMEndOffset, length, internalCharset); @@ -668,34 +596,29 @@ public final class LoadTextUtil { * @param checkFile if the line separator was not detected before, try to detect it * @return the detected line separator or null */ - @Nullable - public static String detectLineSeparator(@NotNull VirtualFile file, boolean checkFile) { + public static @Nullable String detectLineSeparator(@NotNull VirtualFile file, boolean checkFile) { String lineSeparator = file.getDetectedLineSeparator(); if (lineSeparator == null && checkFile) { try { getTextByBinaryPresentation(file.contentsToByteArray(), file); lineSeparator = file.getDetectedLineSeparator(); } - catch (IOException e) { - // null will be returned - } + catch (IOException ignored) { } } return lineSeparator; } - @NotNull - public static CharSequence getTextByBinaryPresentation(byte @NotNull [] bytes, @NotNull Charset charset) { + public static @NotNull CharSequence getTextByBinaryPresentation(byte @NotNull [] bytes, @NotNull Charset charset) { Pair.NonNull pair = getOverriddenCharsetByBOM(bytes, charset); byte[] bom = pair.getSecond(); - final ConvertResult result = convertBytes(bytes, Math.min(bom.length, bytes.length), bytes.length, pair.first); + ConvertResult result = convertBytes(bytes, Math.min(bom.length, bytes.length), bytes.length, pair.first); return result.text; } - @NotNull - private static ConvertResult convertBytes(byte @NotNull [] bytes, - final int startOffset, int endOffset, - @NotNull Charset internalCharset) { + private static @NotNull ConvertResult convertBytes(byte @NotNull [] bytes, + int startOffset, int endOffset, + @NotNull Charset internalCharset) { assert startOffset >= 0 && startOffset <= endOffset && endOffset <= bytes.length: startOffset + "," + endOffset+": "+bytes.length; if (internalCharset instanceof SevenBitCharset || internalCharset == StandardCharsets.US_ASCII) { // optimisation: skip byte-to-char conversion for ascii chars @@ -716,7 +639,7 @@ public final class LoadTextUtil { } private static class ConvertResult { - @NotNull private final CharSequence text; + private final @NotNull CharSequence text; private final int CR_count; private final int LF_count; private final int CRLF_count; @@ -753,8 +676,8 @@ public final class LoadTextUtil { } private static final Key CHARSET_WAS_DETECTED_FROM_BYTES = Key.create("CHARSET_WAS_DETECTED_FROM_BYTES"); - @Nullable("null if was not detected, otherwise the reason it was") - public static AutoDetectionReason getCharsetAutoDetectionReason(@NotNull VirtualFile virtualFile) { + + public static @Nullable("null if was not detected, otherwise the reason it was") AutoDetectionReason getCharsetAutoDetectionReason(@NotNull VirtualFile virtualFile) { return virtualFile.getUserData(CHARSET_WAS_DETECTED_FROM_BYTES); } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/file/impl/PsiVFSListener.java b/platform/lang-impl/src/com/intellij/psi/impl/file/impl/PsiVFSListener.java index 42f7db870286..9cd1792a9ad6 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/file/impl/PsiVFSListener.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/file/impl/PsiVFSListener.java @@ -15,6 +15,7 @@ import com.intellij.openapi.editor.Document; import com.intellij.openapi.extensions.ExtensionPoint; import com.intellij.openapi.fileEditor.FileDocumentManagerListener; import com.intellij.openapi.fileEditor.impl.FileDocumentManagerImpl; +import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.fileTypes.FileTypeEvent; import com.intellij.openapi.fileTypes.FileTypeListener; import com.intellij.openapi.fileTypes.FileTypeManager; @@ -73,7 +74,7 @@ public final class PsiVFSListener implements BulkFileListener { public void runActivity(@NotNull Project project) { MessageBusConnection connection = project.getMessageBus().connect(); - ExtensionPoint> point = LanguageSubstitutors.getInstance().getPoint(); + ExtensionPoint<@NotNull KeyedLazyInstance> point = LanguageSubstitutors.getInstance().getPoint(); if (point != null) { point.addChangeListener(() -> { if (project.isDisposed()) { @@ -114,9 +115,6 @@ public final class PsiVFSListener implements BulkFileListener { } } - /** - * This code is implemented as static method (and not static constructor, as it was done before) to prevent installing listeners in Upsource - */ private static void installGlobalListener() { if (!ourGlobalListenerInstalled.compareAndSet(false, true)) { return; @@ -135,10 +133,9 @@ public final class PsiVFSListener implements BulkFileListener { @Override public void after(@NotNull List events) { Project[] projects = ProjectUtilCore.getOpenProjects(); - // let PushedFilePropertiesUpdater process all pending vfs events and update file properties before we issue PSI events + // let PushedFilePropertiesUpdater process all pending VFS events and update file properties before we issue PSI events for (Project project : projects) { PushedFilePropertiesUpdater updater = PushedFilePropertiesUpdater.getInstance(project); - // false in upsource if (updater instanceof PushedFilePropertiesUpdaterImpl) { ((PushedFilePropertiesUpdaterImpl)updater).processAfterVfsChanges(events); } @@ -150,8 +147,7 @@ public final class PsiVFSListener implements BulkFileListener { }); } - @Nullable - private PsiDirectory getCachedDirectory(VirtualFile parent) { + private @Nullable PsiDirectory getCachedDirectory(VirtualFile parent) { return parent == null ? null : myFileManager.getCachedDirectory(parent); } @@ -176,10 +172,10 @@ public final class PsiVFSListener implements BulkFileListener { } private void beforeFileDeletion(@NotNull VFileDeleteEvent event) { - final VirtualFile vFile = event.getFile(); + VirtualFile vFile = event.getFile(); VirtualFile parent = vFile.getParent(); - final PsiDirectory parentDir = getCachedDirectory(parent); + PsiDirectory parentDir = getCachedDirectory(parent); if (parentDir == null) return; // do not notify listeners if parent directory was never accessed via PSI ApplicationManager.getApplication().runWriteAction( @@ -195,7 +191,7 @@ public final class PsiVFSListener implements BulkFileListener { ); } - // optimization: call myFileManager.removeInvalidFilesAndDirs() once for group of delete events, instead of once for each event + // optimization: call myFileManager.removeInvalidFilesAndDirs() once for a group of deletion events, instead of once for each event private void filesDeleted(@NotNull List events) { boolean needToRemoveInvalidFilesAndDirs = false; for (VFileEvent event : events) { @@ -203,14 +199,14 @@ public final class PsiVFSListener implements BulkFileListener { VirtualFile vFile = de.getFile(); VirtualFile parent = vFile.getParent(); - final PsiFile psiFile = myFileManager.getCachedPsiFileInner(vFile); + PsiFile psiFile = myFileManager.getCachedPsiFileInner(vFile); PsiElement element; if (psiFile != null) { myFileManager.setViewProvider(vFile, null); element = psiFile; } else { - final PsiDirectory psiDir = myFileManager.getCachedDirectory(vFile); + PsiDirectory psiDir = myFileManager.getCachedDirectory(vFile); if (psiDir != null) { needToRemoveInvalidFilesAndDirs = true; element = psiDir; @@ -223,7 +219,7 @@ public final class PsiVFSListener implements BulkFileListener { element = null; } } - final PsiDirectory parentDir = getCachedDirectory(parent); + PsiDirectory parentDir = getCachedDirectory(parent); if (element != null && parentDir != null) { ApplicationManager.getApplication().runWriteAction((ExternalChangeAction)() -> { PsiTreeChangeEventImpl treeEvent = new PsiTreeChangeEventImpl(myManager); @@ -242,14 +238,14 @@ public final class PsiVFSListener implements BulkFileListener { DebugUtil.performPsiModification(why, ()-> myFileManager.setViewProvider(vFile, null)); } - private void beforePropertyChange(@NotNull final VFilePropertyChangeEvent event) { - final VirtualFile vFile = event.getFile(); - final String propertyName = event.getPropertyName(); + private void beforePropertyChange(@NotNull VFilePropertyChangeEvent event) { + VirtualFile vFile = event.getFile(); + String propertyName = event.getPropertyName(); - final FileViewProvider viewProvider = myFileManager.findCachedViewProvider(vFile); + FileViewProvider viewProvider = myFileManager.findCachedViewProvider(vFile); VirtualFile parent = vFile.getParent(); - final PsiDirectory parentDir = viewProvider != null && parent != null ? myFileManager.findDirectory(parent) : getCachedDirectory(parent); + PsiDirectory parentDir = viewProvider != null && parent != null ? myFileManager.findDirectory(parent) : getCachedDirectory(parent); if (parent != null && parentDir == null) return; // do not notifyListeners event if parent directory was never accessed via PSI ApplicationManager.getApplication().runWriteAction( @@ -258,7 +254,7 @@ public final class PsiVFSListener implements BulkFileListener { treeEvent.setParent(parentDir); if (VirtualFile.PROP_NAME.equals(propertyName)) { - final String newName = (String)event.getNewValue(); + String newName = (String)event.getNewValue(); if (parentDir == null) return; @@ -284,7 +280,7 @@ public final class PsiVFSListener implements BulkFileListener { } } else { - final FileViewProvider viewProvider1 = myFileManager.findViewProvider(vFile); + FileViewProvider viewProvider1 = myFileManager.findViewProvider(vFile); PsiFile psiFile = viewProvider1.getPsi(viewProvider1.getBaseLanguage()); PsiFile psiFile1 = createFileCopyWithNewName(vFile, newName); @@ -339,18 +335,18 @@ public final class PsiVFSListener implements BulkFileListener { return false; } - private void propertyChanged(@NotNull final VFilePropertyChangeEvent event) { - final String propertyName = event.getPropertyName(); - final VirtualFile vFile = event.getFile(); + private void propertyChanged(@NotNull VFilePropertyChangeEvent event) { + String propertyName = event.getPropertyName(); + VirtualFile vFile = event.getFile(); FileViewProvider oldFileViewProvider = myFileManager.findCachedViewProvider(vFile); PsiFile oldPsiFile = myFileManager.getCachedPsiFile(vFile); VirtualFile parent = vFile.getParent(); - final PsiDirectory parentDir = oldPsiFile != null && parent != null ? myFileManager.findDirectory(parent) : getCachedDirectory(parent); + PsiDirectory parentDir = oldPsiFile != null && parent != null ? myFileManager.findDirectory(parent) : getCachedDirectory(parent); - if (oldFileViewProvider != null // there is no need to rebuild if there were no PSI in the first place - && FileContentUtilCore.FORCE_RELOAD_REQUESTOR.equals(event.getRequestor())) { + if (oldFileViewProvider != null && FileContentUtilCore.FORCE_RELOAD_REQUESTOR.equals(event.getRequestor())) { + // there is no need to rebuild if there were no PSI in the first place myFileManager.forceReload(vFile); return; } @@ -401,8 +397,8 @@ public final class PsiVFSListener implements BulkFileListener { } } else { - final FileViewProvider fileViewProvider = myFileManager.createFileViewProvider(vFile, true); - final PsiFile newPsiFile = fileViewProvider.getPsi(fileViewProvider.getBaseLanguage()); + FileViewProvider fileViewProvider = myFileManager.createFileViewProvider(vFile, true); + PsiFile newPsiFile = fileViewProvider.getPsi(fileViewProvider.getBaseLanguage()); if (oldPsiFile != null) { if (newPsiFile == null) { clearViewProvider(vFile, "PSI renamed"); @@ -493,7 +489,7 @@ public final class PsiVFSListener implements BulkFileListener { }); } - // optimization: call myFileManager.removeInvalidFilesAndDirs() once for group of move events, instead of once for each event + // optimization: call myFileManager.removeInvalidFilesAndDirs() once for a group of move events, instead of once for each event private void filesMoved(@NotNull List events) { List oldElements = new ArrayList<>(events.size()); List oldParentDirs = new ArrayList<>(events.size()); @@ -503,7 +499,7 @@ public final class PsiVFSListener implements BulkFileListener { for (VFileEvent e : events) { VFileMoveEvent event = (VFileMoveEvent)e; - final VirtualFile vFile = event.getFile(); + VirtualFile vFile = event.getFile(); PsiDirectory oldParentDir = myFileManager.findDirectory(event.getOldParent()); PsiDirectory newParentDir = myFileManager.findDirectory(event.getNewParent()); @@ -515,7 +511,7 @@ public final class PsiVFSListener implements BulkFileListener { if (oldProject != null && oldProject != myProject) { // file moved between projects, remove all associations to the old project myFileManager.removeFilesAndDirsRecursively(vFile); - // avoid crash in filePointer.getElement() + // avoiding crashes in filePointer.getElement() PsiCopyPasteManager.getInstance().fileMovedOutsideProject(vFile); oldElement = null; oldParentDir = null; @@ -530,15 +526,15 @@ public final class PsiVFSListener implements BulkFileListener { for (int i = 0; i < events.size(); i++) { VFileMoveEvent event = (VFileMoveEvent)events.get(i); - final VirtualFile vFile = event.getFile(); + VirtualFile vFile = event.getFile(); - final PsiDirectory oldParentDir = oldParentDirs.get(i); - final PsiDirectory newParentDir = newParentDirs.get(i); + PsiDirectory oldParentDir = oldParentDirs.get(i); + PsiDirectory newParentDir = newParentDirs.get(i); if (oldParentDir == null && newParentDir == null) continue; - final PsiElement oldElement = oldElements.get(i); - final PsiElement newElement; - final FileViewProvider newViewProvider; + PsiElement oldElement = oldElements.get(i); + PsiElement newElement; + FileViewProvider newViewProvider; if (vFile.isDirectory()) { newElement = myFileManager.findDirectory(vFile); newViewProvider = null; @@ -592,14 +588,14 @@ public final class PsiVFSListener implements BulkFileListener { } } - @Nullable - private PsiFile createFileCopyWithNewName(VirtualFile vFile, String name) { + private @Nullable PsiFile createFileCopyWithNewName(VirtualFile vFile, String name) { // TODO[ik] remove this. Event handling and generation must be in view providers mechanism since we // need to track changes in _all_ psi views (e.g. namespace changes in XML) - var typeManager = FileTypeManager.getInstance(); - if (typeManager.isFileIgnored(name)) return null; - var fileType = typeManager.getFileTypeByFileName(name); - return PsiFileFactory.getInstance(myManager.getProject()).createFileFromText(name, fileType, "", vFile.getModificationStamp(), true, false); + FileTypeManager instance = FileTypeManager.getInstance(); + if (instance.isFileIgnored(name)) return null; + FileType fileTypeByFileName = instance.getFileTypeByFileName(name); + return PsiFileFactory.getInstance(myManager.getProject()).createFileFromText( + name, fileTypeByFileName, "", vFile.getModificationStamp(), true, false); } private static final class MyModuleRootListener implements ModuleRootListener { @@ -613,7 +609,7 @@ public final class PsiVFSListener implements BulkFileListener { } @Override - public void beforeRootsChange(@NotNull final ModuleRootEvent event) { + public void beforeRootsChange(@NotNull ModuleRootEvent event) { if (event.isCausedByFileTypesChange()) return; ApplicationManager.getApplication().runWriteAction( (ExternalChangeAction)() -> { @@ -628,7 +624,7 @@ public final class PsiVFSListener implements BulkFileListener { } @Override - public void rootsChanged(@NotNull final ModuleRootEvent event) { + public void rootsChanged(@NotNull ModuleRootEvent event) { fileManager.dispatchPendingEvents(); if (event.isCausedByFileTypesChange()) return; @@ -750,13 +746,12 @@ public final class PsiVFSListener implements BulkFileListener { myReportedUnloadedPsiChange = false; } - // group same type events together and call fireForGrouped() for each batch + // grouping events of the same type together and calling fireForGrouped() for each batch private void groupAndFire(@NotNull List events) { StreamEx.of(events) // group several VFileDeleteEvents together, several VFileMoveEvents together, place all other events into one-element lists - .groupRuns((event1, event2) -> - event1 instanceof VFileDeleteEvent && event2 instanceof VFileDeleteEvent - || event1 instanceof VFileMoveEvent && event2 instanceof VFileMoveEvent) + .groupRuns((e1, e2) -> e1 instanceof VFileDeleteEvent && e2 instanceof VFileDeleteEvent || + e1 instanceof VFileMoveEvent && e2 instanceof VFileMoveEvent) .forEach(this::fireForGrouped); } @@ -772,15 +767,15 @@ public final class PsiVFSListener implements BulkFileListener { assert subList.size() == 1; if (event instanceof VFileCopyEvent) { VFileCopyEvent ce = (VFileCopyEvent)event; - final VirtualFile copy = ce.getNewParent().findChild(ce.getNewChildName()); + VirtualFile copy = ce.getNewParent().findChild(ce.getNewChildName()); if (copy != null) { - fileCreated(copy); // no need to group creation + fileCreated(copy); // no need to group file creation events } } else if (event instanceof VFileCreateEvent) { VirtualFile file = event.getFile(); if (file != null) { - fileCreated(file); // no need to group creation + fileCreated(file); // no need to group file creation events } } else if (event instanceof VFilePropertyChangeEvent) { @@ -789,4 +784,3 @@ public final class PsiVFSListener implements BulkFileListener { } } } -