2 * Copyright 2000-2013 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.jetbrains.python.psi.impl;
18 import com.intellij.facet.Facet;
19 import com.intellij.facet.FacetManager;
20 import com.intellij.openapi.application.Application;
21 import com.intellij.openapi.application.ApplicationManager;
22 import com.intellij.openapi.fileTypes.FileType;
23 import com.intellij.openapi.fileTypes.FileTypeManager;
24 import com.intellij.openapi.fileTypes.FileTypeRegistry;
25 import com.intellij.openapi.module.Module;
26 import com.intellij.openapi.module.ModuleManager;
27 import com.intellij.openapi.module.ModuleType;
28 import com.intellij.openapi.module.ModuleUtilCore;
29 import com.intellij.openapi.project.Project;
30 import com.intellij.openapi.projectRoots.Sdk;
31 import com.intellij.openapi.roots.*;
32 import com.intellij.openapi.roots.impl.FilePropertyPusher;
33 import com.intellij.openapi.roots.impl.PushedFilePropertiesUpdater;
34 import com.intellij.openapi.util.Key;
35 import com.intellij.openapi.vfs.VfsUtilCore;
36 import com.intellij.openapi.vfs.VirtualFile;
37 import com.intellij.openapi.vfs.VirtualFileVisitor;
38 import com.intellij.openapi.vfs.newvfs.FileAttribute;
39 import com.intellij.psi.SingleRootFileViewProvider;
40 import com.intellij.util.FileContentUtil;
41 import com.intellij.util.containers.WeakHashMap;
42 import com.intellij.util.io.DataInputOutputUtil;
43 import com.intellij.util.messages.MessageBus;
44 import com.jetbrains.python.PythonFileType;
45 import com.jetbrains.python.PythonModuleTypeBase;
46 import com.jetbrains.python.facet.PythonFacetSettings;
47 import com.jetbrains.python.psi.LanguageLevel;
48 import com.jetbrains.python.sdk.PythonSdkType;
49 import org.jetbrains.annotations.NotNull;
50 import org.jetbrains.annotations.Nullable;
52 import java.io.DataInputStream;
53 import java.io.DataOutputStream;
54 import java.io.IOException;
60 public class PythonLanguageLevelPusher implements FilePropertyPusher<LanguageLevel> {
61 private final Map<Module, Sdk> myModuleSdks = new WeakHashMap<Module, Sdk>();
63 public static void pushLanguageLevel(final Project project) {
64 PushedFilePropertiesUpdater.getInstance(project).pushAll(new PythonLanguageLevelPusher());
67 public void initExtra(@NotNull Project project, @NotNull MessageBus bus, @NotNull Engine languageLevelUpdater) {
68 final Module[] modules = ModuleManager.getInstance(project).getModules();
69 Set<Sdk> usedSdks = new HashSet<Sdk>();
70 for (Module module : modules) {
71 if (isPythonModule(module)) {
72 final Sdk sdk = PythonSdkType.findPythonSdk(module);
73 myModuleSdks.put(module, sdk);
74 if (sdk != null && !usedSdks.contains(sdk)) {
76 updateSdkLanguageLevel(project, sdk);
83 public Key<LanguageLevel> getFileDataKey() {
84 return LanguageLevel.KEY;
87 public boolean pushDirectoriesOnly() {
92 public LanguageLevel getDefaultValue() {
93 return LanguageLevel.getDefault();
96 public LanguageLevel getImmediateValue(@NotNull Project project, @Nullable VirtualFile file) {
97 return getFileLanguageLevel(project, file);
100 public static LanguageLevel getFileLanguageLevel(Project project, VirtualFile file) {
101 if (ApplicationManager.getApplication().isUnitTestMode() && LanguageLevel.FORCE_LANGUAGE_LEVEL != null) {
102 return LanguageLevel.FORCE_LANGUAGE_LEVEL;
104 if (file == null) return null;
106 final Module module = ModuleUtilCore.findModuleForFile(file, project);
107 if (module != null) {
108 final Sdk sdk = ModuleRootManager.getInstance(module).getSdk();
109 return PythonSdkType.getLanguageLevelForSdk(sdk);
111 final Sdk sdk = findSdk(project, file);
113 return PythonSdkType.getLanguageLevelForSdk(sdk);
119 private static Sdk findSdk(Project project, VirtualFile file) {
121 final List<OrderEntry> orderEntries = ProjectRootManager.getInstance(project).getFileIndex().getOrderEntriesForFile(file);
122 for (OrderEntry orderEntry : orderEntries) {
123 if (orderEntry instanceof JdkOrderEntry) {
124 return ((JdkOrderEntry)orderEntry).getJdk();
131 public LanguageLevel getImmediateValue(@NotNull Module module) {
132 if (ApplicationManager.getApplication().isUnitTestMode() && LanguageLevel.FORCE_LANGUAGE_LEVEL != null) {
133 return LanguageLevel.FORCE_LANGUAGE_LEVEL;
136 final Sdk sdk = ModuleRootManager.getInstance(module).getSdk();
137 return PythonSdkType.getLanguageLevelForSdk(sdk);
140 public boolean acceptsFile(@NotNull VirtualFile file) {
145 public boolean acceptsDirectory(@NotNull VirtualFile file, @NotNull Project project) {
149 private static final FileAttribute PERSISTENCE = new FileAttribute("python_language_level_persistence", 2, true);
151 public void persistAttribute(@NotNull Project project, @NotNull VirtualFile fileOrDir, @NotNull LanguageLevel level) throws IOException {
152 final DataInputStream iStream = PERSISTENCE.readAttribute(fileOrDir);
153 if (iStream != null) {
155 final int oldLevelOrdinal = DataInputOutputUtil.readINT(iStream);
156 if (oldLevelOrdinal == level.ordinal()) return;
163 final DataOutputStream oStream = PERSISTENCE.writeAttribute(fileOrDir);
164 DataInputOutputUtil.writeINT(oStream, level.ordinal());
167 for (VirtualFile child : fileOrDir.getChildren()) {
168 final FileType fileType = FileTypeRegistry.getInstance().getFileTypeByFileName(child.getName());
169 if (!child.isDirectory() && PythonFileType.INSTANCE.equals(fileType)) {
170 PushedFilePropertiesUpdater.getInstance(project).filePropertiesChanged(child);
175 public void afterRootsChanged(@NotNull final Project project) {
176 Set<Sdk> updatedSdks = new HashSet<Sdk>();
177 final Module[] modules = ModuleManager.getInstance(project).getModules();
178 boolean needReparseOpenFiles = false;
179 for (Module module : modules) {
180 if (isPythonModule(module)) {
181 Sdk newSdk = PythonSdkType.findPythonSdk(module);
182 if (myModuleSdks.containsKey(module)) {
183 Sdk oldSdk = myModuleSdks.get(module);
184 if ((newSdk != null || oldSdk != null) && newSdk != oldSdk) {
185 needReparseOpenFiles = true;
188 myModuleSdks.put(module, newSdk);
189 if (newSdk != null && !updatedSdks.contains(newSdk)) {
190 updatedSdks.add(newSdk);
191 updateSdkLanguageLevel(project, newSdk);
195 if (needReparseOpenFiles) {
196 FileContentUtil.reparseFiles(project, Collections.<VirtualFile>emptyList(), true);
200 private static boolean isPythonModule(@NotNull final Module module) {
201 final ModuleType moduleType = ModuleType.get(module);
202 if (moduleType instanceof PythonModuleTypeBase) return true;
203 final Facet[] allFacets = FacetManager.getInstance(module).getAllFacets();
204 for (Facet facet : allFacets) {
205 if (facet.getConfiguration() instanceof PythonFacetSettings) {
212 private void updateSdkLanguageLevel(final Project project, final Sdk sdk) {
213 final LanguageLevel languageLevel = PythonSdkType.getLanguageLevelForSdk(sdk);
214 final VirtualFile[] files = sdk.getRootProvider().getFiles(OrderRootType.CLASSES);
215 final Application application = ApplicationManager.getApplication();
216 application.executeOnPooledThread(new Runnable() {
219 application.runReadAction(new Runnable() {
222 if (project != null && project.isDisposed()) {
225 for (VirtualFile file : files) {
226 if (file.isValid()) {
227 VirtualFile parent = file.getParent();
228 boolean suppressSizeLimit = false;
229 if (parent != null && parent.getName().equals(PythonSdkType.SKELETON_DIR_NAME)) {
230 suppressSizeLimit = true;
232 markRecursively(project, file, languageLevel, suppressSizeLimit);
241 private void markRecursively(final Project project,
242 @NotNull final VirtualFile file,
243 final LanguageLevel languageLevel,
244 final boolean suppressSizeLimit) {
245 final FileTypeManager fileTypeManager = FileTypeManager.getInstance();
246 VfsUtilCore.visitChildrenRecursively(file, new VirtualFileVisitor() {
248 public boolean visitFile(@NotNull VirtualFile file) {
249 if (fileTypeManager.isFileIgnored(file)) {
252 if (file.isDirectory()) {
253 PushedFilePropertiesUpdater.getInstance(project).findAndUpdateValue(file, PythonLanguageLevelPusher.this, languageLevel);
255 if (suppressSizeLimit) {
256 SingleRootFileViewProvider.doNotCheckFileSizeLimit(file);
263 public static void setForcedLanguageLevel(final Project project, @Nullable LanguageLevel languageLevel) {
264 LanguageLevel.FORCE_LANGUAGE_LEVEL = languageLevel;
265 pushLanguageLevel(project);
268 public void flushLanguageLevelCache() {
269 myModuleSdks.clear();