cancel reparsing if psi is reloaded anyway (e.g. FilePropertyPusher reloads psi)...
[idea/community.git] / platform / core-api / src / com / intellij / psi / LanguageSubstitutors.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.psi;
17
18 import com.intellij.injected.editor.VirtualFileWindow;
19 import com.intellij.lang.Language;
20 import com.intellij.lang.LanguageExtension;
21 import com.intellij.openapi.application.ApplicationManager;
22 import com.intellij.openapi.application.ModalityState;
23 import com.intellij.openapi.diagnostic.Logger;
24 import com.intellij.openapi.project.Project;
25 import com.intellij.openapi.util.Key;
26 import com.intellij.openapi.vfs.VirtualFile;
27 import com.intellij.util.FileContentUtilCore;
28 import com.intellij.util.ObjectUtils;
29 import org.jetbrains.annotations.NotNull;
30
31 /**
32  * @author peter
33  */
34 public final class LanguageSubstitutors extends LanguageExtension<LanguageSubstitutor> {
35   public static final LanguageSubstitutors INSTANCE = new LanguageSubstitutors();
36   private static final Logger LOG = Logger.getInstance(LanguageSubstitutors.class);
37   private static final Key<Language> SUBSTITUTED_LANG_KEY = Key.create("SUBSTITUTED_LANG_KEY");
38   private static final Key<Boolean> REPARSING_SCHEDULED = Key.create("REPARSING_SCHEDULED");
39
40   private LanguageSubstitutors() {
41     super("com.intellij.lang.substitutor");
42   }
43
44   @NotNull
45   public Language substituteLanguage(@NotNull Language lang, @NotNull VirtualFile file, @NotNull Project project) {
46     for (LanguageSubstitutor substitutor : forKey(lang)) {
47       Language language = substitutor.getLanguage(file, project);
48       if (language != null) {
49         processLanguageSubstitution(file, lang, language);
50         return language;
51       }
52     }
53     return lang;
54   }
55
56
57   private static void processLanguageSubstitution(@NotNull final VirtualFile file,
58                                                   @NotNull Language originalLang,
59                                                   @NotNull final Language substitutedLang) {
60     if (file instanceof VirtualFileWindow) {
61       // Injected files are created with substituted language, no need to reparse:
62       //   com.intellij.psi.impl.source.tree.injected.MultiHostRegistrarImpl#doneInjecting
63       return;
64     }
65     Language prevSubstitutedLang = SUBSTITUTED_LANG_KEY.get(file);
66     final Language prevLang = ObjectUtils.notNull(prevSubstitutedLang, originalLang);
67     if (!prevLang.is(substitutedLang)) {
68       if (file.replace(SUBSTITUTED_LANG_KEY, prevSubstitutedLang, substitutedLang)) {
69         if (prevSubstitutedLang == null) {
70           return; // no need to reparse for the first language substitution
71         }
72         if (ApplicationManager.getApplication().isUnitTestMode()) {
73           return;
74         }
75         file.putUserData(REPARSING_SCHEDULED, true);
76         ApplicationManager.getApplication().invokeLater(new Runnable() {
77           @Override
78           public void run() {
79             if (file.replace(REPARSING_SCHEDULED, true, null)) {
80               LOG.info("Reparsing " + file.getPath() + " because of language substitution " +
81                        prevLang.getID() + "->" + substitutedLang.getID());
82               FileContentUtilCore.reparseFiles(file);
83             }
84           }
85         }, ModalityState.defaultModalityState());
86       }
87     }
88   }
89
90   public static void cancelReparsing(@NotNull VirtualFile file) {
91     REPARSING_SCHEDULED.set(file, null);
92   }
93 }