Merge branch 'master' of git@git.labs.intellij.net:idea/community

This commit is contained in:
Eugene Kudelevsky
2010-07-09 22:50:01 +04:00
28 changed files with 521 additions and 184 deletions
@@ -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<IntermediateOutputCompiler, Module>, Pair<VirtualFile, VirtualFile>> 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);
@@ -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, String, Boolean>(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);
@@ -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;
}
}
@@ -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<PsiClass> 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<PsiClass> inheritors = new ArrayList<PsiClass>();
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<String> errorMessage = new Ref<String>();
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;
}
@@ -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<PsiElement,String> conflicts = PullUpConflictsUtil.checkConflicts(infos, mySubclass, superClass, null, null, dialog.getContainmentVerifier());
final MultiMap<PsiElement, String> conflicts = new MultiMap<PsiElement, String>();
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();
@@ -0,0 +1,12 @@
class Test {
void f() {
new Runnable() {
public void run() {
int j = 0;
if (j == 0 && <selection>j < 0 && j > 0</selection>) {
assert false;
}
}
};
}
}
@@ -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;
}
}
@@ -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 =
@@ -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);
@@ -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();
}
};
}
}
}
@@ -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<Boolean> ourPreventInlineRenameFlag = new ThreadLocal<Boolean>();
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);
}
}
@@ -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;
}
@@ -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<String, Color> ourDefaultColors;
private static final Map<String, Color> ourHiddenColors;
static {
ourDefaultColors = new LinkedHashMap<String, Color>();
ourHiddenColors = new LinkedHashMap<String, Color>();
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) {
@@ -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();
@@ -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) {
@@ -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);
}
@@ -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<? extends TextChange> getRegisteredSoftWraps();
/**
* Tries to find index of the target soft wrap at {@link #getRegisteredSoftWraps() soft wraps collection}.
* <code>'Target'</code> 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; <code>'-(negative value) - 1'</code> 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 <code>true</code> if given listener was not registered before; <code>false</code> otherwise
*/
boolean addSoftWrapChangeListener(@NotNull SoftWrapChangeListener listener);
}
@@ -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<TextChange> mySoftWrapsOnLastRepaint = new ArrayList<TextChange>();
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<? extends TextChange> 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<? extends TextChange> 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;
}
@@ -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<? extends TextChange> 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);
}
}
@@ -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<TextChangeImpl> myWraps = new ArrayList<TextChangeImpl>();
private final List<TextChangeImpl> myWrapsView = Collections.unmodifiableList(myWraps);
private final List<TextChangeImpl> myWraps = new ArrayList<TextChangeImpl>();
private final List<TextChangeImpl> myWrapsView = Collections.unmodifiableList(myWraps);
private final Set<SoftWrapChangeListener> myListeners = new CopyOnWriteArraySet<SoftWrapChangeListener>();
private final Document myDocument;
public SoftWrapsStorage(Document document) {
myDocument = document;
}
/**
* @return <code>true</code> if there is at least one soft wrap registered at the current storage; <code>false</code> 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 <code>true</code> if given listener was not registered before; <code>false</code> otherwise
*/
public boolean addSoftWrapChangeListener(@NotNull SoftWrapChangeListener listener) {
return myListeners.add(listener);
}
}
@@ -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 =
@@ -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<Element>{
@NonNls private static final String CONELEMENT = "conelement";
@NonNls private static final String FORCED_JDK = "forced_jdk";
private CachedXmlDocumentSet myDocumentSet;
private final Map<String, String> myEclipseVariablePaths = new HashMap<String, String>();
private final Set<String> myEclipseUrls = new HashSet<String>();
private final Set<String> myUnknownCons = new HashSet<String>();
private final Map<String, String> myEclipseVariablePaths = new LinkedHashMap<String, String>();
private final Set<String> myEclipseUrls = new LinkedHashSet<String>();
private final Set<String> myUnknownCons = new LinkedHashSet<String>();
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<Element>{
}
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<Element>{
}
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<Element>{
}
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)) {
@@ -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;
}
@@ -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();
}
}
@@ -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<XmlElement> {
private final NullableLazyValue<SchemaPrefix> myPrefix = new NullableLazyValue<SchemaPrefix>() {
@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<XmlElement> {
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<XmlElement> {
@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<XmlElement> {
}
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;
}
}
@@ -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<String> guessNamespace(final PsiFile file, String tagName) {
@@ -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;
@@ -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<PsiReference> refs) {
protected void addReferenceAtCaret(Collection<PsiReference> refs) {}
}
}.performInplaceRename();
};
}
}
}
public void invoke(@NotNull Project project, @NotNull PsiElement[] elements, DataContext dataContext) {
return null;
}
}