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

This commit is contained in:
Yann Cebron
2010-09-23 20:53:43 +02:00
85 changed files with 1454 additions and 633 deletions
+1 -1
View File
@@ -1 +1 @@
96.SNAPSHOT
98.SNAPSHOT
+1 -1
View File
@@ -67,7 +67,7 @@ def wire_build_date(String buildNumber) {
target('default': 'The default target') {
depends(compile)
layoutAll(
buildNumber: "IC-96.SNAPSHOT",
buildNumber: "IC-98.SNAPSHOT",
version: "IdeaX",
system_selector: "IntelliJIdeaCE10",
jdk_req: "1.6*",
@@ -44,7 +44,10 @@ public class RemoteConfiguration extends ModuleBasedConfiguration<JavaRunConfigu
public void writeExternal(final Element element) throws WriteExternalException {
super.writeExternal(element);
writeModule(element);
final Module module = getConfigurationModule().getModule();
if (module != null) { // default value
writeModule(element);
}
DefaultJDOMExternalizer.writeExternal(this, element);
}
@@ -15,8 +15,6 @@
*/
package com.intellij.facet.impl.ui.libraries;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.ui.configuration.libraryEditor.ExistingLibraryEditor;
import com.intellij.openapi.roots.ui.configuration.libraryEditor.LibraryRootsComponent;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.Disposer;
@@ -39,9 +37,7 @@ public class EditLibraryDialog extends DialogWrapper {
public EditLibraryDialog(Component parent, LibraryCompositionSettings settings) {
super(parent, true);
mySettings = settings;
final Library library = settings.getOrCreateLibrary();
myLibraryRootsComponent = LibraryRootsComponent.createComponent(new ExistingLibraryEditor(library, null));
myLibraryRootsComponent = LibraryRootsComponent.createComponent(settings.getOrCreateLibrary());
Disposer.register(getDisposable(), myLibraryRootsComponent);
@@ -18,11 +18,10 @@ package com.intellij.facet.impl.ui.libraries;
import com.intellij.facet.ui.libraries.LibraryDownloadInfo;
import com.intellij.facet.ui.libraries.LibraryInfo;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.roots.impl.libraries.ApplicationLibraryTable;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.ui.configuration.libraryEditor.NewLibraryEditor;
import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainer;
import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainerFactory;
import com.intellij.openapi.vfs.VfsUtil;
@@ -52,7 +51,7 @@ public class LibraryCompositionSettings implements Disposable {
private final Icon myIcon;
private boolean myDownloadSources = true;
private boolean myDownloadJavadocs = true;
private Library myLibrary;
private NewLibraryEditor myLibraryEditor;
public LibraryCompositionSettings(final @NotNull LibraryInfo[] libraryInfos,
final @NotNull String defaultLibraryName,
@@ -66,16 +65,10 @@ public class LibraryCompositionSettings implements Disposable {
}
public void addFilesToLibrary(VirtualFile[] files, OrderRootType orderRootType) {
final Library.ModifiableModel modifiableModel = getOrCreateLibrary().getModifiableModel();
final NewLibraryEditor libraryEditor = getOrCreateLibrary();
for (VirtualFile file : files) {
modifiableModel.addRoot(file, orderRootType);
libraryEditor.addRoot(file, orderRootType);
}
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
modifiableModel.commit();
}
});
}
@NotNull
@@ -156,9 +149,8 @@ public class LibraryCompositionSettings implements Disposable {
@Nullable
private Library createLibrary(final ModifiableRootModel rootModel, @Nullable LibrariesContainer additionalContainer) {
if (myLibrary != null) {
VirtualFile[] roots = myLibrary.getFiles(OrderRootType.CLASSES);
myLibrary.dispose();
if (myLibraryEditor != null) {
VirtualFile[] roots = myLibraryEditor.getFiles(OrderRootType.CLASSES);
return LibrariesContainerFactory.createLibrary(additionalContainer, LibrariesContainerFactory.createContainer(rootModel),
myLibraryName, myLibraryLevel, roots, VirtualFile.EMPTY_ARRAY);
}
@@ -217,25 +209,19 @@ public class LibraryCompositionSettings implements Disposable {
}
@Nullable
public Library getLibrary() {
return myLibrary;
public NewLibraryEditor getLibraryEditor() {
return myLibraryEditor;
}
@NotNull
public Library getOrCreateLibrary() {
if (myLibrary == null) {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
myLibrary = new ApplicationLibraryTable().createLibrary();
}
});
public NewLibraryEditor getOrCreateLibrary() {
if (myLibraryEditor == null) {
myLibraryEditor = new NewLibraryEditor();
}
return myLibrary;
return myLibraryEditor;
}
@Override
public void dispose() {
System.out.println("I'm disposed!");
}
}
@@ -22,6 +22,7 @@ import com.intellij.openapi.fileChooser.FileChooser;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.ui.configuration.libraryEditor.NewLibraryEditor;
import com.intellij.openapi.roots.ui.configuration.projectRoot.LibrariesContainer;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.Computable;
@@ -93,7 +94,7 @@ public class LibraryOptionsPanel {
showDialog(new DownloadingOptionsDialog(myConfigureButton, mySettings));
break;
case PICK_FILES:
if (mySettings.getLibrary() == null) {
if (mySettings.getLibraryEditor() == null) {
VirtualFile[] files = showFileChooser();
mySettings.addFilesToLibrary(files, OrderRootType.CLASSES);
}
@@ -175,8 +176,8 @@ public class LibraryOptionsPanel {
mySettings.getLibraryName());
break;
case PICK_FILES:
Library library = mySettings.getLibrary();
if (library == null) {
NewLibraryEditor libraryEditor = mySettings.getLibraryEditor();
if (libraryEditor == null) {
myMessage.setForeground(Color.red);
message = "Press Configure button to add classes to the library";
}
@@ -185,7 +186,7 @@ public class LibraryOptionsPanel {
" with {2} file(s) will be created",
mySettings.getLibraryLevel(),
mySettings.getLibraryName(),
library.getFiles(OrderRootType.CLASSES).length);
libraryEditor.getFiles(OrderRootType.CLASSES).length);
}
break;
case DO_NOT_CREATE:
@@ -61,7 +61,7 @@ class NewLibraryChooser implements ClasspathElementChooser<Library> {
List<LibraryTable> tables = Arrays.asList(myRootModel.getModuleLibraryTable(),
registrar.getLibraryTable(myProject),
registrar.getLibraryTable());
CreateNewLibraryDialog dialog = CreateNewLibraryDialog.createDialog(myParentComponent, myProject, this, tables, 1);
CreateNewLibraryDialog dialog = CreateNewLibraryDialog.createDialog(myParentComponent, myProject, tables, 1);
final Module contextModule = DataKeys.MODULE_CONTEXT.getData(DataManager.getInstance().getDataContext(myParentComponent));
dialog.addFileChooserContext(LangDataKeys.MODULE_CONTEXT, contextModule);
dialog.show();
@@ -33,7 +33,6 @@ import com.intellij.openapi.roots.ui.configuration.libraryEditor.CreateNewLibrar
import com.intellij.openapi.roots.ui.configuration.libraryEditor.NewLibraryEditor;
import com.intellij.openapi.roots.ui.configuration.projectRoot.ModuleStructureConfigurable;
import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
import com.intellij.openapi.util.Disposer;
import javax.swing.*;
import java.util.*;
@@ -74,14 +73,10 @@ public class CreateCustomLibraryAction extends DumbAwareAction {
dialog.show();
if (dialog.isOK()) {
final Library library = dialog.createLibrary(myContext.getModifiableLibraryTable(dialog.getSelectedTable()));
Disposer.dispose(libraryEditor);
final ModifiableRootModel rootModel = myContext.getModulesConfigurator().getOrCreateModuleEditor(myModule).getModifiableRootModelProxy();
final LibraryOrderEntry orderEntry = rootModel.addLibraryEntry(library);
myModuleStructureConfigurable.selectOrderEntry(myModule, orderEntry);
}
else {
Disposer.dispose(libraryEditor);
}
}
public static List<AnAction> getActions(StructureConfigurableContext context, ModuleStructureConfigurable moduleStructureConfigurable) {
@@ -15,14 +15,12 @@
*/
package com.intellij.openapi.roots.ui.configuration.libraryEditor;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.libraries.LibraryTable;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.openapi.util.Disposer;
import com.intellij.util.ui.FormBuilder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -38,12 +36,10 @@ public class CreateNewLibraryDialog extends LibraryEditorDialogBase {
private NewLibraryEditor myLibraryEditor;
private ComboBox myLibraryLevelCombobox;
public static CreateNewLibraryDialog createDialog(JComponent parent, @Nullable Project project, @NotNull Disposable parentDisposable,
public static CreateNewLibraryDialog createDialog(JComponent parent, @Nullable Project project,
@NotNull List<LibraryTable> libraryTables,
int selectedTable) {
NewLibraryEditor libraryEditor = new NewLibraryEditor();
Disposer.register(parentDisposable, libraryEditor);
return new CreateNewLibraryDialog(parent, project, libraryEditor, libraryTables, selectedTable);
return new CreateNewLibraryDialog(parent, project, new NewLibraryEditor(), libraryTables, selectedTable);
}
public CreateNewLibraryDialog(@NotNull JComponent parent, @Nullable Project project, @NotNull NewLibraryEditor libraryEditor,
@@ -15,13 +15,14 @@
*/
package com.intellij.openapi.roots.ui.configuration.libraryEditor;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.Nullable;
public class ExistingLibraryEditor implements LibraryEditor {
public class ExistingLibraryEditor implements LibraryEditor, Disposable {
private final Library myLibrary;
private final LibraryEditorListener myListener;
private String myLibraryName = null;
@@ -15,14 +15,13 @@
*/
package com.intellij.openapi.roots.ui.configuration.libraryEditor;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.vfs.VirtualFile;
/**
* @author nik
*/
public interface LibraryEditor extends Disposable {
public interface LibraryEditor {
String getName();
String[] getUrls(OrderRootType rootType);
@@ -102,7 +102,7 @@ public class LibraryRootsComponent implements Disposable {
private final Map<DataKey, Object> myFileChooserUserData = new HashMap<DataKey, Object>();
private final LibraryEditor myLibraryEditor;
private LibraryRootsComponent(Project project, LibraryEditor libraryEditor) {
private LibraryRootsComponent(@Nullable Project project, @NotNull LibraryEditor libraryEditor) {
myProject = project;
myLibraryEditor = libraryEditor;
updateProperties();
@@ -122,9 +122,6 @@ public class LibraryRootsComponent implements Disposable {
public static LibraryRootsComponent createComponent(final @Nullable Project project, @NotNull LibraryEditor libraryEditor) {
LibraryRootsComponent rootsComponent = new LibraryRootsComponent(project, libraryEditor);
rootsComponent.init(new LibraryTreeStructure(rootsComponent));
if (project != null) {
Disposer.register(project, rootsComponent);
}
return rootsComponent;
}
@@ -18,28 +18,25 @@ package com.intellij.openapi.roots.ui.configuration.libraryEditor;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.roots.impl.libraries.LibraryImpl;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.ui.LightFilePointer;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.pointers.VirtualFilePointer;
import com.intellij.openapi.vfs.pointers.VirtualFilePointerContainer;
import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
/**
* @author nik
*/
public class NewLibraryEditor implements LibraryEditor {
private String myLibraryName;
private final Map<OrderRootType, VirtualFilePointerContainer> myRoots;
private final MultiMap<OrderRootType, LightFilePointer> myRoots;
private final Map<String, Boolean> myJarDirectories = new HashMap<String, Boolean>();
public NewLibraryEditor() {
myRoots = new HashMap<OrderRootType, VirtualFilePointerContainer>();
for (OrderRootType rootType : OrderRootType.getAllTypes()) {
myRoots.put(rootType, VirtualFilePointerManager.getInstance().createContainer(this));
}
myRoots = new MultiMap<OrderRootType, LightFilePointer>();
}
@Override
@@ -49,12 +46,29 @@ public class NewLibraryEditor implements LibraryEditor {
@Override
public String[] getUrls(OrderRootType rootType) {
return myRoots.get(rootType).getUrls();
final Collection<LightFilePointer> pointers = myRoots.get(rootType);
List<String> urls = new ArrayList<String>();
for (LightFilePointer pointer : pointers) {
urls.add(pointer.getUrl());
}
return ArrayUtil.toStringArray(urls);
}
@Override
public VirtualFile[] getFiles(OrderRootType rootType) {
return LibraryImpl.getRootFiles(myRoots.get(rootType), myJarDirectories);
List<VirtualFile> result = new ArrayList<VirtualFile>();
for (LightFilePointer pointer : myRoots.get(rootType)) {
final VirtualFile file = pointer.getFile();
if (file.isDirectory()) {
final Boolean recursively = myJarDirectories.get(file.getUrl());
if (recursively != null) {
LibraryImpl.collectJarFiles(file, result, recursively);
continue;
}
}
result.add(file);
}
return VfsUtil.toVirtualFileArray(result);
}
@Override
@@ -64,12 +78,12 @@ public class NewLibraryEditor implements LibraryEditor {
@Override
public void addRoot(VirtualFile file, OrderRootType rootType) {
myRoots.get(rootType).add(file);
myRoots.putValue(rootType, new LightFilePointer(file));
}
@Override
public void addRoot(String url, OrderRootType rootType) {
myRoots.get(rootType).add(url);
myRoots.putValue(rootType, new LightFilePointer(url));
}
@Override
@@ -79,18 +93,14 @@ public class NewLibraryEditor implements LibraryEditor {
@Override
public void addJarDirectory(final String url, boolean recursive) {
myRoots.get(OrderRootType.CLASSES).add(url);
addRoot(url, OrderRootType.CLASSES);
myJarDirectories.put(url, recursive);
}
@Override
public void removeRoot(String url, OrderRootType rootType) {
final VirtualFilePointerContainer container = myRoots.get(rootType);
final VirtualFilePointer pointer = container.findByUrl(url);
if (pointer != null) {
myJarDirectories.remove(pointer.getUrl());
container.remove(pointer);
}
myRoots.removeValue(rootType, new LightFilePointer(url));
myJarDirectories.remove(url);
}
@Override
@@ -105,19 +115,20 @@ public class NewLibraryEditor implements LibraryEditor {
@Override
public boolean isValid(String url, OrderRootType orderRootType) {
final VirtualFilePointer pointer = myRoots.get(orderRootType).findByUrl(url);
return pointer != null && pointer.isValid();
}
@Override
public void dispose() {
final Collection<LightFilePointer> pointers = myRoots.get(orderRootType);
for (LightFilePointer pointer : pointers) {
if (pointer.getUrl().equals(url)) {
return pointer.isValid();
}
}
return false;
}
public void apply(@NotNull Library.ModifiableModel model) {
model.setName(myLibraryName);
for (Map.Entry<OrderRootType, VirtualFilePointerContainer> entry : myRoots.entrySet()) {
for (String url : entry.getValue().getUrls()) {
model.addRoot(url, entry.getKey());
for (OrderRootType type : myRoots.keySet()) {
for (LightFilePointer pointer : myRoots.get(type)) {
model.addRoot(pointer.getUrl(), type);
}
}
for (Map.Entry<String, Boolean> entry : myJarDirectories.entrySet()) {
@@ -137,7 +137,7 @@ public class LibrariesModifiableModel implements LibraryTable.ModifiableModel {
}
private void removeLibraryEditor(final Library library) {
final LibraryEditor libraryEditor = myLibrary2EditorMap.remove(library);
final ExistingLibraryEditor libraryEditor = myLibrary2EditorMap.remove(library);
if (libraryEditor != null) {
Disposer.dispose(libraryEditor);
}
@@ -162,7 +162,7 @@ public class LibrariesModifiableModel implements LibraryTable.ModifiableModel {
Disposer.dispose(library);
}
final LibraryEditor libraryEditor = myLibrary2EditorMap.get(library);
final ExistingLibraryEditor libraryEditor = myLibrary2EditorMap.get(library);
if (libraryEditor != null) {
Disposer.dispose(libraryEditor);
}
@@ -26,6 +26,7 @@ import com.intellij.openapi.roots.ui.configuration.libraryEditor.LibraryEditor;
import com.intellij.openapi.roots.ui.configuration.libraryEditor.LibraryRootsComponent;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.LibraryProjectStructureElement;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
import com.intellij.openapi.util.Disposer;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
@@ -85,7 +86,10 @@ public class LibraryConfigurable extends ProjectStructureElementConfigurable<Lib
}
public void disposeUIResources() {
myLibraryEditor = null;
if (myLibraryEditor != null) {
Disposer.dispose(myLibraryEditor);
myLibraryEditor = null;
}
}
public void setDisplayName(final String name) {
@@ -245,14 +245,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
@Override public void visitClass(PsiClass aClass) {
super.visitClass(aClass);
if (aClass instanceof JspClass) return;
if (aClass.isAnnotationType()) {
if (!PsiUtil.isLanguageLevel5OrHigher(aClass)) {
HighlightInfo info = HighlightInfo
.createHighlightInfo(HighlightInfoType.ERROR, aClass.getNameIdentifier(), JavaErrorMessages.message("annotations.prior.15"));
QuickFixAction.registerQuickFixAction(info, new IncreaseLanguageLevelFix(LanguageLevel.JDK_1_5));
myHolder.add(info);
}
}
if (!myHolder.hasErrorResults()) myHolder.add(GenericsHighlightUtil.checkInterfaceMultipleInheritance(aClass));
if (!myHolder.hasErrorResults()) myHolder.add(HighlightClassUtil.checkDuplicateTopLevelClass(aClass));
if (!myHolder.hasErrorResults()) myHolder.add(GenericsHighlightUtil.checkEnumMustNotBeLocal(aClass));
@@ -391,10 +383,17 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
myHolder.add(HighlightUtil.checkVariableAlreadyDefined((PsiVariable)parent));
}
else if (parent instanceof PsiClass) {
myHolder.add(HighlightClassUtil.checkClassAlreadyImported((PsiClass)parent, identifier));
myHolder.add(HighlightClassUtil.checkExternalizableHasPublicNoArgsConstructor((PsiClass)parent, identifier));
if (!(parent instanceof PsiAnonymousClass)) {
myHolder.add(HighlightNamesUtil.highlightClassName((PsiClass)parent, ((PsiClass)parent).getNameIdentifier()));
PsiClass aClass = (PsiClass)parent;
if (aClass.isAnnotationType() && !PsiUtil.isLanguageLevel5OrHigher(aClass)) {
HighlightInfo info = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, identifier, JavaErrorMessages.message("annotations.prior.15"));
QuickFixAction.registerQuickFixAction(info, new IncreaseLanguageLevelFix(LanguageLevel.JDK_1_5));
myHolder.add(info);
}
myHolder.add(HighlightClassUtil.checkClassAlreadyImported(aClass, identifier));
myHolder.add(HighlightClassUtil.checkExternalizableHasPublicNoArgsConstructor(aClass, identifier));
if (!(parent instanceof PsiAnonymousClass) && aClass.getNameIdentifier() == identifier) {
myHolder.add(HighlightNamesUtil.highlightClassName(aClass, identifier));
}
}
else if (parent instanceof PsiMethod) {
@@ -103,7 +103,7 @@ public abstract class ImportClassFixBase<T extends PsiElement & PsiReference> im
public enum Result {
POPUP_SHOWN,
CLASS_IMPORTED,
CLASS_AUTO_IMPORTED,
POPUP_NOT_SHOWN
}
@@ -149,7 +149,7 @@ public abstract class ImportClassFixBase<T extends PsiElement & PsiReference> im
action.execute();
}
});
return Result.CLASS_IMPORTED;
return Result.CLASS_AUTO_IMPORTED;
}
if (doShow && canImportHere) {
@@ -174,7 +174,7 @@ public abstract class ImportClassFixBase<T extends PsiElement & PsiReference> im
return false;
}
Result result = doFix(editor, true, false);
return result == Result.POPUP_SHOWN || result == Result.CLASS_IMPORTED;
return result == Result.POPUP_SHOWN || result == Result.CLASS_AUTO_IMPORTED;
}
@NotNull
@@ -205,7 +205,7 @@ public class ImportHelperTest extends DaemonAnalyzerTestCase {
myEditor.getCaretModel().moveToOffset(offset - 1);
result = new ImportClassFix((PsiJavaCodeReferenceElement)ref).doFix(getEditor(), true, false);
assertEquals(ImportClassFixBase.Result.CLASS_IMPORTED, result);
assertEquals(ImportClassFixBase.Result.CLASS_AUTO_IMPORTED, result);
UIUtil.dispatchAllInvocationEvents();
assertEmpty(filter(doHighlighting(), HighlightSeverity.ERROR));
@@ -234,7 +234,7 @@ public class ImportHelperTest extends DaemonAnalyzerTestCase {
assertTrue(ref instanceof PsiJavaCodeReferenceElement);
ImportClassFixBase.Result result = new ImportClassFix((PsiJavaCodeReferenceElement)ref).doFix(getEditor(), true, false);
assertEquals(ImportClassFixBase.Result.CLASS_IMPORTED, result);
assertEquals(ImportClassFixBase.Result.CLASS_AUTO_IMPORTED, result);
UIUtil.dispatchAllInvocationEvents();
assertEmpty(filter(doHighlighting(), HighlightSeverity.ERROR));
@@ -265,7 +265,7 @@ public class ImportHelperTest extends DaemonAnalyzerTestCase {
(PsiJavaCodeReferenceElement)getFile().findReferenceAt(getEditor().getCaretModel().getOffset() - 2);
ImportClassFix fix = new ImportClassFix(element);
ImportClassFixBase.Result result = fix.doFix(getEditor(), false, false);
assertEquals(ImportClassFixBase.Result.CLASS_IMPORTED, result);
assertEquals(ImportClassFixBase.Result.CLASS_AUTO_IMPORTED, result);
assertNotSame(0, ((PsiJavaFile)getFile()).getImportList().getAllImportStatements().length);
}
@@ -30,8 +30,6 @@ 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);
}
@@ -42,8 +40,6 @@ public abstract class FileColorManager {
public abstract boolean isEnabledForTabs();
public abstract boolean isHighlightNonProjectFiles();
@SuppressWarnings({"MethodMayBeStatic"})
@Nullable
public abstract Color getColor(@NotNull String name);
@@ -52,19 +52,21 @@ public class Divider {
}
}
}
private static void getInsideAndOutside(@NotNull PsiFile root,
int startOffset,
int endOffset,
@NotNull TextRange range,
@NotNull List<PsiElement> inside,
@NotNull List<PsiElement> outside) {
@NotNull List<PsiElement> outside
) {
final int currentOffset = root.getTextRange().getStartOffset();
final Condition<PsiElement>[] filters = Extensions.getExtensions(CollectHighlightsUtil.EP_NAME);
int offset = currentOffset;
final TIntStack starts = new TIntStack(STARTING_TREE_HEIGHT);
starts.push(startOffset);
final Stack<PsiElement> elements = new Stack<PsiElement>(STARTING_TREE_HEIGHT);
final Stack<PsiElement> children = new Stack<PsiElement>(STARTING_TREE_HEIGHT);
PsiElement element = root;
@@ -96,7 +98,6 @@ public class Divider {
offset += element.getTextLength();
}
if (elements.isEmpty()) break;
int start = starts.pop();
if (startOffset <= start && offset <= endOffset) {
if (range.containsRange(start, offset)) {
@@ -107,6 +108,7 @@ public class Divider {
}
}
if (elements.isEmpty()) break;
element = elements.pop();
child = children.pop();
}
@@ -120,7 +122,5 @@ public class Divider {
child = PsiUtilBase.NULL_PSI_ELEMENT;
}
}
outside.add(root);
}
}
@@ -31,12 +31,15 @@ import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.HighlighterColors;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.editor.colors.EditorColors;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.colors.EditorColorsScheme;
import com.intellij.openapi.editor.colors.TextAttributesKey;
import com.intellij.openapi.editor.ex.MarkupModelEx;
import com.intellij.openapi.editor.markup.MarkupModel;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileTypes.SyntaxHighlighter;
@@ -52,6 +55,7 @@ import com.intellij.problems.Problem;
import com.intellij.problems.WolfTheProblemSolver;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
import com.intellij.psi.impl.source.tree.injected.Place;
import com.intellij.psi.search.PsiSearchHelper;
import com.intellij.psi.search.TodoItem;
import com.intellij.psi.tree.IElementType;
@@ -60,6 +64,7 @@ import com.intellij.util.SmartList;
import com.intellij.util.ui.UIUtil;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.util.*;
@@ -74,10 +79,10 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
private final int myStartOffset;
private final int myEndOffset;
private final boolean myUpdateAll;
private final TextRange myPriorityRange;
private final ProperTextRange myPriorityRange;
private final Editor myEditor;
private volatile Collection<HighlightInfo> myHighlights = Collections.emptyList();
private final Map<TextRange,Collection<HighlightInfo>> myInjectedPsiHighlights = new HashMap<TextRange, Collection<HighlightInfo>>();
private final Collection<HighlightInfo> myHighlights = new ArrayList<HighlightInfo>();
protected volatile boolean myHasErrorElement;
private volatile boolean myErrorFound;
@@ -86,15 +91,15 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
return o1.order() - o2.order();
}
};
private Runnable myApplyCommand;
public GeneralHighlightingPass(@NotNull Project project,
@NotNull PsiFile file,
@NotNull Document document,
int startOffset,
int endOffset,
boolean updateAll
) {
this(project, file, document, startOffset, endOffset, updateAll, new TextRange(0,0));
boolean updateAll) {
this(project, file, document, startOffset, endOffset, updateAll, new ProperTextRange(0,document.getTextLength()), null);
}
public GeneralHighlightingPass(@NotNull Project project,
@NotNull PsiFile file,
@@ -102,18 +107,29 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
int startOffset,
int endOffset,
boolean updateAll,
@NotNull TextRange priorityRange) {
@NotNull ProperTextRange priorityRange,
@Nullable Editor editor) {
super(project, document, PRESENTABLE_NAME, file, true);
myStartOffset = startOffset;
myEndOffset = endOffset;
myUpdateAll = updateAll;
myPriorityRange = priorityRange;
myEditor = editor;
LOG.assertTrue(file.isValid());
setId(Pass.UPDATE_ALL);
myHasErrorElement = !isWholeFileHighlighting() && Boolean.TRUE.equals(myFile.getUserData(HAS_ERROR_ELEMENT));
FileStatusMap fileStatusMap = ((DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(myProject)).getFileStatusMap();
myErrorFound = !isWholeFileHighlighting() && fileStatusMap.wasErrorFound(myDocument);
myApplyCommand = new Runnable() {
public void run() {
ProperTextRange range = new ProperTextRange(myStartOffset, myEndOffset);
MarkupModel model = myDocument.getMarkupModel(myProject);
UpdateHighlightersUtil.cleanFileLevelHighlights(myProject, Pass.UPDATE_ALL,myFile);
UpdateHighlightersUtil.setHighlightersInRange(range, myHighlights, (MarkupModelEx)model, Pass.UPDATE_ALL, myDocument, myProject);
}
};
}
private static final Key<AtomicInteger> HIGHLIGHT_VISITOR_INSTANCE_COUNT = new Key<AtomicInteger>("HIGHLIGHT_VISITOR_INSTANCE_COUNT");
@@ -144,26 +160,94 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
}
protected void collectInformationWithProgress(final ProgressIndicator progress) {
final Collection<HighlightInfo> result = new THashSet<HighlightInfo>(100);
final Set<HighlightInfo> result = new THashSet<HighlightInfo>(100);
final Set<HighlightInfo> outsideResult = new THashSet<HighlightInfo>(100);
DaemonCodeAnalyzer daemonCodeAnalyzer = DaemonCodeAnalyzer.getInstance(myProject);
FileStatusMap fileStatusMap = ((DaemonCodeAnalyzerImpl)daemonCodeAnalyzer).getFileStatusMap();
final FileStatusMap fileStatusMap = ((DaemonCodeAnalyzerImpl)daemonCodeAnalyzer).getFileStatusMap();
HighlightVisitor[] highlightVisitors = createHighlightVisitors();
HighlightVisitor[] filteredVisitors = filterVisitors(highlightVisitors, myFile);
List<PsiElement> inside = new ArrayList<PsiElement>();
List<PsiElement> outside = new ArrayList<PsiElement>();
final HighlightVisitor[] filteredVisitors = filterVisitors(highlightVisitors, myFile);
final List<PsiElement> inside = new ArrayList<PsiElement>();
final List<PsiElement> outside = new ArrayList<PsiElement>();
try {
Divider.getInsideAndOutside(myFile, myStartOffset, myEndOffset, myPriorityRange, inside, outside, HighlightLevelUtil.AnalysisLevel.HIGHLIGHT);
final List<PsiFile> injectedInside = new ArrayList<PsiFile>();
final List<PsiFile> injectedOutside = new ArrayList<PsiFile>();
divideInjectedPsiHighlights(inside, outside, progress, injectedInside, injectedOutside);
List<PsiElement> elements = inside;
elements.addAll(outside);
setProgressLimit((long)(inside.size()+outside.size()) /** filteredVisitors.length*/);
result.addAll(collectHighlights(elements, progress, filteredVisitors));
if (!addInjectedPsiHighlights(elements, progress)) throw new ProcessCanceledException();
final boolean forceHighlightParents = forceHighlightParents();
if (!isDumbMode()) {
result.addAll(highlightTodos(myFile, myDocument.getCharsSequence(), 0, myFile.getTextLength(), progress));
highlightTodos(myFile, myDocument.getCharsSequence(), myStartOffset, myEndOffset, progress, myPriorityRange, result, outsideResult);
}
collectHighlights(inside, new Runnable() {
@Override
public void run() {
if (!addInjectedPsiHighlights(injectedInside, progress, Collections.synchronizedSet(result))) throw new ProcessCanceledException();
if (!outside.isEmpty() || !injectedOutside.isEmpty()) {
if (!inside.isEmpty()) { // do not apply when there were no elements to highlight
// clear infos found in visible area to avoid applying them twice
final List<HighlightInfo> toApply = new ArrayList<HighlightInfo>(result.size());
for (HighlightInfo info : result) {
if (myPriorityRange.containsRange(info.getStartOffset(), info.getEndOffset())) {
toApply.add(info);
}
else {
outsideResult.add(info);
}
}
myHighlights.addAll(toApply);
result.clear();
result.addAll(outsideResult);
UIUtil.invokeLaterIfNeeded(new Runnable() {
@Override
public void run() {
if (progress.isCanceled()) return;
MarkupModel markupModel = myDocument.getMarkupModel(myProject);
UpdateHighlightersUtil.cleanFileLevelHighlights(myProject, Pass.UPDATE_ALL, myFile);
UpdateHighlightersUtil.setHighlightersInRange(myPriorityRange, toApply, (MarkupModelEx)markupModel, Pass.UPDATE_ALL, myDocument, myProject);
}
});
UIUtil.invokeLaterIfNeeded(new Runnable() {
@Override
public void run() {
if (progress.isCanceled() || myEditor == null) return;
new ShowAutoImportPass(myProject, myFile, myEditor).applyInformationToEditor();
}
});
}
final List<HighlightInfo> injectedOutsideInfos = Collections.synchronizedList(new ArrayList<HighlightInfo>());
if (!addInjectedPsiHighlights(injectedOutside, progress, injectedOutsideInfos)) throw new ProcessCanceledException();
myApplyCommand = new Runnable() {
@Override
public void run() {
UpdateHighlightersUtil.setHighlightersToEditorOutsideRange(myProject, myDocument, result, myStartOffset, myEndOffset, myPriorityRange, Pass.UPDATE_ALL);
Map<TextRange,Collection<HighlightInfo>> myInjectedPsiHighlights = new HashMap<TextRange, Collection<HighlightInfo>>();
for (HighlightInfo info : injectedOutsideInfos) {
TextRange textRange = new TextRange(info.getStartOffset(), info.getEndOffset());
Collection<HighlightInfo> storedInfos = myInjectedPsiHighlights.get(textRange);
if (storedInfos == null) {
storedInfos = new SmartList<HighlightInfo>();
myInjectedPsiHighlights.put(textRange, storedInfos);
}
storedInfos.add(info);
}
UpdateHighlightersUtil.setHighlightersToEditor(myProject, myDocument, myInjectedPsiHighlights, Pass.UPDATE_ALL);
}
};
}
}
}, outside, progress, filteredVisitors, result, forceHighlightParents);
if (myUpdateAll) {
fileStatusMap.setErrorFoundFlag(myDocument, myErrorFound);
}
@@ -171,17 +255,21 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
finally {
incVisitorUsageCount(-1);
}
myHighlights = result;
myHighlights.addAll(result);
}
// returns false if canceled
private boolean addInjectedPsiHighlights(@NotNull final List<PsiElement> elements, final ProgressIndicator progress) {
private void divideInjectedPsiHighlights(@NotNull final List<PsiElement> elements1,
@NotNull final List<PsiElement> elements2,
@NotNull final ProgressIndicator progress,
@NotNull List<PsiFile> inside,
@NotNull List<PsiFile> outside) {
List<DocumentWindow> injected = InjectedLanguageUtil.getCachedInjectedDocuments(myFile);
Collection<PsiElement> hosts = new THashSet<PsiElement>(elements.size() + injected.size());
Collection<PsiElement> hosts = new THashSet<PsiElement>(elements1.size() + elements2.size() + injected.size());
// rehighlight all injected PSI regardless the range,
// since change in one place can lead to invalidation of injected PSI in (completely) other place.
for (DocumentWindow documentRange : injected) {
progress.checkCanceled();
if (!documentRange.isValid()) continue;
PsiFile file = PsiDocumentManager.getInstance(myProject).getPsiFile(documentRange);
if (file == null) continue;
@@ -193,40 +281,50 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
hosts.add(context);
}
}
hosts.addAll(elements);
final Collection<PsiFile> injectedFiles = new THashSet<PsiFile>();
EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme();
final TextAttributes injectedAttributes = scheme.getAttributes(EditorColors.INJECTED_LANGUAGE_FRAGMENT);
hosts.addAll(elements1);
hosts.addAll(elements2);
for (PsiElement element : hosts) {
progress.checkCanceled();
final List<PsiFile> destination = myPriorityRange.contains(element.getTextRange()) ? inside : outside;
InjectedLanguageUtil.enumerate(element, myFile, new PsiLanguageInjectionHost.InjectedPsiVisitor() {
public void visit(@NotNull PsiFile injectedPsi, @NotNull List<PsiLanguageInjectionHost.Shred> places) {
if (injectedFiles.add(injectedPsi)) { // for concatenations there can be many injection hosts with only one injected PSI
for (PsiLanguageInjectionHost.Shred place : places) {
TextRange textRange = place.getRangeInsideHost().shiftRight(place.host.getTextRange().getStartOffset());
if (textRange.isEmpty()) continue;
String desc = injectedPsi.getLanguage().getDisplayName() + ": " + injectedPsi.getText();
HighlightInfo info = HighlightInfo.createHighlightInfo(HighlightInfoType.INJECTED_LANGUAGE_FRAGMENT, textRange, null, desc, injectedAttributes);
addHighlightInfo(textRange, info);
}
}
destination.add(injectedPsi); // for concatenations there can be many injection hosts with only one injected PSI
}
}, false);
}
}
// returns false if canceled
private boolean addInjectedPsiHighlights(@NotNull final List<PsiFile> injectedFiles,
final ProgressIndicator progress,
final Collection<HighlightInfo> infos) {
EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme();
final TextAttributes injectedAttributes = scheme.getAttributes(EditorColors.INJECTED_LANGUAGE_FRAGMENT);
if (injectedFiles.isEmpty()) return true;
final InjectedLanguageManager injectedLanguageManager = InjectedLanguageManager.getInstance(myProject);
return JobUtil.invokeConcurrentlyUnderMyProgress(new ArrayList<PsiFile>(injectedFiles), new Processor<PsiFile>() {
public boolean process(final PsiFile injectedPsi) {
DocumentWindow documentWindow = (DocumentWindow)PsiDocumentManager.getInstance(myProject).getCachedDocument(injectedPsi);
Place places = InjectedLanguageUtil.getShreds(injectedPsi);
for (PsiLanguageInjectionHost.Shred place : places) {
TextRange textRange = place.getRangeInsideHost().shiftRight(place.host.getTextRange().getStartOffset());
if (textRange.isEmpty()) continue;
String desc = injectedPsi.getLanguage().getDisplayName() + ": " + injectedPsi.getText();
HighlightInfo info = HighlightInfo.createHighlightInfo(HighlightInfoType.INJECTED_LANGUAGE_FRAGMENT, textRange, null, desc, injectedAttributes);
infos.add(info);
}
HighlightInfoHolder holder = createInfoHolder(injectedPsi);
runHighlightVisitosForInjected(injectedPsi, holder, progress);
for (int i=0; i<holder.size();i++) {
HighlightInfo info = holder.get(i);
final int startOffset = documentWindow.injectedToHost(info.startOffset);
final TextRange fixedTextRange = getFixedTextRange(documentWindow, startOffset);
addPatchedInfos(info, injectedPsi, documentWindow, injectedLanguageManager, fixedTextRange);
addPatchedInfos(info, injectedPsi, documentWindow, injectedLanguageManager, fixedTextRange, infos);
}
holder.clear();
highlightInjectedSyntax(injectedPsi, holder);
@@ -235,20 +333,21 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
final int startOffset = info.startOffset;
final TextRange fixedTextRange = getFixedTextRange(documentWindow, startOffset);
if (fixedTextRange == null) {
addHighlightInfo(new TextRange(info.startOffset, info.endOffset), info);
infos.add(info);
}
else {
HighlightInfo patched =
new HighlightInfo(info.forcedTextAttributes, info.type, fixedTextRange.getStartOffset(), fixedTextRange.getEndOffset(),
info.description, info.toolTip, info.type.getSeverity(null), info.isAfterEndOfLine, null, false);
addHighlightInfo(fixedTextRange, patched);
infos.add(patched);
}
}
if (!isDumbMode()) {
Collection<HighlightInfo> todos = highlightTodos(injectedPsi, injectedPsi.getText(), 0, injectedPsi.getTextLength(), progress);
List<HighlightInfo> todos = new ArrayList<HighlightInfo>();
highlightTodos(injectedPsi, injectedPsi.getText(), 0, injectedPsi.getTextLength(), progress, myPriorityRange, todos, todos);
for (HighlightInfo info : todos) {
addPatchedInfos(info, injectedPsi, documentWindow, injectedLanguageManager, null);
addPatchedInfos(info, injectedPsi, documentWindow, injectedLanguageManager, null, infos);
}
}
return true;
@@ -272,8 +371,12 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
return fixedTextRange;
}
private void addPatchedInfos(HighlightInfo info, PsiFile injectedPsi, DocumentWindow documentWindow, InjectedLanguageManager injectedLanguageManager,
TextRange fixedTextRange) {
private static void addPatchedInfos(HighlightInfo info,
PsiFile injectedPsi,
DocumentWindow documentWindow,
InjectedLanguageManager injectedLanguageManager,
TextRange fixedTextRange,
Collection<HighlightInfo> out) {
ProperTextRange textRange = new ProperTextRange(info.startOffset, info.endOffset);
List<TextRange> editables = injectedLanguageManager.intersectWithAllEditableFragments(injectedPsi, textRange);
for (TextRange editable : editables) {
@@ -297,18 +400,7 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
}
}
}
addHighlightInfo(hostRange, patched);
}
}
private void addHighlightInfo(@NotNull TextRange textRange, @NotNull HighlightInfo highlightInfo) {
synchronized (myInjectedPsiHighlights) {
Collection<HighlightInfo> infos = myInjectedPsiHighlights.get(textRange);
if (infos == null) {
infos = new SmartList<HighlightInfo>();
myInjectedPsiHighlights.put(textRange, infos);
}
infos.add(highlightInfo);
out.add(patched);
}
}
@@ -393,14 +485,7 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
protected void applyInformationWithProgress() {
myFile.putUserData(HAS_ERROR_ELEMENT, myHasErrorElement);
ProperTextRange range = new ProperTextRange(myStartOffset, myEndOffset);
Collection<HighlightInfo> infos = myInjectedPsiHighlights.get(range);
if (infos == null) {
infos = new ArrayList<HighlightInfo>(myHighlights.size());
myInjectedPsiHighlights.put(range, infos);
}
infos.addAll(myHighlights);
UpdateHighlightersUtil.setHighlightersToEditor(myProject, myDocument, myInjectedPsiHighlights, Pass.UPDATE_ALL);
myApplyCommand.run();
if (myUpdateAll) {
reportErrorsToWolf();
@@ -409,27 +494,24 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
@NotNull
Collection<HighlightInfo> getHighlights() {
ArrayList<HighlightInfo> list = new ArrayList<HighlightInfo>(myHighlights);
for (Collection<HighlightInfo> infos : myInjectedPsiHighlights.values()) {
list.addAll(infos);
}
return list;
return new ArrayList<HighlightInfo>(myHighlights);
}
private Collection<HighlightInfo> collectHighlights(@NotNull final List<PsiElement> elements, @NotNull final ProgressIndicator progress,
final HighlightVisitor[] visitors) {
private void collectHighlights(@NotNull final List<PsiElement> elements1,
@NotNull final Runnable after1,
@NotNull final List<PsiElement> elements2,
@NotNull final ProgressIndicator progress,
@NotNull final HighlightVisitor[] visitors,
@NotNull final Set<HighlightInfo> gotHighlights,
final boolean forceHighlightParents) {
final Set<PsiElement> skipParentsSet = new THashSet<PsiElement>();
final Set<HighlightInfo> gotHighlights = new THashSet<HighlightInfo>();
final boolean forceHighlightParents = forceHighlightParents();
final HighlightInfoHolder holder = createInfoHolder(myFile);
setProgressLimit((long)elements.size() * visitors.length);
final int chunkSize = Math.max(1, elements.size() / 100); // one percent precision is enough
for (final HighlightVisitor visitor : visitors) {
Runnable action = new Runnable() {
public void run() {
final int chunkSize = Math.max(1, (elements1.size()+elements2.size()) / 100); // one percent precision is enough
final Runnable action = new Runnable() {
public void run() {
for (List<PsiElement> elements : new List[]{elements1, elements2}) {
int nextLimit = chunkSize;
for (int i = 0; i < elements.size(); i++) {
PsiElement element = elements.get(i);
@@ -445,7 +527,10 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
}
holder.clear();
visitor.visit(element, holder);
for (final HighlightVisitor visitor : visitors) {
visitor.visit(element, holder);
}
if (i == nextLimit) {
advanceProgress(chunkSize);
nextLimit = i + chunkSize;
@@ -466,21 +551,35 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
}
UIUtil.invokeLaterIfNeeded(new Runnable() {
public void run() {
if (myProject.isDisposed()) return;
if (progress.isCanceled()) return;
UpdateHighlightersUtil.addHighlighterToEditorIncrementally(myProject, myDocument, myFile, 0, myDocument.getTextLength(), info, Pass.UPDATE_ALL);
}
});
}
}
advanceProgress(elements.size() - (nextLimit-chunkSize));
if (elements == elements1) after1.run();
}
};
if (!visitor.analyze(action, myUpdateAll, myFile)) {
}
};
analyzeByVisitors(progress, visitors, action, 0);
}
private void analyzeByVisitors(final ProgressIndicator progress, final HighlightVisitor[] visitors, final Runnable action, final int i) {
if (i == visitors.length) {
action.run();
}
else {
if (!visitors[i].analyze(new Runnable() {
@Override
public void run() {
analyzeByVisitors(progress, visitors, action, i+1);
}
}, myUpdateAll, myFile)) {
cancelAndRestartDaemonLater(progress, myProject, this);
}
}
return gotHighlights;
}
private static HighlightVisitor[] filterVisitors(HighlightVisitor[] highlightVisitors, final PsiFile file) {
@@ -529,17 +628,19 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
return new HighlightInfoHolder(file, filters);
}
private static Collection<HighlightInfo> highlightTodos(@NotNull PsiFile file,
@NotNull CharSequence text,
int startOffset,
int endOffset,
@NotNull ProgressIndicator progress) {
private static void highlightTodos(@NotNull PsiFile file,
@NotNull CharSequence text,
int startOffset,
int endOffset,
@NotNull ProgressIndicator progress,
@NotNull ProperTextRange priorityRange,
@NotNull Collection<HighlightInfo> result,
@NotNull Collection<HighlightInfo> outsideResult) {
PsiManager psiManager = file.getManager();
PsiSearchHelper helper = psiManager.getSearchHelper();
TodoItem[] todoItems = helper.findTodoItems(file, startOffset, endOffset);
if (todoItems.length == 0) return Collections.emptyList();
if (todoItems.length == 0) return;
List<HighlightInfo> list = new ArrayList<HighlightInfo>(todoItems.length);
for (TodoItem todoItem : todoItems) {
progress.checkCanceled();
TextRange range = todoItem.getTextRange();
@@ -547,9 +648,13 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
TextAttributes attributes = todoItem.getPattern().getAttributes().getTextAttributes();
HighlightInfo info = HighlightInfo.createHighlightInfo(HighlightInfoType.TODO, range, description, description, attributes);
assert info != null;
list.add(info);
if (priorityRange.containsRange(info.getStartOffset(), info.getEndOffset())) {
result.add(info);
}
else {
outsideResult.add(info);
}
}
return list;
}
private void reportErrorsToWolf() {
@@ -576,7 +681,8 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
return myUpdateAll ? super.getProgress() : -1;
}
private static List<Problem> convertToProblems(final Collection<HighlightInfo> infos, final VirtualFile file,
private static List<Problem> convertToProblems(final Collection<HighlightInfo> infos,
final VirtualFile file,
final boolean hasErrorElement) {
List<Problem> problems = new SmartList<Problem>();
for (HighlightInfo info : infos) {
@@ -23,6 +23,7 @@ import com.intellij.codeHighlighting.TextEditorHighlightingPassRegistrar;
import com.intellij.openapi.components.AbstractProjectComponent;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.ProperTextRange;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NonNls;
@@ -51,7 +52,7 @@ public class GeneralHighlightingPassFactory extends AbstractProjectComponent imp
TextRange textRange = FileStatusMap.getDirtyTextRange(editor, Pass.UPDATE_ALL);
if (textRange == null) return new ProgressableTextEditorHighlightingPass.EmptyPass(myProject, editor.getDocument()
);
TextRange visibleRange = VisibleHighlightingPassFactory.calculateVisibleRange(editor);
return new GeneralHighlightingPass(myProject, file, editor.getDocument(), textRange.getStartOffset(), textRange.getEndOffset(), true, visibleRange);
ProperTextRange visibleRange = VisibleHighlightingPassFactory.calculateVisibleRange(editor);
return new GeneralHighlightingPass(myProject, file, editor.getDocument(), textRange.getStartOffset(), textRange.getEndOffset(), true, visibleRange, editor);
}
}
@@ -64,8 +64,7 @@ public class LineMarkersPass extends ProgressableTextEditorHighlightingPass impl
int startOffset,
int endOffset,
boolean updateAll) {
super(project, document, GeneralHighlightingPass.PRESENTABLE_NAME, file,
false);
super(project, document, GeneralHighlightingPass.PRESENTABLE_NAME, file, false);
myStartOffset = startOffset;
myEndOffset = endOffset;
myUpdateAll = updateAll;
@@ -38,10 +38,7 @@ import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.TextEditor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.*;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
@@ -94,14 +91,8 @@ public class UpdateHighlightersUtil {
return o1.getStartOffset() - o2.getStartOffset();
}
};
private static final Comparator<TextRange> BY_START_OFFSET_OR_CONTAINS = new Comparator<TextRange>() {
public int compare(final TextRange o1, final TextRange o2) {
if (o1.contains(o2) || o2.contains(o1)) return 0;
return o1.getStartOffset() - o2.getStartOffset();
}
};
private static void cleanFileLevelHighlights(@NotNull Project project, final int group, PsiFile psiFile) {
public static void cleanFileLevelHighlights(@NotNull Project project, final int group, PsiFile psiFile) {
if (psiFile == null || !psiFile.getViewProvider().isPhysical()) return;
VirtualFile vFile = psiFile.getViewProvider().getVirtualFile();
final FileEditorManager manager = FileEditorManager.getInstance(project);
@@ -191,11 +182,10 @@ public class UpdateHighlightersUtil {
return;
}
boolean success = createOrReuseHighlighterFor(info, document, group, file, (MarkupModelEx)markup, null, null, startOffset, endOffset,
SeverityRegistrar.getInstance(project));
if (!success) {
return;
}
if (info.getStartOffset() < startOffset || info.getEndOffset() > endOffset) return;
createOrReuseHighlighterFor(info, document, group, file, (MarkupModelEx)markup, null, null,
SeverityRegistrar.getInstance(project));
DaemonCodeAnalyzerImpl.addHighlight(markup, project, info);
clearWhiteSpaceOptimizationFlag(document);
@@ -241,7 +231,24 @@ public class UpdateHighlightersUtil {
}
}
private static void setHighlightersInRange(final TextRange range,
// set highlights inside startOffset,endOffset but outside range
public static void setHighlightersToEditorOutsideRange(@NotNull Project project,
@NotNull Document document,
@NotNull Collection<HighlightInfo> infos,
int startOffset, int endOffset, @NotNull ProperTextRange range,
final int group) {
ApplicationManager.getApplication().assertIsDispatchThread();
PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document);
cleanFileLevelHighlights(project, group, psiFile);
MarkupModel markup = document.getMarkupModel(project);
assertMarkupConsistent(markup, project);
setHighlightersOutsideRange(startOffset, endOffset, range, infos, (MarkupModelEx)markup, group, document, project);
}
public static void setHighlightersInRange(final TextRange range,
Collection<HighlightInfo> highlightsCo,
final MarkupModelEx markup,
final int group,
@@ -293,10 +300,88 @@ public class UpdateHighlightersUtil {
if (isWarningCoveredByError(info, overlappingIntervals, severityRegistrar)) {
return true;
}
boolean success = createOrReuseHighlighterFor(info, document, group, psiFile, markup, infosToRemove,
ranges2markersCache, range.getStartOffset(), range.getEndOffset(),
if (info.getStartOffset() >= range.getStartOffset() && info.getEndOffset() <= range.getEndOffset()) {
createOrReuseHighlighterFor(info, document, group, psiFile, markup, infosToRemove,
ranges2markersCache,
severityRegistrar);
changed[0] = true;
}
return true;
}
});
for (RangeHighlighter highlighter : infosToRemove.forAllInGarbageBin()) {
markup.removeHighlighter(highlighter);
changed[0] = true;
}
if (changed[0]) {
clearWhiteSpaceOptimizationFlag(document);
}
assertMarkupConsistent(markup, project);
}
private static void setHighlightersOutsideRange(final int startOffset, final int endOffset, final TextRange range,
Collection<HighlightInfo> highlightsCo,
final MarkupModelEx markup,
final int group,
final Document document,
final Project project) {
final List<HighlightInfo> highlights = new ArrayList<HighlightInfo>(highlightsCo);
final SeverityRegistrar severityRegistrar = SeverityRegistrar.getInstance(project);
final HighlightersRecycler infosToRemove = new HighlightersRecycler();
DaemonCodeAnalyzerImpl.processHighlights(document, project, null, startOffset, endOffset, new Processor<HighlightInfo>() {
@Override
public boolean process(HighlightInfo info) {
if (info.group == group) {
RangeHighlighter highlighter = info.highlighter;
int endOffset = highlighter.getEndOffset();
int startOffset = highlighter.getStartOffset();
boolean willBeRemoved = endOffset == document.getTextLength() && range.getEndOffset() != document.getTextLength()
|| !range.contains(startOffset)
&& !range.containsRange(startOffset, endOffset);
if (willBeRemoved) {
infosToRemove.recycleHighlighter(highlighter);
info.highlighter = null;
}
}
return true;
}
});
Collections.sort(highlights, BY_START_OFFSET_NODUPS);
final Map<TextRange, RangeMarker> ranges2markersCache = new THashMap<TextRange, RangeMarker>(10);
final PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document);
final boolean[] changed = {false};
RangeMarkerTree.sweep(new RangeMarkerTree.Generator<HighlightInfo>(){
@Override
public boolean generate(final Processor<HighlightInfo> processor) {
return ContainerUtil.process(highlights, new Processor<HighlightInfo>() {
@Override
public boolean process(HighlightInfo info) {
return info.getStartOffset() < startOffset || info.getEndOffset() > endOffset || processor.process(info);
}
});
}
}, new MarkupModelEx.SweepProcessor<HighlightInfo>() {
@Override
public boolean process(int offset, HighlightInfo info, boolean atStart, Collection<HighlightInfo> overlappingIntervals) {
if (!atStart) {
return true;
}
if (info.isFileLevelAnnotation && psiFile != null && psiFile.getViewProvider().isPhysical()) {
addFileLevelHighlight(project, group, info, psiFile);
changed[0] = true;
return true;
}
if (isWarningCoveredByError(info, overlappingIntervals, severityRegistrar)) {
return true;
}
if (new TextRange(startOffset,endOffset).containsRange(info.getStartOffset(), info.getEndOffset()) &&
(info.getStartOffset() < range.getStartOffset() || info.getEndOffset() > range.getEndOffset())) {
createOrReuseHighlighterFor(info, document, group, psiFile, markup, infosToRemove,
ranges2markersCache,
severityRegistrar);
if (success) {
changed[0] = true;
}
return true;
@@ -332,19 +417,16 @@ public class UpdateHighlightersUtil {
}
// return true if changed
private static boolean createOrReuseHighlighterFor(@NotNull final HighlightInfo info,
@NotNull final Document document,
final int group,
@NotNull final PsiFile psiFile,
@NotNull MarkupModelEx markup,
@Nullable HighlightersRecycler infosToRemove,
@Nullable final Map<TextRange, RangeMarker> ranges2markersCache,
int rangeStartOffset,
int rangeEndOffset,
SeverityRegistrar severityRegistrar) {
private static void createOrReuseHighlighterFor(@NotNull final HighlightInfo info,
@NotNull final Document document,
final int group,
@NotNull final PsiFile psiFile,
@NotNull MarkupModelEx markup,
@Nullable HighlightersRecycler infosToRemove,
@Nullable final Map<TextRange, RangeMarker> ranges2markersCache,
SeverityRegistrar severityRegistrar) {
final int infoStartOffset = info.startOffset;
int infoEndOffset = info.endOffset;
if (infoStartOffset < rangeStartOffset || infoEndOffset > rangeEndOffset) return false;
if (infoEndOffset == infoStartOffset && !info.isAfterEndOfLine) {
infoEndOffset++; //show something in case of empty highlightinfo
@@ -398,7 +480,6 @@ public class UpdateHighlightersUtil {
info.getTextAttributes(psiFile) +
"; highlighter:" +
highlighter.getTextAttributes();
return true;
}
@NotNull
@@ -24,6 +24,7 @@ import com.intellij.openapi.components.AbstractProjectComponent;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.LogicalPosition;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.ProperTextRange;
import com.intellij.openapi.util.TextRange;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -36,7 +37,7 @@ public abstract class VisibleHighlightingPassFactory extends AbstractProjectComp
}
@NotNull
public static TextRange calculateVisibleRange(@NotNull Editor editor) {
public static ProperTextRange calculateVisibleRange(@NotNull Editor editor) {
Rectangle rect = editor.getScrollingModel().getVisibleArea();
LogicalPosition startPosition = editor.xyToLogicalPosition(new Point(rect.x, rect.y));
@@ -45,7 +46,7 @@ public abstract class VisibleHighlightingPassFactory extends AbstractProjectComp
int visibleEnd = editor.logicalPositionToOffset(new LogicalPosition(endPosition.line + 1, 0));
return new TextRange(visibleStart, visibleEnd);
return new ProperTextRange(visibleStart, visibleEnd);
}
@Nullable
@@ -475,14 +475,20 @@ public class CommentByLineCommentHandler implements CodeInsightActionHandler {
if (commenter instanceof CommenterWithLineSuffix) {
CommenterWithLineSuffix commenterWithLineSuffix = (CommenterWithLineSuffix)commenter;
String suffix = commenterWithLineSuffix.getLineCommentSuffix();
int theEnd = endOffset > 0 ? endOffset : myDocument.getLineEndOffset(line);
while (theEnd > startOffset && Character.isWhitespace(chars.charAt(theEnd - 1))) {
theEnd--;
}
if (CharArrayUtil.regionMatches(chars, theEnd - suffix.length(), suffix)) {
myDocument.deleteString(theEnd - suffix.length(), theEnd);
String lineText = myDocument.getText(new TextRange(startOffset, theEnd));
if (lineText.indexOf(suffix) != -1) {
int start = startOffset + lineText.indexOf(suffix);
myDocument.deleteString(start, start + suffix.length());
}
}
boolean skipNewLine = false;
@@ -633,4 +639,4 @@ public class CommentByLineCommentHandler implements CodeInsightActionHandler {
}
}
}
}
}
@@ -34,7 +34,7 @@ import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.*;
import com.intellij.openapi.editor.actionSystem.*;
import com.intellij.openapi.editor.actions.ToggleStickToEolToolbarAction;
import com.intellij.openapi.editor.actions.ToggleAutoScrollToTheEndToolbarAction;
import com.intellij.openapi.editor.actions.ToggleUseSoftWrapsToolbarAction;
import com.intellij.openapi.editor.colors.CodeInsightColors;
import com.intellij.openapi.editor.colors.EditorColors;
@@ -1443,7 +1443,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
return myEditor;
}
};
final AnAction stickToEolAction = new ToggleStickToEolToolbarAction() {
final AnAction autoScrollToTheEndAction = new ToggleAutoScrollToTheEndToolbarAction() {
@Override
protected Editor getEditor(AnActionEvent e) {
return myEditor;
@@ -1455,7 +1455,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
consoleActions[0] = prevAction;
consoleActions[1] = nextAction;
consoleActions[2] = switchSoftWrapsAction;
consoleActions[3] = stickToEolAction;
consoleActions[3] = autoScrollToTheEndAction;
for (int i = 0; i < customActions.size(); ++i) {
consoleActions[i + 4] = customActions.get(i);
}
@@ -19,8 +19,6 @@ package com.intellij.openapi.roots.impl.libraries;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.FileTypes;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.roots.OrderRootType;
@@ -40,7 +38,6 @@ import com.intellij.openapi.vfs.pointers.VirtualFilePointer;
import com.intellij.openapi.vfs.pointers.VirtualFilePointerContainer;
import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager;
import com.intellij.util.ArrayUtil;
import com.intellij.util.StringBuilderSpinAllocator;
import com.intellij.util.containers.HashMap;
import com.intellij.util.messages.MessageBusConnection;
import gnu.trove.THashSet;
@@ -141,16 +138,12 @@ public class LibraryImpl implements LibraryEx.ModifiableModelEx, LibraryEx {
@NotNull
public VirtualFile[] getFiles(@NotNull OrderRootType rootType) {
assert !isDisposed();
return getRootFiles(myRoots.get(rootType), myJarDirectories);
}
public static VirtualFile[] getRootFiles(final VirtualFilePointerContainer container, final Map<String, Boolean> jarDirectories) {
final List<VirtualFile> expanded = new ArrayList<VirtualFile>();
for (VirtualFile file : container.getFiles()) {
for (VirtualFile file : myRoots.get(rootType).getFiles()) {
if (file.isDirectory()) {
final Boolean expandRecursively = jarDirectories.get(file.getUrl());
final Boolean expandRecursively = myJarDirectories.get(file.getUrl());
if (expandRecursively != null) {
addChildren(file, expanded, expandRecursively.booleanValue());
collectJarFiles(file, expanded, expandRecursively.booleanValue());
continue;
}
}
@@ -159,26 +152,15 @@ public class LibraryImpl implements LibraryEx.ModifiableModelEx, LibraryEx {
return VfsUtil.toVirtualFileArray(expanded);
}
private static void addChildren(final VirtualFile dir, final List<VirtualFile> container, final boolean recursively) {
public static void collectJarFiles(final VirtualFile dir, final List<VirtualFile> container, final boolean recursively) {
for (VirtualFile child : dir.getChildren()) {
final FileType fileType = child.getFileType();
if (FileTypes.ARCHIVE.equals(fileType)) {
final StringBuilder builder = StringBuilderSpinAllocator.alloc();
try {
builder.append(VirtualFileManager.constructUrl(JarFileSystem.PROTOCOL, child.getPath()));
builder.append(JarFileSystem.JAR_SEPARATOR);
final VirtualFile jarRoot = VirtualFileManager.getInstance().findFileByUrl(builder.toString());
if (jarRoot != null) {
container.add(jarRoot);
}
}
finally {
StringBuilderSpinAllocator.dispose(builder);
}
final VirtualFile jarRoot = JarFileSystem.getInstance().getJarRootForLocalFile(child);
if (jarRoot != null) {
container.add(jarRoot);
}
else {
if (recursively && child.isDirectory()) {
addChildren(child, container, recursively);
collectJarFiles(child, container, recursively);
}
}
}
@@ -33,6 +33,7 @@ import java.util.List;
public class DefaultScopesProvider implements CustomScopesProvider {
private NamedScope myProblemsScope;
private NamedScope myNonProjectFilesScope;
private final Project myProject;
public static DefaultScopesProvider getInstance(Project project) {
@@ -51,6 +52,7 @@ public class DefaultScopesProvider implements CustomScopesProvider {
final List<NamedScope> list = new ArrayList<NamedScope>();
list.add(getProblemsScope());
list.add(getAllScope());
list.add(getNonProjectFilesScope());
return list;
}
@@ -100,4 +102,11 @@ public class DefaultScopesProvider implements CustomScopesProvider {
}
return myProblemsScope;
}
public NamedScope getNonProjectFilesScope() {
if (myNonProjectFilesScope == null) {
myNonProjectFilesScope = new NonProjectFilesScope(myProject);
}
return myNonProjectFilesScope;
}
}
@@ -0,0 +1,60 @@
/*
* 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.packageDependencies;
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.NamedScopesHolder;
import com.intellij.psi.search.scope.packageSet.PackageSet;
import com.intellij.ui.Colored;
import org.intellij.lang.annotations.RegExp;
/**
* @author Konstantin Bulenkov
*/
@Colored(color = NonProjectFilesScope.DEFAULT_COLOR)
public class NonProjectFilesScope extends NamedScope {
public static final String NAME = "Non-Project Files";
@RegExp(prefix = "[0-9a-f]{6}")
public static final String DEFAULT_COLOR = "ffffe4";
public NonProjectFilesScope(final Project project) {
super(NAME, new PackageSet() {
public boolean contains(PsiFile psiFile, NamedScopesHolder holder) {
final VirtualFile file = psiFile.getVirtualFile();
if (file == null) return true;
final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex();
return !(project.isInitialized() && !fileIndex.isIgnored(file) && fileIndex.getContentRootForFile(file) != null);
}
public PackageSet createCopy() {
return this;
}
public String getText() {
return "NonProject";
}
public int getNodePriority() {
return 0;
}
});
}
}
@@ -76,7 +76,7 @@ public class EditorTextFieldProviderImpl implements EditorTextFieldProvider {
@NotNull final Project project,
@NotNull final EditorCustomization.Feature... features)
{
return new LanguageTextField(language, project, "") {
return new LanguageTextField(language, project, "", false) {
@Override
protected EditorEx createEditor() {
final EditorEx ex = super.createEditor();
@@ -35,16 +35,28 @@ public class LanguageTextField extends EditorTextField {
private final Project myProject;
public LanguageTextField(Language language, @NotNull Project project, @NotNull String value) {
this(language, project, value, new SimpleDocumentCreator());
this(language, project, value, true);
}
public LanguageTextField(Language language, @NotNull Project project, @NotNull String value, boolean oneLineMode) {
this(language, project, value, new SimpleDocumentCreator(), oneLineMode);
}
public LanguageTextField(@Nullable Language language,
@NotNull Project project,
@NotNull String value,
@NotNull DocumentCreator documentCreator
) {
@NotNull DocumentCreator documentCreator)
{
this(language, project, value, documentCreator, true);
}
public LanguageTextField(@Nullable Language language,
@NotNull Project project,
@NotNull String value,
@NotNull DocumentCreator documentCreator,
boolean oneLineMode) {
super(documentCreator.createDocument(value, language, project), project,
language != null ? language.getAssociatedFileType() : StdFileTypes.PLAIN_TEXT, language == null);
language != null ? language.getAssociatedFileType() : StdFileTypes.PLAIN_TEXT, language == null, oneLineMode);
myLanguage = language;
myProject = project;
@@ -18,19 +18,21 @@ package com.intellij.ui.tabs;
import com.intellij.openapi.project.Project;
import com.intellij.psi.search.scope.packageSet.NamedScopeManager;
import com.intellij.ui.ColorUtil;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author spleaner
*/
* @author spleaner
* @author Konstantin Bulenkov
*/
class FileColorConfiguration implements Cloneable {
private static final String COLOR = "color";
private static final String SCOPE_NAME = "scope";
private String myScopeName;
private String myColorName;
private static final String SCOPE_NAME = "scope";
public FileColorConfiguration() {
}
@@ -52,6 +54,10 @@ class FileColorConfiguration implements Cloneable {
return myColorName;
}
public String getColorPresentableName() {
return ColorUtil.fromHex(myColorName, null) == null ? myColorName : "Custom";
}
public void setColorName(final String colorName) {
myColorName = colorName;
}
@@ -19,9 +19,12 @@ package com.intellij.ui.tabs;
import com.intellij.notification.impl.ui.StickyButton;
import com.intellij.notification.impl.ui.StickyButtonUI;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.packageDependencies.NonProjectFilesScope;
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.ColorChooser;
import com.intellij.ui.ColorUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.HashMap;
import org.jetbrains.annotations.NotNull;
@@ -32,18 +35,22 @@ import javax.swing.plaf.ButtonUI;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Comparator;
import java.util.Map;
/**
* @author spleaner
* @author Konstantin Bulenkov
*/
public class FileColorConfigurationEditDialog extends DialogWrapper {
private FileColorConfiguration myConfiguration;
private JComboBox myScopeComboBox;
private final FileColorManagerImpl myManager;
private HashMap<String,AbstractButton> myColorToButtonMap;
private static final String CUSTOM_COLOR_NAME = "Custom";
private final Map<String, NamedScope> myScopeNames = new HashMap<String, NamedScope>();
public FileColorConfigurationEditDialog(@NotNull final FileColorManagerImpl manager, @Nullable final FileColorConfiguration configuration) {
super(true);
@@ -55,6 +62,7 @@ public class FileColorConfigurationEditDialog extends DialogWrapper {
myConfiguration = configuration;
init();
updateCustomButton();
updateOKButton();
}
@@ -63,18 +71,25 @@ public class FileColorConfigurationEditDialog extends DialogWrapper {
final JPanel result = new JPanel();
result.setLayout(new BoxLayout(result, BoxLayout.Y_AXIS));
final List<String> scopeNames = new ArrayList<String>();
final NamedScopesHolder[] scopeHolders = NamedScopeManager.getAllNamedScopeHolders(myManager.getProject());
for (final NamedScopesHolder scopeHolder : scopeHolders) {
final NamedScope[] scopes = scopeHolder.getScopes();
for (final NamedScope scope : scopes) {
scopeNames.add(scope.getName());
myScopeNames.put(scope.getName(), scope);
}
}
myScopeComboBox = new JComboBox(ArrayUtil.toStringArray(scopeNames));
final String[] names = ArrayUtil.toStringArray(myScopeNames.keySet());
Arrays.sort(names, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1 == NonProjectFilesScope.NAME ? 1 : o2 == NonProjectFilesScope.NAME ? -1 : 0;
}
});
myScopeComboBox = new JComboBox(names);
myScopeComboBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
updateCustomButton();
updateOKButton();
}
});
@@ -112,6 +127,19 @@ public class FileColorConfigurationEditDialog extends DialogWrapper {
return result;
}
private void updateCustomButton() {
final Object item = myScopeComboBox.getSelectedItem();
if (item instanceof String) {
@SuppressWarnings({"SuspiciousMethodCalls"})
final Color color = ColorUtil.getColor(myScopeNames.get(item).getClass());
final CustomColorButton button = (CustomColorButton)myColorToButtonMap.get(CUSTOM_COLOR_NAME);
if (color != null) {
button.setColor(color);
button.setSelected(true);
}
}
}
@Override
protected void doOKAction() {
close(OK_EXIT_CODE);
@@ -146,18 +174,17 @@ public class FileColorConfigurationEditDialog extends DialogWrapper {
final Collection<String> names = myManager.getColorNames();
for (final String name : names) {
final ColorButton colorButton = new ColorButton(name, myManager.getColor(name));
colorButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
updateOKButton();
}
});
colorButton.setBackground(Color.WHITE);
colorButton.setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10));
group.add(colorButton);
inner.add(colorButton);
myColorToButtonMap.put(name, colorButton);
inner.add(Box.createHorizontalStrut(5));
}
final CustomColorButton customButton = new CustomColorButton();
group.add(customButton);
inner.add(customButton);
myColorToButtonMap.put(customButton.getText(), customButton);
inner.add(Box.createHorizontalStrut(5));
if (configuration != null) {
final AbstractButton button = myColorToButtonMap.get(configuration.getColorName());
@@ -169,14 +196,14 @@ public class FileColorConfigurationEditDialog extends DialogWrapper {
return result;
}
@Nullable
private String getColorName() {
for (String name : myColorToButtonMap.keySet()) {
final AbstractButton button = myColorToButtonMap.get(name);
if (button.isSelected()) {
return name;
return button instanceof CustomColorButton ? ColorUtil.toHex(((CustomColorButton)button).getColor()) : name;
}
}
return null;
}
@@ -200,20 +227,33 @@ public class FileColorConfigurationEditDialog extends DialogWrapper {
}
private class ColorButton extends StickyButton {
private final Color myColor;
protected Color myColor;
private ColorButton(final String text, final Color color) {
protected ColorButton(final String text, final Color color) {
super(text);
setUI(new ColorButtonUI());
myColor = color;
addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
doPerformAction(e);
}
});
setBackground(Color.WHITE);
setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10));
}
protected void doPerformAction(ActionEvent e) {
updateOKButton();
}
Color getColor() {
return myColor;
}
public void setColor(Color color) {
myColor = color;
}
@Override
public Color getForeground() {
if (getModel().isSelected()) {
@@ -231,6 +271,33 @@ public class FileColorConfigurationEditDialog extends DialogWrapper {
}
}
private class CustomColorButton extends ColorButton {
private CustomColorButton() {
super(CUSTOM_COLOR_NAME, Color.WHITE);
myColor = null;
}
@Override
protected void doPerformAction(ActionEvent e) {
final Color color = ColorChooser.chooseColor(FileColorConfigurationEditDialog.this.getRootPane(), "Choose Color", myColor);
if (color != null) {
myColor = color;
}
setSelected(myColor != null);
getOKAction().setEnabled(myColor != null);
}
@Override
public Color getForeground() {
return getModel().isSelected() ? Color.BLACK : Color.GRAY;
}
@Override
Color getColor() {
return myColor == null ? Color.WHITE : myColor;
}
}
private class ColorButtonUI extends StickyButtonUI<ColorButton> {
@Override
@@ -23,6 +23,7 @@ import com.intellij.openapi.components.Storage;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.packageDependencies.NonProjectFilesScope;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.ui.ColorUtil;
@@ -39,35 +40,25 @@ import java.util.List;
/**
* @author spleaner
* @author Konstantin Bulenkov
*/
@State(
name = "FileColors",
storages = {@Storage(
id = "other",
file = "$WORKSPACE_FILE$")})
storages = {@Storage(id = "other", file = "$WORKSPACE_FILE$")})
public class FileColorManagerImpl extends FileColorManager implements PersistentStateComponent<Element> {
public static final Color DEFAULT_NON_PROJECT_FILES_COLOR = new Color(255, 255, 228);
private boolean myEnabled = true;
private final FileColorsModel myModel;
private FileColorSharedConfigurationManager mySharedConfigurationManager;
private final Project myProject;
private final FileColorsModel myModel;
private FileColorSharedConfigurationManager mySharedConfigurationManager;
private boolean myEnabled = true;
private boolean myEnabledForTabs = true;
private boolean myHighlightNonProjectFiles = true;
private Color myNonProjectFilesColor = DEFAULT_NON_PROJECT_FILES_COLOR;
private boolean myEnabledForNonProject = 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));
//ourDefaultColors.put("Green 2", new Color(223, 235, 226));
ourDefaultColors.put("Orange", new Color(246, 224, 202));
ourDefaultColors.put("Rose", new Color(242, 206, 202));
ourDefaultColors.put("Violet", new Color(222, 213, 241));
@@ -101,24 +92,24 @@ 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));
element.setAttribute("nonProjectFilesColor", ColorUtil.toHex(myNonProjectFilesColor));
}
myModel.save(element, shared);
if (!shared) {
final boolean exists = findConfigurationByName(NonProjectFilesScope.NAME, myModel.getLocalConfigurations()) != null;
if (myEnabledForNonProject && !exists) {
myEnabledForNonProject = false;
} else if (!myEnabledForNonProject && exists) {
myEnabledForNonProject = true;
}
element.setAttribute("showNonProject", Boolean.toString(myEnabledForNonProject));
}
return element;
}
@@ -126,17 +117,8 @@ public class FileColorManagerImpl extends FileColorManager implements Persistent
@SuppressWarnings({"MethodMayBeStatic"})
@Nullable
public Color getColor(@NotNull final String name) {
if (OUT_OF_PROJECT_SCOPE_COLOR.equals(name)) return myNonProjectFilesColor;
final Color color = ourDefaultColors.get(name);
return color == null ? ourHiddenColors.get(name) : color;
}
public static String toString(final Color color) {
return Integer.toHexString((color.getRGB() & 0xffffff) | 0x1000000).substring(1);
}
public static Color fromString(final String hex) throws NumberFormatException {
return Color.decode(String.format("#%s", hex));
return color == null ? ColorUtil.fromHex(name, null) : color;
}
public Element getState() {
@@ -153,13 +135,15 @@ public class FileColorManagerImpl extends FileColorManager implements Persistent
final String enabledForTabs = state.getAttributeValue("enabledForTabs");
myEnabledForTabs = enabledForTabs == null ? true : Boolean.valueOf(enabledForTabs);
final String highlightNonProjectFiles = state.getAttributeValue("highlightNonProjectFiles");
myHighlightNonProjectFiles = highlightNonProjectFiles == null ? true : Boolean.valueOf(highlightNonProjectFiles);
myNonProjectFilesColor = ColorUtil.fromHex(state.getAttributeValue("nonProjectFilesColor"), DEFAULT_NON_PROJECT_FILES_COLOR);
final String showNonProject = state.getAttributeValue("showNonProject");
myEnabledForNonProject = showNonProject == null ? true : Boolean.valueOf(showNonProject);
}
myModel.load(state, shared);
final List<FileColorConfiguration> local = myModel.getLocalConfigurations();
if (!shared && myEnabledForNonProject && findConfigurationByName(NonProjectFilesScope.NAME, local) == null) {
local.add(new FileColorConfiguration(NonProjectFilesScope.NAME, NonProjectFilesScope.DEFAULT_COLOR));
}
}
@SuppressWarnings({"MethodMayBeStatic"})
@@ -233,11 +217,13 @@ public class FileColorManagerImpl extends FileColorManager implements Persistent
return myModel.getSharedConfigurations();
}
public Color getNonProjectFilesColor() {
return myNonProjectFilesColor;
}
public void setNonProjectFilesColor(Color color) {
myNonProjectFilesColor = color;
@Nullable
private static FileColorConfiguration findConfigurationByName(String name, List<FileColorConfiguration> configurations) {
for (FileColorConfiguration configuration : configurations) {
if (name.equals(configuration.getScopeName())) {
return configuration;
}
}
return null;
}
}
@@ -273,7 +273,7 @@ public abstract class FileColorSettingsTable extends StripeTable {
final FileColorConfiguration configuration = (FileColorConfiguration)value;
setIconColor(myManager.getColor(configuration.getColorName()));
setText(configuration.getColorName());
setText(configuration.getColorPresentableName());
return this;
}
@@ -21,7 +21,6 @@ import com.intellij.ide.util.scopeChooser.EditScopesDialog;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.StripeTable;
import com.intellij.ui.CheckBoxWithColorChooser;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
@@ -38,7 +37,6 @@ public class FileColorsConfigurablePanel extends JPanel implements Disposable {
private FileColorManagerImpl myManager;
private final JCheckBox myEnabledCheckBox;
private final JCheckBox myTabsEnabledCheckBox;
private final CheckBoxWithColorChooser myHighlightNonProjectFilesCheckBox;
private final FileColorSettingsTable myLocalTable;
private final FileColorSettingsTable mySharedTable;
@@ -59,9 +57,6 @@ public class FileColorsConfigurablePanel extends JPanel implements Disposable {
myTabsEnabledCheckBox.setMnemonic('T');
topPanel.add(myTabsEnabledCheckBox);
myHighlightNonProjectFilesCheckBox = new CheckBoxWithColorChooser("Highlight Non-Project Files", false, myManager.getNonProjectFilesColor());
myHighlightNonProjectFilesCheckBox.setMnemonic('N');
topPanel.add(myHighlightNonProjectFilesCheckBox);
topPanel.add(Box.createHorizontalGlue());
add(topPanel, BorderLayout.NORTH);
@@ -70,7 +65,8 @@ public class FileColorsConfigurablePanel extends JPanel implements Disposable {
mainPanel.setPreferredSize(new Dimension(300, 500));
mainPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
myLocalTable = new FileColorSettingsTable(manager, manager.getLocalConfigurations()) {
final List<FileColorConfiguration> local = manager.getLocalConfigurations();
myLocalTable = new FileColorSettingsTable(manager, local) {
protected void apply(@NotNull List<FileColorConfiguration> configurations) {
final List<FileColorConfiguration> copied = new ArrayList<FileColorConfiguration>();
for (final FileColorConfiguration configuration : configurations) {
@@ -128,7 +124,7 @@ public class FileColorsConfigurablePanel extends JPanel implements Disposable {
final JPanel warningPanel = new JPanel(new BorderLayout());
warningPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
warningPanel.add(new JLabel("Scopes are processed from top to bottom with Local colors first.",
MessageType.WARNING.getDefaultIcon(), SwingConstants.LEFT));
MessageType.WARNING.getDefaultIcon(), SwingConstants.LEFT));
final JButton editScopes = new JButton("Manage Scopes...");
editScopes.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
@@ -139,6 +135,7 @@ public class FileColorsConfigurablePanel extends JPanel implements Disposable {
add(warningPanel, BorderLayout.SOUTH);
}
private static JButton createAddButton(final FileColorSettingsTable table, final FileColorManagerImpl manager) {
final JButton addButton = new JButton("Add...");
addButton.setMaximumSize(new Dimension(Integer.MAX_VALUE, addButton.getMaximumSize().height));
@@ -235,9 +232,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();
modified |= myHighlightNonProjectFilesCheckBox.getColor().getRGB() != myManager.getNonProjectFilesColor().getRGB();
return modified;
}
@@ -245,8 +240,6 @@ public class FileColorsConfigurablePanel extends JPanel implements Disposable {
public void apply() {
myManager.setEnabled(myEnabledCheckBox.isSelected());
myManager.setEnabledForTabs(myTabsEnabledCheckBox.isSelected());
myManager.setHighlightNonProjectFiles(myHighlightNonProjectFilesCheckBox.isSelected());
myManager.setNonProjectFilesColor(myHighlightNonProjectFilesCheckBox.getColor());
myLocalTable.apply();
mySharedTable.apply();
@@ -257,9 +250,8 @@ 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();
if (myLocalTable.isModified()) myLocalTable.reset();
if (mySharedTable.isModified()) mySharedTable.reset();
}
}
@@ -17,14 +17,10 @@
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;
@@ -34,6 +30,7 @@ import java.util.List;
/**
* @author spleaner
* @author Konstantin Bulenkov
*/
// todo[spL]: listen to scope rename
public class FileColorsModel implements Cloneable {
@@ -143,20 +140,9 @@ public class FileColorsModel implements Cloneable {
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) {
@@ -718,11 +718,11 @@ public class PluginManager {
ourBuildNumber = BuildNumber.fromString(new String(FileUtil.loadFileText(buildTxtFile)).trim());
}
else {
ourBuildNumber = BuildNumber.fromString("96.SNAPSHOT");
ourBuildNumber = BuildNumber.fromString("98.SNAPSHOT");
}
}
catch (IOException e) {
ourBuildNumber = BuildNumber.fromString("96.SNAPSHOT");
ourBuildNumber = BuildNumber.fromString("98.SNAPSHOT");
}
}
}
@@ -15,9 +15,6 @@
*/
package com.intellij.openapi.editor;
import gnu.trove.TIntHashSet;
import org.jetbrains.annotations.NotNull;
/**
* Default {@link LineWrapPositionStrategy} implementation. Is assumed to provide language-agnostic algorithm that may
* be used with almost any kind of text.
@@ -25,116 +22,22 @@ import org.jetbrains.annotations.NotNull;
* @author Denis Zhdanov
* @since Aug 25, 2010 11:33:00 AM
*/
public class DefaultLineWrapPositionStrategy implements LineWrapPositionStrategy {
public class DefaultLineWrapPositionStrategy extends GenericLineWrapPositionStrategy {
/** Contains white space characters (has special treatment during soft wrap position calculation). */
private static final TIntHashSet WHITE_SPACES = new TIntHashSet();
static {
WHITE_SPACES.add(' ');
WHITE_SPACES.add('\t');
}
public DefaultLineWrapPositionStrategy() {
// Commas.
addRule(new Rule(',', WrapCondition.AFTER, Rule.DEFAULT_WEIGHT * 1.4));
/**
* Contains symbols that are special in that soft wrap is allowed to be performed only
* after them (not before).
*/
private static final TIntHashSet SPECIAL_SYMBOLS_TO_WRAP_AFTER = new TIntHashSet();
static {
SPECIAL_SYMBOLS_TO_WRAP_AFTER.add(',');
SPECIAL_SYMBOLS_TO_WRAP_AFTER.add(';');
SPECIAL_SYMBOLS_TO_WRAP_AFTER.add(')');
}
// Symbols to wrap either before or after.
addRule(new Rule(' '));
addRule(new Rule('\t'));
/**
* Contains symbols that are special in that soft wrap is allowed to be performed only
* before them (not after).
*/
private static final TIntHashSet SPECIAL_SYMBOLS_TO_WRAP_BEFORE = new TIntHashSet();
static {
SPECIAL_SYMBOLS_TO_WRAP_BEFORE.add('(');
SPECIAL_SYMBOLS_TO_WRAP_BEFORE.add('.');
}
// Symbols to wrap after.
addRule(new Rule(';', WrapCondition.AFTER));
addRule(new Rule(')', WrapCondition.AFTER));
@Override
public int calculateWrapPosition(@NotNull CharSequence text,
int startOffset,
int endOffset,
final int maxPreferredOffset,
boolean allowToBeyondMaxPreferredOffset)
{
if (endOffset <= startOffset) {
return endOffset;
}
// Normalization.
int maxPreferredOffsetToUse = maxPreferredOffset >= endOffset ? endOffset - 1 : maxPreferredOffset;
maxPreferredOffsetToUse = maxPreferredOffsetToUse < startOffset ? startOffset : maxPreferredOffsetToUse;
// Try to find target offset that is not greater than preferred position.
for (int i = maxPreferredOffsetToUse; i > startOffset; i--) {
char c = text.charAt(i);
if (c == '\n') {
return i + 1;
}
if (WHITE_SPACES.contains(c)) {
return i < maxPreferredOffsetToUse ? i + 1 : i;
}
// Don't wrap on the non-id symbol preceded by another non-id symbol. E.g. consider that we have a statement
// like 'foo(int... args)'. We don't want to wrap on the second or third dots then.
if (i > startOffset + 1 && !isIdSymbol(c) && !isIdSymbol(text.charAt(i - 1))) {
continue;
}
if (SPECIAL_SYMBOLS_TO_WRAP_AFTER.contains(c)) {
if (i < maxPreferredOffsetToUse) {
return i + 1;
}
continue;
}
if (SPECIAL_SYMBOLS_TO_WRAP_BEFORE.contains(c) || WHITE_SPACES.contains(c)) {
return i;
}
// Don't wrap on a non-id symbol followed by non-id symbol, e.g. don't wrap between two pluses at i++.
// Also don't wrap before non-id symbol preceded by a space - wrap on space instead;
if (!isIdSymbol(c) && (i < startOffset + 2 || (isIdSymbol(text.charAt(i - 1)) && !WHITE_SPACES.contains(text.charAt(i - 1))))) {
return i;
}
}
// Try to find target offset that is greater than preferred position.
for (int i = maxPreferredOffsetToUse + 1; i < endOffset; i++) {
char c = text.charAt(i);
if (c == '\n') {
return i + 1;
}
if (WHITE_SPACES.contains(c)) {
return i;
}
// Don't wrap on the non-id symbol preceded by another non-id symbol. E.g. consider that we have a statement
// like 'foo(int... args)'. We don't want to wrap on the second or third dots then.
if (i < endOffset - 1 && !isIdSymbol(c) && !isIdSymbol(text.charAt(i + 1)) && !isIdSymbol(text.charAt(i - 1))) {
continue;
}
if (SPECIAL_SYMBOLS_TO_WRAP_BEFORE.contains(c)) {
return i;
}
if (SPECIAL_SYMBOLS_TO_WRAP_AFTER.contains(c) && i < endOffset - 1) {
return i + 1;
}
// Don't wrap on a non-id symbol followed by non-id symbol, e.g. don't wrap between two pluses at i++;
if (!isIdSymbol(c) && (i >= endOffset - 1 || isIdSymbol(text.charAt(i + 1)))) {
return i;
}
}
return allowToBeyondMaxPreferredOffset ? endOffset : maxPreferredOffset;
}
private static boolean isIdSymbol(char c) {
return c == '_' || c == '$' || (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
// Symbols to wrap before
addRule(new Rule('(', WrapCondition.BEFORE));
addRule(new Rule('.', WrapCondition.BEFORE));
}
}
@@ -0,0 +1,275 @@
/*
* 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;
import gnu.trove.*;
import org.jetbrains.annotations.NotNull;
/**
* Highly customizable {@link LineWrapPositionStrategy} implementation.
* <p/>
* Not thread-safe.
*
* @author Denis Zhdanov
* @since Sep 23, 2010 12:04:52 PM
*/
public class GenericLineWrapPositionStrategy implements LineWrapPositionStrategy {
private static final TIntIntProcedure GT_COMPARATOR = new TIntIntProcedure() {
@Override
public boolean execute(int a, int b) {
return a > b;
}
};
//private static final TIntIntProcedure LT_COMPARATOR = new TIntIntProcedure() {
// @Override
// public boolean execute(int a, int b) {
// return a < b;
// }
//};
/**
* We consider that it's possible to wrap line on non-id symbol. However, weight of such position is expected to be less
* than weight of wrap position bound to explicitly configured symbol.
*/
private static final int NON_ID_WEIGHT = (Rule.DEFAULT_WEIGHT - 1) / 2;
/** Holds symbols wrap rules by symbol. */
private final TIntObjectHashMap<Rule> myRules = new TIntObjectHashMap<Rule>();
@Override
public int calculateWrapPosition(@NotNull CharSequence text,
int startOffset,
int endOffset,
int maxPreferredOffset,
boolean allowToBeyondMaxPreferredOffset)
{
if (endOffset <= startOffset) {
return endOffset;
}
// Normalization.
int maxPreferredOffsetToUse = maxPreferredOffset >= endOffset ? endOffset - 1 : maxPreferredOffset;
maxPreferredOffsetToUse = maxPreferredOffsetToUse < startOffset ? startOffset : maxPreferredOffsetToUse;
TIntDoubleHashMap offset2Weight = new TIntDoubleHashMap();
// Try to find out wrap position before preferred offset.
for (int i = maxPreferredOffsetToUse; i > startOffset; i--) {
char c = text.charAt(i);
if (c == '\n') {
return i + 1;
}
Rule rule = myRules.get(c);
if (rule != null) {
if (rule.condition == WrapCondition.BOTH || rule.condition == WrapCondition.AFTER) {
int target = i+1;
if (rule.symbol != ' ') {
while(i < maxPreferredOffsetToUse && text.charAt(target) == ' ') {
target++;
}
}
if (target <= maxPreferredOffsetToUse) {
offset2Weight.put(target, rule.weight);
}
}
if (rule.condition == WrapCondition.BOTH || rule.condition == WrapCondition.BEFORE) {
offset2Weight.put(i, rule.weight);
}
continue;
}
// Don't wrap on a non-id symbol followed by non-id symbol, e.g. don't wrap between two pluses at i++.
// Also don't wrap before non-id symbol preceded by a space - wrap on space instead;
if (!isIdSymbol(c) && (i < startOffset + 2 || (isIdSymbol(text.charAt(i - 1)) && !myRules.contains(text.charAt(i - 1))))) {
offset2Weight.put(i, NON_ID_WEIGHT);
}
}
int result = chooseOffset(offset2Weight, GT_COMPARATOR, startOffset);
if (result > 0) {
return result;
}
// Try to find target offset that is beyond preferred offset.
// Note that we don't consider symbol weights here and just break on the first appropriate position.
if (!allowToBeyondMaxPreferredOffset) {
return maxPreferredOffset;
}
for (int i = maxPreferredOffsetToUse + 1; i < endOffset; i++) {
char c = text.charAt(i);
if (c == '\n') {
return i + 1;
}
Rule rule = myRules.get(c);
if (rule != null) {
switch (rule.condition) {
case BOTH:
case BEFORE: return i;
case AFTER: if (i < endOffset - 1) return i + 1;
}
}
// Don't wrap on a non-id symbol followed by non-id symbol, e.g. don't wrap between two pluses at i++;
if (!isIdSymbol(c) && (i >= endOffset - 1 || isIdSymbol(text.charAt(i + 1)))) {
return i;
}
}
return endOffset;
}
/**
* Registers given rule with the current strategy.
*
* @param rule rule to register
* @throws IllegalArgumentException if another rule for the same symbol is already registered within the current strategy
*/
public void addRule(@NotNull Rule rule) throws IllegalArgumentException {
Rule existing = myRules.get(rule.symbol);
if (existing != null) {
throw new IllegalArgumentException(String.format(
"Can't register given wrap rule (%s) within the current line wrap position strategy. Reason: another rule is already "
+ "registered for it - '%s'", rule, existing
));
}
existing = myRules.put(rule.symbol, rule);
assert existing == null;
}
private static boolean isIdSymbol(char c) {
return c == '_' || c == '$' || (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}
/**
* Tries to derive offset to use at the given map assuming that it contains mappings like '{@code offset -> weight}'.
*
* @param offset2Weight map that holds '{@code offset -> weight}' entries (is allows to be empty)
* @param comparator strategy interface that is expected to return <code>'true'</code> if the first parameter
* given to it is more preferred than the second
* @param startOffset start offset of the line that is being wrapped
* @return one of the keys of the given map to use; negative value if no appropriate key is found or the map is empty
*/
private static int chooseOffset(@NotNull TIntDoubleHashMap offset2Weight, @NotNull final TIntIntProcedure comparator,
final int startOffset)
{
if (offset2Weight.isEmpty()) {
return -1;
}
final double[] resultingWeight = new double[1];
final int[] resultingOffset = new int[1];
offset2Weight.forEachEntry(new TIntDoubleProcedure() {
@Override
public boolean execute(int offset, double weight) {
boolean change = false;
// Check if current candidate is certainly better than the current result.
if (comparator.execute(offset, resultingOffset[0])) {
if (weight >= resultingWeight[0]) {
change = true;
}
}
// Check if it's worth to use current mapping because of it's weight.
if (!change && weight > resultingWeight[0]) {
change = (offset - startOffset) * weight > (resultingOffset[0] - startOffset) * resultingWeight[0];
}
if (change) {
resultingWeight[0] = weight;
resultingOffset[0] = offset;
}
return true;
}
});
return resultingOffset[0];
}
/**
* Defines how wrapping may be performed for particular symbol.
*
* @see Rule
*/
public enum WrapCondition {
/** Means that wrap is allowed only after particular symbol. */
AFTER,
/** Means that wrap is allowed only before particular symbol. */
BEFORE,
/** Means that wrap is allowed before and after particular symbol. */
BOTH
}
/**
* Encapsulates information about rule to use during line wrapping.
*/
public static class Rule {
public static final int DEFAULT_WEIGHT = 10;
public final char symbol;
public final WrapCondition condition;
/**
* There is a possible case that there are more than one appropriate wrap positions on a line and we need to choose between them.
* Here 'weight' characteristics comes into play.
* <p/>
* The general idea is that it's possible to prefer position with lower offset if it's weight is more than the one from
* position with higher offset and distance between them is not too big.
* <p/>
* Current algorithm uses the <code>'weight'</code> in a following manner:
* <p/>
* <pre>
* <ol>
* <li>Calculate product of line length on first wrap location and its weight;</li>
* <li>Calculate product of line length on second wrap location and its weight;</li>
* <li>Compare those products;</li>
* </ol>
* </pre>
* <p/>
* <b>Example</b>
* Suppose we have two positions that define lines of length 30 and 10 symbols. Suppose that the weights are <code>'1'</code>
* and <code>'4'</code> correspondingly.Position with greater weight is preferred because it's product is higher
* ({@code 10 * 4 > 30 * 1})
*/
public final double weight;
public Rule(char symbol) {
this(symbol, WrapCondition.BOTH, DEFAULT_WEIGHT);
}
public Rule(char symbol, WrapCondition condition) {
this(symbol, condition, DEFAULT_WEIGHT);
}
public Rule(char symbol, double weight) {
this(symbol, WrapCondition.BOTH, weight);
}
public Rule(char symbol, WrapCondition condition, double weight) {
this.symbol = symbol;
this.condition = condition;
this.weight = weight;
}
}
}
@@ -26,10 +26,15 @@ public class LightFilePointer {
private final String myUrl;
private VirtualFile myFile;
public LightFilePointer(String url) {
public LightFilePointer(@NotNull String url) {
myUrl = url;
}
public LightFilePointer(@NotNull VirtualFile file) {
myUrl = file.getUrl();
myFile = file;
}
public VirtualFile getFile() {
refreshFile();
return myFile;
@@ -75,4 +80,18 @@ public class LightFilePointer {
VirtualFile virtualFile = VirtualFileManager.getInstance().findFileByUrl(myUrl);
myFile = virtualFile != null && virtualFile.isValid() ? virtualFile : null;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof LightFilePointer)) return false;
return myUrl.equals(((LightFilePointer)o).myUrl);
}
@Override
public int hashCode() {
return myUrl.hashCode();
}
}
@@ -18,6 +18,7 @@ package com.intellij.openapi.vfs;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.fileTypes.FileTypes;
import com.intellij.openapi.vfs.newvfs.NewVirtualFileSystem;
import com.intellij.util.StringBuilderSpinAllocator;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -43,7 +44,18 @@ public abstract class JarFileSystem extends NewVirtualFileSystem {
@Nullable
public VirtualFile getJarRootForLocalFile(@NotNull VirtualFile virtualFile) {
if (virtualFile.getFileType() != FileTypes.ARCHIVE) return null;
return findFileByPath(virtualFile.getPath() + JAR_SEPARATOR);
final StringBuilder builder = StringBuilderSpinAllocator.alloc();
final String path;
try {
builder.append(virtualFile.getPath());
builder.append(JAR_SEPARATOR);
path = builder.toString();
}
finally {
StringBuilderSpinAllocator.dispose(builder);
}
return findFileByPath(path);
}
}
@@ -18,13 +18,15 @@ package com.intellij.openapi.editor;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertSame;
/**
* @author Denis Zhdanov
* @since Aug 25, 2010 3:20:41 PM
*/
public class DefaultLineWrapPositionStrategyTest {
private static final String MAX_PREFERRED_MARKER = "<PREFERRED>";
private static final String EDGE_MARKER = "<EDGE>";
private static final String WRAP_MARKER = "<WRAP>";
private DefaultLineWrapPositionStrategy myStrategy;
@@ -37,14 +39,21 @@ public class DefaultLineWrapPositionStrategyTest {
@Test
public void commaNotSeparated() {
String document =
"void method(String <WRAP>p1, String p2) {}";
doTest(document);
"void method(String <WRAP>p1<EDGE>, String p2) {}";
doTest(document, false);
}
@Test
public void wrapOnExceedingWhiteSpace() {
String document =
"void method(String p1,<WRAP> String p2) {}";
"void method(String p1,<WRAP><EDGE> String p2) {}";
doTest(document);
}
@Test
public void preferWrapOnComma() {
String document =
"int variable = testMethod(var1 + var2, <WRAP>var3 + va<EDGE>r4);";
doTest(document);
}
@@ -55,9 +64,10 @@ public class DefaultLineWrapPositionStrategyTest {
private void doTest(final String document, boolean allowToBeyondMaxPreferredOffset) {
final Context context = new Context(document);
context.init();
myStrategy.calculateWrapPosition(
context.document, 0, context.document.length(), context.preferredIndex, allowToBeyondMaxPreferredOffset
int actual = myStrategy.calculateWrapPosition(
context.document, 0, context.document.length(), context.edgeIndex, allowToBeyondMaxPreferredOffset
);
assertSame(context.wrapIndex, actual);
}
/**
@@ -73,7 +83,9 @@ public class DefaultLineWrapPositionStrategyTest {
private String document;
private int index;
private int wrapIndex;
private int preferredIndex;
private int tmpWrapIndex;
private int edgeIndex;
private int tmpEdgeIndex;
Context(String rawDocument) {
if (rawDocument.contains("\n")) {
@@ -85,10 +97,10 @@ public class DefaultLineWrapPositionStrategyTest {
}
public void init() {
wrapIndex = rawDocument.indexOf(WRAP_MARKER);
preferredIndex = rawDocument.indexOf(MAX_PREFERRED_MARKER);
if (wrapIndex >= 0 && preferredIndex >= 0) {
if (wrapIndex < preferredIndex) {
tmpWrapIndex = rawDocument.indexOf(WRAP_MARKER);
tmpEdgeIndex = rawDocument.indexOf(EDGE_MARKER);
if (tmpWrapIndex >= 0 && tmpEdgeIndex >= 0) {
if (tmpWrapIndex < tmpEdgeIndex) {
processWrap();
processMaxPreferredIndex();
}
@@ -98,33 +110,35 @@ public class DefaultLineWrapPositionStrategyTest {
}
}
else {
if (wrapIndex >= 0) {
if (tmpWrapIndex >= 0) {
processWrap();
}
if (preferredIndex >= 0) {
if (tmpEdgeIndex >= 0) {
processMaxPreferredIndex();
}
}
buffer.append(rawDocument.substring(index));
document = buffer.toString();
if (preferredIndex <= 0) {
preferredIndex = document.length();
if (edgeIndex <= 0) {
edgeIndex = document.length();
}
}
private void processWrap() {
buffer.append(rawDocument.substring(index, wrapIndex));
index = wrapIndex + WRAP_MARKER.length();
buffer.append(rawDocument.substring(index, tmpWrapIndex));
index = tmpWrapIndex + WRAP_MARKER.length();
wrapIndex = buffer.length();
if (rawDocument.indexOf(WRAP_MARKER, index) >= 0) {
throw new IllegalArgumentException(String.format("More than one wrap indicator is found at the document '%s'", rawDocument));
}
}
private void processMaxPreferredIndex() {
buffer.append(rawDocument.substring(index, preferredIndex));
index = preferredIndex + MAX_PREFERRED_MARKER.length();
if (rawDocument.indexOf(MAX_PREFERRED_MARKER, index) >= 0) {
buffer.append(rawDocument.substring(index, tmpEdgeIndex));
index = tmpEdgeIndex + EDGE_MARKER.length();
edgeIndex = buffer.length();
if (rawDocument.indexOf(EDGE_MARKER, index) >= 0) {
throw new IllegalArgumentException(String.format("More than one max preferred offset is found at the document '%s'", rawDocument));
}
}
@@ -43,7 +43,9 @@ public class ConfirmExitDialog extends OptionsDialog {
protected Action[] createActions() {
setOKButtonText(CommonBundle.getYesButtonText());
setCancelButtonText(CommonBundle.getNoButtonText());
return new Action[] {getOKAction(), getCancelAction()};
final Action cancelAction = getCancelAction();
cancelAction.putValue(FOCUSED_ACTION, Boolean.TRUE);
return new Action[] {getOKAction(), cancelAction};
}
protected boolean isToBeShown() {
@@ -18,6 +18,7 @@ package com.intellij.openapi.components.impl.stores;
import com.intellij.CommonBundle;
import com.intellij.ide.highlighter.ProjectFileType;
import com.intellij.ide.highlighter.WorkspaceFileType;
import com.intellij.notification.NotificationsManager;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ApplicationNamesInfo;
import com.intellij.openapi.components.*;
@@ -29,7 +30,6 @@ import com.intellij.openapi.project.ex.ProjectEx;
import com.intellij.openapi.project.impl.ProjectImpl;
import com.intellij.openapi.project.impl.ProjectManagerImpl;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.ex.MessagesEx;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.Pair;
@@ -455,12 +455,16 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject
protected void collectSubfilesToSave(final List<IFile> result) throws IOException { }
public SaveSession save() throws IOException {
final ProjectImpl.UnableToSaveProjectNotification[] notifications =
NotificationsManager.getNotificationsManager().getNotificationsOfType(ProjectImpl.UnableToSaveProjectNotification.class, myProject);
if (notifications.length > 0) throw new SaveCancelledException();
final ReadonlyStatusHandler.OperationStatus operationStatus = ensureConfigFilesWritable();
if (operationStatus == null) {
throw new IOException();
}
else if (operationStatus.hasReadonlyFiles()) {
MessagesEx.error(myProject, ProjectBundle.message("project.save.error", operationStatus.getReadonlyFilesMessage())).showLater();
ProjectImpl.dropUnableToSaveProjectNotification(myProject, operationStatus.getReadonlyFiles());
throw new SaveCancelledException();
}
@@ -611,7 +615,7 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject
}
if (!componentNames.isEmpty()) {
StorageUtil.logStateDiffInfo(changedFiles, componentNames);
StorageUtil.logStateDiffInfo(changedFiles, componentNames);
}
if (!isReloadPossible(componentNames)) {
@@ -637,7 +641,5 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject
return true;
}
}
@@ -26,11 +26,11 @@ import org.jetbrains.annotations.Nullable;
/**
* @author oleg
*/
public class ToggleStickToEolToolbarAction extends ToggleAction {
public class ToggleAutoScrollToTheEndToolbarAction extends ToggleAction {
public ToggleStickToEolToolbarAction() {
public ToggleAutoScrollToTheEndToolbarAction() {
super();
final String message = ActionsBundle.message("action.EditorConsoleToggleStickToEndOfOutput.text");
final String message = ActionsBundle.message("action.EditorConsoleAutoScrollToTheEnd.text");
getTemplatePresentation().setDescription(message);
getTemplatePresentation().setText(message);
getTemplatePresentation().setIcon(IconLoader.getIcon("/runConfigurations/scroll_down.png"));
@@ -309,7 +309,7 @@ public class SoftWrapModelImpl implements SoftWrapModelEx, PrioritizedDocumentLi
* @return <code>true</code> if soft wraps-aware processing should be used; <code>false</code> otherwise
*/
private boolean prepareToMapping() {
boolean useSoftWraps = myActive <= 0 && isSoftWrappingEnabled() && !myStorage.isEmpty() && myEditor.getDocument().getTextLength() > 0;
boolean useSoftWraps = myActive <= 0 && isSoftWrappingEnabled() && myEditor.getDocument().getTextLength() > 0;
if (!useSoftWraps) {
return useSoftWraps;
}
@@ -358,12 +358,18 @@ public class CachingSoftWrapDataMapper implements SoftWrapDataMapper, SoftWrapAw
if (!myBeforeChangeState.valid) {
return;
}
//System.out.println("Before recalculation (" + startOffset + "-" + endOffset + ")");
//dumpCache();
myNotAffectedByUpdateTailCacheEntries.clear();
myNotAffectedByUpdateTailCacheEntries.addAll(myCache.subList(myBeforeChangeState.endCacheEntryIndex + 1, myCache.size()));
myCache.subList(myBeforeChangeState.startCacheEntryIndex + 1, myCache.size()).clear();
for (CacheEntry entry : myNotAffectedByUpdateTailCacheEntries) {
entry.locked = true;
}
//System.out.println("Dropped all cache records starting from index " + (myBeforeChangeState.startCacheEntryIndex + 1));
}
@Override
@@ -381,45 +387,60 @@ public class CachingSoftWrapDataMapper implements SoftWrapDataMapper, SoftWrapAw
for (CacheEntry entry : myNotAffectedByUpdateTailCacheEntries) {
entry.locked = false;
}
//System.out.println("Before applying state change:");
//dumpCache();
applyStateChange();
//System.out.println("After Applying state change");
//dumpCache();
}
//Document document = myEditor.getDocument();
//CharSequence text = document.getCharsSequence();
//System.out.println("--------------------------------------------------");
//System.out.println("|");
//System.out.println("|");
//System.out.println(text);
//System.out.println("- - - - - - - - - - - - - - - - - -");
//System.out.println("text length: " + text.length() + ", soft wraps: " + myStorage.getSoftWraps());
//for (int i = 0; i < myCache.size(); i++) {
// CacheEntry entry = myCache.get(i);
// try {
// System.out.printf("line %d. %d-%d: '%s'%n", i, entry.startOffset, entry.endOffset,
// text.subSequence(entry.startOffset,Math.min(entry.endOffset, text.length())));
// }
// catch (Throwable e) {
// e.printStackTrace();
// }
//}
//if (!myCache.isEmpty() && myCache.get(myCache.size() - 1).endOffset < text.length() - 1) {
// System.out.printf("Incomplete re-parsing detected! Document length is %d but last processed offset is %s%n", text.length(),
// myCache.get(myCache.size() - 1).endOffset);
//}
@SuppressWarnings({"UseOfSystemOutOrSystemErr", "UnusedDeclaration", "CallToPrintStackTrace"})
private void dumpCache() {
Document document = myEditor.getDocument();
CharSequence text = document.getCharsSequence();
System.out.println("--------------------------------------------------");
System.out.println("|");
System.out.println("|");
System.out.println(text);
System.out.println("- - - - - - - - - - - - - - - - - -");
System.out.println("text length: " + text.length() + ", soft wraps: " + myStorage.getSoftWraps());
for (int i = 0; i < myCache.size(); i++) {
CacheEntry entry = myCache.get(i);
try {
System.out.printf("line %d. %d-%d: '%s'%n", i, entry.startOffset, entry.endOffset,
text.subSequence(Math.min(text.length() - 1, entry.startOffset) ,Math.min(entry.endOffset, text.length() - 1)));
}
catch (Throwable e) {
e.printStackTrace();
}
}
if (!myCache.isEmpty() && myCache.get(myCache.size() - 1).endOffset < text.length() - 1) {
System.out.printf("Incomplete re-parsing detected! Document length is %d but last processed offset is %s%n", text.length(),
myCache.get(myCache.size() - 1).endOffset);
}
//for (CacheEntry cacheEntry : myCache) {
// if (cacheEntry.startOffset > 0) {
// if (text.charAt(cacheEntry.startOffset - 1) != '\n' && myStorage.getSoftWrap(cacheEntry.startOffset) == null) {
// assert false;
// }
// }
// if (cacheEntry.endOffset < document.getTextLength()) {
// if (text.charAt(cacheEntry.endOffset) != '\n' && myStorage.getSoftWrap(cacheEntry.endOffset) == null) {
// assert false;
// }
// }
//}
for (CacheEntry cacheEntry : myCache) {
if (cacheEntry.startOffset > 0) {
if (text.charAt(cacheEntry.startOffset - 1) != '\n' && myStorage.getSoftWrap(cacheEntry.startOffset) == null) {
assert false;
}
}
if (cacheEntry.endOffset < document.getTextLength() - 1) {
if (text.charAt(cacheEntry.endOffset) != '\n' && myStorage.getSoftWrap(cacheEntry.endOffset) == null) {
assert false;
}
}
}
if (!myCache.isEmpty()) {
if (myCache.get(myCache.size() - 1).endOffset < myEditor.getDocument().getTextLength() - 1) {
assert false;
}
}
}
/**
@@ -39,7 +39,8 @@ class OffsetToLogicalCalculationStrategy extends AbstractMappingStrategy<Logical
super(new Computable<Pair<CacheEntry, LogicalPosition>>() {
@Override
public Pair<CacheEntry, LogicalPosition> compute() {
if (targetOffset >= editor.getDocument().getTextLength()) {
Document document = editor.getDocument();
if (targetOffset >= document.getTextLength()) {
if (cache.isEmpty()) {
return new Pair<CacheEntry, LogicalPosition>(null, new LogicalPosition(0, 0, 0, 0, 0, 0, 0));
}
@@ -53,6 +54,16 @@ class OffsetToLogicalCalculationStrategy extends AbstractMappingStrategy<Logical
return new Pair<CacheEntry, LogicalPosition>(null, eager);
}
}
else if (!cache.isEmpty()) {
CacheEntry lastEntry = cache.get(cache.size() - 1);
if (targetOffset > lastEntry.endOffset) {
LogicalPosition eager = new LogicalPosition(
lastEntry.endLogicalLine + 1, 0, lastEntry.endSoftWrapLinesBefore + lastEntry.endSoftWrapLinesCurrent,
0, 0, lastEntry.endFoldedLines, 0
);
return new Pair<CacheEntry, LogicalPosition>(null, eager);
}
}
int i = MappingUtil.getCacheEntryIndexForOffset(targetOffset, editor.getDocument(), cache);
return new Pair<CacheEntry, LogicalPosition>(cache.get(i), null);
@@ -78,6 +78,7 @@ public class SoftWrapApplianceManager implements FoldingListener, DocumentListen
private boolean myCustomIndentUsedLastTime;
private int myCustomIndentValueUsedLastTime;
private int myVisibleAreaWidth;
private long myLastDocumentStamp;
public SoftWrapApplianceManager(@NotNull SoftWrapsStorage storage,
@NotNull EditorEx editor,
@@ -109,6 +110,7 @@ public class SoftWrapApplianceManager implements FoldingListener, DocumentListen
return;
}
myLastDocumentStamp = myEditor.getDocument().getModificationStamp();
//TODO den think about sorting and merging dirty ranges here.
for (DirtyRegion dirtyRegion : myDirtyRegions) {
recalculateSoftWraps(dirtyRegion);
@@ -419,7 +421,7 @@ public class SoftWrapApplianceManager implements FoldingListener, DocumentListen
myLineWrapPositionStrategy = LanguageLineWrapPositionStrategy.INSTANCE.forEditor(myEditor);
}
int softWrapOffset = myLineWrapPositionStrategy.calculateWrapPosition(
document.getCharsSequence(), minOffset, maxOffset, preferredOffset, minOffset != preferredOffset
document.getCharsSequence(), minOffset, maxOffset, preferredOffset, true
);
if (softWrapOffset >= lineData.endLineOffset) {
return null;
@@ -454,12 +456,18 @@ public class SoftWrapApplianceManager implements FoldingListener, DocumentListen
context.softWrapColumnDiff += softWrap.getIndentInColumns();
}
public void dropDataIfNecessary() {
dropDataIfNecessary(myEditor.getDocument().getModificationStamp());
}
/**
* There is a possible case that we need to reparse the whole document (e.g. visible area width is changed or user-defined
* soft wrap indent is changed etc). This method encapsulates that logic, i.e. it checks if necessary conditions are satisfied
* and updates internal state as necessary.
*
* @param documentStamp document modification stamp to use if document was changed while soft wrapping was off
*/
public void dropDataIfNecessary() {
public void dropDataIfNecessary(long documentStamp) {
// Check if we need to recalculate soft wraps due to indent settings change.
boolean indentChanged = false;
IndentType currentIndentType = getIndentToUse();
@@ -473,7 +481,7 @@ public class SoftWrapApplianceManager implements FoldingListener, DocumentListen
// Check if we need to recalculate soft wraps due to visible area width change.
int currentVisibleAreaWidth = myWidthProvider.getVisibleAreaWidth();
if (!indentChanged && myVisibleAreaWidth == currentVisibleAreaWidth) {
if (!indentChanged && myVisibleAreaWidth == currentVisibleAreaWidth && documentStamp == myLastDocumentStamp) {
return;
}
@@ -585,6 +593,7 @@ public class SoftWrapApplianceManager implements FoldingListener, DocumentListen
@Override
public void documentChanged(DocumentEvent event) {
dropDataIfNecessary(event.getOldTimeStamp());
recalculateSoftWraps();
}
@@ -219,7 +219,7 @@ public class KeymapImpl implements Keymap, ExternalizableScheme {
}
list.add(shortcut);
if (checkParentShortcut && myParent != null && areShortcutsEqual(getParentShortcuts(actionId), getShortcuts(actionId))) {
if (checkParentShortcut && myParent != null && areShortcutsEqual(getParentShortcuts(actionId), list.toArray(new Shortcut[list.size()]))) {
myActionId2ListOfShortcuts.remove(actionId);
}
myKeystroke2ListOfIds = null;
@@ -494,11 +494,6 @@ public class KeymapImpl implements Keymap, ExternalizableScheme {
}
public Shortcut[] getShortcuts(String actionId) {
KeymapManagerEx keymapManager = getKeymapManager();
if (keymapManager.getBoundActions().contains(actionId)) {
return getShortcuts(keymapManager.getActionBinding(actionId));
}
LinkedHashSet<Shortcut> shortcuts = myActionId2ListOfShortcuts.get(actionId);
if (shortcuts == null) {
@@ -506,6 +501,11 @@ public class KeymapImpl implements Keymap, ExternalizableScheme {
return getParentShortcuts(actionId);
}
else {
KeymapManagerEx keymapManager = getKeymapManager();
if (keymapManager.getBoundActions().contains(actionId)) {
return getShortcuts(keymapManager.getActionBinding(actionId));
}
return ourEmptyShortcutsArray;
}
}
@@ -54,6 +54,7 @@ import org.picocontainer.*;
import org.picocontainer.defaults.CachingComponentAdapter;
import org.picocontainer.defaults.ConstructorInjectionComponentAdapter;
import javax.swing.event.HyperlinkEvent;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -409,4 +410,50 @@ public class ProjectImpl extends ComponentManagerImpl implements ProjectEx {
+ (isDefault() ? "(Default) " : "'" + getLocation()+"'")
;
}
public static void dropUnableToSaveProjectNotification(@NotNull final Project project, final VirtualFile[] readOnlyFiles) {
final UnableToSaveProjectNotification[] notifications =
NotificationsManager.getNotificationsManager().getNotificationsOfType(UnableToSaveProjectNotification.class, project);
if (notifications.length == 0) {
Notifications.Bus.notify(new UnableToSaveProjectNotification(project, readOnlyFiles), NotificationDisplayType.STICKY_BALLOON, project);
}
}
public static class UnableToSaveProjectNotification extends Notification {
private Project myProject;
private UnableToSaveProjectNotification(@NotNull final Project project, final VirtualFile[] readOnlyFiles) {
super("Project Settings", "Could not save project!", buildMessage(readOnlyFiles), NotificationType.ERROR, new NotificationListener() {
@Override
public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) {
final UnableToSaveProjectNotification unableToSaveProjectNotification = (UnableToSaveProjectNotification)notification;
final Project _project = unableToSaveProjectNotification.getProject();
notification.expire();
if (_project != null && !_project.isDisposed()) {
_project.save();
}
}
});
myProject = project;
}
private static String buildMessage(final VirtualFile[] readOnlyFiles) {
final StringBuffer sb = new StringBuffer(
"<p>Unable to save project files. Please ensure project files are writable and you have permissions to modify them.");
return sb.append(" <a href=\"\">Try to save project again</a>.</p>").toString();
}
public Project getProject() {
return myProject;
}
@Override
public void expire() {
myProject = null;
super.expire();
}
}
}
@@ -19,16 +19,14 @@ import com.intellij.ide.AppLifecycleListener;
import com.intellij.ide.highlighter.WorkspaceFileType;
import com.intellij.ide.impl.ProjectUtil;
import com.intellij.ide.startup.impl.StartupManagerImpl;
import com.intellij.notification.NotificationsManager;
import com.intellij.openapi.application.*;
import com.intellij.openapi.application.ex.ApplicationManagerEx;
import com.intellij.openapi.application.impl.ApplicationImpl;
import com.intellij.openapi.components.ExportableApplicationComponent;
import com.intellij.openapi.components.StateStorage;
import com.intellij.openapi.components.TrackingPathMacroSubstitutor;
import com.intellij.openapi.components.impl.stores.IComponentStore;
import com.intellij.openapi.components.impl.stores.IProjectStore;
import com.intellij.openapi.components.impl.stores.StorageUtil;
import com.intellij.openapi.components.impl.stores.XmlElementStorage;
import com.intellij.openapi.components.impl.stores.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.progress.ProcessCanceledException;
@@ -42,6 +40,7 @@ import com.intellij.openapi.project.ex.ProjectEx;
import com.intellij.openapi.project.ex.ProjectManagerEx;
import com.intellij.openapi.startup.StartupManager;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.ex.MessagesEx;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
@@ -875,13 +874,17 @@ public class ProjectManagerImpl extends ProjectManagerEx implements NamedJDOMExt
FileDocumentManager.getInstance().saveAllDocuments();
project.save();
}
fireProjectClosing(project);
myOpenProjects.remove(project);
cacheOpenProjects();
if (ensureCouldCloseIfUnableToSave(project)) {
fireProjectClosing(project);
myChangedProjectFiles.remove(project);
fireProjectClosed(project);
myOpenProjects.remove(project);
cacheOpenProjects();
myChangedProjectFiles.remove(project);
fireProjectClosed(project);
}
else return false;
}
finally {
shutDownTracker.unregisterStopperThread(Thread.currentThread());
@@ -980,6 +983,16 @@ public class ProjectManagerImpl extends ProjectManagerEx implements NamedJDOMExt
return true;
}
private static boolean ensureCouldCloseIfUnableToSave(@NotNull final Project project) {
final ProjectImpl.UnableToSaveProjectNotification[] notifications =
NotificationsManager.getNotificationsManager().getNotificationsOfType(ProjectImpl.UnableToSaveProjectNotification.class, project);
if (notifications.length == 0) return true;
final String msg = String.format("%s was unable to save some project files,\nare you sure you want to close this project anyway?",
ApplicationNamesInfo.getInstance().getProductName());
return MessagesEx.showDialog(project, msg, "Unsaved project!", new String[]{"Yes", "No"}, 0, 1, Messages.getWarningIcon()) == 0;
}
public void writeExternal(Element parentNode) throws WriteExternalException {
if (myDefaultProject != null) {
myDefaultProject.save();
@@ -81,7 +81,8 @@ public class NewDirectoryProjectDialog extends DialogWrapper {
myProjectNameTextField.getDocument().addDocumentListener(new DocumentAdapter() {
protected void textChanged(final DocumentEvent e) {
myLocationField.setText(new File(myBaseDir, myProjectNameTextField.getText()).getPath());
File f = new File(myLocationField.getText());
myLocationField.setText(new File(f.getParent(), myProjectNameTextField.getText()).getPath());
}
});
@@ -68,6 +68,7 @@ public class EditorTextField extends JPanel implements DocumentListener, TextCom
private final ArrayList<DocumentListener> myDocumentListeners = new ArrayList<DocumentListener>();
private boolean myIsListenerInstalled = false;
private boolean myIsViewer;
private boolean myOneLineMode;
private boolean myIsSupplementary;
private boolean myInheritSwingFont = true;
private Color myEnforcedBgColor = null;
@@ -81,15 +82,20 @@ public class EditorTextField extends JPanel implements DocumentListener, TextCom
}
public EditorTextField(@NotNull String text, Project project, FileType fileType) {
this(EditorFactory.getInstance().createDocument(text), project, fileType, false);
this(EditorFactory.getInstance().createDocument(text), project, fileType, false, true);
}
public EditorTextField(Document document, Project project, FileType fileType) {
this(document, project, fileType, false);
this(document, project, fileType, false, true);
}
public EditorTextField(Document document, Project project, FileType fileType, boolean isViewer) {
this(document, project, fileType, isViewer, true);
}
public EditorTextField(Document document, Project project, FileType fileType, boolean isViewer, boolean oneLineMode) {
myIsViewer = isViewer;
myOneLineMode = oneLineMode;
setDocument(document);
myProject = project;
myFileType = fileType;
@@ -397,7 +403,7 @@ public class EditorTextField extends JPanel implements DocumentListener, TextCom
});
}
editor.setOneLineMode(true);
editor.setOneLineMode(myOneLineMode);
editor.getCaretModel().moveToOffset(myDocument.getTextLength());
if (!shouldHaveBorder()) {
@@ -125,7 +125,7 @@ action.EditorToggleShowLineNumbers.text=Show L_ine Numbers
action.EditorToggleShowLineNumbers.description=Toggle display line numbers in current editor
action.EditorToggleUseSoftWraps.text=Use Soft Wraps
action.EditorToggleUseSoftWraps.description=Toggle using soft wraps in current editor
action.EditorConsoleToggleStickToEndOfOutput.text=Stick to the end of console output
action.EditorConsoleAutoScrollToTheEnd.text=Autoscroll to the end
action.ViewImportPopups.text=Show Import Popups
action.ViewImportPopups.description=Toggle display import popups in current file
action.EditorScrollToCenter.text=Scroll to Center
@@ -42,8 +42,8 @@ column.name.name=Name
changeSignature.default.value.column=Default Value
parameters.border.title=Parameters
changeSignature.exceptions.panel.border.title=Exceptions
changeSignature.propagate.parameters.title=Propagate Parameters...
changeSignature.propagate.exceptions.title=Propagate Exceptions...
changeSignature.propagate.parameters.title=Propa&gate Parameters...
changeSignature.propagate.exceptions.title=Propagate E&xceptions...
changeSignature.parameter.caller.chooser=Select Methods To Propagate New Parameters
changeSignature.exception.caller.chooser=Select Methods To Propagate New Exceptions
name.prompt=&Name:
@@ -345,7 +345,6 @@
<hectorComponentProvider implementation="com.intellij.codeInsight.daemon.impl.FileIncludeContextHectorProvider" />
<programRunner implementation="com.intellij.tools.ToolProgramRunner"/>
<programRunner implementation="com.intellij.execution.runners.BasicProgramRunner" order="last"/>
<ideRootPaneNorth implementation="com.intellij.ide.navigationToolbar.NavBarRootPaneExtension"/>
<navbar implementation="com.intellij.ide.navigationToolbar.DefaultNavBarExtension"/>
@@ -81,6 +81,8 @@
<getDataRule key="context.ProjectFileDirectory" implementationClass="com.intellij.platform.PlatformFileProjectDirectoryRule"/>
<programRunner implementation="com.intellij.execution.runners.BasicProgramRunner" order="last"/>
<applicationConfigurable provider="com.intellij.profile.codeInspection.ui.PlatformInspectionsConfigurableProvider" order="before intentions"/>
<checkoutCompletedListener implementation="com.intellij.openapi.vcs.checkout.PlatformProjectCheckoutListener"/>
@@ -26,7 +26,7 @@ public class BuildNumber implements Comparable<BuildNumber> {
private final int myBaselineVersion;
private final int myBuildNumber;
private static final String BUILD_NUMBER = "__BUILD_NUMBER__";
private static final int TOP_BASELINE_VERSION = 96;
private static final int TOP_BASELINE_VERSION = 98;
public BuildNumber(String productCode, int baselineVersion, int buildNumber) {
myProductCode = productCode;
@@ -19,6 +19,8 @@ import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
/**
* @author max
* @author Konstantin Bulenkov
@@ -56,6 +58,11 @@ public class Key<T> {
return holder == null ? null : holder.getUserData(this);
}
@Nullable
public T get(@Nullable Map<Key, Object> holder) {
return holder == null ? null : (T)holder.get(this);
}
public T get(@Nullable UserDataHolder holder, T defaultValue) {
final T t = get(holder);
return t == null ? defaultValue : t;
@@ -78,4 +85,10 @@ public class Key<T> {
holder.putUserData(this, value);
}
}
public void set(@Nullable Map<Key, Object> holder, T value) {
if (holder != null) {
holder.put(this, value);
}
}
}
@@ -20,8 +20,10 @@
package com.intellij.ui;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.lang.annotation.Annotation;
/**
* @author max
@@ -87,4 +89,13 @@ public class ColorUtil {
return defaultValue;
}
}
@Nullable
public static Color getColor(@NotNull Class<?> cls) {
final Annotation annotation = cls.getAnnotation(Colored.class);
if (annotation instanceof Colored) {
return fromHex(((Colored)annotation).color(), null);
}
return null;
}
}
@@ -0,0 +1,39 @@
/*
* 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.ui;
import org.intellij.lang.annotations.RegExp;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.TYPE;
/**
* Associates an entity with some color in hex-format
*
* @author Konstantin Bulenkov
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({TYPE})
public @interface Colored {
/**
* @return color as hex-string
*/
@RegExp(prefix = "[0-9a-f]{6}")
String color() default "ffffff";
}
@@ -33,11 +33,11 @@ public class ProcessingContext {
}
@SuppressWarnings({"ConstantConditions"})
public Object get(@NotNull @NonNls final String key) {
public Object get(@NotNull @NonNls final Object key) {
return myMap == null? null : myMap.get(key);
}
public void put(@NotNull @NonNls final String key, @NotNull final Object value) {
public void put(@NotNull @NonNls final Object key, @NotNull final Object value) {
checkMapInitialized();
myMap.put(key, value);
}
@@ -493,7 +493,7 @@ public class ContainerUtil {
@NotNull
public static <T> List<T> concat(@NotNull Iterable<? extends Collection<T>> list) {
final ArrayList<T> result = new ArrayList<T>();
List<T> result = new ArrayList<T>();
for (final Collection<T> ts : list) {
result.addAll(ts);
}
@@ -117,7 +117,13 @@ public class RecursiveFileHolder implements FileHolder {
mySwitchRoots = new ArrayList<VirtualFile>(myFiles.keySet());
Collections.sort(mySwitchRoots, FilePathComparator.getInstance());
preCheckFiles();
for (VirtualFile file : mySwitchRoots) {
// collapsing to common ancestors only makes sense if at least some of the files are directories
if (file.isDirectory()) {
preCheckFiles();
break;
}
}
Collections.reverse(mySwitchRoots);
}
@@ -1,7 +1,7 @@
<html>
<body><table> <tr> <td valign="top" height="150">
<font face="verdana" size="-1">
This inspection reports any instances of <b><font color="#000080">Serializable</font></b> classes which do not provide a <b><font color="#000080">serialVersionUID</font></b> field.
This inspection reports any <b><font color="#000080">Serializable</font></b> classes which do not provide a <b><font color="#000080">serialVersionUID</font></b> field.
Without a <b><font color="#000080">serialVersionUID</font></b> field, any change to a class will make previously serialized versions unreadable.
<p>
Use the table below to specify what specific classes and inheritors should be excluded
@@ -55,4 +55,11 @@
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Manual array copy</problem_class>
<description>Manual array copy #loc</description>
</problem>
<problem>
<file>ManualArrayCopy.java</file>
<line>87</line>
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Manual array copy</problem_class>
<description>Manual array copy #loc</description>
</problem>
</problems>
@@ -5,7 +5,7 @@ import java.io.Serializable;
public class SerializableHasSerialVersionUIDField<T extends Serializable> implements Serializable {
public abstract class DoNotWarnOnMe implements Serializable { }
public abstract class DoWarnOnMe implements Serializable { }
public interface X extends Serializable {}
}
@@ -1,10 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<problems>
<problem>
<file>SerializableHasSerialVersionUIDField.java</file>
<line>5</line>
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Serializable class without 'serialVersionUID'</problem_class>
<description>&lt;code&gt;SerializableHasSerialVersionUIDField&lt;/code&gt; does not define a 'serialVersionUID' field #loc</description>
</problem>
<problem>
<file>SerializableHasSerialVersionUIDField.java</file>
<line>8</line>
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Serializable class without 'serialVersionUID'</problem_class>
<description>&lt;code&gt;DoNotWarnOnMe&lt;/code&gt; does not define a 'serialVersionUID' field #loc</description>
</problem>
</problems>
@@ -178,6 +178,8 @@ public class GroovyAnnotator extends GroovyElementVisitor implements Annotator {
registerAccessFix(annotation, referenceExpression, ((PsiMember)resolved));
}
}
//todo uncomment when correct isStatic() is working
if (!resolveResult.isStaticsOK() && resolved instanceof PsiModifierListOwner) {
if (!((PsiModifierListOwner)resolved).hasModifierProperty(GrModifier.STATIC)) {
Annotation annotation = myHolder.createInfoAnnotation(referenceExpression,
@@ -70,7 +70,6 @@ public abstract class AbstractGroovyLibraryManager extends LibraryManager {
editor.setName(model.getName());
fillLibrary(path, editor);
editor.apply(model);
Disposer.dispose(editor);
}
protected abstract void fillLibrary(String path, LibraryEditor libraryEditor);
@@ -67,7 +67,7 @@ public class ExpressionStatement implements GroovyElementTypes {
final IElementType result = parseExpressionStatement(builder, parser);
if (result != CALL_EXPRESSION) {
marker.drop();
return result != null;
return result != WRONGWAY;
}
while (true) {
@@ -37,6 +37,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariableDeclaration;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
@@ -137,6 +138,7 @@ public class PsiImplUtil {
GrClosableBlock closure = PsiTreeUtil.getParentOfType(refExpr, GrClosableBlock.class);
while (closure != null) {
PsiElement parent = closure.getParent();
if (parent instanceof GrArgumentList) parent = parent.getParent();
if (parent instanceof GrMethodCall) {
GrExpression funExpr = ((GrMethodCall)parent).getInvokedExpression();
if (funExpr instanceof GrReferenceExpression) {
@@ -326,6 +328,7 @@ public class PsiImplUtil {
method.hasModifierProperty(PsiModifier.STATIC);
}
@Nullable
public static PsiMethod resolveMethod(GrMethodCall expression) {
final GrExpression methodExpr = expression.getInvokedExpression();
if (methodExpr instanceof GrReferenceExpression) {
@@ -42,6 +42,7 @@ import org.jetbrains.plugins.groovy.lang.lexer.TokenSets;
import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes;
import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField;
@@ -580,20 +581,20 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl implements
}
if (qualifier instanceof GrReferenceExpression && "class".equals(((GrReferenceExpression)qualifier).getReferenceName())) {
processIfJavaLangClass(processor, qualifier.getType());
processIfJavaLangClass(processor, qualifier.getType(), qualifier);
} else if (qualifier instanceof GrThisReferenceExpression) {
processIfJavaLangClass(processor, qualifier.getType());
processIfJavaLangClass(processor, qualifier.getType(), qualifier);
}
}
}
private void processIfJavaLangClass(ResolverProcessor processor, PsiType type) {
private void processIfJavaLangClass(ResolverProcessor processor, PsiType type, GroovyPsiElement resolveContext) {
if (type instanceof PsiClassType) {
final PsiClass psiClass = ((PsiClassType)type).resolve();
if (psiClass != null && CommonClassNames.JAVA_LANG_CLASS.equals(psiClass.getQualifiedName())) {
final PsiType[] params = ((PsiClassType)type).getParameters();
if (params.length == 1) {
processClassQualifierType(processor, params[0]);
processClassQualifierType(processor, params[0], resolveContext);
}
}
}
@@ -611,13 +612,13 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl implements
if (substitutor != null) {
PsiType componentType = substitutor.substitute(listClass.getTypeParameters()[0]);
if (componentType != null) {
processClassQualifierType(processor, componentType);
processClassQualifierType(processor, componentType, qualifier);
}
}
}
}
} else if (qualifierType instanceof PsiArrayType) {
processClassQualifierType(processor, ((PsiArrayType) qualifierType).getComponentType());
processClassQualifierType(processor, ((PsiArrayType) qualifierType).getComponentType(), qualifier);
}
}
@@ -627,22 +628,22 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl implements
if (qualifier instanceof GrReferenceExpression) {
PsiElement resolved = ((GrReferenceExpression) qualifier).resolve();
if (resolved instanceof PsiPackage) {
if (!resolved.processDeclarations(processor, ResolveState.initial(), null, this)) //noinspection UnnecessaryReturnStatement
if (!resolved.processDeclarations(processor, ResolveState.initial().put(ResolverProcessor.RESOLVE_CONTEXT, qualifier), null, this)) //noinspection UnnecessaryReturnStatement
return;
}
else {
qualifierType = JavaPsiFacade.getInstance(getProject()).getElementFactory()
.createTypeByFQClassName(CommonClassNames.JAVA_LANG_OBJECT, getResolveScope());
processClassQualifierType(processor, qualifierType);
processClassQualifierType(processor, qualifierType, qualifier);
}
}
} else {
if (qualifierType instanceof PsiIntersectionType) {
for (PsiType conjunct : ((PsiIntersectionType) qualifierType).getConjuncts()) {
processClassQualifierType(processor, conjunct);
processClassQualifierType(processor, conjunct, qualifier);
}
} else {
processClassQualifierType(processor, qualifierType);
processClassQualifierType(processor, qualifierType, qualifier);
if (qualifier instanceof GrReferenceExpression) {
PsiElement resolved = ((GrReferenceExpression) qualifier).resolve();
if (resolved instanceof PsiClass) { //omitted .class
@@ -666,14 +667,14 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl implements
}
}
private void processClassQualifierType(ResolverProcessor processor, PsiType qualifierType) {
private void processClassQualifierType(ResolverProcessor processor, PsiType qualifierType, GroovyPsiElement resolveContext) {
Project project = getProject();
if (qualifierType instanceof PsiClassType) {
PsiClassType.ClassResolveResult qualifierResult = ((PsiClassType) qualifierType).resolveGenerics();
PsiClass qualifierClass = qualifierResult.getElement();
if (qualifierClass != null) {
if (!qualifierClass.processDeclarations(processor,
ResolveState.initial().put(PsiSubstitutor.KEY, qualifierResult.getSubstitutor()), null, this))
if (!qualifierClass.processDeclarations(processor, ResolveState.initial().put(PsiSubstitutor.KEY, qualifierResult.getSubstitutor())
.put(ResolverProcessor.RESOLVE_CONTEXT, resolveContext), null, this))
return;
}
if (!ResolveUtil.processCategoryMembers(this, processor)) return;
@@ -682,7 +683,7 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl implements
if (!arrayClass.processDeclarations(processor, ResolveState.initial(), null, this)) return;
} else if (qualifierType instanceof PsiIntersectionType) {
for (PsiType conjunct : ((PsiIntersectionType) qualifierType).getConjuncts()) {
processClassQualifierType(processor, conjunct);
processClassQualifierType(processor, conjunct, resolveContext);
}
return;
}
@@ -53,7 +53,6 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgument
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrLiteral;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrCallExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrIndexProperty;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression;
@@ -68,7 +67,6 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.GrClosureType;
import org.jetbrains.plugins.groovy.lang.psi.impl.GrMapType;
import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyResolveResultImpl;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.literals.GrLiteralImpl;
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GroovyScriptClass;
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.JavaIdentifier;
import org.jetbrains.plugins.groovy.lang.psi.impl.types.GrClosureSignatureUtil;
@@ -384,6 +382,10 @@ public class PsiUtil {
}
public static boolean isStaticsOK(PsiModifierListOwner owner, PsiElement place) {
return isStaticsOK(owner, place, owner);
}
public static boolean isStaticsOK(PsiModifierListOwner owner, PsiElement place, PsiElement resolveContext) {
if (owner instanceof PsiMember) {
if (place instanceof GrReferenceExpression) {
GrExpression qualifier = ((GrReferenceExpression)place).getQualifierExpression();
@@ -459,9 +461,14 @@ public class PsiUtil {
if (((PsiMember)owner).getContainingClass() == null) return true;
if (owner instanceof GrVariable && !(owner instanceof GrField)) return true;
if (owner.hasModifierProperty(GrModifier.STATIC)) return true;
final GrMember placeOwner = PsiTreeUtil.getParentOfType(place, GrMember.class);
if (placeOwner == null) return true;
return !placeOwner.hasModifierProperty(GrModifier.STATIC);
PsiElement stopAt = resolveContext != null ? PsiTreeUtil.findCommonParent(place, resolveContext) : null;
while (place != null && place != stopAt && !(place instanceof GrMember)) {
if (place instanceof PsiFile) break;
place = place.getParent();
}
if (place == null || place instanceof PsiFile || place == stopAt) return true;
return !((GrMember)place).hasModifierProperty(GrModifier.STATIC);
}
}
}
@@ -109,7 +109,7 @@ public class ResolverProcessor implements PsiScopeProcessor, NameHint, ClassHint
if (resolveContext instanceof GrImportStatement) return true;
if (element instanceof PsiModifierListOwner) {
return PsiUtil.isStaticsOK((PsiModifierListOwner) element, myPlace);
return PsiUtil.isStaticsOK((PsiModifierListOwner) element, myPlace, resolveContext);
}
return true;
}
@@ -20,13 +20,20 @@ import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.SelectionModel;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pass;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.refactoring.IntroduceTargetChooser;
import com.intellij.refactoring.RefactoringActionHandler;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -36,8 +43,7 @@ import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.*;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrCodeBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrStringInjection;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter;
@@ -49,6 +55,7 @@ import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringUtil;
import org.jetbrains.plugins.groovy.refactoring.NameValidator;
import java.util.ArrayList;
import java.util.List;
/**
* @author ilyas
@@ -59,14 +66,78 @@ public abstract class GroovyIntroduceVariableBase implements RefactoringActionHa
protected static String REFACTORING_NAME = GroovyRefactoringBundle.message("introduce.variable.title");
private PsiElement positionElement = null;
public void invoke(@NotNull Project project, Editor editor, PsiFile file, @Nullable DataContext dataContext) {
if (!editor.getSelectionModel().hasSelection()) {
editor.getSelectionModel().selectLineAtCaret();
public void invoke(final @NotNull Project project, final Editor editor, final PsiFile file, final @Nullable DataContext dataContext) {
final SelectionModel selectionModel = editor.getSelectionModel();
if (!selectionModel.hasSelection()) {
final int offset = editor.getCaretModel().getOffset();
if (!selectionModel.hasSelection()) {
final List<GrExpression> expressions = collectExpressions(file, editor, offset);
if (expressions.isEmpty()) {
selectionModel.selectLineAtCaret();
} else if (expressions.size() == 1) {
final TextRange textRange = expressions.get(0).getTextRange();
selectionModel.setSelection(textRange.getStartOffset(), textRange.getEndOffset());
}
else {
IntroduceTargetChooser.showChooser(editor, expressions,
new Pass<GrExpression>() {
public void pass(final GrExpression selectedValue) {
invoke(project, editor, file, selectedValue.getTextRange().getStartOffset(),
selectedValue.getTextRange().getEndOffset());
}
},
new Function<GrExpression, String>() {
@Override
public String fun(GrExpression grExpression) {
return grExpression.getText();
}
});
return;
}
}
}
GroovyRefactoringUtil.trimSpacesAndComments(editor, file, true);
invoke(project, editor, file, editor.getSelectionModel().getSelectionStart(), editor.getSelectionModel().getSelectionEnd());
invoke(project, editor, file, selectionModel.getSelectionStart(), selectionModel.getSelectionEnd());
}
public static List<GrExpression> collectExpressions(final PsiFile file, final Editor editor, final int offset) {
Document document = editor.getDocument();
CharSequence text = document.getCharsSequence();
int correctedOffset = offset;
int textLength = document.getTextLength();
if (offset >= textLength) {
correctedOffset = textLength - 1;
}
else if (!Character.isJavaIdentifierPart(text.charAt(offset))) {
correctedOffset--;
}
if (correctedOffset < 0) {
correctedOffset = offset;
}
else if (!Character.isJavaIdentifierPart(text.charAt(correctedOffset))) {
if (text.charAt(correctedOffset) == ';') {//initially caret on the end of line
correctedOffset--;
}
if (text.charAt(correctedOffset) != ')') {
correctedOffset = offset;
}
}
final PsiElement elementAtCaret = file.findElementAt(correctedOffset);
final List<GrExpression> expressions = new ArrayList<GrExpression>();
GrExpression expression = PsiTreeUtil.getParentOfType(elementAtCaret, GrExpression.class);
while (expression != null) {
if (!expressions.contains(expression) && !(expression instanceof GrParenthesizedExpression) && !(expression instanceof GrSuperReferenceExpression) && expression.getType() != PsiType.VOID) {
if (!(expression instanceof GrReferenceExpression && (expression.getParent() instanceof GrMethodCallExpression ||((GrReferenceExpression)expression).resolve() instanceof PsiClass))&& !(expression instanceof GrAssignmentExpression)) {
expressions.add(expression);
}
}
expression = PsiTreeUtil.getParentOfType(expression, GrExpression.class);
}
return expressions;
}
private boolean invoke(final Project project, final Editor editor, PsiFile file, int startOffset, int endOffset) {
PsiDocumentManager.getInstance(project).commitAllDocuments();
if (!(file instanceof GroovyFileBase)) {
@@ -147,8 +218,8 @@ public abstract class GroovyIntroduceVariableBase implements RefactoringActionHa
if (!CommonRefactoringUtil.checkReadOnlyStatus(project, file)) return false;
// Find occurrences
final PsiElement[] occurrences = GroovyRefactoringUtil.getExpressionOccurrences(
(GrExpression)PsiUtil.skipParentheses(selectedExpr, false), tempContainer);
final PsiElement[] occurrences =
GroovyRefactoringUtil.getExpressionOccurrences(PsiUtil.skipParentheses(selectedExpr, false), tempContainer);
if (occurrences == null || occurrences.length == 0) {
String message = RefactoringBundle.getCannotRefactorMessage(GroovyRefactoringBundle.message("no.occurences.found"));
showErrorMessage(project, editor, message);
@@ -19,6 +19,7 @@ package org.jetbrains.plugins.groovy.lang.resolve;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
@@ -74,4 +75,10 @@ public abstract class GroovyResolveTestCase extends LightGroovyTestCase {
return configureByFile(filePath, null);
}
@Nullable
protected PsiElement resolve(String fileName) {
PsiReference ref = configureByFile(getTestName(true) + "/" + fileName);
return ref.resolve();
}
}
@@ -602,11 +602,6 @@ class Zoo {
assertInstanceOf resolved, GrVariable
}
private PsiElement resolve(String fileName) {
PsiReference ref = configureByFile("${getTestName(true)}/$fileName");
ref.resolve()
}
public void testCommandExpressionStatement1() {
PsiElement method = resolve("A.groovy")
assertInstanceOf method, GrMethod
@@ -581,4 +581,17 @@ set<caret>Foo(2)
PsiElement resolved = ref.resolve()
assertInstanceOf resolved, PsiField
}
public void testFieldAccessInStaticContext() {
def ref = configureByFile("fieldAccessInStaticContext/A.groovy")
def resolveResult = ref.advancedResolve()
assertTrue !resolveResult.staticsOK
}
public void testFieldAccessInClosureVsStaticContext() {
def ref = configureByFile("fieldAccessInClosureVsStaticContext/A.groovy")
def resolveResult = ref.advancedResolve()
assertTrue resolveResult.staticsOK
}
}
@@ -0,0 +1,18 @@
class MyDom {
static void main(String[] args) {
new Foo().foo({
print ba<ref>r
});
}
}
class Foo {
def bar = 2
def foo(Closure c) {
c.delegate = this
c.call()
}
}
@@ -0,0 +1,7 @@
class Foo {
def field = 2
static def foo() {
print fiel<ref>d
}
}
@@ -40,7 +40,7 @@ public class SpellCheckerSeveritiesProvider extends SeveritiesProvider {
final TextAttributes attributes = new TextAttributes();
attributes.setEffectType(EffectType.WAVE_UNDERSCORE);
attributes.setEffectColor(Color.GRAY);
attributes.setEffectColor(new Color(0, 128, 0));
result.add(new HighlightInfoType.HighlightInfoTypeImpl(TYPO,
TextAttributesKey.createTextAttributesKey("TYPO", attributes)));