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.FileTypeManager;
23 import com.intellij.openapi.module.Module;
24 import com.intellij.openapi.module.ModuleManager;
25 import com.intellij.openapi.module.ModuleType;
26 import com.intellij.openapi.module.ModuleUtilCore;
27 import com.intellij.openapi.project.Project;
28 import com.intellij.openapi.projectRoots.Sdk;
29 import com.intellij.openapi.roots.*;
30 import com.intellij.openapi.roots.impl.FilePropertyPusher;
31 import com.intellij.openapi.roots.impl.PushedFilePropertiesUpdater;
32 import com.intellij.openapi.util.Key;
33 import com.intellij.openapi.vfs.VfsUtilCore;
34 import com.intellij.openapi.vfs.VirtualFile;
35 import com.intellij.openapi.vfs.VirtualFileVisitor;
36 import com.intellij.openapi.vfs.newvfs.FileAttribute;
37 import com.intellij.psi.SingleRootFileViewProvider;
38 import com.intellij.util.FileContentUtil;
39 import com.intellij.util.containers.WeakHashMap;
40 import com.intellij.util.io.DataInputOutputUtil;
41 import com.intellij.util.messages.MessageBus;
42 import com.jetbrains.python.PythonFileType;
43 import com.jetbrains.python.PythonModuleTypeBase;
44 import com.jetbrains.python.facet.PythonFacetSettings;
45 import com.jetbrains.python.psi.LanguageLevel;
46 import com.jetbrains.python.sdk.PythonSdkType;
47 import org.jetbrains.annotations.NotNull;
48 import org.jetbrains.annotations.Nullable;
50 import java.io.DataInputStream;
51 import java.io.DataOutputStream;
52 import java.io.IOException;
58 public class PythonLanguageLevelPusher implements FilePropertyPusher<LanguageLevel> {
59 private final Map<Module, Sdk> myModuleSdks = new WeakHashMap<Module, Sdk>();
61 public static void pushLanguageLevel(final Project project) {
62 PushedFilePropertiesUpdater.getInstance(project).pushAll(new PythonLanguageLevelPusher());
65 public void initExtra(@NotNull Project project, @NotNull MessageBus bus, @NotNull Engine languageLevelUpdater) {
66 final Module[] modules = ModuleManager.getInstance(project).getModules();
67 Set<Sdk> usedSdks = new HashSet<Sdk>();
68 for (Module module : modules) {
69 if (isPythonModule(module)) {
70 final Sdk sdk = PythonSdkType.findPythonSdk(module);
71 myModuleSdks.put(module, sdk);
72 if (sdk != null && !usedSdks.contains(sdk)) {
74 updateSdkLanguageLevel(project, sdk);
81 public Key<LanguageLevel> getFileDataKey() {
82 return LanguageLevel.KEY;
85 public boolean pushDirectoriesOnly() {
90 public LanguageLevel getDefaultValue() {
91 return LanguageLevel.getDefault();
94 public LanguageLevel getImmediateValue(@NotNull Project project, @Nullable VirtualFile file) {
95 return getFileLanguageLevel(project, file);
98 public static LanguageLevel getFileLanguageLevel(Project project, VirtualFile file) {
99 if (ApplicationManager.getApplication().isUnitTestMode() && LanguageLevel.FORCE_LANGUAGE_LEVEL != null) {
100 return LanguageLevel.FORCE_LANGUAGE_LEVEL;
102 if (file == null) return null;
104 final Module module = ModuleUtilCore.findModuleForFile(file, project);
105 if (module != null) {
106 final Sdk sdk = ModuleRootManager.getInstance(module).getSdk();
107 return PythonSdkType.getLanguageLevelForSdk(sdk);
109 final Sdk sdk = findSdk(project, file);
111 return PythonSdkType.getLanguageLevelForSdk(sdk);
117 private static Sdk findSdk(Project project, VirtualFile file) {
119 final List<OrderEntry> orderEntries = ProjectRootManager.getInstance(project).getFileIndex().getOrderEntriesForFile(file);
120 for (OrderEntry orderEntry : orderEntries) {
121 if (orderEntry instanceof JdkOrderEntry) {
122 return ((JdkOrderEntry)orderEntry).getJdk();
129 public LanguageLevel getImmediateValue(@NotNull Module module) {
130 if (ApplicationManager.getApplication().isUnitTestMode() && LanguageLevel.FORCE_LANGUAGE_LEVEL != null) {
131 return LanguageLevel.FORCE_LANGUAGE_LEVEL;
134 final Sdk sdk = ModuleRootManager.getInstance(module).getSdk();
135 return PythonSdkType.getLanguageLevelForSdk(sdk);
138 public boolean acceptsFile(@NotNull VirtualFile file) {
143 public boolean acceptsDirectory(@NotNull VirtualFile file, @NotNull Project project) {
147 private static final FileAttribute PERSISTENCE = new FileAttribute("python_language_level_persistence", 2, true);
149 public void persistAttribute(@NotNull Project project, @NotNull VirtualFile fileOrDir, @NotNull LanguageLevel level) throws IOException {
150 final DataInputStream iStream = PERSISTENCE.readAttribute(fileOrDir);
151 if (iStream != null) {
153 final int oldLevelOrdinal = DataInputOutputUtil.readINT(iStream);
154 if (oldLevelOrdinal == level.ordinal()) return;
161 final DataOutputStream oStream = PERSISTENCE.writeAttribute(fileOrDir);
162 DataInputOutputUtil.writeINT(oStream, level.ordinal());
165 for (VirtualFile child : fileOrDir.getChildren()) {
166 if (!child.isDirectory() && PythonFileType.INSTANCE.equals(child.getFileType())) {
167 PushedFilePropertiesUpdater.getInstance(project).filePropertiesChanged(child);
172 public void afterRootsChanged(@NotNull final Project project) {
173 Set<Sdk> updatedSdks = new HashSet<Sdk>();
174 final Module[] modules = ModuleManager.getInstance(project).getModules();
175 boolean needReparseOpenFiles = false;
176 for (Module module : modules) {
177 if (isPythonModule(module)) {
178 Sdk newSdk = PythonSdkType.findPythonSdk(module);
179 if (myModuleSdks.containsKey(module)) {
180 Sdk oldSdk = myModuleSdks.get(module);
181 if ((newSdk != null || oldSdk != null) && newSdk != oldSdk) {
182 needReparseOpenFiles = true;
185 myModuleSdks.put(module, newSdk);
186 if (newSdk != null && !updatedSdks.contains(newSdk)) {
187 updatedSdks.add(newSdk);
188 updateSdkLanguageLevel(project, newSdk);
192 if (needReparseOpenFiles) {
193 FileContentUtil.reparseFiles(project, Collections.<VirtualFile>emptyList(), true);
197 private static boolean isPythonModule(@NotNull final Module module) {
198 final ModuleType moduleType = ModuleType.get(module);
199 if (moduleType instanceof PythonModuleTypeBase) return true;
200 final Facet[] allFacets = FacetManager.getInstance(module).getAllFacets();
201 for (Facet facet : allFacets) {
202 if (facet.getConfiguration() instanceof PythonFacetSettings) {
209 private void updateSdkLanguageLevel(final Project project, final Sdk sdk) {
210 final LanguageLevel languageLevel = PythonSdkType.getLanguageLevelForSdk(sdk);
211 final VirtualFile[] files = sdk.getRootProvider().getFiles(OrderRootType.CLASSES);
212 final Application application = ApplicationManager.getApplication();
213 application.executeOnPooledThread(new Runnable() {
216 application.runReadAction(new Runnable() {
219 if (project != null && project.isDisposed()) {
222 for (VirtualFile file : files) {
223 if (file.isValid()) {
224 VirtualFile parent = file.getParent();
225 boolean suppressSizeLimit = false;
226 if (parent != null && parent.getName().equals(PythonSdkType.SKELETON_DIR_NAME)) {
227 suppressSizeLimit = true;
229 markRecursively(project, file, languageLevel, suppressSizeLimit);
238 private void markRecursively(final Project project,
239 @NotNull final VirtualFile file,
240 final LanguageLevel languageLevel,
241 final boolean suppressSizeLimit) {
242 final FileTypeManager fileTypeManager = FileTypeManager.getInstance();
243 VfsUtilCore.visitChildrenRecursively(file, new VirtualFileVisitor() {
245 public boolean visitFile(@NotNull VirtualFile file) {
246 if (fileTypeManager.isFileIgnored(file)) {
249 if (file.isDirectory()) {
250 PushedFilePropertiesUpdater.getInstance(project).findAndUpdateValue(file, PythonLanguageLevelPusher.this, languageLevel);
252 if (suppressSizeLimit) {
253 SingleRootFileViewProvider.doNotCheckFileSizeLimit(file);
260 public static void setForcedLanguageLevel(final Project project, @Nullable LanguageLevel languageLevel) {
261 LanguageLevel.FORCE_LANGUAGE_LEVEL = languageLevel;
262 pushLanguageLevel(project);
265 public void flushLanguageLevelCache() {
266 myModuleSdks.clear();