LightVirtualFiles must not be ignored
[fedora-idea.git] / lang-impl / src / com / intellij / psi / SingleRootFileViewProvider.java
blob8e139afc91501bbf6c6f21f98c9117c4955fb5d5
1 /*
2 * Copyright (c) 2005 JetBrains s.r.o. All Rights Reserved.
3 */
4 package com.intellij.psi;
6 import com.intellij.lang.Language;
7 import com.intellij.lang.LanguageParserDefinitions;
8 import com.intellij.lang.ParserDefinition;
9 import com.intellij.openapi.command.undo.UndoManager;
10 import com.intellij.openapi.diagnostic.Logger;
11 import com.intellij.openapi.editor.Document;
12 import com.intellij.openapi.extensions.Extensions;
13 import com.intellij.openapi.fileEditor.FileDocumentManager;
14 import com.intellij.openapi.fileEditor.impl.LoadTextUtil;
15 import com.intellij.openapi.fileTypes.*;
16 import com.intellij.openapi.progress.ProcessCanceledException;
17 import com.intellij.openapi.project.Project;
18 import com.intellij.openapi.util.UserDataHolderBase;
19 import com.intellij.openapi.vfs.VirtualFile;
20 import com.intellij.openapi.vfs.ex.dummy.DummyFileSystem;
21 import com.intellij.openapi.vfs.ex.temp.TempFileSystem;
22 import com.intellij.psi.impl.PsiFileEx;
23 import com.intellij.psi.impl.PsiManagerEx;
24 import com.intellij.psi.impl.PsiManagerImpl;
25 import com.intellij.psi.impl.file.PsiBinaryFileImpl;
26 import com.intellij.psi.impl.file.impl.FileManagerImpl;
27 import com.intellij.psi.impl.source.PostprocessReformattingAspect;
28 import com.intellij.psi.impl.source.PsiFileImpl;
29 import com.intellij.psi.impl.source.PsiPlainTextFileImpl;
30 import com.intellij.psi.impl.source.tree.FileElement;
31 import com.intellij.testFramework.LightVirtualFile;
32 import com.intellij.util.LocalTimeCounter;
33 import com.intellij.util.ReflectionCache;
34 import org.jetbrains.annotations.NotNull;
35 import org.jetbrains.annotations.Nullable;
37 import java.lang.ref.SoftReference;
38 import java.util.Collections;
39 import java.util.List;
40 import java.util.Set;
41 import java.util.concurrent.atomic.AtomicReference;
43 public class SingleRootFileViewProvider extends UserDataHolderBase implements FileViewProvider {
44 private static final Logger LOG = Logger.getInstance("#" + SingleRootFileViewProvider.class.getCanonicalName());
45 private final PsiManager myManager;
46 private final VirtualFile myVirtualFile;
47 private final boolean myEventSystemEnabled;
48 private final boolean myPhysical;
49 private final AtomicReference<PsiFile> myPsiFile = new AtomicReference<PsiFile>();
50 private volatile Content myContent;
51 private volatile SoftReference<Document> myDocument;
52 private final Language myBaseLanguage;
54 public SingleRootFileViewProvider(@NotNull PsiManager manager, @NotNull VirtualFile file) {
55 this(manager, file, true);
58 public SingleRootFileViewProvider(@NotNull PsiManager manager, @NotNull VirtualFile virtualFile, final boolean physical) {
59 this(manager, virtualFile, physical, calcBaseLanguage(virtualFile, manager.getProject()));
62 private SingleRootFileViewProvider(@NotNull PsiManager manager, @NotNull VirtualFile virtualFile, final boolean physical, @NotNull Language language) {
63 myManager = manager;
64 myVirtualFile = virtualFile;
65 myEventSystemEnabled = physical;
66 myBaseLanguage = language;
67 setContent(new VirtualFileContent());
68 myPhysical = isEventSystemEnabled() &&
69 !(virtualFile instanceof LightVirtualFile) &&
70 !(virtualFile.getFileSystem() instanceof DummyFileSystem) &&
71 !(virtualFile.getFileSystem() instanceof TempFileSystem);
74 @NotNull
75 public Language getBaseLanguage() {
76 return myBaseLanguage;
79 private static Language calcBaseLanguage(VirtualFile file, Project project) {
80 if (file instanceof LightVirtualFile) {
81 final Language language = ((LightVirtualFile)file).getLanguage();
82 if (language != null) {
83 return language;
87 final FileType fileType = file.getFileType();
88 if (fileType.isBinary()) return Language.ANY;
89 if (isTooLarge(file)) return PlainTextLanguage.INSTANCE;
91 if (fileType instanceof LanguageFileType) {
92 return LanguageSubstitutors.INSTANCE.substituteLanguage(((LanguageFileType)fileType).getLanguage(), file, project);
95 final ContentBasedClassFileProcessor[] processors = Extensions.getExtensions(ContentBasedClassFileProcessor.EP_NAME);
96 for (ContentBasedClassFileProcessor processor : processors) {
97 Language language = processor.obtainLanguageForFile(file);
98 if (language != null) return language;
101 return PlainTextLanguage.INSTANCE;
104 @NotNull
105 public Set<Language> getLanguages() {
106 return Collections.singleton(getBaseLanguage());
109 @Nullable
110 public final PsiFile getPsi(@NotNull Language target) {
111 if (!isPhysical()) {
112 ((PsiManagerEx)myManager).getFileManager().setViewProvider(getVirtualFile(), this);
114 return getPsiInner(target);
117 @NotNull
118 public List<PsiFile> getAllFiles() {
119 return Collections.singletonList(getPsi(getBaseLanguage()));
122 @Nullable
123 protected PsiFile getPsiInner(final Language target) {
124 if (target != getBaseLanguage()) {
125 return null;
127 PsiFile psiFile = myPsiFile.get();
128 if (psiFile == null) {
129 psiFile = createFile();
130 myPsiFile.compareAndSet(null, psiFile);
131 psiFile = myPsiFile.get();
133 return psiFile;
136 public void beforeContentsSynchronized() {
137 unsetPsiContent();
140 public void contentsSynchronized() {
141 unsetPsiContent();
144 private void unsetPsiContent() {
145 if (!(myContent instanceof PsiFileContent)) return;
146 final Document cachedDocument = getCachedDocument();
147 setContent(cachedDocument == null ? new VirtualFileContent() : new DocumentContent());
150 public void beforeDocumentChanged() {
151 final PostprocessReformattingAspect component = myManager.getProject().getComponent(PostprocessReformattingAspect.class);
152 if (component.isViewProviderLocked(this)) {
153 throw new RuntimeException("Document is locked by write PSI operations. Use PsiDocumentManager.doPostponedOperationsAndUnblockDocument() to commit PSI changes to the document.");
155 component.doPostponedFormatting();
156 final PsiFileImpl psiFile = (PsiFileImpl)getCachedPsi(getBaseLanguage());
157 if (psiFile != null && psiFile.isContentsLoaded() && getContent()instanceof DocumentContent) {
158 setContent(new PsiFileContent(psiFile, getModificationStamp()));
162 public void rootChanged(PsiFile psiFile) {
163 if (((PsiFileEx)psiFile).isContentsLoaded()) {
164 setContent(new PsiFileContent((PsiFileImpl)psiFile, LocalTimeCounter.currentTime()));
168 public boolean isEventSystemEnabled() {
169 return myEventSystemEnabled;
172 public boolean isPhysical() {
173 return myPhysical;
176 public long getModificationStamp() {
177 return getContent().getModificationStamp();
180 public boolean supportsIncrementalReparse(final Language rootLanguage) {
181 return true;
185 public PsiFile getCachedPsi(Language target) {
186 return myPsiFile.get();
189 public FileElement[] getKnownTreeRoots() {
190 PsiFile psiFile = myPsiFile.get();
191 if (psiFile == null || !(psiFile instanceof PsiFileImpl)) return new FileElement[0];
192 if (((PsiFileImpl)psiFile).getTreeElement() == null) return new FileElement[0];
193 return new FileElement[]{(FileElement)psiFile.getNode()};
196 private PsiFile createFile() {
198 try {
199 final VirtualFile vFile = getVirtualFile();
200 if (vFile.isDirectory()) return null;
201 if (isIgnored()) return null;
203 final Project project = myManager.getProject();
204 if (isPhysical()) { // check directories consistency
205 final VirtualFile parent = vFile.getParent();
206 if (parent == null) return null;
207 final PsiDirectory psiDir = getManager().findDirectory(parent);
208 if (psiDir == null) return null;
211 return creatFile(project, vFile, vFile.getFileType());
213 catch (ProcessCanceledException e) {
214 throw e;
216 catch (Throwable e) {
217 LOG.error(e);
218 return null;
222 protected boolean isIgnored() {
223 final VirtualFile file = getVirtualFile();
224 if (file instanceof LightVirtualFile) return false;
225 final FileTypeManager fileTypeManager = FileTypeManager.getInstance();
226 return fileTypeManager.isFileIgnored(file.getName());
229 @Nullable
230 protected PsiFile creatFile(final Project project, final VirtualFile vFile, final FileType fileType) {
231 if (fileType.isBinary()) {
232 return new PsiBinaryFileImpl((PsiManagerImpl)getManager(), this);
235 if (!isTooLarge(vFile)) {
236 final PsiFile psiFile = createFile(getBaseLanguage());
237 if (psiFile != null) return psiFile;
240 return new PsiPlainTextFileImpl(this);
243 public static boolean isTooLarge(final VirtualFile vFile) {
244 return FileManagerImpl.MAX_INTELLISENSE_FILESIZE != -1 && fileSizeIsGreaterThan(vFile, FileManagerImpl.MAX_INTELLISENSE_FILESIZE);
247 private static boolean fileSizeIsGreaterThan(final VirtualFile vFile, final long maxInBytes) {
248 if (vFile instanceof LightVirtualFile) {
249 // This is optimization in order to avoid conversion of [large] file contents to bytes
250 final int lengthInChars = ((LightVirtualFile)vFile).getContent().length();
251 if (lengthInChars < maxInBytes / 2) return false;
252 if (lengthInChars > maxInBytes ) return true;
255 return vFile.getLength() > maxInBytes;
258 @Nullable
259 protected PsiFile createFile(Language lang) {
260 if (lang != getBaseLanguage()) return null;
261 final ParserDefinition parserDefinition = LanguageParserDefinitions.INSTANCE.forLanguage(lang);
262 if (parserDefinition != null) {
263 return parserDefinition.createFile(this);
265 return null;
268 @NotNull
269 public PsiManager getManager() {
270 return myManager;
273 @NotNull
274 public CharSequence getContents() {
275 return getContent().getText();
278 @NotNull
279 public VirtualFile getVirtualFile() {
280 return myVirtualFile;
283 @Nullable
284 private Document getCachedDocument() {
285 final Document document = myDocument != null ? myDocument.get() : null;
286 if (document != null) return document;
287 return FileDocumentManager.getInstance().getCachedDocument(getVirtualFile());
290 public Document getDocument() {
291 Document document = myDocument != null ? myDocument.get() : null;
292 if (document == null/* TODO[ik] make this change && isEventSystemEnabled()*/) {
293 document = FileDocumentManager.getInstance().getDocument(getVirtualFile());
294 myDocument = new SoftReference<Document>(document);
296 if (document != null && getContent() instanceof VirtualFileContent) {
297 setContent(new DocumentContent());
299 return document;
302 public FileViewProvider clone() {
303 final VirtualFile origFile = getVirtualFile();
304 LightVirtualFile copy = new LightVirtualFile(origFile.getName(), origFile.getFileType(), getContents(), origFile.getCharset(), getModificationStamp());
305 copy.putUserData(UndoManager.DONT_RECORD_UNDO, Boolean.TRUE);
306 copy.setCharset(origFile.getCharset());
307 return createCopy(copy);
310 public SingleRootFileViewProvider createCopy(final LightVirtualFile copy) {
311 return new SingleRootFileViewProvider(getManager(), copy, false, myBaseLanguage);
314 public PsiReference findReferenceAt(final int offset) {
315 final PsiFileImpl psiFile = (PsiFileImpl)getPsi(getBaseLanguage());
316 return findReferenceAt(psiFile, offset);
319 public PsiElement findElementAt(final int offset, final Language language) {
320 final PsiFile psiFile = getPsi(language);
321 return psiFile != null ? findElementAt(psiFile, offset) : null;
324 @Nullable
325 public PsiReference findReferenceAt(final int offset, @NotNull final Language language) {
326 final PsiFile psiFile = getPsi(language);
327 return psiFile != null ? findReferenceAt(psiFile, offset) : null;
330 public boolean isLockedByPsiOperations() {
331 final PostprocessReformattingAspect component = myManager.getProject().getComponent(PostprocessReformattingAspect.class);
332 return component.isViewProviderLocked(this);
335 @Nullable
336 private static PsiReference findReferenceAt(final PsiFile psiFile, final int offset) {
337 if (psiFile == null) return null;
338 int offsetInElement = offset;
339 PsiElement child = psiFile.getFirstChild();
340 while (child != null) {
341 final int length = child.getTextLength();
342 if (length <= offsetInElement) {
343 offsetInElement -= length;
344 child = child.getNextSibling();
345 continue;
347 return child.findReferenceAt(offsetInElement);
349 return null;
352 public PsiElement findElementAt(final int offset) {
353 return findElementAt(getPsi(getBaseLanguage()), offset);
357 public PsiElement findElementAt(int offset, Class<? extends Language> lang) {
358 if (!ReflectionCache.isAssignable(lang, getBaseLanguage().getClass())) return null;
359 return findElementAt(offset);
362 @Nullable
363 protected static PsiElement findElementAt(final PsiElement psiFile, final int offset) {
364 if (psiFile == null) return null;
365 int offsetInElement = offset;
366 PsiElement child = psiFile.getFirstChild();
367 while (child != null) {
368 final int length = child.getTextLength();
369 if (length <= offsetInElement) {
370 offsetInElement -= length;
371 child = child.getNextSibling();
372 continue;
374 return child.findElementAt(offsetInElement);
376 return null;
379 public void forceCachedPsi(final PsiFile psiFile) {
380 myPsiFile.set(psiFile);
381 ((PsiManagerEx)myManager).getFileManager().setViewProvider(getVirtualFile(), this);
384 private Content getContent() {
385 return myContent;
388 private void setContent(final Content content) {
389 myContent = content;
392 private interface Content {
393 CharSequence getText();
395 long getModificationStamp();
398 private class VirtualFileContent implements Content {
399 public CharSequence getText() {
400 final VirtualFile virtualFile = getVirtualFile();
401 if (virtualFile instanceof LightVirtualFile) {
402 Document doc = getCachedDocument();
403 if (doc != null) return doc.getCharsSequence();
404 return ((LightVirtualFile)virtualFile).getContent();
407 final Document document = getDocument();
408 if (document == null) {
409 return LoadTextUtil.loadText(virtualFile);
411 else {
412 return document.getCharsSequence();
416 public long getModificationStamp() {
417 return getVirtualFile().getModificationStamp();
421 private class DocumentContent implements Content {
422 public CharSequence getText() {
423 final Document document = getDocument();
424 assert document != null;
425 return document.getCharsSequence();
428 public long getModificationStamp() {
429 Document document = myDocument == null ? null : myDocument.get();
430 if (document != null) return document.getModificationStamp();
431 return myVirtualFile.getModificationStamp();
435 private class PsiFileContent implements Content {
436 private final PsiFileImpl myFile;
437 private CharSequence myContent = null;
438 private final long myModificationStamp;
440 private PsiFileContent(final PsiFileImpl file, final long modificationStamp) {
441 myFile = file;
442 myModificationStamp = modificationStamp;
445 public CharSequence getText() {
446 if (!myFile.isContentsLoaded()) {
447 unsetPsiContent();
448 return getContents();
450 if (myContent != null) return myContent;
451 return myContent = myFile.calcTreeElement().getText();
454 public long getModificationStamp() {
455 if (!myFile.isContentsLoaded()) {
456 unsetPsiContent();
457 return SingleRootFileViewProvider.this.getModificationStamp();
459 return myModificationStamp;