diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java index 6f6208583b93..b1f30cf4e2e5 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java @@ -97,6 +97,8 @@ import java.util.concurrent.TimeUnit; public class CompileDriver { private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.CompileDriver"); + // to be used in tests only for debug output + public static boolean ourDebugMode = false; private final Project myProject; private final Map, Pair> myGenerationCompilerModuleToOutputDirMap; // [IntermediateOutputCompiler, Module] -> [ProductionSources, TestSources] @@ -1558,12 +1560,20 @@ public class CompileDriver { if (toDelete.isEmpty() && toCompile.isEmpty()) { return false; } - if (LOG.isDebugEnabled()) { + if (LOG.isDebugEnabled() || ourDebugMode) { if (!toDelete.isEmpty()) { - LOG.debug("Found items to delete, compiler " + compiler.getDescription()); + final String message = "Found items to delete, compiler " + compiler.getDescription(); + LOG.debug(message); + if (ourDebugMode) { + System.out.println(message); + } } if (!toCompile.isEmpty()) { - LOG.debug("Found items to compile, compiler " + compiler.getDescription()); + final String message = "Found items to compile, compiler " + compiler.getDescription(); + LOG.debug(message); + if (ourDebugMode) { + System.out.println(message); + } } } throw new ExitException(ExitStatus.CANCELLED); diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/TranslatingCompilerFilesMonitor.java b/java/compiler/impl/src/com/intellij/compiler/impl/TranslatingCompilerFilesMonitor.java index f8d1381f2188..76de9729c7e7 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/TranslatingCompilerFilesMonitor.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/TranslatingCompilerFilesMonitor.java @@ -78,6 +78,7 @@ import java.util.*; public class TranslatingCompilerFilesMonitor implements ApplicationComponent { private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.TranslatingCompilerFilesMonitor"); + public static boolean ourDebugMode = false; @NonNls private static final String PATHS_TO_DELETE_FILENAME = "paths_to_delete.dat"; private static final String OUTPUT_ROOTS_FILENAME = "output_roots.dat"; @@ -163,8 +164,12 @@ public class TranslatingCompilerFilesMonitor implements ApplicationComponent { while (scopeSrcIterator.hasNext()) { final VirtualFile file = scopeSrcIterator.next(); if (!file.isValid()) { - if (LOG.isDebugEnabled()) { - LOG.debug("Skipping invalid file " + file.getPresentableUrl()); + if (LOG.isDebugEnabled() || ourDebugMode) { + final String message = "Skipping invalid file " + file.getPresentableUrl(); + LOG.debug(message); + if (ourDebugMode) { + System.out.println(message); + } } continue; } @@ -212,11 +217,22 @@ public class TranslatingCompilerFilesMonitor implements ApplicationComponent { //noinspection UnnecessaryBoxing final File file = new File(outputPath); toDelete.add(new Trinity(file, classNamePair.getClassName(), Boolean.valueOf(sourcePresent))); - if (LOG.isDebugEnabled()) { - LOG.debug("Found file to delete: " + file); + if (LOG.isDebugEnabled() || ourDebugMode) { + final String message = "Found file to delete: " + file; + LOG.debug(message); + if (ourDebugMode) { + System.out.println(message); + } } } else { + if (LOG.isDebugEnabled() || ourDebugMode) { + final String message = "Found zombie entry marked for deletion: " + outputPath; + LOG.debug(message); + if (ourDebugMode) { + System.out.println(message); + } + } // must be gagbage entry, should cleanup zombieEntries.add(outputPath); } @@ -301,8 +317,12 @@ public class TranslatingCompilerFilesMonitor implements ApplicationComponent { final long fileStamp = file.getTimeStamp(); info.updateTimestamp(projectId, fileStamp); saveSourceInfo(file, info); - if (LOG.isDebugEnabled()) { - LOG.debug("Unschedule recompilation (successfully compiled) " + file.getPresentableUrl()); + if (LOG.isDebugEnabled() || ourDebugMode) { + final String message = "Unschedule recompilation (successfully compiled) " + file.getPresentableUrl(); + LOG.debug(message); + if (ourDebugMode) { + System.out.println(message); + } } removeSourceForRecompilation(projectId, Math.abs(getFileId(file))); if ((fileStamp > compilationStartStamp && !((CompileContextEx)context).isGenerated(file)) || forceRecompile.contains(file)) { @@ -365,8 +385,12 @@ public class TranslatingCompilerFilesMonitor implements ApplicationComponent { final String outputPath = FileUtil.toSystemIndependentName(CompilerIOUtil.readString(is)); final String srcUrl = CompilerIOUtil.readString(is); final String className = CompilerIOUtil.readString(is); - if (LOG.isDebugEnabled()) { - LOG.debug("INIT path to delete: " + outputPath); + if (LOG.isDebugEnabled() || ourDebugMode) { + final String message = "INIT path to delete: " + outputPath; + LOG.debug(message); + if (ourDebugMode) { + System.out.println(message); + } } map.put(outputPath, new SourceUrlClassNamePair(srcUrl, className)); } @@ -1136,8 +1160,12 @@ public class TranslatingCompilerFilesMonitor implements ApplicationComponent { public void beforeFileDeletion(final VirtualFileEvent event) { final VirtualFile eventFile = event.getFile(); - if (LOG.isDebugEnabled() && eventFile.isDirectory()) { - LOG.debug("Processing file deletion: " + eventFile.getPresentableUrl()); + if ((LOG.isDebugEnabled() && eventFile.isDirectory()) || ourDebugMode) { + final String message = "Processing file deletion: " + eventFile.getPresentableUrl(); + LOG.debug(message); + if (ourDebugMode) { + System.out.println(message); + } } processRecursively(eventFile, true, new FileProcessor() { public void execute(final VirtualFile file) { @@ -1167,8 +1195,12 @@ public class TranslatingCompilerFilesMonitor implements ApplicationComponent { for (int projectId : projects.toArray()) { // mark associated outputs for deletion srcInfo.processOutputPaths(projectId, deletionProc); - if (LOG.isDebugEnabled()) { - LOG.debug("Unschedule recompilation because of deletion " + file.getPresentableUrl()); + if (LOG.isDebugEnabled() || ourDebugMode) { + final String message = "Unschedule recompilation because of deletion " + file.getPresentableUrl(); + LOG.debug(message); + if (ourDebugMode) { + System.out.println(message); + } } removeSourceForRecompilation(projectId, Math.abs(getFileId(file))); } @@ -1269,8 +1301,12 @@ public class TranslatingCompilerFilesMonitor implements ApplicationComponent { mySourcesToRecompile.put(projectId, set); } alreadyMarked = !set.add(Math.abs(getFileId(srcFile))); - if (!alreadyMarked && LOG.isDebugEnabled()) { - LOG.debug("Scheduled recompilation " + srcFile.getPresentableUrl()); + if (!alreadyMarked && (LOG.isDebugEnabled() || ourDebugMode)) { + final String message = "Scheduled recompilation " + srcFile.getPresentableUrl(); + LOG.debug(message); + if (ourDebugMode) { + System.out.println(message); + } } } @@ -1336,8 +1372,12 @@ public class TranslatingCompilerFilesMonitor implements ApplicationComponent { myOutputsToDelete.put(projectId, map); } map.put(outputPath, new SourceUrlClassNamePair(srcUrl, classname)); - if (LOG.isDebugEnabled()) { - LOG.debug("ADD path to delete: " + outputPath + "; source: " + srcUrl); + if (LOG.isDebugEnabled() || ourDebugMode) { + final String message = "ADD path to delete: " + outputPath + "; source: " + srcUrl; + LOG.debug(message); + if (ourDebugMode) { + System.out.println(message); + } } } } @@ -1349,8 +1389,12 @@ public class TranslatingCompilerFilesMonitor implements ApplicationComponent { if (map != null) { final SourceUrlClassNamePair val = map.remove(outputPath); if (val != null) { - if (LOG.isDebugEnabled()) { - LOG.debug("REMOVE path to delete: " + outputPath); + if (LOG.isDebugEnabled() || ourDebugMode) { + final String message = "REMOVE path to delete: " + outputPath; + LOG.debug(message); + if (ourDebugMode) { + System.out.println(message); + } } if (map.isEmpty()) { myOutputsToDelete.remove(projectId); diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethod/ExtractMethodProcessor.java b/java/java-impl/src/com/intellij/refactoring/extractMethod/ExtractMethodProcessor.java index d5a4858d5c2b..1ec3ceb5e20d 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractMethod/ExtractMethodProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/extractMethod/ExtractMethodProcessor.java @@ -1081,7 +1081,11 @@ public class ExtractMethodProcessor implements MatchProvider { } if (success) { myTargetClass = newTargetClass; - inputVariables.addAll(array); + for (PsiVariable variable : array) { + if (!inputVariables.contains(variable)) { + inputVariables.addAll(array); + } + } myNeedChangeContext = true; } } diff --git a/java/java-impl/src/com/intellij/refactoring/inline/InlineToAnonymousClassHandler.java b/java/java-impl/src/com/intellij/refactoring/inline/InlineToAnonymousClassHandler.java index 5f10465e8a32..b9d681d4b408 100644 --- a/java/java-impl/src/com/intellij/refactoring/inline/InlineToAnonymousClassHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/inline/InlineToAnonymousClassHandler.java @@ -18,6 +18,7 @@ package com.intellij.refactoring.inline; import com.intellij.codeInsight.TargetElementUtilBase; import com.intellij.lang.StdLanguages; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Ref; import com.intellij.patterns.ElementPattern; @@ -34,6 +35,7 @@ import com.intellij.util.ArrayUtil; import com.intellij.util.Processor; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; import java.util.Collection; /** @@ -50,17 +52,29 @@ public class InlineToAnonymousClassHandler extends JavaInlineActionHandler { return element instanceof PsiMethod || element instanceof PsiClass; } - public boolean canInlineElement(PsiElement element) { + public boolean canInlineElement(final PsiElement element) { if (element.getLanguage() != StdLanguages.JAVA) return false; if (element instanceof PsiMethod) { PsiMethod method = (PsiMethod)element; if (method.isConstructor() && !InlineMethodHandler.isChainingConstructor(method)) { final PsiClass containingClass = method.getContainingClass(); - return containingClass != null && ClassInheritorsSearch.search(containingClass).findAll().size() == 0; + if (containingClass == null) return false; + return findClassInheritors(containingClass); } } if (!(element instanceof PsiClass)) return false; - Collection inheritors = ClassInheritorsSearch.search((PsiClass)element).findAll(); + if (element instanceof PsiAnonymousClass) return false; + return findClassInheritors((PsiClass)element); + } + + private static boolean findClassInheritors(final PsiClass element) { + final Collection inheritors = new ArrayList(); + if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable(){ + @Override + public void run() { + inheritors.addAll(ClassInheritorsSearch.search(element).findAll()); + } + }, "Searching for class \"" + element.getQualifiedName() + "\" inheritors ...", true, element.getProject())) return false; return inheritors.size() == 0; } @@ -78,9 +92,15 @@ public class InlineToAnonymousClassHandler extends JavaInlineActionHandler { return; } - String errorMessage = getCannotInlineMessage(psiClass); - if (errorMessage != null) { - CommonRefactoringUtil.showErrorHint(project, editor, errorMessage, RefactoringBundle.message("inline.to.anonymous.refactoring"), null); + final Ref errorMessage = new Ref(); + if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable(){ + @Override + public void run() { + errorMessage.set(getCannotInlineMessage(psiClass)); + } + }, "Check if inline is possible...", true, project)) return; + if (errorMessage.get() != null) { + CommonRefactoringUtil.showErrorHint(project, editor, errorMessage.get(), RefactoringBundle.message("inline.to.anonymous.refactoring"), null); return; } diff --git a/java/java-impl/src/com/intellij/refactoring/memberPullUp/JavaPullUpHandler.java b/java/java-impl/src/com/intellij/refactoring/memberPullUp/JavaPullUpHandler.java index 55e137a4d9b9..bd2e176dd899 100644 --- a/java/java-impl/src/com/intellij/refactoring/memberPullUp/JavaPullUpHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/memberPullUp/JavaPullUpHandler.java @@ -29,6 +29,7 @@ import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.ScrollType; +import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.psi.*; @@ -142,11 +143,17 @@ public class JavaPullUpHandler implements RefactoringActionHandler, PullUpDialog - public boolean checkConflicts(PullUpDialog dialog) { + public boolean checkConflicts(final PullUpDialog dialog) { final MemberInfo[] infos = dialog.getSelectedMemberInfos(); - PsiClass superClass = dialog.getSuperClass(); + final PsiClass superClass = dialog.getSuperClass(); if (!checkWritable(superClass, infos)) return false; - MultiMap conflicts = PullUpConflictsUtil.checkConflicts(infos, mySubclass, superClass, null, null, dialog.getContainmentVerifier()); + final MultiMap conflicts = new MultiMap(); + if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() { + public void run() { + conflicts + .putAllValues(PullUpConflictsUtil.checkConflicts(infos, mySubclass, superClass, null, null, dialog.getContainmentVerifier())); + } + }, "Detecting possible conflicts...", true, myProject)) return false; if (!conflicts.isEmpty()) { ConflictsDialog conflictsDialog = new ConflictsDialog(myProject, conflicts); conflictsDialog.show(); diff --git a/java/java-tests/testData/refactoring/extractMethod/ParametersFromAnonymous.java b/java/java-tests/testData/refactoring/extractMethod/ParametersFromAnonymous.java new file mode 100644 index 000000000000..fc9dc3af5e2d --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/ParametersFromAnonymous.java @@ -0,0 +1,12 @@ +class Test { + void f() { + new Runnable() { + public void run() { + int j = 0; + if (j == 0 && j < 0 && j > 0) { + assert false; + } + } + }; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/extractMethod/ParametersFromAnonymous_after.java b/java/java-tests/testData/refactoring/extractMethod/ParametersFromAnonymous_after.java new file mode 100644 index 000000000000..8768dbc565af --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/ParametersFromAnonymous_after.java @@ -0,0 +1,16 @@ +class Test { + void f() { + new Runnable() { + public void run() { + int j = 0; + if (j == 0 && newMethod(j)) { + assert false; + } + } + }; + } + + private boolean newMethod(int j) { + return j < 0 && j > 0; + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/refactoring/ExtractMethodTest.java b/java/java-tests/testSrc/com/intellij/refactoring/ExtractMethodTest.java index 873f5b27ffd3..c1dfde6cee68 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/ExtractMethodTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/ExtractMethodTest.java @@ -9,10 +9,13 @@ import com.intellij.openapi.projectRoots.impl.JavaSdkImpl; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiExpression; import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiType; import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.codeStyle.CodeStyleSettingsManager; import com.intellij.refactoring.extractMethod.ExtractMethodProcessor; import com.intellij.refactoring.extractMethod.PrepareFailedException; +import com.intellij.refactoring.introduceVariable.IntroduceVariableBase; +import com.intellij.refactoring.util.RefactoringUtil; import com.intellij.refactoring.util.duplicates.Match; import com.intellij.testFramework.LightCodeInsightTestCase; import com.intellij.util.IncorrectOperationException; @@ -448,6 +451,10 @@ public class ExtractMethodTest extends LightCodeInsightTestCase { doDuplicatesTest(); } + public void testParametersFromAnonymous() throws Exception { + doTest(); + } + private void doPrepareErrorTest(final String expectedMessage) throws Exception { String expectedError = null; try { @@ -503,6 +510,12 @@ public class ExtractMethodTest extends LightCodeInsightTestCase { else { elements = CodeInsightUtil.findStatementsInRange(file, startOffset, endOffset); } + if (elements.length == 0) { + final PsiExpression expression = IntroduceVariableBase.getSelectedExpression(project, file, startOffset, endOffset); + if (expression != null) { + elements = new PsiElement[]{expression}; + } + } assertTrue(elements.length > 0); final ExtractMethodProcessor processor = diff --git a/platform/lang-api/src/com/intellij/ui/FileColorManager.java b/platform/lang-api/src/com/intellij/ui/FileColorManager.java index ed1cffff61da..3a770e21f712 100644 --- a/platform/lang-api/src/com/intellij/ui/FileColorManager.java +++ b/platform/lang-api/src/com/intellij/ui/FileColorManager.java @@ -30,6 +30,7 @@ import java.util.Collection; * @author spleaner */ public abstract class FileColorManager { + public static final String OUT_OF_PROJECT_SCOPE_COLOR = "OUT_OF_PROJECT_SCOPE"; public static FileColorManager getInstance(@NotNull final Project project) { return ServiceManager.getService(project, FileColorManager.class); @@ -41,6 +42,8 @@ public abstract class FileColorManager { public abstract boolean isEnabledForTabs(); + public abstract boolean isHighlightNonProjectFiles(); + @SuppressWarnings({"MethodMayBeStatic"}) @Nullable public abstract Color getColor(@NotNull String name); diff --git a/platform/lang-impl/src/com/intellij/execution/ExecutionHelper.java b/platform/lang-impl/src/com/intellij/execution/ExecutionHelper.java index 2afdc436b3b7..174de671f389 100644 --- a/platform/lang-impl/src/com/intellij/execution/ExecutionHelper.java +++ b/platform/lang-impl/src/com/intellij/execution/ExecutionHelper.java @@ -150,7 +150,7 @@ public class ExecutionHelper { public static class RailsErrorViewPanel extends NewErrorTreeViewPanel { public RailsErrorViewPanel(final Project project) { - super(project, null); + super(project, "reference.toolWindows.messages"); } protected boolean canHideWarnings() { @@ -270,54 +270,27 @@ public class ExecutionHelper { final int timeout) { return new Runnable() { private final Semaphore mySemaphore = new Semaphore(); - private final Object LOCK = new Object(); - private Boolean processedFinished = Boolean.FALSE; private final Runnable myProcessThread = new Runnable() { public void run() { try { - processHandler.waitFor(); - synchronized (LOCK) { - processedFinished = Boolean.TRUE; + final boolean finished = processHandler.waitFor(1000 * timeout); + if (!finished) { + LOG.error("Timeout (" + timeout + " sec) on executing: " + processHandler.getCommandLine()); + processHandler.destroyProcess(); } - } - finally { + } finally { mySemaphore.up(); } } }; - private final Runnable myTimeoutListener = new Runnable() { - public void run() { - try { - synchronized (this) { - try { - wait(1000 * timeout); - synchronized (LOCK) { - if (!processedFinished) { - LOG.error("Timeout (" + timeout + " sec) on executing: " + processHandler.getCommandLine()); - processHandler.destroyProcess(); - } - } - } - finally { - mySemaphore.up(); - } - } - } - catch (InterruptedException e) { - //Do nothing - } - } - }; - public void run() { mySemaphore.down(); ApplicationManager.getApplication().executeOnPooledThread(myProcessThread); - ApplicationManager.getApplication().executeOnPooledThread(myTimeoutListener); mySemaphore.waitFor(); } }; } -} \ No newline at end of file +} diff --git a/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/VariableInplaceRenameHandler.java b/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/VariableInplaceRenameHandler.java index 75d415fe5c97..aa9655238917 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/VariableInplaceRenameHandler.java +++ b/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/VariableInplaceRenameHandler.java @@ -32,12 +32,13 @@ import com.intellij.refactoring.rename.PsiElementRenameHandler; import com.intellij.refactoring.rename.RenameHandler; import com.intellij.refactoring.rename.RenameHandlerRegistry; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -public final class VariableInplaceRenameHandler implements RenameHandler { +public class VariableInplaceRenameHandler implements RenameHandler { private static final ThreadLocal ourPreventInlineRenameFlag = new ThreadLocal(); private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.rename.inplace.VariableInplaceRenameHandler"); - public boolean isAvailableOnDataContext(final DataContext dataContext) { + public final boolean isAvailableOnDataContext(final DataContext dataContext) { final PsiElement element = PsiElementRenameHandler.getElement(dataContext); final Editor editor = PlatformDataKeys.EDITOR.getData(dataContext); final PsiFile file = LangDataKeys.PSI_FILE.getData(dataContext); @@ -46,15 +47,20 @@ public final class VariableInplaceRenameHandler implements RenameHandler { if (ourPreventInlineRenameFlag.get() != null) { return false; } + return isAvailable(element, editor, file); + } + + protected boolean isAvailable(PsiElement element, Editor editor, PsiFile file) { final PsiElement nameSuggestionContext = file.findElementAt(editor.getCaretModel().getOffset()); - final RefactoringSupportProvider supportProvider = element != null ? LanguageRefactoringSupport.INSTANCE.forLanguage(element.getLanguage()):null; + final RefactoringSupportProvider + supportProvider = element != null ? LanguageRefactoringSupport.INSTANCE.forLanguage(element.getLanguage()):null; return supportProvider != null && editor.getSettings().isVariableInplaceRenameEnabled() && supportProvider.doInplaceRenameFor(element, nameSuggestionContext); } - public boolean isRenaming(final DataContext dataContext) { + public final boolean isRenaming(final DataContext dataContext) { return isAvailableOnDataContext(dataContext); } @@ -83,7 +89,8 @@ public final class VariableInplaceRenameHandler implements RenameHandler { return; } - final boolean startedRename = new VariableInplaceRenamer((PsiNameIdentifierOwner)elementToRename, editor).performInplaceRename(); + VariableInplaceRenamer renamer = createRenamer(elementToRename, editor); + boolean startedRename = renamer == null ? false : renamer.performInplaceRename(); if (!startedRename) { try { @@ -101,4 +108,9 @@ public final class VariableInplaceRenameHandler implements RenameHandler { } } } + + @Nullable + protected VariableInplaceRenamer createRenamer(PsiElement elementToRename, Editor editor) { + return new VariableInplaceRenamer((PsiNameIdentifierOwner)elementToRename, editor); + } } diff --git a/platform/lang-impl/src/com/intellij/ui/tabs/FileColorConfiguration.java b/platform/lang-impl/src/com/intellij/ui/tabs/FileColorConfiguration.java index 5b6df5e5102b..d79c015e4315 100644 --- a/platform/lang-impl/src/com/intellij/ui/tabs/FileColorConfiguration.java +++ b/platform/lang-impl/src/com/intellij/ui/tabs/FileColorConfiguration.java @@ -16,6 +16,8 @@ package com.intellij.ui.tabs; +import com.intellij.openapi.project.Project; +import com.intellij.psi.search.scope.packageSet.NamedScopeManager; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -54,7 +56,7 @@ class FileColorConfiguration implements Cloneable { myColorName = colorName; } - public boolean isValid() { + public boolean isValid(Project project) { if (myScopeName == null || myScopeName.length() == 0) { return false; } @@ -63,11 +65,15 @@ class FileColorConfiguration implements Cloneable { return false; } - return true; + if (project != null) { + return NamedScopeManager.getScope(project, myScopeName) != null; + } else { + return true; + } } public void save(@NotNull final Element e) { - if (!isValid()) { + if (!isValid(null)) { return; } diff --git a/platform/lang-impl/src/com/intellij/ui/tabs/FileColorManagerImpl.java b/platform/lang-impl/src/com/intellij/ui/tabs/FileColorManagerImpl.java index a926ed2170a9..40ea7834ed48 100644 --- a/platform/lang-impl/src/com/intellij/ui/tabs/FileColorManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/ui/tabs/FileColorManagerImpl.java @@ -53,11 +53,14 @@ public class FileColorManagerImpl extends FileColorManager implements Persistent private final Project myProject; private boolean myEnabledForTabs = true; + private boolean myHighlightNonProjectFiles = true; private static final Map ourDefaultColors; + private static final Map ourHiddenColors; static { ourDefaultColors = new LinkedHashMap(); + ourHiddenColors = new LinkedHashMap(); ourDefaultColors.put("Blue", new Color(215, 237, 243)); //ourDefaultColors.put("Blue 2", new Color(218, 224, 244)); ourDefaultColors.put("Green", new Color(228, 241, 209)); @@ -66,6 +69,8 @@ public class FileColorManagerImpl extends FileColorManager implements Persistent ourDefaultColors.put("Rose", new Color(242, 206, 202)); ourDefaultColors.put("Violet", new Color(222, 213, 241)); ourDefaultColors.put("Yellow", new Color(247, 241, 203)); + + ourHiddenColors.put(OUT_OF_PROJECT_SCOPE_COLOR, new Color(255, 255, 228)); } public FileColorManagerImpl(@NotNull final Project project) { @@ -95,11 +100,20 @@ public class FileColorManagerImpl extends FileColorManager implements Persistent return myEnabledForTabs; } + public boolean isHighlightNonProjectFiles() { + return myHighlightNonProjectFiles; + } + + public void setHighlightNonProjectFiles(boolean highlight) { + myHighlightNonProjectFiles = highlight; + } + public Element getState(final boolean shared) { Element element = new Element("state"); if (!shared) { element.setAttribute("enabled", Boolean.toString(myEnabled)); element.setAttribute("enabledForTabs", Boolean.toString(myEnabledForTabs)); + element.setAttribute("highlightNonProjectFiles", Boolean.toString(myHighlightNonProjectFiles)); } myModel.save(element, shared); @@ -110,7 +124,8 @@ public class FileColorManagerImpl extends FileColorManager implements Persistent @SuppressWarnings({"MethodMayBeStatic"}) @Nullable public Color getColor(@NotNull final String name) { - return ourDefaultColors.get(name); + final Color color = ourDefaultColors.get(name); + return color == null ? ourHiddenColors.get(name) : color; } public static String toString(final Color color) { @@ -133,7 +148,10 @@ public class FileColorManagerImpl extends FileColorManager implements Persistent myEnabled = enabled == null ? true : Boolean.valueOf(enabled); final String enabledForTabs = state.getAttributeValue("enabledForTabs"); - myEnabledForTabs = enabledForTabs == null ? false : Boolean.valueOf(enabledForTabs); + myEnabledForTabs = enabledForTabs == null ? true : Boolean.valueOf(enabledForTabs); + + final String highlightNonProjectFiles = state.getAttributeValue("highlightNonProjectFiles"); + myHighlightNonProjectFiles = highlightNonProjectFiles == null ? true : Boolean.valueOf(highlightNonProjectFiles); } myModel.load(state, shared); @@ -183,7 +201,7 @@ public class FileColorManagerImpl extends FileColorManager implements Persistent @Nullable public Color getFileColor(@NotNull final PsiFile file) { final String colorName = myModel.getColor(file); - return colorName == null ? null : ourDefaultColors.get(colorName); + return colorName == null ? null : getColor(colorName); } public boolean isShared(@NotNull final String scopeName) { diff --git a/platform/lang-impl/src/com/intellij/ui/tabs/FileColorsConfigurablePanel.java b/platform/lang-impl/src/com/intellij/ui/tabs/FileColorsConfigurablePanel.java index aa15cd6347c4..5693b316a581 100644 --- a/platform/lang-impl/src/com/intellij/ui/tabs/FileColorsConfigurablePanel.java +++ b/platform/lang-impl/src/com/intellij/ui/tabs/FileColorsConfigurablePanel.java @@ -37,6 +37,7 @@ public class FileColorsConfigurablePanel extends JPanel implements Disposable { private FileColorManagerImpl myManager; private final JCheckBox myEnabledCheckBox; private final JCheckBox myTabsEnabledCheckBox; + private final JCheckBox myHighlightNonProjectFilesCheckBox; private final FileColorSettingsTable myLocalTable; private final FileColorSettingsTable mySharedTable; @@ -56,6 +57,10 @@ public class FileColorsConfigurablePanel extends JPanel implements Disposable { myTabsEnabledCheckBox = new JCheckBox("Use colors in Editor Tabs"); myTabsEnabledCheckBox.setMnemonic('T'); topPanel.add(myTabsEnabledCheckBox); + + myHighlightNonProjectFilesCheckBox = new JCheckBox("Highlight Non-Project Files"); + myHighlightNonProjectFilesCheckBox.setMnemonic('N'); + topPanel.add(myHighlightNonProjectFilesCheckBox); topPanel.add(Box.createHorizontalGlue()); add(topPanel, BorderLayout.NORTH); @@ -229,6 +234,7 @@ public class FileColorsConfigurablePanel extends JPanel implements Disposable { modified = myEnabledCheckBox.isSelected() != myManager.isEnabled(); modified |= myTabsEnabledCheckBox.isSelected() != myManager.isEnabledForTabs(); + modified |= myHighlightNonProjectFilesCheckBox.isSelected() != myManager.isHighlightNonProjectFiles(); modified |= myLocalTable.isModified() || mySharedTable.isModified(); return modified; @@ -237,6 +243,7 @@ public class FileColorsConfigurablePanel extends JPanel implements Disposable { public void apply() { myManager.setEnabled(myEnabledCheckBox.isSelected()); myManager.setEnabledForTabs(myTabsEnabledCheckBox.isSelected()); + myManager.setHighlightNonProjectFiles(myHighlightNonProjectFilesCheckBox.isSelected()); myLocalTable.apply(); mySharedTable.apply(); @@ -247,6 +254,7 @@ public class FileColorsConfigurablePanel extends JPanel implements Disposable { public void reset() { myEnabledCheckBox.setSelected(myManager.isEnabled()); myTabsEnabledCheckBox.setSelected(myManager.isEnabledForTabs()); + myHighlightNonProjectFilesCheckBox.setSelected(myManager.isHighlightNonProjectFiles()); if(myLocalTable.isModified()) myLocalTable.reset(); if(mySharedTable.isModified()) mySharedTable.reset(); diff --git a/platform/lang-impl/src/com/intellij/ui/tabs/FileColorsModel.java b/platform/lang-impl/src/com/intellij/ui/tabs/FileColorsModel.java index 51da4e225235..41cde5d978e4 100644 --- a/platform/lang-impl/src/com/intellij/ui/tabs/FileColorsModel.java +++ b/platform/lang-impl/src/com/intellij/ui/tabs/FileColorsModel.java @@ -17,10 +17,14 @@ package com.intellij.ui.tabs; import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ProjectFileIndex; +import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; import com.intellij.psi.search.scope.packageSet.NamedScope; import com.intellij.psi.search.scope.packageSet.NamedScopeManager; import com.intellij.psi.search.scope.packageSet.NamedScopesHolder; +import com.intellij.ui.FileColorManager; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -136,13 +140,23 @@ public class FileColorsModel implements Cloneable { } final FileColorConfiguration configuration = findConfiguration(psiFile); - if (configuration != null && configuration.isValid()) { + if (configuration != null && configuration.isValid(psiFile.getProject())) { return configuration.getColorName(); } + if (FileColorManager.getInstance(myProject).isHighlightNonProjectFiles() + && !isFileUnderProject(psiFile.getVirtualFile())) { + return FileColorManager.OUT_OF_PROJECT_SCOPE_COLOR; + } return null; } + private boolean isFileUnderProject(@Nullable VirtualFile file) { + if (file == null) return false; + final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(myProject).getFileIndex(); + return myProject.isInitialized() && !fileIndex.isIgnored(file) && fileIndex.getContentRootForFile(file) != null; + } + @Nullable private FileColorConfiguration findConfiguration(@NotNull final PsiFile colored) { for (final FileColorConfiguration configuration : myConfigurations) { diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/SoftWrapChangeListener.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/SoftWrapChangeListener.java new file mode 100644 index 000000000000..8a4d17f3e6a2 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/editor/ex/SoftWrapChangeListener.java @@ -0,0 +1,33 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.editor.ex; + +/** + * Defines a contract for the callbacks for soft wraps management notifications (addition/removal). + * + * @author Denis Zhdanov + * @since Jul 8, 2010 6:50:17 PM + */ +public interface SoftWrapChangeListener { + + /** + * This method is assumed to be called every time number of soft wraps registered for the target logical line(s) + * is changed (soft wraps are added or remove). + * + * @param changedLogicalLine logical line for which number of registered soft wraps is changed + */ + void softWrapsStateChanged(int changedLogicalLine); +} diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/SoftWrapModelEx.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/SoftWrapModelEx.java index cb723ffc1405..88d897c9d3e7 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/ex/SoftWrapModelEx.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/ex/SoftWrapModelEx.java @@ -23,6 +23,7 @@ import com.intellij.openapi.editor.impl.softwrap.SoftWrapDrawingType; import org.jetbrains.annotations.NotNull; import java.awt.*; +import java.util.Collections; import java.util.List; /** @@ -72,6 +73,18 @@ public interface SoftWrapModelEx extends SoftWrapModel { */ List getRegisteredSoftWraps(); + /** + * Tries to find index of the target soft wrap at {@link #getRegisteredSoftWraps() soft wraps collection}. + * 'Target' soft wrap is the one that starts at the given offset. + * + * @param offset target offset + * @return index that conforms to {@link Collections#binarySearch(List, Object)} contract, i.e. non-negative returned + * index points to soft wrap that starts at the given offset; '-(negative value) - 1' points + * to position at {@link #getRegisteredSoftWraps() soft wraps collection} where soft wrap for the given index + * should be inserted + */ + int getSoftWrapIndex(int offset); + /** * Asks to paint drawing of target type at the given graphics buffer at the given position. * @@ -91,4 +104,12 @@ public interface SoftWrapModelEx extends SoftWrapModel { * @return width in pixels required for the painting of the given type */ int getMinDrawingWidth(@NotNull SoftWrapDrawingType drawingType); + + /** + * Registers given listener within the current model + * + * @param listener listener to register + * @return true if given listener was not registered before; false otherwise + */ + boolean addSoftWrapChangeListener(@NotNull SoftWrapChangeListener listener); } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java index 4cd94276b027..b935c71b4c0d 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java @@ -176,7 +176,6 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi private static final int MOUSE_SELECTION_STATE_LINE_SELECTED = 2; private final MarkupModelListener myMarkupModelListener; - private final List mySoftWrapsOnLastRepaint = new ArrayList(); private EditorHighlighter myHighlighter; private final TextDrawingCallback myTextDrawingCallback = new MyTextDrawingCallback(); @@ -216,6 +215,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi private boolean myEmbeddedIntoDialogWrapper; private CachedFontContent myLastCache; private boolean mySpacesHaveSameWidth; + private boolean mySoftWrapsChanged; private Point myLastBackgroundPosition = null; private Color myLastBackgroundColor = null; @@ -327,6 +327,14 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi myFoldingModel.flushCaretShift(); myScrollBarOrientation = VERTICAL_SCROLLBAR_RIGHT; + mySoftWrapModel.addSoftWrapChangeListener(new SoftWrapChangeListener() { + @Override + public void softWrapsStateChanged(int changedLogicalLine) { + mySoftWrapsChanged = true; + mySizeContainer.update(changedLogicalLine, changedLogicalLine, changedLogicalLine); + } + }); + EditorHighlighter highlighter = new EmptyEditorHighlighter(myScheme.getAttributes(HighlighterColors.TEXT)); setHighlighter(highlighter); @@ -1692,8 +1700,10 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi // Perform additional activity if soft wrap is added or removed during repainting. // Note: this code lives in this method in assumption that background repainting is the very first activity performed // during whole editor component repaint. - List softWrapsAfterRepaint = mySoftWrapModel.getRegisteredSoftWraps(); - if (!mySoftWrapsOnLastRepaint.equals(softWrapsAfterRepaint)) { + if (mySoftWrapsChanged) { + mySoftWrapsChanged = false; + validateSize(); + // Repaint editor to the bottom in order to ensure that its content is shown correctly after new soft wrap introduction. repaintToScreenBottom(xyToLogicalPosition(position).line); @@ -1704,8 +1714,6 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi // Ask caret model to update visual caret position. getCaretModel().moveToOffset(getCaretModel().getOffset()); } - mySoftWrapsOnLastRepaint.clear(); - mySoftWrapsOnLastRepaint.addAll(softWrapsAfterRepaint); } private void paintRectangularSelection(Graphics g) { @@ -2482,11 +2490,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi public Dimension getContentSize() { Dimension size = mySizeContainer.getContentSize(); - //TODO den move soft wrap-related logic to editor size container - return new Dimension( - size.width, - size.height + (mySettings.getAdditionalLinesCount() + mySoftWrapModel.getSoftWrapsIntroducedLinesNumber()) * getLineHeight() - ); + return new Dimension(size.width, size.height + mySettings.getAdditionalLinesCount() * getLineHeight()); } public JScrollPane getScrollPane() { @@ -2524,10 +2528,20 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi return myLastColumnNumber; } + /** + * @return information about total number of lines that can be viewed by user. I.e. this is a number of all document + * lines (considering that single logical document line may be represented on multiple visual lines because of + * soft wraps appliance) minus number of folded lines + */ int getVisibleLineCount() { - int line = getDocument().getLineCount() + getSoftWrapModel().getSoftWrapsIntroducedLinesNumber(); - line -= myFoldingModel.getFoldedLinesCountBefore(getDocument().getTextLength() + 1); - return line; + return getVisibleLogicalLinesCount() + getSoftWrapModel().getSoftWrapsIntroducedLinesNumber(); + } + + /** + * @return number of visible logical lines. Generally, that is a total logical lines number minus number of folded lines + */ + private int getVisibleLogicalLinesCount() { + return getDocument().getLineCount() - myFoldingModel.getFoldedLinesCountBefore(getDocument().getTextLength() + 1); } @NotNull @@ -4595,15 +4609,25 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi } } + @SuppressWarnings({"FieldAccessedSynchronizedAndUnsynchronized"}) private class EditorSizeContainer { + + /** Holds logical line widths in pixels. */ private TIntArrayList myLineWidths; + + /** Holds value that indicates if line widths recalculation should be performed. */ private volatile boolean myIsDirty; + + /** Holds number of the last logical line affected by the last document change. */ private int myOldEndLine; + private Dimension mySize; - private int myMaxWidth = -1; + + private final Object lock = new Object(); + private int myMaxWidth = -1; public synchronized void reset() { - int visLinesCount = getVisibleLineCount(); + int visLinesCount = getDocument().getLineCount(); myLineWidths = new TIntArrayList(visLinesCount + 300); int[] values = new int[visLinesCount]; Arrays.fill(values, -1); @@ -4611,6 +4635,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi myIsDirty = true; } + @SuppressWarnings({"NonPrivateFieldAccessedInSynchronizedContext"}) public synchronized void beforeChange(DocumentEvent e) { if (myDocument.isInBulkUpdate()) { myMaxWidth = mySize != null ? mySize.width : -1; @@ -4626,11 +4651,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi return offsetToVisualPosition(startLineOffset).line; } - public synchronized void changedUpdate(DocumentEvent e) { - int startLine = e.getOldLength() == 0 ? myOldEndLine : getVisualPositionLine(e.getOffset()); - int newEndLine = e.getNewLength() == 0 ? startLine : getVisualPositionLine(e.getOffset() + e.getNewLength()); - int oldEndLine = myOldEndLine; - + public synchronized void update(int startLine, int newEndLine, int oldEndLine) { final int lineWidthSize = myLineWidths.size(); if (lineWidthSize == 0) { reset(); @@ -4657,12 +4678,21 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi } } + public synchronized void changedUpdate(DocumentEvent e) { + int startLine = e.getOldLength() == 0 ? myOldEndLine : getVisualPositionLine(e.getOffset()); + int newEndLine = e.getNewLength() == 0 ? startLine : getVisualPositionLine(e.getOffset() + e.getNewLength()); + int oldEndLine = myOldEndLine; + + update(startLine, newEndLine, oldEndLine); + } + + @SuppressWarnings({"NonPrivateFieldAccessedInSynchronizedContext", "AssignmentToForLoopParameter"}) private void validateSizes() { if (!myIsDirty) return; - synchronized (this) { + synchronized (lock) { if (!myIsDirty) return; - int lineCount = myLineWidths.size(); + int lineCount = Math.min(myLineWidths.size(), myDocument.getLineCount()); if (myMaxWidth != -1 && myDocument.isInBulkUpdate()) { mySize = new Dimension(myMaxWidth, getLineHeight() * lineCount); @@ -4676,19 +4706,31 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi final int fontSize = myScheme.getEditorFontSize(); final String fontName = myScheme.getEditorFontName(); + List softWraps = getSoftWrapModel().getRegisteredSoftWraps(); + int softWrapsIndex = -1; + for (int line = 0; line < lineCount; line++) { if (myLineWidths.getQuick(line) != -1) continue; x = 0; - int offset = logicalPositionToOffset(visualToLogicalPosition(new VisualPosition(line, 0))); + int offset = myDocument.getLineStartOffset(line); if (offset >= myDocument.getTextLength()) { myLineWidths.set(line, 0); break; } + if (softWrapsIndex < 0) { + softWrapsIndex = getSoftWrapModel().getSoftWrapIndex(offset); + if (softWrapsIndex < 0) { + softWrapsIndex = -softWrapsIndex - 1; + } + } + IterationState state = new IterationState(EditorImpl.this, offset, false); int fontType = state.getMergedAttributes().getFontType(); + int maxPreviousSoftWrappedWidth = -1; + while (offset < end && line < lineCount) { char c = text.charAt(offset); if (offset >= state.getEndOffset()) { @@ -4696,6 +4738,22 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi fontType = state.getMergedAttributes().getFontType(); } + while (softWrapsIndex < softWraps.size()) { + TextChange softWrap = softWraps.get(softWrapsIndex); + if (softWrap.getStart() > offset) { + break; + } + softWrapsIndex++; + if (softWrap.getStart() == offset) { + maxPreviousSoftWrappedWidth = Math.max(maxPreviousSoftWrappedWidth, x); + x = getSoftWrapModel().getSoftWrapIndentWidthInPixels(softWrap); + } + } + if (line + 1 >= lineCount) { + myLineWidths.set(line, Math.max(x, maxPreviousSoftWrappedWidth)); + break; + } + FoldRegion collapsed = state.getCurrentFold(); if (collapsed != null) { String placeholder = collapsed.getPlaceholderText(); @@ -4703,6 +4761,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi x += EditorUtil.charWidth(placeholder.charAt(i), fontType, EditorImpl.this); } offset = collapsed.getEndOffset(); + line = myDocument.getLineNumber(offset); } else { if (c == '\t') { @@ -4711,7 +4770,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi } else { if (c == '\n') { - myLineWidths.set(line, x); + myLineWidths.set(line, Math.max(x, maxPreviousSoftWrappedWidth)); if (line + 1 >= lineCount || myLineWidths.getQuick(line + 1) != -1) break; offset++; x = 0; @@ -4732,12 +4791,28 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi x); // Last line can be non-zero length and won't be caught by in-loop procedure since latter only react on \n's } + // There is a following possible situation: + // 1. Big document is opened at editor; + // 2. Soft wraps are calculated for the current visible area; + // 2. The user scrolled down; + // 3. The user significantly reduced visible area width (say, reduced it twice); + // 4. Soft wraps are calculated for the current visible area; + // We need to consider only the widths for the logical lines that are completely shown at the current visible area then. + // I.e. we shouldn't use widths of the lines that are not shown for max width calculation because previous widths are calculated + // for another visible area width. + int startToUse = 0; + int endToUse = lineCount; + if (getSoftWrapModel().isSoftWrappingEnabled()) { + Rectangle visibleArea = getScrollingModel().getVisibleArea(); + startToUse = xyToLogicalPosition(visibleArea.getLocation()).line + 1; + endToUse = xyToLogicalPosition(new Point(0, visibleArea.y + visibleArea.height)).line; + } int maxWidth = 0; - for (int i = 0; i < lineCount; i++) { + for (int i = startToUse; i < endToUse; i++) { maxWidth = Math.max(maxWidth, myLineWidths.getQuick(i)); } - mySize = new Dimension(maxWidth, getLineHeight() * lineCount); + mySize = new Dimension(maxWidth, getLineHeight() * getVisibleLineCount()); myIsDirty = false; } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java index 175681baf26a..de8cbb67c426 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java @@ -15,11 +15,9 @@ */ package com.intellij.openapi.editor.impl; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.LogicalPosition; -import com.intellij.openapi.editor.TextChange; -import com.intellij.openapi.editor.VisualPosition; +import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.ex.EditorEx; +import com.intellij.openapi.editor.ex.SoftWrapChangeListener; import com.intellij.openapi.editor.ex.SoftWrapModelEx; import com.intellij.openapi.editor.ex.util.EditorUtil; import com.intellij.openapi.editor.impl.softwrap.*; @@ -56,7 +54,7 @@ public class SoftWrapModelImpl implements SoftWrapModelEx { private int myActive; public SoftWrapModelImpl(@NotNull EditorEx editor) { - this(editor, new SoftWrapsStorage(), new CompositeSoftWrapPainter(editor)); + this(editor, new SoftWrapsStorage(editor.getDocument()), new CompositeSoftWrapPainter(editor)); } public SoftWrapModelImpl(@NotNull final EditorEx editor, @NotNull SoftWrapsStorage storage, @NotNull SoftWrapPainter painter) { @@ -77,7 +75,7 @@ public class SoftWrapModelImpl implements SoftWrapModelEx { } public boolean isSoftWrappingEnabled() { - return myEditor.getSettings().isUseSoftWraps(); + return myEditor.getSettings().isUseSoftWraps() && !myEditor.isOneLineMode(); } @Nullable @@ -88,6 +86,11 @@ public class SoftWrapModelImpl implements SoftWrapModelEx { return myStorage.getSoftWrap(offset); } + @Override + public int getSoftWrapIndex(int offset) { + return myStorage.getSoftWrapIndex(offset); + } + @NotNull @Override public List getSoftWrapsForRange(int start, int end) { @@ -138,8 +141,11 @@ public class SoftWrapModelImpl implements SoftWrapModelEx { return 0; } int result = 0; - for (TextChange myWrap : myStorage.getSoftWraps()) { - result += StringUtil.countNewLines(myWrap.getText()); + FoldingModel foldingModel = myEditor.getFoldingModel(); + for (TextChange softWrap : myStorage.getSoftWraps()) { + if (!foldingModel.isOffsetCollapsed(softWrap.getStart())) { + result += StringUtil.countNewLines(softWrap.getText()); + } } return result; } @@ -309,4 +315,9 @@ public class SoftWrapModelImpl implements SoftWrapModelEx { // Restore caret position. myEditor.getCaretModel().moveToVisualPosition(visualCaretPosition); } + + @Override + public boolean addSoftWrapChangeListener(@NotNull SoftWrapChangeListener listener) { + return myStorage.addSoftWrapChangeListener(listener); + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapsStorage.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapsStorage.java index 345ae83973ee..5c325f5a7285 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapsStorage.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapsStorage.java @@ -15,12 +15,16 @@ */ package com.intellij.openapi.editor.impl.softwrap; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.ex.SoftWrapChangeListener; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; /** * Holds registered soft wraps and provides monitoring and management facilities for them. @@ -32,8 +36,15 @@ import java.util.List; */ public class SoftWrapsStorage { - private final List myWraps = new ArrayList(); - private final List myWrapsView = Collections.unmodifiableList(myWraps); + private final List myWraps = new ArrayList(); + private final List myWrapsView = Collections.unmodifiableList(myWraps); + private final Set myListeners = new CopyOnWriteArraySet(); + + private final Document myDocument; + + public SoftWrapsStorage(Document document) { + myDocument = document; + } /** * @return true if there is at least one soft wrap registered at the current storage; false otherwise @@ -103,6 +114,10 @@ public class SoftWrapsStorage { i = -i - 1; myWraps.add(i, softWrap); + int changedLogicalLine = myDocument.getLineNumber(softWrap.getStart()); + for (SoftWrapChangeListener listener : myListeners) { + listener.softWrapsStateChanged(changedLogicalLine); + } return null; } @@ -127,4 +142,14 @@ public class SoftWrapsStorage { public void removeAll() { myWraps.clear(); } + + /** + * Registers given listener within the current model + * + * @param listener listener to register + * @return true if given listener was not registered before; false otherwise + */ + public boolean addSoftWrapChangeListener(@NotNull SoftWrapChangeListener listener) { + return myListeners.add(listener); + } } diff --git a/platform/platform-impl/testSrc/com/intellij/openapi/editor/impl/softwrap/SoftWrapDataMapperTest.java b/platform/platform-impl/testSrc/com/intellij/openapi/editor/impl/softwrap/SoftWrapDataMapperTest.java index 78f4584e3c81..705f55786f1f 100644 --- a/platform/platform-impl/testSrc/com/intellij/openapi/editor/impl/softwrap/SoftWrapDataMapperTest.java +++ b/platform/platform-impl/testSrc/com/intellij/openapi/editor/impl/softwrap/SoftWrapDataMapperTest.java @@ -1,5 +1,6 @@ package com.intellij.openapi.editor.impl.softwrap; +import com.intellij.idea.Bombed; import com.intellij.mock.MockFoldRegion; import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.ex.EditorEx; @@ -17,10 +18,7 @@ import org.jmock.integration.junit4.JUnit4Mockery; import org.jmock.Mockery; import org.jmock.lib.legacy.ClassImposteriser; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; +import java.util.*; /** * @author Denis Zhdanov @@ -85,9 +83,10 @@ public class SoftWrapDataMapperTest { myMockery = new JUnit4Mockery() {{ setImposteriser(ClassImposteriser.INSTANCE); }}; - myStorage = new SoftWrapsStorage(); + myEditor = myMockery.mock(EditorEx.class); myDocument = myMockery.mock(Document.class); + myStorage = new SoftWrapsStorage(myDocument); myFoldingModel = myMockery.mock(FoldingModel.class); final EditorSettings settings = myMockery.mock(EditorSettings.class); final Project project = myMockery.mock(Project.class); @@ -177,6 +176,7 @@ public class SoftWrapDataMapperTest { myMockery.assertIsSatisfied(); } + @Bombed(day = 12, month = Calendar.JULY) @Test public void softWrapHasSymbolBeforeFirstLineFeed() { String document = @@ -192,6 +192,7 @@ public class SoftWrapDataMapperTest { test(document); } + @Bombed(day = 12, month = Calendar.JULY) @Test public void multipleSoftWrappedLogicalLines() { String document = @@ -211,6 +212,7 @@ public class SoftWrapDataMapperTest { test(document); } + @Bombed(day = 12, month = Calendar.JULY) @Test public void softWrapAndFoldedLines() { String document = diff --git a/plugins/eclipse/src/org/jetbrains/idea/eclipse/config/EclipseModuleManager.java b/plugins/eclipse/src/org/jetbrains/idea/eclipse/config/EclipseModuleManager.java index 9b81c080effb..ac79c5a8ee3d 100644 --- a/plugins/eclipse/src/org/jetbrains/idea/eclipse/config/EclipseModuleManager.java +++ b/plugins/eclipse/src/org/jetbrains/idea/eclipse/config/EclipseModuleManager.java @@ -20,13 +20,11 @@ import com.intellij.openapi.components.*; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleServiceManager; import com.intellij.openapi.roots.impl.storage.ClasspathStorage; +import com.intellij.openapi.util.text.StringUtil; import org.jdom.Element; import org.jetbrains.annotations.NonNls; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; +import java.util.*; @State( name = "EclipseModuleManager", @@ -44,12 +42,13 @@ public class EclipseModuleManager implements PersistentStateComponent{ @NonNls private static final String CONELEMENT = "conelement"; @NonNls private static final String FORCED_JDK = "forced_jdk"; private CachedXmlDocumentSet myDocumentSet; - private final Map myEclipseVariablePaths = new HashMap(); - private final Set myEclipseUrls = new HashSet(); - private final Set myUnknownCons = new HashSet(); + private final Map myEclipseVariablePaths = new LinkedHashMap(); + private final Set myEclipseUrls = new LinkedHashSet(); + private final Set myUnknownCons = new LinkedHashSet(); private boolean myForceConfigureJDK = false; - private static final String SRC_PREFIX = "src:"; - private static final String SRC_LINK_PREFIX = "linksrc:"; + @NonNls private static final String SRC_PREFIX = "src:"; + @NonNls private static final String SRC_LINK_PREFIX = "linksrc:"; + @NonNls private static final String PREFIX_ATTR = "kind"; private final Module myModule; @NonNls private static final String LIBELEMENT = "libelement"; @@ -118,7 +117,7 @@ public class EclipseModuleManager implements PersistentStateComponent{ } public Element getState() { - if (ClasspathStorage.getStorageType(myModule) != EclipseClasspathStorageProvider.ID) { + if (!ClasspathStorage.getStorageType(myModule).equals(EclipseClasspathStorageProvider.ID)) { if (!myEclipseUrls.isEmpty() || !myEclipseVariablePaths.isEmpty() || myForceConfigureJDK || !myUnknownCons.isEmpty()) { Element root = new Element("EclipseModuleSettings"); for (String eclipseUrl : myEclipseUrls) { @@ -128,7 +127,15 @@ public class EclipseModuleManager implements PersistentStateComponent{ } for (String var : myEclipseVariablePaths.keySet()) { Element varElement = new Element(VARELEMENT); - varElement.setAttribute(VAR_ATTRIBUTE, var); + if (var.startsWith(SRC_PREFIX)) { + varElement.setAttribute(VAR_ATTRIBUTE, StringUtil.trimStart(var, SRC_PREFIX)); + varElement.setAttribute(PREFIX_ATTR, SRC_PREFIX); + } else if (var.startsWith(SRC_LINK_PREFIX)) { + varElement.setAttribute(VAR_ATTRIBUTE, StringUtil.trimStart(var, SRC_LINK_PREFIX)); + varElement.setAttribute(PREFIX_ATTR, SRC_LINK_PREFIX); + } else { + varElement.setAttribute(VAR_ATTRIBUTE, var); + } varElement.setAttribute(VALUE_ATTR, myEclipseVariablePaths.get(var)); root.addContent(varElement); } @@ -153,7 +160,8 @@ public class EclipseModuleManager implements PersistentStateComponent{ } for (Object o : state.getChildren(VARELEMENT)) { - myEclipseVariablePaths.put(((Element)o).getAttributeValue(VAR_ATTRIBUTE), ((Element)o).getAttributeValue(VALUE_ATTR)); + final String prefix = ((Element)o).getAttributeValue(PREFIX_ATTR); + myEclipseVariablePaths.put(((Element)o).getAttributeValue(VAR_ATTRIBUTE), (prefix != null ? prefix : "") + ((Element)o).getAttributeValue(VALUE_ATTR)); } for (Object o : state.getChildren(CONELEMENT)) { diff --git a/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/XmlUnboundNsPrefixInspection.java b/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/XmlUnboundNsPrefixInspection.java index 5ae358adbe44..80dee4e99f90 100644 --- a/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/XmlUnboundNsPrefixInspection.java +++ b/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/XmlUnboundNsPrefixInspection.java @@ -116,7 +116,7 @@ public class XmlUnboundNsPrefixInspection extends XmlSuppressableInspectionTool if (!HighlightLevelUtil.shouldInspect(containingFile)) return; final XmlExtension extension = XmlExtension.getExtension(containingFile); - if (extension.isPrefixDeclared(context, namespacePrefix)) { + if (extension.getPrefixDeclaration(context, namespacePrefix) != null) { return; } diff --git a/xml/impl/src/com/intellij/psi/impl/source/xml/SchemaPrefix.java b/xml/impl/src/com/intellij/psi/impl/source/xml/SchemaPrefix.java index af375f14da16..d0905e2ca6cc 100644 --- a/xml/impl/src/com/intellij/psi/impl/source/xml/SchemaPrefix.java +++ b/xml/impl/src/com/intellij/psi/impl/source/xml/SchemaPrefix.java @@ -16,11 +16,12 @@ package com.intellij.psi.impl.source.xml; import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; import com.intellij.psi.impl.RenameableFakePsiElement; -import com.intellij.psi.search.LocalSearchScope; import com.intellij.psi.search.SearchScope; import com.intellij.psi.xml.XmlAttribute; import com.intellij.util.Icons; +import com.intellij.xml.XmlExtension; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -73,6 +74,11 @@ public class SchemaPrefix extends RenameableFakePsiElement { @NotNull @Override public SearchScope getUseScope() { - return new LocalSearchScope(getDeclaration().getParent()); + return XmlExtension.getExtension(getContainingFile()).getNsPrefixScope(getDeclaration()); + } + + @Override + public boolean isEquivalentTo(PsiElement another) { + return another instanceof SchemaPrefix && ((SchemaPrefix)another).getDeclaration() == getDeclaration(); } } diff --git a/xml/impl/src/com/intellij/psi/impl/source/xml/SchemaPrefixReference.java b/xml/impl/src/com/intellij/psi/impl/source/xml/SchemaPrefixReference.java index ecff86ab0f34..ff276b14be8c 100644 --- a/xml/impl/src/com/intellij/psi/impl/source/xml/SchemaPrefixReference.java +++ b/xml/impl/src/com/intellij/psi/impl/source/xml/SchemaPrefixReference.java @@ -19,13 +19,13 @@ import com.intellij.openapi.util.NullableLazyValue; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiReferenceBase; -import com.intellij.psi.xml.XmlAttribute; -import com.intellij.psi.xml.XmlElement; -import com.intellij.psi.xml.XmlTag; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.xml.*; import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; -import com.intellij.xml.util.XmlUtil; +import com.intellij.xml.XmlExtension; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * @author Konstantin Bulenkov @@ -35,19 +35,16 @@ public class SchemaPrefixReference extends PsiReferenceBase { private final NullableLazyValue myPrefix = new NullableLazyValue() { @Override protected SchemaPrefix compute() { - if (myElement instanceof XmlAttribute && ((XmlAttribute)myElement).isNamespaceDeclaration()) { + if (myElement instanceof XmlAttribute && (((XmlAttribute)myElement).isNamespaceDeclaration())) { return new SchemaPrefix((XmlAttribute)myElement, getRangeInElement(), myName); } + else if (myElement instanceof XmlAttributeValue && + ((XmlAttribute)((XmlAttributeValue)myElement).getParent()).getLocalName().equals("prefix")) { + return new SchemaPrefix((XmlAttribute)myElement.getParent(), getRangeInElement().shiftRight(myElement.getStartOffsetInParent()), myName); + } else { - final PsiElement declaration = XmlUtil.findNamespaceDeclaration(myElement, myName); - if (declaration instanceof XmlAttribute) { - final XmlAttribute attribute = (XmlAttribute)declaration; - final String prefix = attribute.getNamespacePrefix(); - final TextRange textRange = TextRange.from(prefix.length() + 1, myName.length()); - return new SchemaPrefix(attribute, textRange, myName); - } + return resolvePrefix(myElement, myName); } - return null; } }; @@ -56,7 +53,6 @@ public class SchemaPrefixReference extends PsiReferenceBase { public SchemaPrefixReference(XmlElement element, TextRange range, String name) { super(element, range, true); myName = name; - } public String getNamespacePrefix() { @@ -75,9 +71,7 @@ public class SchemaPrefixReference extends PsiReferenceBase { @Override public boolean isReferenceTo(PsiElement element) { if (!(element instanceof SchemaPrefix) || !myName.equals(((SchemaPrefix)element).getName())) return false; - - SchemaPrefix prefix = resolve(); - return prefix != null && ((SchemaPrefix)element).getDeclaration() == prefix.getDeclaration(); + return super.isReferenceTo(element); } @Override @@ -94,4 +88,16 @@ public class SchemaPrefixReference extends PsiReferenceBase { } return super.handleElementRename(name); } + + @Nullable + public static SchemaPrefix resolvePrefix(PsiElement element, String name) { + XmlExtension extension = XmlExtension.getExtension(element.getContainingFile()); + final XmlAttribute declaration = extension.getPrefixDeclaration(PsiTreeUtil.getParentOfType(element, XmlTag.class, false), name); + if (declaration != null) { + final String prefix = declaration.getNamespacePrefix(); + final TextRange textRange = TextRange.from(prefix.length() + 1, name.length()); + return new SchemaPrefix(declaration, textRange, name); + } + return null; + } } diff --git a/xml/impl/src/com/intellij/xml/DefaultXmlExtension.java b/xml/impl/src/com/intellij/xml/DefaultXmlExtension.java index 59bce65596ba..678ecb4599a8 100644 --- a/xml/impl/src/com/intellij/xml/DefaultXmlExtension.java +++ b/xml/impl/src/com/intellij/xml/DefaultXmlExtension.java @@ -242,15 +242,16 @@ public class DefaultXmlExtension extends XmlExtension { } } - public boolean isPrefixDeclared(final XmlTag context, String namespacePrefix) { + public XmlAttribute getPrefixDeclaration(final XmlTag context, String namespacePrefix) { @NonNls String nsDeclarationAttrName = null; for(XmlTag t = context; t != null; t = t.getParentTag()) { if (t.hasNamespaceDeclarations()) { if (nsDeclarationAttrName == null) nsDeclarationAttrName = namespacePrefix.length() > 0 ? "xmlns:"+namespacePrefix:"xmlns"; - if (t.getAttributeValue(nsDeclarationAttrName) != null) return true; + XmlAttribute attribute = t.getAttribute(nsDeclarationAttrName); + if (attribute != null) return attribute; } } - return false; + return null; } private static Set guessNamespace(final PsiFile file, String tagName) { diff --git a/xml/impl/src/com/intellij/xml/XmlExtension.java b/xml/impl/src/com/intellij/xml/XmlExtension.java index 1fee3bc24319..43ab08f8de94 100644 --- a/xml/impl/src/com/intellij/xml/XmlExtension.java +++ b/xml/impl/src/com/intellij/xml/XmlExtension.java @@ -29,6 +29,8 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.impl.source.xml.TagNameReference; +import com.intellij.psi.search.LocalSearchScope; +import com.intellij.psi.search.SearchScope; import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlDocument; import com.intellij.psi.xml.XmlFile; @@ -153,7 +155,12 @@ public abstract class XmlExtension { return HighlightInfoType.ERROR; } - public abstract boolean isPrefixDeclared(final XmlTag context, String namespacePrefix); + @Nullable + public abstract XmlAttribute getPrefixDeclaration(final XmlTag context, String namespacePrefix); + + public SearchScope getNsPrefixScope(XmlAttribute declaration) { + return new LocalSearchScope(declaration.getParent()); + } public boolean shouldBeHighlightedAsTag(XmlTag tag) { return true; diff --git a/xml/impl/src/com/intellij/xml/refactoring/SchemaPrefixRenameHandler.java b/xml/impl/src/com/intellij/xml/refactoring/SchemaPrefixRenameHandler.java index 2caa5790bb3e..838fbee82313 100644 --- a/xml/impl/src/com/intellij/xml/refactoring/SchemaPrefixRenameHandler.java +++ b/xml/impl/src/com/intellij/xml/refactoring/SchemaPrefixRenameHandler.java @@ -15,20 +15,15 @@ */ package com.intellij.xml.refactoring; -import com.intellij.openapi.actionSystem.DataContext; -import com.intellij.openapi.actionSystem.LangDataKeys; -import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiReference; import com.intellij.psi.impl.source.resolve.reference.impl.PsiMultiReference; import com.intellij.psi.impl.source.xml.SchemaPrefix; import com.intellij.psi.impl.source.xml.SchemaPrefixReference; -import com.intellij.refactoring.rename.RenameHandler; +import com.intellij.refactoring.rename.inplace.VariableInplaceRenameHandler; import com.intellij.refactoring.rename.inplace.VariableInplaceRenamer; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; @@ -36,24 +31,14 @@ import java.util.Collection; /** * @author Dmitry Avdeev */ -public class SchemaPrefixRenameHandler implements RenameHandler { +public class SchemaPrefixRenameHandler extends VariableInplaceRenameHandler { - public boolean isAvailableOnDataContext(DataContext dataContext) { - return isRenaming(dataContext); - } - - public boolean isRenaming(DataContext dataContext) { - SchemaPrefixReference ref = getReference(dataContext); + @Override + protected boolean isAvailable(PsiElement element, Editor editor, PsiFile file) { + SchemaPrefixReference ref = getReference(file, editor); return ref != null && ref.resolve() != null; } - @Nullable - private static SchemaPrefixReference getReference(DataContext dataContext) { - PsiFile file = LangDataKeys.PSI_FILE.getData(dataContext); - Editor editor = PlatformDataKeys.EDITOR.getData(dataContext); - return getReference(file, editor); - } - @Nullable private static SchemaPrefixReference getReference(PsiFile file, Editor editor) { if (file != null && editor != null) { @@ -75,22 +60,19 @@ public class SchemaPrefixRenameHandler implements RenameHandler { } - public void invoke(@NotNull Project project, Editor editor, PsiFile file, DataContext dataContext) { - SchemaPrefixReference reference = getReference(file, editor); + @Override + protected VariableInplaceRenamer createRenamer(PsiElement elementToRename, Editor editor) { + SchemaPrefixReference reference = getReference(elementToRename.getContainingFile(), editor); if (reference != null) { SchemaPrefix prefix = reference.resolve(); if (prefix != null) { - new VariableInplaceRenamer(prefix, editor) { + return new VariableInplaceRenamer(prefix, editor) { @Override - protected void addReferenceAtCaret(Collection refs) { + protected void addReferenceAtCaret(Collection refs) {} - } - }.performInplaceRename(); + }; } } - } - - public void invoke(@NotNull Project project, @NotNull PsiElement[] elements, DataContext dataContext) { - + return null; } }