mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-10-01 09:03:31 +07:00
Merge branch 'master' of git.labs.intellij.net:idea/community
This commit is contained in:
@@ -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);
|
||||
|
||||
|
||||
+12
-26
@@ -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:
|
||||
|
||||
+1
-1
@@ -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();
|
||||
|
||||
-5
@@ -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) {
|
||||
|
||||
+2
-6
@@ -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,
|
||||
|
||||
+2
-1
@@ -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;
|
||||
|
||||
+1
-2
@@ -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);
|
||||
|
||||
+1
-4
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+41
-30
@@ -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()) {
|
||||
|
||||
+2
-2
@@ -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);
|
||||
}
|
||||
|
||||
+5
-1
@@ -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) {
|
||||
|
||||
+11
-12
@@ -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) {
|
||||
|
||||
+3
-3
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+200
-94
@@ -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) {
|
||||
|
||||
+3
-2
@@ -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;
|
||||
|
||||
+113
-32
@@ -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
|
||||
|
||||
+3
-2
@@ -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
|
||||
|
||||
+9
-3
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+13
-110
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
+275
@@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
+34
-20
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -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() {
|
||||
|
||||
+7
-5
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -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;
|
||||
}
|
||||
|
||||
+55
-34
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+12
-1
@@ -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);
|
||||
|
||||
+12
-3
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+22
-9
@@ -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
-1
@@ -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
|
||||
|
||||
+7
@@ -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>
|
||||
+1
-1
@@ -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 {}
|
||||
}
|
||||
+9
-1
@@ -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><code>SerializableHasSerialVersionUIDField</code> 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><code>DoNotWarnOnMe</code> 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,
|
||||
|
||||
-1
@@ -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);
|
||||
|
||||
+1
-1
@@ -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) {
|
||||
|
||||
+15
-14
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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;
|
||||
}
|
||||
|
||||
+80
-9
@@ -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);
|
||||
|
||||
+7
@@ -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
|
||||
|
||||
+13
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
+18
@@ -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
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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)));
|
||||
|
||||
Reference in New Issue
Block a user