mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -397,7 +397,7 @@ public class ProjectTypeStep extends ModuleWizardStep implements Disposable {
|
||||
private boolean showCustomOptions(@NotNull ModuleBuilder builder) {
|
||||
String card = builder.getBuilderId();
|
||||
if (!myCustomSteps.containsKey(card)) {
|
||||
ModuleWizardStep step = builder.getCustomOptionsStep(this);
|
||||
ModuleWizardStep step = builder.getCustomOptionsStep(myContext, this);
|
||||
if (step == null) return false;
|
||||
step.updateStep();
|
||||
myCustomSteps.put(card, step);
|
||||
|
||||
+12
-7
@@ -180,17 +180,12 @@ public abstract class FrameworkSupportModelBase extends UserDataHolderBase imple
|
||||
}
|
||||
|
||||
public void onFrameworkSelectionChanged(FrameworkSupportNode node) {
|
||||
final FrameworkSupportModelListener multicaster = myDispatcher.getMulticaster();
|
||||
final FrameworkSupportInModuleProvider provider = node.getUserObject();
|
||||
//todo[nik]
|
||||
boolean checked = node.isChecked();
|
||||
if (provider instanceof OldFrameworkSupportProviderWrapper) {
|
||||
final FrameworkSupportProvider oldProvider = ((OldFrameworkSupportProviderWrapper) provider).getProvider();
|
||||
if (node.isChecked()) {
|
||||
multicaster.frameworkSelected(oldProvider);
|
||||
}
|
||||
else {
|
||||
multicaster.frameworkUnselected(oldProvider);
|
||||
}
|
||||
selectFramework(oldProvider, checked);
|
||||
}
|
||||
for (FrameworkSupportInModuleProvider.FrameworkDependency dependency : provider.getDependenciesFrameworkIds()) {
|
||||
if (!dependency.isOptional()) {
|
||||
@@ -200,6 +195,16 @@ public abstract class FrameworkSupportModelBase extends UserDataHolderBase imple
|
||||
}
|
||||
}
|
||||
|
||||
public void selectFramework(FrameworkSupportProvider provider, boolean checked) {
|
||||
final FrameworkSupportModelListener multicaster = myDispatcher.getMulticaster();
|
||||
if (checked) {
|
||||
multicaster.frameworkSelected(provider);
|
||||
}
|
||||
else {
|
||||
multicaster.frameworkUnselected(provider);
|
||||
}
|
||||
}
|
||||
|
||||
public void fireWizardStepUpdated() {
|
||||
myDispatcher.getMulticaster().wizardStepUpdated();
|
||||
}
|
||||
|
||||
@@ -17,12 +17,8 @@ package com.intellij.ide.util.projectWizard;
|
||||
|
||||
import com.intellij.CommonBundle;
|
||||
import com.intellij.ide.IdeBundle;
|
||||
import com.intellij.ide.util.PropertiesComponent;
|
||||
import com.intellij.openapi.module.ModuleType;
|
||||
import com.intellij.openapi.options.ConfigurationException;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectManager;
|
||||
import com.intellij.openapi.projectRoots.ProjectJdkTable;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.projectRoots.SdkTypeId;
|
||||
import com.intellij.openapi.roots.ProjectRootManager;
|
||||
@@ -35,8 +31,6 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
/**
|
||||
* @author Dmitry Avdeev
|
||||
@@ -47,64 +41,29 @@ public class SdkSettingsStep extends ModuleWizardStep {
|
||||
protected final WizardContext myWizardContext;
|
||||
protected final ProjectSdksModel myModel;
|
||||
private final ModuleBuilder myModuleBuilder;
|
||||
private final JPanel myJdkPanel;
|
||||
|
||||
public SdkSettingsStep(@NotNull SettingsStep settingsStep, @NotNull ModuleBuilder moduleBuilder, @NotNull Condition<SdkTypeId> sdkFilter) {
|
||||
public SdkSettingsStep(SettingsStep settingsStep, @NotNull ModuleBuilder moduleBuilder,
|
||||
@NotNull Condition<SdkTypeId> sdkFilter) {
|
||||
|
||||
this(settingsStep.getContext(), moduleBuilder, sdkFilter);
|
||||
settingsStep.addSettingsField(getSdkFieldLabel(settingsStep.getContext().getProject()), myJdkPanel);
|
||||
}
|
||||
|
||||
public SdkSettingsStep(WizardContext context,
|
||||
@NotNull ModuleBuilder moduleBuilder,
|
||||
@NotNull Condition<SdkTypeId> sdkFilter) {
|
||||
myModuleBuilder = moduleBuilder;
|
||||
|
||||
myWizardContext = settingsStep.getContext();
|
||||
myWizardContext = context;
|
||||
myModel = new ProjectSdksModel();
|
||||
Project project = myWizardContext.getProject();
|
||||
myModel.reset(project);
|
||||
|
||||
myJdkComboBox = new JdkComboBox(myModel, sdkFilter);
|
||||
|
||||
final PropertiesComponent component = project == null ? PropertiesComponent.getInstance() : PropertiesComponent.getInstance(project);
|
||||
ModuleType moduleType = moduleBuilder.getModuleType();
|
||||
final String selectedJdkProperty = "jdk.selected." + (moduleType == null ? "" : moduleType.getId());
|
||||
myJdkComboBox.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
Sdk jdk = myJdkComboBox.getSelectedJdk();
|
||||
if (jdk != null) {
|
||||
component.setValue(selectedJdkProperty, jdk.getName());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (project != null) {
|
||||
Sdk sdk = ProjectRootManager.getInstance(project).getProjectSdk();
|
||||
if (sdk != null && moduleBuilder.isSuitableSdkType(sdk.getSdkType())) {
|
||||
// use project SDK
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// set default project SDK
|
||||
Project defaultProject = ProjectManager.getInstance().getDefaultProject();
|
||||
Sdk sdk = ProjectRootManager.getInstance(defaultProject).getProjectSdk();
|
||||
if (sdk != null && sdkFilter.value(sdk.getSdkType())) {
|
||||
myJdkComboBox.setSelectedJdk(sdk);
|
||||
}
|
||||
}
|
||||
|
||||
String value = component.getValue(selectedJdkProperty);
|
||||
if (value != null) {
|
||||
Sdk jdk = ProjectJdkTable.getInstance().findJdk(value);
|
||||
if (jdk != null) {
|
||||
myJdkComboBox.setSelectedJdk(jdk);
|
||||
}
|
||||
}
|
||||
|
||||
JButton button = new JButton("Ne\u001Bw...");
|
||||
myJdkComboBox.setSetupButton(button, project, myModel,
|
||||
project == null ? new JdkComboBox.NoneJdkComboBoxItem() : new JdkComboBox.ProjectJdkComboBoxItem(),
|
||||
null,
|
||||
false);
|
||||
JPanel jdkPanel = new JPanel(new BorderLayout(4, 0));
|
||||
jdkPanel.add(myJdkComboBox);
|
||||
jdkPanel.add(button, BorderLayout.EAST);
|
||||
settingsStep.addSettingsField(getSdkFieldLabel(project), jdkPanel);
|
||||
|
||||
myJdkComboBox = JdkComboBox.createSdkComboBox(moduleBuilder, sdkFilter, project, myModel);
|
||||
myJdkPanel = new JPanel(new BorderLayout(4, 0));
|
||||
myJdkPanel.add(myJdkComboBox);
|
||||
myJdkPanel.add(myJdkComboBox.getSetUpButton(), BorderLayout.EAST);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -112,9 +71,13 @@ public class SdkSettingsStep extends ModuleWizardStep {
|
||||
return (project == null ? "Project" : "Module") + " \u001BSDK:";
|
||||
}
|
||||
|
||||
public JdkComboBox getJdkComboBox() {
|
||||
return myJdkComboBox;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getComponent() {
|
||||
return null;
|
||||
return myJdkPanel;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,13 +16,19 @@
|
||||
package com.intellij.openapi.roots.ui.configuration;
|
||||
|
||||
import com.intellij.ide.DataManager;
|
||||
import com.intellij.ide.util.PropertiesComponent;
|
||||
import com.intellij.ide.util.projectWizard.ModuleBuilder;
|
||||
import com.intellij.ide.util.projectWizard.ProjectJdkListRenderer;
|
||||
import com.intellij.openapi.actionSystem.*;
|
||||
import com.intellij.openapi.module.ModuleType;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectBundle;
|
||||
import com.intellij.openapi.project.ProjectManager;
|
||||
import com.intellij.openapi.projectRoots.ProjectJdkTable;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.projectRoots.SdkType;
|
||||
import com.intellij.openapi.projectRoots.SdkTypeId;
|
||||
import com.intellij.openapi.roots.ProjectRootManager;
|
||||
import com.intellij.openapi.roots.ui.configuration.projectRoot.JdkListConfigurable;
|
||||
import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel;
|
||||
import com.intellij.openapi.ui.ComboBoxWithWidePopup;
|
||||
@@ -57,6 +63,7 @@ public class JdkComboBox extends ComboBoxWithWidePopup {
|
||||
private final Condition<SdkTypeId> myFilter;
|
||||
@Nullable
|
||||
private final Condition<SdkTypeId> myCreationFilter;
|
||||
private JButton mySetUpButton;
|
||||
|
||||
public JdkComboBox(@NotNull final ProjectSdksModel jdkModel) {
|
||||
this(jdkModel, null);
|
||||
@@ -103,6 +110,57 @@ public class JdkComboBox extends ComboBoxWithWidePopup {
|
||||
});
|
||||
}
|
||||
|
||||
public static JdkComboBox createSdkComboBox(ModuleBuilder moduleBuilder,
|
||||
Condition<SdkTypeId> sdkFilter,
|
||||
Project project,
|
||||
ProjectSdksModel model) {
|
||||
final JdkComboBox comboBox = new JdkComboBox(model, sdkFilter);
|
||||
|
||||
final PropertiesComponent component = project == null ? PropertiesComponent.getInstance() : PropertiesComponent.getInstance(project);
|
||||
ModuleType moduleType = moduleBuilder.getModuleType();
|
||||
final String selectedJdkProperty = "jdk.selected." + (moduleType == null ? "" : moduleType.getId());
|
||||
comboBox.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
Sdk jdk = comboBox.getSelectedJdk();
|
||||
if (jdk != null) {
|
||||
component.setValue(selectedJdkProperty, jdk.getName());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (project != null) {
|
||||
Sdk sdk = ProjectRootManager.getInstance(project).getProjectSdk();
|
||||
if (sdk != null && moduleBuilder.isSuitableSdkType(sdk.getSdkType())) {
|
||||
// use project SDK
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// set default project SDK
|
||||
Project defaultProject = ProjectManager.getInstance().getDefaultProject();
|
||||
Sdk sdk = ProjectRootManager.getInstance(defaultProject).getProjectSdk();
|
||||
if (sdk != null && sdkFilter.value(sdk.getSdkType())) {
|
||||
comboBox.setSelectedJdk(sdk);
|
||||
}
|
||||
}
|
||||
|
||||
String value = component.getValue(selectedJdkProperty);
|
||||
if (value != null) {
|
||||
Sdk jdk = ProjectJdkTable.getInstance().findJdk(value);
|
||||
if (jdk != null) {
|
||||
comboBox.setSelectedJdk(jdk);
|
||||
}
|
||||
}
|
||||
|
||||
JButton button = new JButton("Ne\u001Bw...");
|
||||
comboBox.setSetupButton(button, project, model,
|
||||
project == null ? new NoneJdkComboBoxItem() : new ProjectJdkComboBoxItem(),
|
||||
null,
|
||||
false);
|
||||
return comboBox;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
final Rectangle rec = ScreenUtil.getScreenRectangle(0, 0);
|
||||
@@ -140,7 +198,9 @@ public class JdkComboBox extends ComboBoxWithWidePopup {
|
||||
final JdkComboBoxItem firstItem,
|
||||
@Nullable final Condition<Sdk> additionalSetup,
|
||||
final String actionGroupTitle) {
|
||||
setUpButton.addActionListener(new ActionListener() {
|
||||
|
||||
mySetUpButton = setUpButton;
|
||||
mySetUpButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
DefaultActionGroup group = new DefaultActionGroup();
|
||||
@@ -199,6 +259,10 @@ public class JdkComboBox extends ComboBoxWithWidePopup {
|
||||
});
|
||||
}
|
||||
|
||||
public JButton getSetUpButton() {
|
||||
return mySetUpButton;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdkComboBoxItem getSelectedItem() {
|
||||
return (JdkComboBoxItem)super.getSelectedItem();
|
||||
|
||||
@@ -161,7 +161,7 @@ public class ControlFlowFactory {
|
||||
Reference<ConcurrentList<ControlFlowContext>> cachedRef = cachedFlows.get(element);
|
||||
ConcurrentList<ControlFlowContext> cached = com.intellij.reference.SoftReference.dereference(cachedRef);
|
||||
if (cached == null) {
|
||||
cached = ContainerUtil.createLockFreeCopyOnWriteList();
|
||||
cached = ContainerUtil.createConcurrentList();
|
||||
cachedFlows.put(element, new SoftReference<ConcurrentList<ControlFlowContext>>(cached));
|
||||
}
|
||||
return cached;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2013 JetBrains s.r.o.
|
||||
* Copyright 2000-2014 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.
|
||||
@@ -30,6 +30,8 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
@@ -73,7 +75,7 @@ public class JobUtilTest extends PlatformTestCase {
|
||||
return COUNT.incrementAndGet();
|
||||
}
|
||||
|
||||
public void testJobUtilFinishes() throws Exception {
|
||||
public void testJobUtilCorrectlySplitsUpHugeWorkAndFinishes() throws Exception {
|
||||
COUNT.set(0);
|
||||
int N = 100000;
|
||||
List<String> list = Collections.nCopies(N, null);
|
||||
@@ -259,7 +261,7 @@ public class JobUtilTest extends PlatformTestCase {
|
||||
public void testJobUtilRecursiveCancel() throws Exception {
|
||||
final List<String> list = Collections.nCopies(100, "");
|
||||
final List<Integer> ilist = Collections.nCopies(100, 0);
|
||||
for (int i=0; i<1/*0*/; i++) {
|
||||
for (int i=0; i<10; i++) {
|
||||
COUNT.set(0);
|
||||
long start = System.currentTimeMillis();
|
||||
boolean success = false;
|
||||
@@ -277,7 +279,7 @@ public class JobUtilTest extends PlatformTestCase {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
System.out.println("nestedSuccess = " + nestedSuccess);
|
||||
//System.out.println("nestedSuccess = " + nestedSuccess);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
@@ -294,4 +296,35 @@ public class JobUtilTest extends PlatformTestCase {
|
||||
assertFalse(success);
|
||||
}
|
||||
}
|
||||
|
||||
public void testSaturation() throws InterruptedException {
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
for (int i=0; i<100; i++) {
|
||||
JobLauncher.getInstance().submitToJobThread(0, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
latch.await();
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
JobLauncher.getInstance().submitToJobThread(0, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
boolean scheduled = latch.await(3, TimeUnit.SECONDS);
|
||||
assertFalse(scheduled); // pool saturated, no thread can be scheduled
|
||||
}
|
||||
finally {
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ import com.intellij.openapi.roots.ModifiableRootModel;
|
||||
import com.intellij.openapi.roots.OrderRootType;
|
||||
import com.intellij.openapi.roots.libraries.Library;
|
||||
import com.intellij.openapi.roots.libraries.LibraryTable;
|
||||
import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
@@ -90,11 +89,6 @@ public class JavaModuleBuilder extends ModuleBuilder implements SourcePathsBuild
|
||||
return StdModuleTypes.JAVA.modifySettingsStep(settingsStep, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModuleWizardStep[] createWizardSteps(@NotNull WizardContext wizardContext, @NotNull ModulesProvider modulesProvider) {
|
||||
return getModuleType().createWizardSteps(wizardContext, this, modulesProvider);
|
||||
}
|
||||
|
||||
public void setupRootModel(ModifiableRootModel rootModel) throws ConfigurationException {
|
||||
final CompilerModuleExtension compilerModuleExtension = rootModel.getModuleExtension(CompilerModuleExtension.class);
|
||||
compilerModuleExtension.setExcludeOutput(true);
|
||||
|
||||
@@ -76,7 +76,7 @@ com.siyeh.ig.bugs.ReturnNullInspection
|
||||
com.siyeh.ig.classlayout.EmptyClassInspection
|
||||
com.siyeh.ig.classlayout.InnerClassOnInterfaceInspection
|
||||
com.siyeh.ig.classlayout.ListenerMayUseAdapterInspection
|
||||
com.siyeh.ig.classlayout.MissingOverrideAnnotationInspection
|
||||
com.siyeh.ig.inheritance.MissingOverrideAnnotationInspection
|
||||
com.siyeh.ig.classlayout.UtilityClassInspection
|
||||
com.siyeh.ig.classlayout.UtilityClassWithoutPrivateConstructorInspection
|
||||
com.siyeh.ig.classmetrics.AnonymousClassComplexityInspection
|
||||
@@ -163,7 +163,6 @@ com.siyeh.ig.methodmetrics.ThreeNegationsPerMethodInspection
|
||||
com.siyeh.ig.methodmetrics.ThrownExceptionsPerMethodInspection
|
||||
com.siyeh.ig.migration.ForCanBeForeachInspection
|
||||
com.siyeh.ig.migration.IfCanBeSwitchInspection
|
||||
com.siyeh.ig.migration.MethodCanBeVariableArityMethodInspection
|
||||
com.siyeh.ig.modularization.ModuleWithTooFewClassesInspection
|
||||
com.siyeh.ig.modularization.ModuleWithTooManyClassesInspection
|
||||
com.siyeh.ig.naming.AnnotationNamingConventionInspection
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2013 JetBrains s.r.o.
|
||||
* Copyright 2000-2014 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.
|
||||
@@ -67,9 +67,19 @@ public abstract class JobLauncher {
|
||||
@NotNull Processor<T> thingProcessor);
|
||||
|
||||
|
||||
/**
|
||||
* NEVER EVER submit runnable which can lock itself for indeterminate amount of time.
|
||||
* This will cause deadlock since this thread pool is an easily exhaustible resource.
|
||||
* Use {@link com.intellij.openapi.application.Application#executeOnPooledThread(java.lang.Runnable)} instead
|
||||
*/
|
||||
@NotNull
|
||||
public abstract Job<Void> submitToJobThread(int priority, @NotNull final Runnable action, @Nullable Consumer<Future> onDoneCallback);
|
||||
|
||||
/**
|
||||
* NEVER EVER submit runnable which can lock itself for indeterminate amount of time.
|
||||
* This will cause deadlock since this thread pool is an easily exhaustible resource.
|
||||
* Use {@link com.intellij.openapi.application.Application#executeOnPooledThread(java.lang.Runnable)} instead
|
||||
*/
|
||||
@NotNull
|
||||
public Job<Void> submitToJobThread(int priority, @NotNull final Runnable action) {
|
||||
return submitToJobThread(priority, action, null);
|
||||
|
||||
@@ -64,7 +64,7 @@ public class PluginManagerCore {
|
||||
public static final float PLUGINS_PROGRESS_MAX_VALUE = 0.3f;
|
||||
static final Map<PluginId,Integer> ourId2Index = new THashMap<PluginId, Integer>();
|
||||
@NonNls static final String MODULE_DEPENDENCY_PREFIX = "com.intellij.module";
|
||||
static final List<String> ourAvailableModules = new ArrayList<String>();
|
||||
static final Map<String, IdeaPluginDescriptorImpl> ourModulesToContainingPlugins = new HashMap<String, IdeaPluginDescriptorImpl>();
|
||||
static final PluginClassCache ourPluginClasses = new PluginClassCache();
|
||||
@NonNls static final String SPECIAL_IDEA_PLUGIN = "IDEA CORE";
|
||||
static final String DISABLE = "disable";
|
||||
@@ -236,7 +236,8 @@ public class PluginManagerCore {
|
||||
if (processed.contains(dependentPluginId)) continue;
|
||||
|
||||
// TODO[yole] should this condition be a parameter?
|
||||
if (isModuleDependency(dependentPluginId) && (ourAvailableModules.isEmpty() || ourAvailableModules.contains(dependentPluginId.getIdString()))) {
|
||||
if (isModuleDependency(dependentPluginId) && (ourModulesToContainingPlugins.isEmpty() || ourModulesToContainingPlugins.containsKey(
|
||||
dependentPluginId.getIdString()))) {
|
||||
continue;
|
||||
}
|
||||
if (!optionalDependencies.contains(dependentPluginId)) {
|
||||
@@ -483,9 +484,8 @@ public class PluginManagerCore {
|
||||
}
|
||||
|
||||
private static void addModulesAsDependents(Map<PluginId, ? super IdeaPluginDescriptorImpl> map) {
|
||||
for (String module : ourAvailableModules) {
|
||||
// fake plugin descriptors to satisfy dependencies
|
||||
map.put(PluginId.getId(module), new IdeaPluginDescriptorImpl());
|
||||
for (Map.Entry<String, IdeaPluginDescriptorImpl> entry : ourModulesToContainingPlugins.entrySet()) {
|
||||
map.put(PluginId.getId(entry.getKey()), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -967,7 +967,7 @@ public class PluginManagerCore {
|
||||
}
|
||||
final List<String> pluginIds = pluginId == null ? null : StringUtil.split(pluginId, ",");
|
||||
|
||||
final boolean checkModuleDependencies = !ourAvailableModules.isEmpty() && !ourAvailableModules.contains("com.intellij.modules.all");
|
||||
final boolean checkModuleDependencies = !ourModulesToContainingPlugins.isEmpty() && !ourModulesToContainingPlugins.containsKey("com.intellij.modules.all");
|
||||
if (checkModuleDependencies && !hasModuleDependencies(descriptor)) {
|
||||
return true;
|
||||
}
|
||||
@@ -1058,7 +1058,11 @@ public class PluginManagerCore {
|
||||
for (IdeaPluginDescriptorImpl descriptor : pluginDescriptors) {
|
||||
final List<String> modules = descriptor.getModules();
|
||||
if (modules != null) {
|
||||
ourAvailableModules.addAll(modules);
|
||||
for (String module : modules) {
|
||||
if (!ourModulesToContainingPlugins.containsKey(module)) {
|
||||
ourModulesToContainingPlugins.put(module, descriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!shouldSkipPlugin(descriptor, pluginDescriptors)) {
|
||||
@@ -1087,6 +1091,7 @@ public class PluginManagerCore {
|
||||
}
|
||||
|
||||
mergeOptionalConfigs(idToDescriptorMap);
|
||||
addModulesAsDependents(idToDescriptorMap);
|
||||
|
||||
// sort descriptors according to plugin dependencies
|
||||
Collections.sort(result, getPluginDescriptorComparator(idToDescriptorMap));
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ public abstract class AbstractModuleBuilder extends ProjectBuilder {
|
||||
* Custom UI to be shown on the first wizard page
|
||||
*/
|
||||
@Nullable
|
||||
public ModuleWizardStep getCustomOptionsStep(Disposable parentDisposable) {
|
||||
public ModuleWizardStep getCustomOptionsStep(WizardContext context, Disposable parentDisposable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
+3
-9
@@ -13,16 +13,10 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.remoteServer.util.ssh;
|
||||
package com.intellij.codeInsight.actions;
|
||||
|
||||
import javax.swing.*;
|
||||
public interface DirectoryFormattingOptions extends ReformatFilesOptions {
|
||||
|
||||
/**
|
||||
* @author michael.golubev
|
||||
*/
|
||||
public interface PublicSshKeyPanel {
|
||||
boolean isIncludeSubdirectories();
|
||||
|
||||
String getSshKey();
|
||||
|
||||
JComponent getMainPanel();
|
||||
}
|
||||
@@ -15,12 +15,10 @@
|
||||
*/
|
||||
package com.intellij.codeInsight.actions;
|
||||
|
||||
public interface LayoutCodeOptions extends ReformatFilesOptions {
|
||||
public interface LayoutCodeOptions extends DirectoryFormattingOptions {
|
||||
|
||||
boolean isProcessWholeFile();
|
||||
|
||||
boolean isProcessDirectory();
|
||||
|
||||
boolean isIncludeSubdirectories();
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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.codeInsight.actions;
|
||||
|
||||
import com.intellij.codeInsight.CodeInsightBundle;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
public class LayoutDirectoryDialog extends LayoutProjectCodeDialog implements DirectoryFormattingOptions {
|
||||
private JCheckBox myIncludeSubdirs;
|
||||
|
||||
public LayoutDirectoryDialog(@NotNull Project project,
|
||||
String title,
|
||||
String text,
|
||||
boolean enableOnlyVCSChangedTextCb)
|
||||
{
|
||||
super(project, title, text, true, enableOnlyVCSChangedTextCb);
|
||||
}
|
||||
|
||||
public void setEnabledIncludeSubdirsCb(boolean isEnabled) {
|
||||
myIncludeSubdirs.setEnabled(isEnabled);
|
||||
}
|
||||
|
||||
public void setSelectedIncludeSubdirsCb(boolean isSelected) {
|
||||
myIncludeSubdirs.setSelected(isSelected);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JComponent createCenterPanel() {
|
||||
JPanel panel = new JPanel(new BorderLayout(0, 7));
|
||||
JComponent layoutProjectDialog = super.createCenterPanel();
|
||||
|
||||
myIncludeSubdirs = new JCheckBox(CodeInsightBundle.message("reformat.option.include.subdirectories"));
|
||||
|
||||
panel.add(layoutProjectDialog, BorderLayout.CENTER);
|
||||
panel.add(myIncludeSubdirs, BorderLayout.SOUTH);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIncludeSubdirectories() {
|
||||
return myIncludeSubdirs.isSelected();
|
||||
}
|
||||
|
||||
}
|
||||
+25
-30
@@ -19,12 +19,10 @@ package com.intellij.codeInsight.actions;
|
||||
import com.intellij.codeInsight.CodeInsightBundle;
|
||||
import com.intellij.ide.util.PropertiesComponent;
|
||||
import com.intellij.openapi.help.HelpManager;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
@@ -36,26 +34,27 @@ public class LayoutProjectCodeDialog extends DialogWrapper implements ReformatFi
|
||||
private static @NonNls final String HELP_ID = "editing.codeReformatting";
|
||||
|
||||
private final String myText;
|
||||
private final boolean myEnableOnlyVCSChangedTextCb;
|
||||
private final boolean mySuggestOptimizeImports;
|
||||
private final Project myProject;
|
||||
private final Module myModule;
|
||||
|
||||
|
||||
private JCheckBox myCbOptimizeImports;
|
||||
private JCheckBox myCbOnlyVcsChangedRegions;
|
||||
private JCheckBox myCbRearrangeEntries;
|
||||
|
||||
public LayoutProjectCodeDialog(@NotNull Project project,
|
||||
@Nullable Module module,
|
||||
String title,
|
||||
String text,
|
||||
boolean suggestOptimizeImports)
|
||||
boolean suggestOptimizeImports,
|
||||
boolean enableOnlyVCSChangedTextCb)
|
||||
{
|
||||
super(project, false);
|
||||
myText = text;
|
||||
mySuggestOptimizeImports = suggestOptimizeImports;
|
||||
myProject = project;
|
||||
myModule = module;
|
||||
|
||||
myEnableOnlyVCSChangedTextCb = enableOnlyVCSChangedTextCb;
|
||||
|
||||
setOKButtonText(CodeInsightBundle.message("reformat.code.accept.button.text"));
|
||||
setTitle(title);
|
||||
init();
|
||||
@@ -63,26 +62,26 @@ public class LayoutProjectCodeDialog extends DialogWrapper implements ReformatFi
|
||||
|
||||
@Override
|
||||
protected JComponent createCenterPanel() {
|
||||
if (!mySuggestOptimizeImports) return new JLabel(myText);
|
||||
if (!mySuggestOptimizeImports) {
|
||||
return new JLabel(myText);
|
||||
}
|
||||
|
||||
JPanel panel = new JPanel(new GridLayout(4, 1));
|
||||
panel.add(new JLabel(myText));
|
||||
myCbOptimizeImports = new JCheckBox(CodeInsightBundle.message("reformat.option.optimize.imports"));
|
||||
panel.add(myCbOptimizeImports);
|
||||
myCbOptimizeImports.setSelected(PropertiesComponent.getInstance().getBoolean(LayoutCodeConstants.OPTIMIZE_IMPORTS_KEY, false));
|
||||
|
||||
myCbOnlyVcsChangedRegions = new JCheckBox(CodeInsightBundle.message("reformat.option.vcs.changed.region"));
|
||||
panel.add(myCbOnlyVcsChangedRegions);
|
||||
final boolean canTargetVcsRegions = canTargetVcsRegions();
|
||||
myCbOnlyVcsChangedRegions.setEnabled(canTargetVcsRegions);
|
||||
myCbOnlyVcsChangedRegions.setSelected(
|
||||
canTargetVcsRegions && PropertiesComponent.getInstance().getBoolean(LayoutCodeConstants.PROCESS_CHANGED_TEXT_KEY, false)
|
||||
);
|
||||
|
||||
|
||||
myCbRearrangeEntries = new JCheckBox(CodeInsightBundle.message("reformat.option.rearrange.entries"));
|
||||
myCbOnlyVcsChangedRegions = new JCheckBox(CodeInsightBundle.message("reformat.option.vcs.changed.region"));
|
||||
|
||||
panel.add(new JLabel(myText));
|
||||
panel.add(myCbOptimizeImports);
|
||||
panel.add(myCbRearrangeEntries);
|
||||
boolean previousSelectedState = LayoutCodeSettingsStorage.getLastSavedRearrangeEntriesCbStateFor(myProject);
|
||||
myCbRearrangeEntries.setSelected(previousSelectedState);
|
||||
panel.add(myCbOnlyVcsChangedRegions);
|
||||
|
||||
myCbOptimizeImports.setSelected(PropertiesComponent.getInstance().getBoolean(LayoutCodeConstants.OPTIMIZE_IMPORTS_KEY, false));
|
||||
myCbRearrangeEntries.setSelected(LayoutCodeSettingsStorage.getLastSavedRearrangeEntriesCbStateFor(myProject));
|
||||
myCbOnlyVcsChangedRegions.setEnabled(myEnableOnlyVCSChangedTextCb);
|
||||
myCbOnlyVcsChangedRegions.setSelected(
|
||||
myEnableOnlyVCSChangedTextCb && PropertiesComponent.getInstance().getBoolean(LayoutCodeConstants.PROCESS_CHANGED_TEXT_KEY, false)
|
||||
);
|
||||
|
||||
return panel;
|
||||
}
|
||||
@@ -109,6 +108,9 @@ public class LayoutProjectCodeDialog extends DialogWrapper implements ReformatFi
|
||||
if (mySuggestOptimizeImports) {
|
||||
PropertiesComponent.getInstance().setValue(LayoutCodeConstants.OPTIMIZE_IMPORTS_KEY, Boolean.toString(isOptimizeImports()));
|
||||
LayoutCodeSettingsStorage.saveRearrangeEntriesOptionFor(myProject, isRearrangeEntries());
|
||||
if (myEnableOnlyVCSChangedTextCb) {
|
||||
PropertiesComponent.getInstance().setValue(LayoutCodeConstants.PROCESS_CHANGED_TEXT_KEY, Boolean.toString(myCbOnlyVcsChangedRegions.isSelected()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,12 +121,5 @@ public class LayoutProjectCodeDialog extends DialogWrapper implements ReformatFi
|
||||
public boolean isProcessOnlyChangedText() {
|
||||
return myCbOnlyVcsChangedRegions.isEnabled() && myCbOnlyVcsChangedRegions.isSelected();
|
||||
}
|
||||
|
||||
private boolean canTargetVcsRegions() {
|
||||
if (myModule != null) {
|
||||
return FormatChangedTextUtil.hasChanges(myModule);
|
||||
}
|
||||
|
||||
return FormatChangedTextUtil.hasChanges(myProject);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ public class OptimizeImportsAction extends AnAction {
|
||||
text = CodeInsightBundle.message("process.scope.project", projectContext.getPresentableUrl());
|
||||
}
|
||||
LayoutProjectCodeDialog dialog
|
||||
= new LayoutProjectCodeDialog(project, null, CodeInsightBundle.message("process.optimize.imports"), text, false);
|
||||
= new LayoutProjectCodeDialog(project, CodeInsightBundle.message("process.optimize.imports"), text, false, FormatChangedTextUtil.hasChanges(project));
|
||||
dialog.show();
|
||||
if (!dialog.isOK()) return;
|
||||
if (moduleContext != null) {
|
||||
|
||||
@@ -139,7 +139,14 @@ public class ReformatCodeAction extends AnAction implements DumbAware {
|
||||
|
||||
final boolean showDialog = EditorSettingsExternalizable.getInstance().getOptions().SHOW_REFORMAT_DIALOG;
|
||||
|
||||
if (showDialog || (file == null && dir != null)) {
|
||||
if (file == null && dir != null) {
|
||||
DirectoryFormattingOptions options = getDirectoryFormattingOptions(project, dir);
|
||||
if (options != null) {
|
||||
reformatDirectory(project, dir, options);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (showDialog) {
|
||||
LayoutCodeOptions selectedFlags = getLayoutCodeOptions(project, file, dir, hasSelection);
|
||||
if (selectedFlags == null)
|
||||
return;
|
||||
@@ -148,21 +155,14 @@ public class ReformatCodeAction extends AnAction implements DumbAware {
|
||||
rearrangeEntries = selectedFlags.isRearrangeEntries();
|
||||
processWholeFile = selectedFlags.isProcessWholeFile();
|
||||
processChangedTextOnly = selectedFlags.isProcessOnlyChangedText();
|
||||
|
||||
if (selectedFlags.isProcessDirectory()) {
|
||||
AbstractLayoutCodeProcessor processor = new ReformatCodeProcessor(project, dir, selectedFlags.isIncludeSubdirectories(), processChangedTextOnly);
|
||||
if (optimizeImports) {
|
||||
processor = new OptimizeImportsProcessor(processor);
|
||||
}
|
||||
if (selectedFlags.isRearrangeEntries()) {
|
||||
processor = new RearrangeCodeProcessor(processor, null);
|
||||
}
|
||||
|
||||
processor.run();
|
||||
if (selectedFlags.isProcessDirectory()) {
|
||||
assert dir != null : "File = " + file + ", Element = " + CommonDataKeys.PSI_ELEMENT.getData(dataContext);
|
||||
reformatDirectory(project, dir, selectedFlags);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final TextRange range;
|
||||
if (!processWholeFile && editor != null && editor.getSelectionModel().hasSelection()){
|
||||
range = TextRange.create(editor.getSelectionModel().getSelectionStart(), editor.getSelectionModel().getSelectionEnd());
|
||||
@@ -172,12 +172,7 @@ public class ReformatCodeAction extends AnAction implements DumbAware {
|
||||
}
|
||||
|
||||
if (optimizeImports && range == null) {
|
||||
if (file != null || dir == null) {
|
||||
new OptimizeImportsProcessor(new ReformatCodeProcessor(project, file, null, processChangedTextOnly)).run();
|
||||
}
|
||||
else {
|
||||
new OptimizeImportsProcessor(new ReformatCodeProcessor(project, dir, true, processChangedTextOnly)).run();
|
||||
}
|
||||
new OptimizeImportsProcessor(new ReformatCodeProcessor(project, file, null, processChangedTextOnly)).run();
|
||||
}
|
||||
else {
|
||||
new ReformatCodeProcessor(project, file, range, processChangedTextOnly).run();
|
||||
@@ -204,6 +199,47 @@ public class ReformatCodeAction extends AnAction implements DumbAware {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static DirectoryFormattingOptions getDirectoryFormattingOptions(@NotNull Project project, @NotNull PsiDirectory dir) {
|
||||
LayoutDirectoryDialog dialog = new LayoutDirectoryDialog(
|
||||
project,
|
||||
CodeInsightBundle.message("process.reformat.code"),
|
||||
CodeInsightBundle.message("process.scope.directory", dir.getVirtualFile().getPath()),
|
||||
FormatChangedTextUtil.hasChanges(dir)
|
||||
);
|
||||
|
||||
boolean enableIncludeDirectoriesCb = dir.getSubdirectories().length > 0;
|
||||
dialog.setEnabledIncludeSubdirsCb(enableIncludeDirectoriesCb);
|
||||
dialog.setSelectedIncludeSubdirsCb(enableIncludeDirectoriesCb);
|
||||
|
||||
dialog.show();
|
||||
if (dialog.isOK()) {
|
||||
return dialog;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void reformatDirectory(@NotNull Project project,
|
||||
@NotNull PsiDirectory dir,
|
||||
@NotNull DirectoryFormattingOptions options)
|
||||
{
|
||||
AbstractLayoutCodeProcessor processor = new ReformatCodeProcessor(
|
||||
project,
|
||||
dir,
|
||||
options.isIncludeSubdirectories(),
|
||||
options.isProcessOnlyChangedText()
|
||||
);
|
||||
|
||||
if (options.isOptimizeImports()) {
|
||||
processor = new OptimizeImportsProcessor(processor);
|
||||
}
|
||||
if (options.isRearrangeEntries()) {
|
||||
processor = new RearrangeCodeProcessor(processor, null);
|
||||
}
|
||||
|
||||
processor.run();
|
||||
}
|
||||
|
||||
private static void reformatModule(@NotNull Project project,
|
||||
@Nullable Module moduleContext,
|
||||
@NotNull ReformatFilesOptions selectedFlags)
|
||||
@@ -345,7 +381,10 @@ public class ReformatCodeAction extends AnAction implements DumbAware {
|
||||
final String text = module != null ? CodeInsightBundle.message("process.scope.module", module.getModuleFilePath())
|
||||
: CodeInsightBundle.message("process.scope.project", project.getPresentableUrl());
|
||||
|
||||
LayoutProjectCodeDialog dialog = new LayoutProjectCodeDialog(project, module, CodeInsightBundle.message("process.reformat.code"), text, true);
|
||||
final boolean enableOnlyVCSChangedRegions = module != null ? FormatChangedTextUtil.hasChanges(module)
|
||||
: FormatChangedTextUtil.hasChanges(project);
|
||||
|
||||
LayoutProjectCodeDialog dialog = new LayoutProjectCodeDialog(project, CodeInsightBundle.message("process.reformat.code"), text, true, enableOnlyVCSChangedRegions);
|
||||
dialog.show();
|
||||
if (!dialog.isOK()) return null;
|
||||
return dialog;
|
||||
|
||||
@@ -138,7 +138,10 @@ public class TypedHandler extends TypedActionHandlerBase {
|
||||
Project project = CommonDataKeys.PROJECT.getData(dataContext);
|
||||
PsiFile file;
|
||||
|
||||
if (project == null || editor.isColumnMode() || (file = PsiUtilBase.getPsiFileInEditor(editor, project)) == null) {
|
||||
if (project == null
|
||||
|| editor.isColumnMode()
|
||||
|| editor.getCaretModel().getAllCarets().size() > 1
|
||||
|| (file = PsiUtilBase.getPsiFileInEditor(editor, project)) == null) {
|
||||
if (myOriginalHandler != null){
|
||||
myOriginalHandler.execute(editor, charTyped, dataContext);
|
||||
}
|
||||
|
||||
+6
-5
@@ -26,8 +26,6 @@ package com.intellij.codeInsight.highlighting;
|
||||
|
||||
import com.intellij.codeInsight.CodeInsightSettings;
|
||||
import com.intellij.codeInsight.hint.EditorFragmentComponent;
|
||||
import com.intellij.concurrency.Job;
|
||||
import com.intellij.concurrency.JobLauncher;
|
||||
import com.intellij.injected.editor.EditorWindow;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
@@ -115,7 +113,7 @@ public class BraceHighlightingHandler {
|
||||
}
|
||||
final int offset = editor.getCaretModel().getOffset();
|
||||
final PsiFile psiFile = PsiUtilBase.getPsiFileInEditor(editor, project);
|
||||
JobLauncher.getInstance().submitToJobThread(Job.DEFAULT_PRIORITY, new Runnable() {
|
||||
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!ApplicationManagerEx.getApplicationEx().tryRunReadAction(new Runnable() {
|
||||
@@ -123,8 +121,11 @@ public class BraceHighlightingHandler {
|
||||
public void run() {
|
||||
final PsiFile injected;
|
||||
try {
|
||||
injected = psiFile == null || psiFile instanceof PsiCompiledElement || psiFile instanceof PsiBinaryFile || isReallyDisposed(editor, project)
|
||||
? null : getInjectedFileIfAny(editor, project, offset, psiFile, alarm);
|
||||
injected = psiFile == null ||
|
||||
psiFile instanceof PsiCompiledElement ||
|
||||
psiFile instanceof PsiBinaryFile ||
|
||||
isReallyDisposed(editor, project)
|
||||
? null : getInjectedFileIfAny(editor, project, offset, psiFile, alarm);
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
// Reset processing flag in case of unexpected exception.
|
||||
|
||||
+1
-2
@@ -60,8 +60,7 @@ public abstract class BaseConsoleExecuteActionHandler {
|
||||
|
||||
console.setInputText("");
|
||||
|
||||
UndoManager manager = UndoManager.getInstance(console.getProject());
|
||||
((UndoManagerImpl)manager).invalidateActionsFor(DocumentReferenceManager.getInstance().create(document));
|
||||
((UndoManagerImpl)UndoManager.getInstance(console.getProject())).invalidateActionsFor(DocumentReferenceManager.getInstance().create(document));
|
||||
|
||||
myConsoleHistoryModel.addToHistory(text);
|
||||
if (consoleView == null) {
|
||||
|
||||
@@ -3,22 +3,13 @@ package com.intellij.execution.console;
|
||||
import com.intellij.codeInsight.hint.TooltipController;
|
||||
import com.intellij.codeInsight.hint.TooltipGroup;
|
||||
import com.intellij.ide.ui.UISettings;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.impl.ApplicationImpl;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.LogicalPosition;
|
||||
import com.intellij.openapi.editor.VisualPosition;
|
||||
import com.intellij.openapi.editor.colors.EditorFontType;
|
||||
import com.intellij.openapi.editor.event.DocumentAdapter;
|
||||
import com.intellij.openapi.editor.event.DocumentEvent;
|
||||
import com.intellij.openapi.editor.ex.DocumentBulkUpdateListener;
|
||||
import com.intellij.openapi.editor.ex.DocumentEx;
|
||||
import com.intellij.openapi.editor.ex.EditorMarkupModel;
|
||||
import com.intellij.openapi.editor.impl.EditorImpl;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.ui.HintHint;
|
||||
import com.intellij.ui.JBColor;
|
||||
import com.intellij.ui.awt.RelativePoint;
|
||||
@@ -32,10 +23,11 @@ import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseMotionListener;
|
||||
|
||||
public class ConsoleGutterComponent extends JComponent implements MouseMotionListener {
|
||||
class ConsoleGutterComponent extends JComponent implements MouseMotionListener {
|
||||
private static final TooltipGroup TOOLTIP_GROUP = new TooltipGroup("CONSOLE_GUTTER_TOOLTIP_GROUP", 0);
|
||||
private static final int RIGHT_INSET = 6;
|
||||
|
||||
private int annotationGuttersSize = 0;
|
||||
private int maxAnnotationWidth = 0;
|
||||
private int myLastPreferredHeight = -1;
|
||||
private final EditorImpl editor;
|
||||
|
||||
@@ -43,65 +35,29 @@ public class ConsoleGutterComponent extends JComponent implements MouseMotionLis
|
||||
|
||||
private int lastGutterToolTipLine = -1;
|
||||
|
||||
public ConsoleGutterComponent(@NotNull Editor editor, @NotNull GutterContentProvider provider, @NotNull Disposable parentDisposable) {
|
||||
public ConsoleGutterComponent(@NotNull Editor editor, @NotNull GutterContentProvider provider) {
|
||||
this.editor = (EditorImpl)editor;
|
||||
gutterContentProvider = provider;
|
||||
addListeners(parentDisposable);
|
||||
addListeners();
|
||||
|
||||
addMouseMotionListener(this);
|
||||
}
|
||||
|
||||
private void addListeners(@NotNull Disposable parentDisposable) {
|
||||
Project project = editor.getProject();
|
||||
assert project != null;
|
||||
project.getMessageBus().connect(parentDisposable).subscribe(DocumentBulkUpdateListener.TOPIC, new DocumentBulkUpdateListener.Adapter() {
|
||||
@Override
|
||||
public void updateFinished(@NotNull Document document) {
|
||||
if (document.getTextLength() == 0) {
|
||||
gutterContentProvider.documentCleared(editor);
|
||||
}
|
||||
updateSize();
|
||||
}
|
||||
});
|
||||
|
||||
editor.getDocument().addDocumentListener(new DocumentAdapter() {
|
||||
@Override
|
||||
public void documentChanged(DocumentEvent event) {
|
||||
DocumentEx document = editor.getDocument();
|
||||
if (document.isInBulkUpdate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (document.getTextLength() > 0) {
|
||||
int startDocLine = document.getLineNumber(event.getOffset());
|
||||
int endDocLine = document.getLineNumber(event.getOffset() + event.getNewLength());
|
||||
if (event.getOldLength() > event.getNewLength() || startDocLine != endDocLine || StringUtil.indexOf(event.getOldFragment(), '\n') != -1) {
|
||||
updateSize();
|
||||
}
|
||||
}
|
||||
else if (event.getOldLength() > 0) {
|
||||
gutterContentProvider.documentCleared(editor);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
private void addListeners() {
|
||||
addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
if (e.isPopupTrigger()) {
|
||||
return;
|
||||
if (!e.isPopupTrigger()) {
|
||||
gutterContentProvider.doAction(getLineAtPoint(e.getPoint()), editor);
|
||||
}
|
||||
|
||||
int line = getLineAtPoint(e.getPoint());
|
||||
gutterContentProvider.doAction(line, editor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void updateSize() {
|
||||
int oldAnnotationsWidth = annotationGuttersSize;
|
||||
computeAnnotationsSize();
|
||||
if (oldAnnotationsWidth != annotationGuttersSize || myLastPreferredHeight != editor.getPreferredHeight()) {
|
||||
int oldAnnotationsWidth = maxAnnotationWidth;
|
||||
computeMaxAnnotationWidth();
|
||||
if (oldAnnotationsWidth != maxAnnotationWidth || myLastPreferredHeight != editor.getPreferredHeight()) {
|
||||
fireResized();
|
||||
}
|
||||
repaint();
|
||||
@@ -111,26 +67,29 @@ public class ConsoleGutterComponent extends JComponent implements MouseMotionLis
|
||||
processComponentEvent(new ComponentEvent(this, ComponentEvent.COMPONENT_RESIZED));
|
||||
}
|
||||
|
||||
private void computeAnnotationsSize() {
|
||||
private void computeMaxAnnotationWidth() {
|
||||
if (!gutterContentProvider.hasText()) {
|
||||
maxAnnotationWidth = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
FontMetrics fontMetrics = editor.getFontMetrics(Font.PLAIN);
|
||||
int lineCount = editor.getDocument().getLineCount();
|
||||
GutterContentProvider gutterProvider = gutterContentProvider;
|
||||
gutterProvider.beforeUiComponentUpdate(editor);
|
||||
gutterContentProvider.beforeUiComponentUpdate(editor);
|
||||
int gutterSize = 0;
|
||||
for (int i = 0; i < lineCount; i++) {
|
||||
String text = gutterProvider.getText(i, editor);
|
||||
String text = gutterContentProvider.getText(i, editor);
|
||||
if (text != null) {
|
||||
gutterSize = Math.max(gutterSize, fontMetrics.stringWidth(text));
|
||||
}
|
||||
}
|
||||
annotationGuttersSize = gutterSize;
|
||||
maxAnnotationWidth = gutterSize + RIGHT_INSET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
int w = annotationGuttersSize;
|
||||
myLastPreferredHeight = editor.getPreferredHeight();
|
||||
return new Dimension(w, myLastPreferredHeight);
|
||||
return new Dimension(maxAnnotationWidth, myLastPreferredHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -138,17 +97,13 @@ public class ConsoleGutterComponent extends JComponent implements MouseMotionLis
|
||||
((ApplicationImpl)ApplicationManager.getApplication()).editorPaintStart();
|
||||
try {
|
||||
Rectangle clip = g.getClipBounds();
|
||||
if (clip.height < 0) {
|
||||
if (clip.height < 0 || maxAnnotationWidth == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
g.setColor(editor.getBackgroundColor());
|
||||
g.fillRect(clip.x, clip.y, clip.width, clip.height);
|
||||
|
||||
if (annotationGuttersSize == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
UISettings.setupAntialiasing(g);
|
||||
|
||||
Graphics2D g2 = (Graphics2D)g;
|
||||
@@ -170,33 +125,29 @@ public class ConsoleGutterComponent extends JComponent implements MouseMotionLis
|
||||
}
|
||||
|
||||
private void paintAnnotations(Graphics g, Rectangle clip) {
|
||||
g.setColor(JBColor.blue);
|
||||
g.setFont(editor.getColorsScheme().getFont(EditorFontType.PLAIN));
|
||||
|
||||
int lineHeight = editor.getLineHeight();
|
||||
int startLineNumber = clip.y / lineHeight;
|
||||
int endLineNumber = (clip.y + clip.height) / lineHeight + 1;
|
||||
int lastLine = editor.logicalToVisualPosition(new LogicalPosition(getEndLineNumber(), 0)).line;
|
||||
endLineNumber = Math.min(endLineNumber, lastLine + 1);
|
||||
if (startLineNumber >= endLineNumber) {
|
||||
int startLine = clip.y / lineHeight;
|
||||
int endLine = Math.min(((clip.y + clip.height) / lineHeight) + 1, editor.getVisibleLineCount());
|
||||
if (startLine >= endLine) {
|
||||
return;
|
||||
}
|
||||
|
||||
gutterContentProvider.beforeUiComponentUpdate(editor);
|
||||
|
||||
for (int i = startLineNumber; i < endLineNumber; i++) {
|
||||
int logLine = editor.visualToLogicalPosition(new VisualPosition(i, 0)).line;
|
||||
String text = gutterContentProvider.getText(logLine, editor);
|
||||
g.setColor(JBColor.BLUE);
|
||||
g.setFont(editor.getColorsScheme().getFont(EditorFontType.PLAIN));
|
||||
int y = ((startLine + 1) * lineHeight) - editor.getDescent();
|
||||
FontMetrics fontMetrics = editor.getFontMetrics(Font.PLAIN);
|
||||
for (int i = startLine; i < endLine; i++) {
|
||||
String text = gutterContentProvider.getText(editor.visualToLogicalPosition(new VisualPosition(i, 0)).line, editor);
|
||||
if (text != null) {
|
||||
g.drawString(text, 0, (i + 1) * lineHeight - editor.getDescent());
|
||||
// right-aligned
|
||||
g.drawString(text, maxAnnotationWidth - RIGHT_INSET - fontMetrics.stringWidth(text), y);
|
||||
}
|
||||
y += lineHeight;
|
||||
}
|
||||
}
|
||||
|
||||
private int getEndLineNumber() {
|
||||
return Math.max(0, editor.getDocument().getLineCount() - 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(MouseEvent e) {
|
||||
TooltipController.getInstance().cancelTooltips();
|
||||
@@ -232,7 +183,7 @@ public class ConsoleGutterComponent extends JComponent implements MouseMotionLis
|
||||
}
|
||||
}
|
||||
|
||||
private int getLineAtPoint(final Point clickPoint) {
|
||||
private int getLineAtPoint(@NotNull Point clickPoint) {
|
||||
return editor.yPositionToLogicalLine(clickPoint.y);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package com.intellij.execution.console;
|
||||
|
||||
import com.intellij.ide.ui.UISettings;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.impl.ApplicationImpl;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.VisualPosition;
|
||||
import com.intellij.openapi.editor.colors.EditorFontType;
|
||||
import com.intellij.openapi.editor.impl.EditorImpl;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ComponentEvent;
|
||||
|
||||
class ConsoleIconGutterComponent extends JComponent {
|
||||
static final int ICON_AREA_WIDTH = 15;
|
||||
|
||||
private int myLastPreferredHeight = -1;
|
||||
private final EditorImpl editor;
|
||||
|
||||
private final GutterContentProvider gutterContentProvider;
|
||||
|
||||
public ConsoleIconGutterComponent(@NotNull Editor editor, @NotNull GutterContentProvider provider) {
|
||||
this.editor = (EditorImpl)editor;
|
||||
gutterContentProvider = provider;
|
||||
}
|
||||
|
||||
public void updateSize() {
|
||||
if (myLastPreferredHeight != editor.getPreferredHeight()) {
|
||||
fireResized();
|
||||
}
|
||||
repaint();
|
||||
}
|
||||
|
||||
private void fireResized() {
|
||||
processComponentEvent(new ComponentEvent(this, ComponentEvent.COMPONENT_RESIZED));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
myLastPreferredHeight = editor.getPreferredHeight();
|
||||
return new Dimension(ICON_AREA_WIDTH, myLastPreferredHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint(Graphics g) {
|
||||
((ApplicationImpl)ApplicationManager.getApplication()).editorPaintStart();
|
||||
try {
|
||||
Rectangle clip = g.getClipBounds();
|
||||
if (clip.height < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
g.setColor(editor.getBackgroundColor());
|
||||
g.fillRect(clip.x, clip.y, clip.width, clip.height);
|
||||
|
||||
UISettings.setupAntialiasing(g);
|
||||
|
||||
Graphics2D g2 = (Graphics2D)g;
|
||||
Object hint = g2.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
|
||||
if (!UIUtil.isRetina()) {
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
|
||||
}
|
||||
|
||||
try {
|
||||
paintAnnotations(g, clip);
|
||||
}
|
||||
finally {
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, hint);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
((ApplicationImpl)ApplicationManager.getApplication()).editorPaintFinish();
|
||||
}
|
||||
}
|
||||
|
||||
private void paintAnnotations(Graphics g, Rectangle clip) {
|
||||
int lineHeight = editor.getLineHeight();
|
||||
int startLine = clip.y / lineHeight;
|
||||
int endLine = Math.min(((clip.y + clip.height) / lineHeight) + 1, editor.getVisibleLineCount());
|
||||
if (startLine >= endLine) {
|
||||
return;
|
||||
}
|
||||
|
||||
gutterContentProvider.beforeUiComponentUpdate(editor);
|
||||
|
||||
g.setFont(editor.getColorsScheme().getFont(EditorFontType.PLAIN));
|
||||
int y = ((startLine + 1) * lineHeight) - editor.getDescent();
|
||||
for (int i = startLine; i < endLine; i++) {
|
||||
gutterContentProvider.drawIcon(editor.visualToLogicalPosition(new VisualPosition(i, 0)).line, g, y, editor);
|
||||
y += lineHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import com.intellij.openapi.editor.Editor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
public abstract class GutterContentProvider {
|
||||
public void beforeUiComponentUpdate(@NotNull Editor editor) {
|
||||
}
|
||||
@@ -11,6 +13,11 @@ public abstract class GutterContentProvider {
|
||||
public void documentCleared(@NotNull Editor editor) {
|
||||
}
|
||||
|
||||
public void beforeEvaluate(@NotNull Editor editor) {
|
||||
}
|
||||
|
||||
public abstract boolean hasText();
|
||||
|
||||
@Nullable
|
||||
public abstract String getText(int line, @NotNull Editor editor);
|
||||
|
||||
@@ -18,4 +25,6 @@ public abstract class GutterContentProvider {
|
||||
public abstract String getToolTip(int line, @NotNull Editor editor);
|
||||
|
||||
public abstract void doAction(int line, @NotNull Editor editor);
|
||||
|
||||
public abstract void drawIcon(int line, @NotNull Graphics g, int y, @NotNull Editor editor);
|
||||
}
|
||||
+121
-18
@@ -2,10 +2,18 @@ package com.intellij.execution.console;
|
||||
|
||||
import com.intellij.execution.process.ProcessHandler;
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.colors.EditorColors;
|
||||
import com.intellij.openapi.editor.event.DocumentAdapter;
|
||||
import com.intellij.openapi.editor.event.DocumentEvent;
|
||||
import com.intellij.openapi.editor.ex.DocumentBulkUpdateListener;
|
||||
import com.intellij.openapi.editor.ex.DocumentEx;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.editor.impl.EditorImpl;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.Conditions;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -17,16 +25,13 @@ public class LanguageConsoleBuilder {
|
||||
private LanguageConsoleView myConsoleView;
|
||||
private Condition<LanguageConsoleImpl> myExecutionEnabled = Conditions.alwaysTrue();
|
||||
|
||||
@Nullable
|
||||
private GutterContentProvider myGutterProvider;
|
||||
|
||||
public LanguageConsoleBuilder(@NotNull LanguageConsoleView consoleView) {
|
||||
myConsole = consoleView.getConsole();
|
||||
myConsoleView = consoleView;
|
||||
}
|
||||
|
||||
public LanguageConsoleBuilder(@NotNull Project project, @NotNull Language language) {
|
||||
myConsole = new LanguageConsoleImpl(project, language.getDisplayName() + " Console", language, false);
|
||||
myConsole = new MyLanguageConsole(project, language);
|
||||
}
|
||||
|
||||
public LanguageConsoleBuilder processHandler(@NotNull ProcessHandler processHandler) {
|
||||
@@ -50,7 +55,7 @@ public class LanguageConsoleBuilder {
|
||||
}
|
||||
|
||||
public LanguageConsoleBuilder historyAnnotation(@Nullable GutterContentProvider provider) {
|
||||
myGutterProvider = provider;
|
||||
((MyLanguageConsole)myConsole).gutterContentProvider = provider;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -64,19 +69,6 @@ public class LanguageConsoleBuilder {
|
||||
myConsole.setShowSeparatorLine(false);
|
||||
myConsole.initComponents();
|
||||
|
||||
if (myGutterProvider != null) {
|
||||
// todo consider to fix platform
|
||||
myConsole.getConsoleEditor().getSettings().setAdditionalLinesCount(1);
|
||||
|
||||
EditorEx editor = myConsole.getHistoryViewer();
|
||||
|
||||
JScrollPane scrollPane = editor.getScrollPane();
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
panel.add(scrollPane.getViewport().getView(), BorderLayout.CENTER);
|
||||
panel.add(new ConsoleGutterComponent(editor, myGutterProvider, myConsole), BorderLayout.LINE_END);
|
||||
scrollPane.setViewportView(panel);
|
||||
}
|
||||
|
||||
ensureConsoleViewCreated();
|
||||
return myConsoleView;
|
||||
}
|
||||
@@ -93,4 +85,115 @@ public class LanguageConsoleBuilder {
|
||||
return !myProcessHandler.isProcessTerminated();
|
||||
}
|
||||
}
|
||||
|
||||
private static class GutteredEditorPanel extends JPanel {
|
||||
private final EditorEx editor;
|
||||
|
||||
public GutteredEditorPanel(EditorEx editor) {
|
||||
super(new BorderLayout());
|
||||
|
||||
this.editor = editor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint(Graphics g) {
|
||||
super.paint(g);
|
||||
|
||||
Rectangle clip = g.getClipBounds();
|
||||
if (clip.height < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int lineHeight = editor.getLineHeight();
|
||||
int startLine = clip.y / lineHeight;
|
||||
int endLine = Math.min(((clip.y + clip.height) / lineHeight) + 1, ((EditorImpl)editor).getVisibleLineCount());
|
||||
if (startLine >= endLine) {
|
||||
return;
|
||||
}
|
||||
|
||||
int y = ((startLine + 1) * lineHeight);
|
||||
g.setColor(editor.getColorsScheme().getColor(EditorColors.INDENT_GUIDE_COLOR));
|
||||
for (int i = startLine; i < endLine; i++) {
|
||||
g.drawLine(ConsoleIconGutterComponent.ICON_AREA_WIDTH, y, clip.width, y);
|
||||
y += lineHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyLanguageConsole extends LanguageConsoleImpl {
|
||||
@Nullable
|
||||
private GutterContentProvider gutterContentProvider;
|
||||
|
||||
public MyLanguageConsole(@NotNull Project project, @NotNull Language language) {
|
||||
super(project, language.getDisplayName() + " Console", language, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setupEditorDefault(@NotNull EditorEx editor) {
|
||||
super.setupEditorDefault(editor);
|
||||
|
||||
if (editor == getConsoleEditor()) {
|
||||
// todo consider to fix platform
|
||||
editor.getSettings().setAdditionalLinesCount(1);
|
||||
}
|
||||
else if (gutterContentProvider != null) {
|
||||
JScrollPane scrollPane = editor.getScrollPane();
|
||||
JPanel panel = new GutteredEditorPanel(editor);
|
||||
|
||||
final ConsoleIconGutterComponent lineStartGutter = new ConsoleIconGutterComponent(editor, gutterContentProvider);
|
||||
panel.add(lineStartGutter, BorderLayout.LINE_START);
|
||||
|
||||
panel.add(scrollPane.getViewport().getView(), BorderLayout.CENTER);
|
||||
|
||||
final ConsoleGutterComponent lineEndGutter = new ConsoleGutterComponent(editor, gutterContentProvider);
|
||||
panel.add(lineEndGutter, BorderLayout.LINE_END);
|
||||
|
||||
scrollPane.setViewportView(panel);
|
||||
|
||||
getProject().getMessageBus().connect(this).subscribe(DocumentBulkUpdateListener.TOPIC, new DocumentBulkUpdateListener.Adapter() {
|
||||
@Override
|
||||
public void updateFinished(@NotNull Document document) {
|
||||
if (document.getTextLength() == 0) {
|
||||
gutterContentProvider.documentCleared(getHistoryViewer());
|
||||
}
|
||||
lineStartGutter.updateSize();
|
||||
lineEndGutter.updateSize();
|
||||
}
|
||||
});
|
||||
|
||||
editor.getDocument().addDocumentListener(new DocumentAdapter() {
|
||||
@Override
|
||||
public void documentChanged(DocumentEvent event) {
|
||||
EditorEx editor = getHistoryViewer();
|
||||
DocumentEx document = editor.getDocument();
|
||||
if (document.isInBulkUpdate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (document.getTextLength() > 0) {
|
||||
int startDocLine = document.getLineNumber(event.getOffset());
|
||||
int endDocLine = document.getLineNumber(event.getOffset() + event.getNewLength());
|
||||
if (event.getOldLength() > event.getNewLength() || startDocLine != endDocLine || StringUtil.indexOf(event.getOldFragment(), '\n') != -1) {
|
||||
lineStartGutter.updateSize();
|
||||
lineEndGutter.updateSize();
|
||||
}
|
||||
}
|
||||
else if (event.getOldLength() > 0) {
|
||||
gutterContentProvider.documentCleared(editor);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doAddPromptToHistory() {
|
||||
if (gutterContentProvider == null) {
|
||||
super.doAddPromptToHistory();
|
||||
}
|
||||
else {
|
||||
gutterContentProvider.beforeEvaluate(getHistoryViewer());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -185,8 +185,7 @@ public class LanguageConsoleImpl implements Disposable, TypeSafeDataProvider {
|
||||
@Override
|
||||
public void componentResized(ComponentEvent e) {
|
||||
if (myForceScrollToEnd.getAndSet(false)) {
|
||||
final JScrollBar scrollBar = myHistoryViewer.getScrollPane().getVerticalScrollBar();
|
||||
scrollBar.setValue(scrollBar.getMaximum());
|
||||
scrollHistoryToEnd();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,7 +298,7 @@ public class LanguageConsoleImpl implements Disposable, TypeSafeDataProvider {
|
||||
queueUiUpdate(true);
|
||||
}
|
||||
|
||||
private void setupEditorDefault(@NotNull EditorEx editor) {
|
||||
protected void setupEditorDefault(@NotNull EditorEx editor) {
|
||||
ConsoleViewUtil.setupConsoleEditor(editor, false, false);
|
||||
editor.getContentComponent().setFocusCycleRoot(false);
|
||||
editor.setHorizontalScrollbarVisible(false);
|
||||
@@ -410,30 +409,26 @@ public class LanguageConsoleImpl implements Disposable, TypeSafeDataProvider {
|
||||
}
|
||||
|
||||
protected void addTextToHistory(@Nullable CharSequence text, @Nullable TextAttributes attributes) {
|
||||
if (StringUtil.isEmpty(text)) {
|
||||
if (StringUtil.isEmpty(text) || attributes == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Document history = myHistoryViewer.getDocument();
|
||||
MarkupModel markupModel = DocumentMarkupModel.forDocument(history, myProject, true);
|
||||
int offset = appendToHistoryDocument(history, text);
|
||||
if (attributes == null) return;
|
||||
markupModel.addRangeHighlighter(offset, offset + text.length(), HighlighterLayer.SYNTAX, attributes, HighlighterTargetArea.EXACT_RANGE);
|
||||
}
|
||||
|
||||
public String addCurrentToHistory(final TextRange textRange, final boolean erase, final boolean preserveMarkup) {
|
||||
public String addCurrentToHistory(@NotNull TextRange textRange, boolean erase, boolean preserveMarkup) {
|
||||
return addToHistoryInner(textRange, myConsoleEditor, erase, preserveMarkup);
|
||||
}
|
||||
|
||||
public String addToHistory(final TextRange textRange, final EditorEx editor, final boolean preserveMarkup) {
|
||||
public String addToHistory(@NotNull TextRange textRange, @NotNull EditorEx editor, boolean preserveMarkup) {
|
||||
return addToHistoryInner(textRange, editor, false, preserveMarkup);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected String addToHistoryInner(@NotNull final TextRange textRange,
|
||||
@NotNull final EditorEx editor,
|
||||
final boolean erase,
|
||||
final boolean preserveMarkup) {
|
||||
protected String addToHistoryInner(@NotNull final TextRange textRange, @NotNull final EditorEx editor, boolean erase, final boolean preserveMarkup) {
|
||||
String result = ApplicationManager.getApplication().runReadAction(new Computable<String>() {
|
||||
@Override
|
||||
public String compute() {
|
||||
@@ -462,13 +457,15 @@ public class LanguageConsoleImpl implements Disposable, TypeSafeDataProvider {
|
||||
|
||||
private void scrollHistoryToEnd() {
|
||||
final int lineCount = myHistoryViewer.getDocument().getLineCount();
|
||||
if (lineCount == 0) return;
|
||||
if (lineCount == 0) {
|
||||
return;
|
||||
}
|
||||
myHistoryViewer.getCaretModel().moveToOffset(myHistoryViewer.getDocument().getLineStartOffset(lineCount - 1), false);
|
||||
myHistoryViewer.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected String addTextRangeToHistory(@NotNull TextRange textRange, @NotNull final EditorEx consoleEditor, boolean preserveMarkup) {
|
||||
protected String addTextRangeToHistory(@NotNull TextRange textRange, @NotNull EditorEx consoleEditor, boolean preserveMarkup) {
|
||||
final Document history = myHistoryViewer.getDocument();
|
||||
final MarkupModel markupModel = DocumentMarkupModel.forDocument(history, myProject, true);
|
||||
doAddPromptToHistory();
|
||||
@@ -667,17 +664,21 @@ public class LanguageConsoleImpl implements Disposable, TypeSafeDataProvider {
|
||||
@Override
|
||||
public void layoutContainer(@NotNull final Container parent) {
|
||||
final int componentCount = parent.getComponentCount();
|
||||
if (componentCount == 0) return;
|
||||
if (componentCount == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
final EditorEx history = myHistoryViewer;
|
||||
final EditorEx editor = componentCount == 2 ? myConsoleEditor : null;
|
||||
|
||||
if (editor == null) {
|
||||
parent.getComponent(0).setBounds(parent.getBounds());
|
||||
return;
|
||||
}
|
||||
|
||||
final Dimension panelSize = parent.getSize();
|
||||
if (panelSize.getHeight() <= 0) return;
|
||||
if (panelSize.getHeight() <= 0) {
|
||||
return;
|
||||
}
|
||||
final Dimension historySize = history.getContentSize();
|
||||
final Dimension editorSize = editor.getContentSize();
|
||||
final Dimension newEditorSize = new Dimension();
|
||||
@@ -690,7 +691,9 @@ public class LanguageConsoleImpl implements Disposable, TypeSafeDataProvider {
|
||||
history.getSettings().setAdditionalColumnsCount(2 + (width - historySize.width) / EditorUtil.getSpaceWidth(Font.PLAIN, history));
|
||||
|
||||
// deal with height
|
||||
if (historySize.width == 0) historySize.height = 0;
|
||||
if (historySize.width == 0) {
|
||||
historySize.height = 0;
|
||||
}
|
||||
final int minHistorySize = historySize.height > 0 ? 2 * history.getLineHeight() + (myShowSeparatorLine ? SEPARATOR_THICKNESS : 0) : 0;
|
||||
final int minEditorSize = editor.isViewer() ? 0 : editor.getLineHeight();
|
||||
final int editorPreferred = editor.isViewer() ? 0 : Math.max(minEditorSize, editorSize.height);
|
||||
|
||||
+1
-1
@@ -65,7 +65,7 @@ public class RunnerAndConfigurationSettingsImpl implements JDOMExternalizable, C
|
||||
@NonNls
|
||||
private static final String EDIT_BEFORE_RUN = "editBeforeRun";
|
||||
@NonNls
|
||||
private static final String SINGLETON = "singleton";
|
||||
public static final String SINGLETON = "singleton";
|
||||
|
||||
|
||||
/** for compatibility */
|
||||
|
||||
+1
-1
@@ -328,7 +328,7 @@ public class InjectedLanguageUtil {
|
||||
// modification of cachedInjectedDocuments must be under PsiLock only
|
||||
ConcurrentList<DocumentWindow> injected = hostPsiFile.getUserData(INJECTED_DOCS_KEY);
|
||||
if (injected == null) {
|
||||
injected = ((UserDataHolderEx)hostPsiFile).putUserDataIfAbsent(INJECTED_DOCS_KEY, ContainerUtil.<DocumentWindow>createLockFreeCopyOnWriteList());
|
||||
injected = ((UserDataHolderEx)hostPsiFile).putUserDataIfAbsent(INJECTED_DOCS_KEY, ContainerUtil.<DocumentWindow>createConcurrentList());
|
||||
}
|
||||
return injected;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.intellij.execution.process;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.configurations.GeneralCommandLine;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
@@ -105,4 +106,24 @@ public class CapturingProcessHandler extends OSProcessHandler {
|
||||
}
|
||||
return super.getCharset();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ProcessOutput runProcessWithProgressIndicator(@NotNull ProgressIndicator indicator) {
|
||||
startNotify();
|
||||
while (!waitFor(100)) {
|
||||
if (indicator.isCanceled()) {
|
||||
if (!isProcessTerminating() && !isProcessTerminated()) {
|
||||
destroyProcess();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (waitFor()) {
|
||||
myOutput.setExitCode(getProcess().exitValue());
|
||||
}
|
||||
else {
|
||||
LOG.info("runProcess: exit value unavailable");
|
||||
}
|
||||
return myOutput;
|
||||
}
|
||||
}
|
||||
@@ -62,8 +62,8 @@ public class LoadingDecorator {
|
||||
Disposer.register(parent, myFadeOutAnimator);
|
||||
|
||||
|
||||
myPane.add(content, 0, JLayeredPane.DEFAULT_LAYER);
|
||||
myPane.add(myLoadingLayer, 1, JLayeredPane.DRAG_LAYER);
|
||||
myPane.add(content, JLayeredPane.DEFAULT_LAYER, 0);
|
||||
myPane.add(myLoadingLayer, JLayeredPane.DRAG_LAYER, 1);
|
||||
|
||||
Disposer.register(parent, myLoadingLayer.myProgress);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2013 JetBrains s.r.o.
|
||||
* Copyright 2000-2014 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.
|
||||
@@ -185,7 +185,6 @@ public class JobLauncherImpl extends JobLauncher {
|
||||
|
||||
@Override
|
||||
protected void setRawResult(Void value) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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.ide.util;
|
||||
|
||||
import com.intellij.openapi.extensions.AbstractExtensionPointBean;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.extensions.Extensions;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.util.xmlb.annotations.Attribute;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author gregsh
|
||||
*/
|
||||
public class TipAndTrickBean extends AbstractExtensionPointBean {
|
||||
public static final ExtensionPointName<TipAndTrickBean> EP_NAME = ExtensionPointName.create("com.intellij.tipAndTrick");
|
||||
|
||||
@Attribute("file")
|
||||
public String myFileName;
|
||||
|
||||
@Attribute("feature-id")
|
||||
public String myFeatureId;
|
||||
|
||||
public String getFileName() {
|
||||
return myFileName;
|
||||
}
|
||||
|
||||
public String getFeatureId() {
|
||||
return myFeatureId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static TipAndTrickBean findByFileName(String tipFileName) {
|
||||
for (TipAndTrickBean tip : Extensions.getExtensions(EP_NAME)) {
|
||||
if (Comparing.equal(tipFileName, tip.getFileName())) {
|
||||
return tip;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,18 +15,17 @@
|
||||
*/
|
||||
package com.intellij.ide.util;
|
||||
|
||||
import com.intellij.featureStatistics.FeatureDescriptor;
|
||||
import com.intellij.featureStatistics.ProductivityFeaturesProvider;
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.ide.BrowserUtil;
|
||||
import com.intellij.ide.GeneralSettings;
|
||||
import com.intellij.ide.IdeBundle;
|
||||
import com.intellij.openapi.application.ApplicationNamesInfo;
|
||||
import com.intellij.openapi.util.JDOMUtil;
|
||||
import com.intellij.openapi.extensions.Extensions;
|
||||
import com.intellij.ui.ScrollPaneFactory;
|
||||
import com.intellij.ui.SimpleTextAttributes;
|
||||
import com.intellij.ui.components.JBLabel;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jdom.Document;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.HyperlinkEvent;
|
||||
@@ -35,20 +34,16 @@ import javax.swing.text.html.HTMLEditorKit;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ItemEvent;
|
||||
import java.awt.event.ItemListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class TipPanel extends JPanel {
|
||||
private static final int DEFAULT_WIDTH = 400;
|
||||
private static final int DEFAULT_HEIGHT = 200;
|
||||
private final JCheckBox myCheckBox;
|
||||
private final JEditorPane browser;
|
||||
private final ArrayList<String> myTipPaths = new ArrayList<String>();
|
||||
private final HashMap<String, Class< ? extends ProductivityFeaturesProvider>> myPathsToProviderMap = new HashMap<String, Class<? extends ProductivityFeaturesProvider>>();
|
||||
@NonNls
|
||||
private static final String ELEMENT_TIP = "tip";
|
||||
@NonNls
|
||||
private static final String ATTRIBUTE_FILE = "file";
|
||||
|
||||
private final JEditorPane myBrowserPanel;
|
||||
private final JLabel myPoweredByLabel;
|
||||
private final List<TipAndTrickBean> myTips = ContainerUtil.newArrayList();
|
||||
|
||||
public TipPanel() {
|
||||
setLayout(new BorderLayout());
|
||||
@@ -63,45 +58,42 @@ public class TipPanel extends JPanel {
|
||||
jpanel.add(jlabel1, BorderLayout.CENTER);
|
||||
jpanel.setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 0));
|
||||
add(jpanel, BorderLayout.NORTH);
|
||||
browser = new JEditorPane();
|
||||
browser.setEditable(false);
|
||||
browser.setEditorKit(new HTMLEditorKit());
|
||||
browser.setBackground(UIUtil.getTextFieldBackground());
|
||||
browser.addHyperlinkListener(
|
||||
myBrowserPanel = new JEditorPane();
|
||||
myBrowserPanel.setEditable(false);
|
||||
myBrowserPanel.setEditorKit(new HTMLEditorKit());
|
||||
myBrowserPanel.setBackground(UIUtil.getTextFieldBackground());
|
||||
myBrowserPanel.addHyperlinkListener(
|
||||
new HyperlinkListener() {
|
||||
public void hyperlinkUpdate(HyperlinkEvent e) {
|
||||
if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
|
||||
//TODO: Open url in browser
|
||||
BrowserUtil.browse(e.getURL());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(browser);
|
||||
JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(myBrowserPanel);
|
||||
add(scrollPane, BorderLayout.CENTER);
|
||||
myCheckBox = new JCheckBox(IdeBundle.message("checkbox.show.tips.on.startup"), true);
|
||||
myCheckBox.setBorder(BorderFactory.createEmptyBorder(5, 0, 0, 0));
|
||||
|
||||
JPanel southPanel = new JPanel(new BorderLayout());
|
||||
JCheckBox showOnStartCheckBox = new JCheckBox(IdeBundle.message("checkbox.show.tips.on.startup"), true);
|
||||
showOnStartCheckBox.setBorder(BorderFactory.createEmptyBorder(5, 0, 0, 0));
|
||||
final GeneralSettings settings = GeneralSettings.getInstance();
|
||||
myCheckBox.setSelected(settings.showTipsOnStartup());
|
||||
myCheckBox.addItemListener(new ItemListener() {
|
||||
showOnStartCheckBox.setSelected(settings.showTipsOnStartup());
|
||||
showOnStartCheckBox.addItemListener(new ItemListener() {
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
settings.setShowTipsOnStartup(e.getStateChange() == ItemEvent.SELECTED);
|
||||
}
|
||||
});
|
||||
add(myCheckBox, BorderLayout.SOUTH);
|
||||
try {
|
||||
readTips("/tips/tips.xml");
|
||||
readTips("/tips/IdeSpecificTips.xml");
|
||||
}
|
||||
catch (Exception exception) {//
|
||||
}
|
||||
southPanel.add(showOnStartCheckBox, BorderLayout.WEST);
|
||||
|
||||
for (ProductivityFeaturesProvider provider : ProductivityFeaturesProvider.EP_NAME.getExtensions()) {
|
||||
final FeatureDescriptor[] descriptors = provider.getFeatureDescriptors();
|
||||
for (int j = 0; descriptors != null && j < descriptors.length; j++) {
|
||||
FeatureDescriptor descriptor = descriptors[j];
|
||||
myPathsToProviderMap.put(descriptor.getTipFileName(), descriptor.getProvider());
|
||||
}
|
||||
}
|
||||
myPoweredByLabel = new JBLabel();
|
||||
myPoweredByLabel.setHorizontalAlignment(SwingConstants.RIGHT);
|
||||
myPoweredByLabel.setForeground(SimpleTextAttributes.GRAY_ITALIC_ATTRIBUTES.getFgColor());
|
||||
|
||||
southPanel.add(myPoweredByLabel, BorderLayout.EAST);
|
||||
add(southPanel, BorderLayout.SOUTH);
|
||||
|
||||
Collections.addAll(myTips, Extensions.getExtensions(TipAndTrickBean.EP_NAME));
|
||||
}
|
||||
|
||||
public Dimension getPreferredSize() {
|
||||
@@ -109,57 +101,49 @@ public class TipPanel extends JPanel {
|
||||
}
|
||||
|
||||
public void prevTip() {
|
||||
if (myTipPaths.size() == 0) {
|
||||
browser.setText(IdeBundle.message("error.tips.not.found", ApplicationNamesInfo.getInstance().getFullProductName()));
|
||||
if (myTips.size() == 0) {
|
||||
myBrowserPanel.setText(IdeBundle.message("error.tips.not.found", ApplicationNamesInfo.getInstance().getFullProductName()));
|
||||
return;
|
||||
}
|
||||
final GeneralSettings settings = GeneralSettings.getInstance();
|
||||
int lastTip = settings.getLastTip();
|
||||
|
||||
final String path;
|
||||
final TipAndTrickBean tip;
|
||||
lastTip--;
|
||||
if (lastTip <= 0) {
|
||||
path = myTipPaths.get(myTipPaths.size() - 1);
|
||||
lastTip = myTipPaths.size();
|
||||
tip = myTips.get(myTips.size() - 1);
|
||||
lastTip = myTips.size();
|
||||
}
|
||||
else {
|
||||
path = myTipPaths.get(lastTip - 1);
|
||||
tip = myTips.get(lastTip - 1);
|
||||
}
|
||||
|
||||
setTip(path, lastTip, browser, settings);
|
||||
setTip(tip, lastTip, myBrowserPanel, settings);
|
||||
}
|
||||
|
||||
private void setTip (String path, int lastTip, JEditorPane browser, GeneralSettings settings) {
|
||||
TipUIUtil.openTipInBrowser(path, browser, myPathsToProviderMap.get(path));
|
||||
private void setTip (TipAndTrickBean tip, int lastTip, JEditorPane browser, GeneralSettings settings) {
|
||||
TipUIUtil.openTipInBrowser(tip, browser);
|
||||
myPoweredByLabel.setText(TipUIUtil.getPoweredByText(tip));
|
||||
settings.setLastTip(lastTip);
|
||||
}
|
||||
|
||||
public void nextTip() {
|
||||
if (myTipPaths.size() == 0) {
|
||||
browser.setText(IdeBundle.message("error.tips.not.found", ApplicationNamesInfo.getInstance().getFullProductName()));
|
||||
if (myTips.size() == 0) {
|
||||
myBrowserPanel.setText(IdeBundle.message("error.tips.not.found", ApplicationNamesInfo.getInstance().getFullProductName()));
|
||||
return;
|
||||
}
|
||||
GeneralSettings settings = GeneralSettings.getInstance();
|
||||
int lastTip = settings.getLastTip();
|
||||
String path;
|
||||
TipAndTrickBean tip;
|
||||
lastTip++;
|
||||
if (lastTip - 1 >= myTipPaths.size()) {
|
||||
path = myTipPaths.get(0);
|
||||
if (lastTip - 1 >= myTips.size()) {
|
||||
tip = myTips.get(0);
|
||||
lastTip = 1;
|
||||
}
|
||||
else {
|
||||
path = myTipPaths.get(lastTip - 1);
|
||||
tip = myTips.get(lastTip - 1);
|
||||
}
|
||||
|
||||
setTip(path, lastTip, browser, settings);
|
||||
}
|
||||
|
||||
private void readTips(String tipsURL) throws Exception {
|
||||
final Document document = JDOMUtil.loadDocument(getClass().getResource(tipsURL).openStream());
|
||||
|
||||
for (Object o : document.getRootElement().getChildren(ELEMENT_TIP)) {
|
||||
Element tip = (Element)o;
|
||||
myTipPaths.add(tip.getAttributeValue(ATTRIBUTE_FILE));
|
||||
}
|
||||
setTip(tip, lastTip, myBrowserPanel, settings);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,17 +16,23 @@
|
||||
package com.intellij.ide.util;
|
||||
|
||||
import com.intellij.ide.IdeBundle;
|
||||
import com.intellij.ide.plugins.IdeaPluginDescriptor;
|
||||
import com.intellij.ide.plugins.PluginManagerCore;
|
||||
import com.intellij.openapi.actionSystem.KeyboardShortcut;
|
||||
import com.intellij.openapi.actionSystem.Shortcut;
|
||||
import com.intellij.openapi.application.ApplicationInfo;
|
||||
import com.intellij.openapi.application.ApplicationNamesInfo;
|
||||
import com.intellij.openapi.extensions.PluginDescriptor;
|
||||
import com.intellij.openapi.keymap.Keymap;
|
||||
import com.intellij.openapi.keymap.KeymapManager;
|
||||
import com.intellij.openapi.keymap.KeymapUtil;
|
||||
import com.intellij.openapi.keymap.impl.DefaultKeymap;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.ResourceUtil;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
@@ -46,7 +52,25 @@ public class TipUIUtil {
|
||||
private TipUIUtil() {
|
||||
}
|
||||
|
||||
public static void openTipInBrowser(String tipPath, JEditorPane browser, Class providerClass) {
|
||||
@NotNull
|
||||
public static String getPoweredByText(@NotNull TipAndTrickBean tip) {
|
||||
PluginDescriptor descriptor = tip.getPluginDescriptor();
|
||||
return descriptor instanceof IdeaPluginDescriptor &&
|
||||
!PluginManagerCore.CORE_PLUGIN_ID.equals(descriptor.getPluginId().getIdString()) ?
|
||||
((IdeaPluginDescriptor)descriptor).getName() : "";
|
||||
}
|
||||
|
||||
public static void openTipInBrowser(String tipFileName, JEditorPane browser, Class providerClass) {
|
||||
TipAndTrickBean tip = TipAndTrickBean.findByFileName(tipFileName);
|
||||
if (tip == null && StringUtil.isNotEmpty(tipFileName)) {
|
||||
tip = new TipAndTrickBean();
|
||||
tip.myFileName = tipFileName;
|
||||
}
|
||||
openTipInBrowser(tip, browser);
|
||||
}
|
||||
|
||||
public static void openTipInBrowser(@Nullable TipAndTrickBean tip, JEditorPane browser) {
|
||||
if (tip == null) return;
|
||||
/* TODO: detect that file is not present
|
||||
if (!file.exists()) {
|
||||
browser.read(new StringReader("Tips for '" + feature.getDisplayName() + "' not found. Make sure you installed IntelliJ IDEA correctly."), null);
|
||||
@@ -54,18 +78,20 @@ public class TipUIUtil {
|
||||
}
|
||||
*/
|
||||
try {
|
||||
if (tipPath == null) return;
|
||||
if (providerClass == null) providerClass = TipUIUtil.class;
|
||||
URL url = ResourceUtil.getResource(providerClass, "/tips/", tipPath);
|
||||
PluginDescriptor pluginDescriptor = tip.getPluginDescriptor();
|
||||
ClassLoader tipLoader = pluginDescriptor == null ? TipUIUtil.class.getClassLoader() :
|
||||
ObjectUtils.notNull(pluginDescriptor.getPluginClassLoader(), TipUIUtil.class.getClassLoader());
|
||||
|
||||
URL url = ResourceUtil.getResource(tipLoader, "/tips/", tip.getFileName());
|
||||
|
||||
if (url == null) {
|
||||
setCantReadText(browser, tipPath);
|
||||
setCantReadText(browser, tip);
|
||||
return;
|
||||
}
|
||||
|
||||
StringBuffer text = new StringBuffer(ResourceUtil.loadText(url));
|
||||
updateShortcuts(text);
|
||||
updateImages(text, providerClass);
|
||||
updateImages(text, tipLoader);
|
||||
String replaced = text.toString().replace("&productName;", ApplicationNamesInfo.getInstance().getFullProductName());
|
||||
replaced = replaced.replace("&majorVersion;", ApplicationInfo.getInstance().getMajorVersion());
|
||||
replaced = replaced.replace("&minorVersion;", ApplicationInfo.getInstance().getMinorVersion());
|
||||
@@ -75,11 +101,11 @@ public class TipUIUtil {
|
||||
browser.read(new StringReader(replaced), url);
|
||||
}
|
||||
catch (IOException e) {
|
||||
setCantReadText(browser, tipPath);
|
||||
setCantReadText(browser, tip);
|
||||
}
|
||||
}
|
||||
|
||||
private static void setCantReadText(JEditorPane browser, String missingFile) {
|
||||
private static void setCantReadText(JEditorPane browser, TipAndTrickBean missingFile) {
|
||||
try {
|
||||
browser.read(new StringReader(
|
||||
IdeBundle.message("error.unable.to.read.tip.of.the.day", missingFile, ApplicationNamesInfo.getInstance().getFullProductName())), null);
|
||||
@@ -88,7 +114,7 @@ public class TipUIUtil {
|
||||
}
|
||||
}
|
||||
|
||||
private static void updateImages(StringBuffer text, Class providerClass) {
|
||||
private static void updateImages(StringBuffer text, ClassLoader tipLoader) {
|
||||
final boolean dark = UIUtil.isUnderDarcula();
|
||||
final boolean retina = UIUtil.isRetina();
|
||||
// if (!dark && !retina) {
|
||||
@@ -109,7 +135,7 @@ public class TipUIUtil {
|
||||
String path = img.substring(srcIndex + 5, endIndex);
|
||||
if (!path.endsWith("_dark") && !path.endsWith("@2x")) {
|
||||
path += suffix + ".png";
|
||||
URL url = ResourceUtil.getResource(providerClass, "/tips/", path);
|
||||
URL url = ResourceUtil.getResource(tipLoader, "/tips/", path);
|
||||
if (url != null) {
|
||||
String newImgTag = "<img src=\"" + path + "\" ";
|
||||
if (retina) {
|
||||
|
||||
+10
-6
@@ -17,8 +17,11 @@ package com.intellij.openapi.diff.actions;
|
||||
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys;
|
||||
import com.intellij.openapi.diff.DiffViewer;
|
||||
import com.intellij.openapi.diff.ex.DiffPanelEx;
|
||||
import com.intellij.openapi.diff.impl.DiffPanelImpl;
|
||||
import com.intellij.openapi.diff.impl.incrementalMerge.ui.MergePanel2;
|
||||
import com.intellij.openapi.project.DumbAware;
|
||||
import com.intellij.ui.ToggleActionButton;
|
||||
|
||||
@@ -29,15 +32,16 @@ public class ToggleAutoScrollAction extends ToggleActionButton implements DumbAw
|
||||
|
||||
@Override
|
||||
public boolean isSelected(AnActionEvent e) {
|
||||
DiffPanelEx diffPanel = DiffPanelImpl.fromDataContext(e.getDataContext());
|
||||
return diffPanel == null || diffPanel.isAutoScrollEnabled();
|
||||
DiffViewer viewer = PlatformDataKeys.DIFF_VIEWER.getData(e.getDataContext());
|
||||
if (viewer instanceof DiffPanelEx) return ((DiffPanelEx)viewer).isAutoScrollEnabled();
|
||||
if (viewer instanceof MergePanel2) return ((MergePanel2)viewer).isAutoScrollEnabled();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelected(AnActionEvent e, boolean state) {
|
||||
final DiffPanelImpl diffPanel = DiffPanelImpl.fromDataContext(e.getDataContext());
|
||||
if (diffPanel != null) {
|
||||
diffPanel.setAutoScrollEnabled(state);
|
||||
}
|
||||
DiffViewer viewer = PlatformDataKeys.DIFF_VIEWER.getData(e.getDataContext());
|
||||
if (viewer instanceof DiffPanelEx) ((DiffPanelEx)viewer).setAutoScrollEnabled(state);
|
||||
if (viewer instanceof MergePanel2) ((MergePanel2)viewer).setAutoScrollEnabled(state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,10 +176,15 @@ public class DiffPanelImpl implements DiffPanelEx, ContentChangeListener, TwoSid
|
||||
|
||||
final ComparisonPolicy comparisonPolicy = getComparisonPolicy();
|
||||
final ComparisonPolicy defaultComparisonPolicy = DiffManagerImpl.getInstanceEx().getComparisonPolicy();
|
||||
final HighlightMode highlightMode = getHighlightMode();
|
||||
final HighlightMode defaultHighlightMode = DiffManagerImpl.getInstanceEx().getHighlightMode();
|
||||
|
||||
if (defaultComparisonPolicy != null && comparisonPolicy != defaultComparisonPolicy) {
|
||||
setComparisonPolicy(defaultComparisonPolicy);
|
||||
}
|
||||
if (defaultHighlightMode != null && highlightMode != defaultHighlightMode) {
|
||||
setHighlightMode(defaultHighlightMode);
|
||||
}
|
||||
myVisibleAreaListener = new VisibleAreaListener() {
|
||||
@Override
|
||||
public void visibleAreaChanged(VisibleAreaEvent e) {
|
||||
@@ -463,6 +468,10 @@ public class DiffPanelImpl implements DiffPanelEx, ContentChangeListener, TwoSid
|
||||
return myData.getComparisonPolicy();
|
||||
}
|
||||
|
||||
public void setComparisonPolicy(ComparisonPolicy comparisonPolicy) {
|
||||
setComparisonPolicy(comparisonPolicy, true);
|
||||
}
|
||||
|
||||
private void setComparisonPolicy(ComparisonPolicy policy, boolean notifyManager) {
|
||||
myData.setComparisonPolicy(policy);
|
||||
rediff();
|
||||
@@ -472,6 +481,24 @@ public class DiffPanelImpl implements DiffPanelEx, ContentChangeListener, TwoSid
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public HighlightMode getHighlightMode() {
|
||||
return myData.getHighlightMode();
|
||||
}
|
||||
|
||||
public void setHighlightMode(@NotNull HighlightMode mode) {
|
||||
setHighlightMode(mode, true);
|
||||
}
|
||||
|
||||
public void setHighlightMode(@NotNull HighlightMode mode, boolean notifyManager) {
|
||||
myData.setHighlightMode(mode);
|
||||
rediff();
|
||||
|
||||
if (notifyManager) {
|
||||
DiffManagerImpl.getInstanceEx().setHighlightMode(mode);
|
||||
}
|
||||
}
|
||||
|
||||
public void setAutoScrollEnabled(boolean enabled) {
|
||||
myScrollSupport.setEnabled(enabled);
|
||||
}
|
||||
@@ -480,20 +507,6 @@ public class DiffPanelImpl implements DiffPanelEx, ContentChangeListener, TwoSid
|
||||
return myScrollSupport.isEnabled();
|
||||
}
|
||||
|
||||
public void setComparisonPolicy(ComparisonPolicy comparisonPolicy) {
|
||||
setComparisonPolicy(comparisonPolicy, true);
|
||||
}
|
||||
|
||||
public void setHighlightMode(@NotNull HighlightMode highlightMode) {
|
||||
myData.setHighlightMode(highlightMode);
|
||||
rediff();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public HighlightMode getHighlightMode() {
|
||||
return myData.getHighlightMode();
|
||||
}
|
||||
|
||||
public Rediffers getDiffUpdater() {
|
||||
return myDiffUpdater;
|
||||
}
|
||||
|
||||
+31
-3
@@ -24,6 +24,7 @@ import com.intellij.openapi.diff.DiffTool;
|
||||
import com.intellij.openapi.diff.impl.ComparisonPolicy;
|
||||
import com.intellij.openapi.diff.impl.DiffPanelImpl;
|
||||
import com.intellij.openapi.diff.impl.mergeTool.MergeTool;
|
||||
import com.intellij.openapi.diff.impl.processing.HighlightMode;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.markup.MarkupEditorFilter;
|
||||
import com.intellij.openapi.project.Project;
|
||||
@@ -78,7 +79,10 @@ public class DiffManagerImpl extends DiffManager implements JDOMExternalizable {
|
||||
}
|
||||
};
|
||||
private ComparisonPolicy myComparisonPolicy;
|
||||
private HighlightMode myHighlightMode;
|
||||
|
||||
@NonNls public static final String COMPARISON_POLICY_ATTR_NAME = "COMPARISON_POLICY";
|
||||
@NonNls public static final String HIGHLIGHT_MODE_ATTR_NAME = "HIGHLIGHT_MODE";
|
||||
|
||||
public DiffManagerImpl() {
|
||||
myProperties = new ExternalizablePropertyContainer();
|
||||
@@ -153,12 +157,13 @@ public class DiffManagerImpl extends DiffManager implements JDOMExternalizable {
|
||||
return (DiffManagerImpl)DiffManager.getInstance();
|
||||
}
|
||||
|
||||
public void readExternal(Element element) throws InvalidDataException {
|
||||
public void readExternal(@NotNull Element element) throws InvalidDataException {
|
||||
myProperties.readExternal(element);
|
||||
readPolicy(element);
|
||||
readMode(element);
|
||||
}
|
||||
|
||||
private void readPolicy(final Element element) {
|
||||
private void readPolicy(@NotNull final Element element) {
|
||||
final String policyName = element.getAttributeValue(COMPARISON_POLICY_ATTR_NAME);
|
||||
if (policyName != null) {
|
||||
ComparisonPolicy[] policies = ComparisonPolicy.getAllInstances();
|
||||
@@ -171,11 +176,25 @@ public class DiffManagerImpl extends DiffManager implements JDOMExternalizable {
|
||||
}
|
||||
}
|
||||
|
||||
public void writeExternal(Element element) throws WriteExternalException {
|
||||
private void readMode(@NotNull final Element element) {
|
||||
final String modeName = element.getAttributeValue(HIGHLIGHT_MODE_ATTR_NAME);
|
||||
if (modeName != null) {
|
||||
try {
|
||||
myHighlightMode = HighlightMode.valueOf(modeName);
|
||||
}
|
||||
catch (IllegalArgumentException ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void writeExternal(@NotNull Element element) throws WriteExternalException {
|
||||
myProperties.writeExternal(element);
|
||||
if (myComparisonPolicy != null) {
|
||||
element.setAttribute(COMPARISON_POLICY_ATTR_NAME, myComparisonPolicy.getName());
|
||||
}
|
||||
if (myHighlightMode != null) {
|
||||
element.setAttribute(HIGHLIGHT_MODE_ATTR_NAME, myHighlightMode.name());
|
||||
}
|
||||
}
|
||||
|
||||
public AbstractProperty.AbstractPropertyContainer getProperties() { return myProperties; }
|
||||
@@ -206,4 +225,13 @@ public class DiffManagerImpl extends DiffManager implements JDOMExternalizable {
|
||||
public ComparisonPolicy getComparisonPolicy() {
|
||||
return myComparisonPolicy;
|
||||
}
|
||||
|
||||
public void setHighlightMode(HighlightMode highlightMode) {
|
||||
myHighlightMode = highlightMode;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public HighlightMode getHighlightMode() {
|
||||
return myHighlightMode;
|
||||
}
|
||||
}
|
||||
|
||||
+13
-4
@@ -16,16 +16,15 @@
|
||||
package com.intellij.openapi.diff.impl.incrementalMerge.ui;
|
||||
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.ide.actions.EditSourceAction;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.actionSystem.ActionManager;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.actionSystem.IdeActions;
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys;
|
||||
import com.intellij.openapi.actionSystem.*;
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.diff.*;
|
||||
import com.intellij.openapi.diff.actions.NextDiffAction;
|
||||
import com.intellij.openapi.diff.actions.PreviousDiffAction;
|
||||
import com.intellij.openapi.diff.actions.ToggleAutoScrollAction;
|
||||
import com.intellij.openapi.diff.impl.DiffUtil;
|
||||
import com.intellij.openapi.diff.impl.EditingSides;
|
||||
import com.intellij.openapi.diff.impl.GenericDataProvider;
|
||||
@@ -151,6 +150,8 @@ public class MergePanel2 implements DiffViewer {
|
||||
toolbar.addAction(new OpenPartialDiffAction(0, 2, AllIcons.Diff.BranchDiff));
|
||||
toolbar.addSeparator();
|
||||
toolbar.addAction(new ApplyNonConflicts(myPanel));
|
||||
toolbar.addSeparator();
|
||||
toolbar.addAction(new ToggleAutoScrollAction());
|
||||
Project project = myData.getProject();
|
||||
if (project != null) {
|
||||
toolbar.addSeparator();
|
||||
@@ -262,6 +263,14 @@ public class MergePanel2 implements DiffViewer {
|
||||
return new MyEditingSides(FragmentSide.SIDE2);
|
||||
}
|
||||
|
||||
public void setAutoScrollEnabled(boolean enabled) {
|
||||
myScrollSupport.setEnabled(enabled);
|
||||
}
|
||||
|
||||
public boolean isAutoScrollEnabled() {
|
||||
return myScrollSupport.isEnabled();
|
||||
}
|
||||
|
||||
public void setHighlighterSettings(@Nullable EditorColorsScheme settings) {
|
||||
for (EditorPlace place : getEditorPlaces()) {
|
||||
setHighlighterSettings(settings, place);
|
||||
|
||||
+4
@@ -51,6 +51,10 @@ public class TextCompareProcessor {
|
||||
}
|
||||
|
||||
public ArrayList<LineFragment> process(String text1, String text2) throws FilesTooBigForDiffException {
|
||||
if (text1.isEmpty() || text2.isEmpty()) {
|
||||
return new DummyDiffFragmentsProcessor().process(text1, text2);
|
||||
}
|
||||
|
||||
DiffFragment[] woFormattingBlocks = myDiffPolicy.buildFragments(text1, text2);
|
||||
DiffFragment[] step1lineFragments = new DiffCorrection.TrueLineBlocks(myComparisonPolicy).correctAndNormalize(woFormattingBlocks);
|
||||
ArrayList<LineFragment> lineBlocks = new DiffFragmentsProcessor().process(step1lineFragments);
|
||||
|
||||
@@ -384,6 +384,7 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener,
|
||||
|
||||
@Override
|
||||
public void runBatchCaretOperation(@NotNull Runnable runnable) {
|
||||
myEditor.assertIsDispatchThread();
|
||||
doWithCaretMerging(runnable);
|
||||
}
|
||||
|
||||
@@ -402,8 +403,8 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener,
|
||||
}
|
||||
CaretImpl currCaret = it.next();
|
||||
if (prevCaret != null && (currCaret.getVisualPosition().equals(prevCaret.getVisualPosition())
|
||||
|| Math.max(currCaret.getSelectionStart(), prevCaret.getSelectionStart())
|
||||
< Math.min(currCaret.getSelectionEnd(), prevCaret.getSelectionEnd()))) {
|
||||
|| regionsIntersect(currCaret.getSelectionStart(), currCaret.getSelectionEnd(),
|
||||
prevCaret.getSelectionStart(), prevCaret.getSelectionEnd()))) {
|
||||
int newSelectionStart = Math.min(currCaret.getSelectionStart(), prevCaret.getSelectionStart());
|
||||
int newSelectionEnd = Math.max(currCaret.getSelectionEnd(), prevCaret.getSelectionEnd());
|
||||
CaretImpl toRetain, toRemove;
|
||||
@@ -428,6 +429,12 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener,
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean regionsIntersect(int firstStart, int firstEnd, int secondStart, int secondEnd) {
|
||||
return firstStart < secondStart && firstEnd > secondStart
|
||||
|| firstStart > secondStart && firstStart < secondEnd
|
||||
|| firstStart == secondStart && secondEnd > secondStart && firstEnd > firstStart;
|
||||
}
|
||||
|
||||
void doWithCaretMerging(Runnable runnable) {
|
||||
if (myPerformCaretMergingAfterCurrentOperation) {
|
||||
runnable.run();
|
||||
|
||||
@@ -3645,7 +3645,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
* lines (considering that single logical document line may be represented on multiple visual lines because of
|
||||
* soft wraps appliance) minus number of folded lines
|
||||
*/
|
||||
int getVisibleLineCount() {
|
||||
public int getVisibleLineCount() {
|
||||
return getVisibleLogicalLinesCount() + getSoftWrapModel().getSoftWrapsIntroducedLinesNumber();
|
||||
}
|
||||
|
||||
|
||||
@@ -772,7 +772,7 @@ action.mark=Mark
|
||||
action.do.not.mark=Do Not Mark
|
||||
error.source.directory.should.be.under.module.content.root.directory=Source directory should be under module content root directory
|
||||
progress.searching.for.sources=Searching for sources in {0}. Please wait.
|
||||
prompt.select.source.directory=Select source directory
|
||||
prompt.select.source.directory=Select Source Directory
|
||||
prompt.new.project.file.name=Enter a file name to create a new {0} {1}
|
||||
prompt.enter.project.file.location=Enter {0} file location
|
||||
directory.project.file.directory=The {0} file directory\n
|
||||
|
||||
@@ -328,6 +328,7 @@ junit_sm_runner=false
|
||||
testng_sm_runner=false
|
||||
show.flex.debug.design.view=false
|
||||
git.new.log=true
|
||||
diff.patience.alg=false
|
||||
|
||||
svn.use.terminal=false
|
||||
|
||||
|
||||
@@ -215,5 +215,6 @@
|
||||
</extensionPoint>
|
||||
|
||||
<extensionPoint qualifiedName="org.jetbrains.urlOpener" interface="com.intellij.ide.browsers.UrlOpener"/>
|
||||
<extensionPoint name="tipAndTrick" beanClass="com.intellij.ide.util.TipAndTrickBean"/>
|
||||
</extensionPoints>
|
||||
</idea-plugin>
|
||||
|
||||
@@ -102,6 +102,8 @@
|
||||
</project-components>
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<tipAndTrick file="TagNameCompletion.html"/>
|
||||
|
||||
<fileTypeFactory implementation="com.intellij.xml.XmlFileTypeFactory"/>
|
||||
|
||||
<lang.syntaxHighlighterFactory key="XML" implementationClass="com.intellij.lang.xml.XmlSyntaxHighlighterFactory"/>
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
// Each line contains plugin ID and list of versions that are broken.
|
||||
// If plugin name or version contains a space you can quote it like in command line.
|
||||
|
||||
NodeJS 134.680 134.31 134.307 134.262 134.198 134.125
|
||||
NodeJS 134.1145 134.1081 134.1039 134.985 134.680 134.31 134.307 134.262 134.198 134.125
|
||||
com.jetbrains.php 133.679 133.51 133.326 131.98 131.374 131.332 131.235 131.205 130.1639 130.1481 130.1176 129.91 129.814 129.672 129.362 127.67 127.100 126.334 123.66 122.875 121.62 121.390 121.215 121.12
|
||||
com.jetbrains.lang.ejs 131.17 131.12
|
||||
com.jetbrains.twig 133.51 130.1639
|
||||
//org.jetbrains.plugins.ruby 6.0.0.20140207
|
||||
Pythonid 3.1
|
||||
Karma 134.686 134.31
|
||||
org.intellij.scala 0.32.512
|
||||
org.jetbrains.kannotator 0.2.420
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
<selection>some text
|
||||
some <caret>other text</selection>
|
||||
@@ -0,0 +1,2 @@
|
||||
some<caret> text
|
||||
some <selection><caret>other</selection> text
|
||||
@@ -0,0 +1,3 @@
|
||||
s<caret>ome te<caret>xt
|
||||
s<caret>ome oth<selection><caret>er t</selection>ext
|
||||
a<caret>nother <selection><caret>line</selection> of text
|
||||
@@ -0,0 +1,3 @@
|
||||
some te<caret>xt
|
||||
s<caret>ome other text
|
||||
a<caret>nother <selection><caret>line</selection> of text
|
||||
@@ -0,0 +1,3 @@
|
||||
s<caret>ome text
|
||||
s<caret>ome other <selection>text<caret></selection>
|
||||
a<caret>not<caret>her lin<selection>e of<caret></selection> text
|
||||
@@ -0,0 +1,3 @@
|
||||
s<caret>ome text
|
||||
s<caret>ome other <selection>text<caret></selection>
|
||||
anot<caret>her line of text
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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 com.intellij.openapi.application.Result;
|
||||
import com.intellij.openapi.application.WriteAction;
|
||||
import com.intellij.openapi.command.WriteCommandAction;
|
||||
import com.intellij.openapi.editor.impl.DocumentImpl;
|
||||
import com.intellij.openapi.fileEditor.FileEditor;
|
||||
import com.intellij.openapi.fileEditor.HeavyFileEditorManagerTestCase;
|
||||
import com.intellij.openapi.fileEditor.TextEditor;
|
||||
import com.intellij.openapi.fileTypes.PlainTextFileType;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.testFramework.EditorTestUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class EditorMultiCaretStateRestoreTest extends HeavyFileEditorManagerTestCase {
|
||||
public void testRestoreState() throws Exception {
|
||||
EditorTestUtil.enableMultipleCarets();
|
||||
try {
|
||||
String text = "some<caret> text<caret>\n" +
|
||||
"some <selection><caret>other</selection> <selection>text<caret></selection>\n" +
|
||||
"<selection>ano<caret>ther</selection> line";
|
||||
PsiFile psiFile = myFixture.configureByText(PlainTextFileType.INSTANCE, text);
|
||||
VirtualFile virtualFile = psiFile.getVirtualFile();
|
||||
assertNotNull(virtualFile);
|
||||
myManager.openFile(virtualFile, false);
|
||||
myManager.closeAllFiles();
|
||||
FileEditor[] fileEditors = myManager.openFile(virtualFile, false);
|
||||
assertNotNull(fileEditors);
|
||||
assertEquals(1, fileEditors.length);
|
||||
Editor editor = ((TextEditor)fileEditors[0]).getEditor();
|
||||
|
||||
verifyEditorState(editor, text);
|
||||
}
|
||||
finally {
|
||||
EditorTestUtil.disableMultipleCarets();
|
||||
}
|
||||
}
|
||||
|
||||
private static void verifyEditorState(Editor editor, String textWithMarkup) {
|
||||
final Document document = new DocumentImpl(textWithMarkup);
|
||||
EditorTestUtil.CaretsState caretAndSelectionState = new WriteCommandAction<EditorTestUtil.CaretsState>(null) {
|
||||
@Override
|
||||
protected void run(@NotNull Result<EditorTestUtil.CaretsState> result) throws Throwable {
|
||||
result.setResult(EditorTestUtil.extractCaretAndSelectionMarkers(document));
|
||||
}
|
||||
}.execute().getResultObject();
|
||||
assertEquals(document.getCharsSequence().toString(), editor.getDocument().getText());
|
||||
EditorTestUtil.verifyCaretAndSelectionState(editor, caretAndSelectionState);
|
||||
}
|
||||
}
|
||||
+71
-4
@@ -17,6 +17,11 @@ package com.intellij.openapi.editor;
|
||||
|
||||
import com.intellij.openapi.editor.ex.EditorSettingsExternalizable;
|
||||
import com.intellij.openapi.editor.impl.AbstractEditorTest;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
|
||||
import com.intellij.openapi.fileEditor.impl.EditorHistoryManager;
|
||||
import com.intellij.openapi.project.ex.ProjectManagerEx;
|
||||
import com.intellij.openapi.project.impl.ProjectManagerImpl;
|
||||
import com.intellij.testFramework.EditorTestUtil;
|
||||
import com.intellij.testFramework.TestFileType;
|
||||
import com.intellij.testFramework.fixtures.EditorScrollingFixture;
|
||||
@@ -46,19 +51,19 @@ public class EditorMultiCaretTest extends AbstractEditorTest {
|
||||
checkResultByText("some <selection>t<caret>ext</selection>\n" +
|
||||
"a<caret>nother line");
|
||||
|
||||
mouse().alt().shift().clickAt(0,8); // alt-shift-click in existing selection
|
||||
mouse().alt().shift().clickAt(0, 8); // alt-shift-click in existing selection
|
||||
checkResultByText("some <selection>t<caret>ext</selection>\n" +
|
||||
"a<caret>nother line");
|
||||
|
||||
mouse().alt().shift().clickAt(0,6); // alt-shift-click at existing caret with selection
|
||||
mouse().alt().shift().clickAt(0, 6); // alt-shift-click at existing caret with selection
|
||||
checkResultByText("some text\n" +
|
||||
"a<caret>nother line");
|
||||
|
||||
mouse().alt().shift().clickAt(1,1); // alt-shift-click at the sole caret
|
||||
mouse().alt().shift().clickAt(1, 1); // alt-shift-click at the sole caret
|
||||
checkResultByText("some text\n" +
|
||||
"a<caret>nother line");
|
||||
|
||||
mouse().alt().shift().clickAt(0,30); // alt-shift-click in virtual space
|
||||
mouse().alt().shift().clickAt(0, 30); // alt-shift-click in virtual space
|
||||
checkResultByText("some text<caret>\n" +
|
||||
"a<caret>nother line");
|
||||
|
||||
@@ -142,4 +147,66 @@ public class EditorMultiCaretTest extends AbstractEditorTest {
|
||||
"long l<selection><caret>ine</selection>\n" +
|
||||
"line");
|
||||
}
|
||||
|
||||
public void testTyping() throws Exception {
|
||||
init("some<caret> text<caret>\n" +
|
||||
"some <selection><caret>other</selection> <selection>text<caret></selection>\n" +
|
||||
"<selection>ano<caret>ther</selection> line",
|
||||
TestFileType.TEXT
|
||||
);
|
||||
type('A');
|
||||
checkResultByText("someA<caret> textA<caret>\n" +
|
||||
"some A<caret> A<caret>\n" +
|
||||
"A<caret> line");
|
||||
}
|
||||
|
||||
public void testCopyPaste() throws Exception {
|
||||
init("<selection><caret>one</selection> two \n" +
|
||||
"<selection><caret>three</selection> four ",
|
||||
TestFileType.TEXT);
|
||||
executeAction("EditorCopy");
|
||||
executeAction("EditorLineEnd");
|
||||
executeAction("EditorPaste");
|
||||
checkResultByText("one twoone<caret> \n" +
|
||||
"three fourthree<caret> ");
|
||||
}
|
||||
|
||||
public void testCutAndPaste() throws Exception {
|
||||
init("<selection>one<caret></selection> two \n" +
|
||||
"<selection>three<caret></selection> four ",
|
||||
TestFileType.TEXT
|
||||
);
|
||||
executeAction("EditorCut");
|
||||
executeAction("EditorLineEnd");
|
||||
executeAction("EditorPaste");
|
||||
checkResultByText(" twoone<caret> \n" +
|
||||
" fourthree<caret> ");
|
||||
}
|
||||
|
||||
public void testPasteSingleItem() throws Exception {
|
||||
init("<selection>one<caret></selection> two \n" +
|
||||
"three four ",
|
||||
TestFileType.TEXT);
|
||||
executeAction("EditorCopy");
|
||||
executeAction("EditorCloneCaretBelow");
|
||||
executeAction("EditorLineEnd");
|
||||
executeAction("EditorPaste");
|
||||
checkResultByText("one twoone<caret> \n" +
|
||||
"three fourone<caret> ");
|
||||
}
|
||||
|
||||
public void testCutAndPasteMultiline() throws Exception {
|
||||
init("one <selection>two \n" +
|
||||
"three<caret></selection> four \n" +
|
||||
"five <selection>six \n" +
|
||||
"seven<caret></selection> eight",
|
||||
TestFileType.TEXT);
|
||||
executeAction("EditorCut");
|
||||
executeAction("EditorLineEnd");
|
||||
executeAction("EditorPaste");
|
||||
checkResultByText("one fourtwo \n" +
|
||||
"three<caret> \n" +
|
||||
"five eightsix \n" +
|
||||
"seven<caret>");
|
||||
}
|
||||
}
|
||||
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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 com.intellij.openapi.application.Result;
|
||||
import com.intellij.openapi.application.WriteAction;
|
||||
import com.intellij.openapi.command.CommandProcessor;
|
||||
import com.intellij.openapi.command.impl.CurrentEditorProvider;
|
||||
import com.intellij.openapi.command.impl.UndoManagerImpl;
|
||||
import com.intellij.openapi.command.undo.UndoManager;
|
||||
import com.intellij.openapi.editor.ex.EditorSettingsExternalizable;
|
||||
import com.intellij.openapi.editor.impl.AbstractEditorTest;
|
||||
import com.intellij.openapi.fileEditor.FileEditor;
|
||||
import com.intellij.openapi.fileEditor.TextEditor;
|
||||
import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider;
|
||||
import com.intellij.testFramework.EditorTestUtil;
|
||||
import com.intellij.testFramework.TestFileType;
|
||||
import com.intellij.testFramework.fixtures.EditorScrollingFixture;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class EditorMultiCaretUndoRedoTest extends AbstractEditorTest {
|
||||
private CurrentEditorProvider mySavedCurrentEditorProvider;
|
||||
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
EditorTestUtil.enableMultipleCarets();
|
||||
mySavedCurrentEditorProvider = getUndoManager().getEditorProvider();
|
||||
}
|
||||
|
||||
public void tearDown() throws Exception {
|
||||
getUndoManager().setEditorProvider(mySavedCurrentEditorProvider);
|
||||
EditorTestUtil.disableMultipleCarets();
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
// disabling execution of tests in command
|
||||
protected void runTest() throws Throwable {
|
||||
new WriteAction<Void>() {
|
||||
@Override
|
||||
protected void run(@NotNull Result<Void> result) throws Throwable {
|
||||
doRunTest();
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
public void testUndoRedo() throws Exception {
|
||||
init("some<caret> text<caret>\n" +
|
||||
"some <selection><caret>other</selection> <selection>text<caret></selection>\n" +
|
||||
"<selection>ano<caret>ther</selection> line",
|
||||
TestFileType.TEXT);
|
||||
setupEditorProvider();
|
||||
type('A');
|
||||
executeAction("EditorDelete");
|
||||
mouse().clickAt(0, 1);
|
||||
undo();
|
||||
checkResult("someA<caret>textA<caret>some A<caret>A<caret>A<caret>line");
|
||||
undo();
|
||||
checkResult("someA<caret> textA<caret>\n" +
|
||||
"some A<caret> A<caret>\n" +
|
||||
"A<caret> line");
|
||||
undo();
|
||||
checkResult("some<caret> text<caret>\n" +
|
||||
"some <selection><caret>other</selection> <selection>text<caret></selection>\n" +
|
||||
"<selection>ano<caret>ther</selection> line");
|
||||
redo();
|
||||
checkResult("someA<caret> textA<caret>\n" +
|
||||
"some A<caret> A<caret>\n" +
|
||||
"A<caret> line");
|
||||
}
|
||||
|
||||
private void checkResult(final String text) {
|
||||
CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
checkResultByText(text);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void undo() {
|
||||
getUndoManager().undo(getTextEditor());
|
||||
}
|
||||
|
||||
private static void redo() {
|
||||
getUndoManager().redo(getTextEditor());
|
||||
}
|
||||
|
||||
private static UndoManagerImpl getUndoManager() {
|
||||
return (UndoManagerImpl) UndoManager.getInstance(ourProject);
|
||||
}
|
||||
|
||||
private static TextEditor getTextEditor() {
|
||||
return TextEditorProvider.getInstance().getTextEditor(myEditor);
|
||||
}
|
||||
|
||||
private static void setupEditorProvider() {
|
||||
getUndoManager().setEditorProvider(new CurrentEditorProvider() {
|
||||
@Override
|
||||
public FileEditor getCurrentEditor() {
|
||||
return getTextEditor();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
+19
-19
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2013 JetBrains s.r.o.
|
||||
* Copyright 2000-2014 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.
|
||||
@@ -524,27 +524,27 @@ public class VirtualFilePointerTest extends PlatformLangTestCase {
|
||||
JobLauncher.getInstance().submitToJobThread(Job.DEFAULT_PRIORITY, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ApplicationManager.getApplication().runReadAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
VirtualFile file = pointer.getFile();
|
||||
if (file != null && !file.isValid()) {
|
||||
throw new IncorrectOperationException("I've caught it. I am that good");
|
||||
}
|
||||
ApplicationManager.getApplication().runReadAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
VirtualFile file = pointer.getFile();
|
||||
if (file != null && !file.isValid()) {
|
||||
throw new IncorrectOperationException("I've caught it. I am that good");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}, new Consumer<Future>() {
|
||||
@Override
|
||||
public void consume(Future future) {
|
||||
try {
|
||||
future.get();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public void consume(Future future) {
|
||||
try {
|
||||
future.get();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.intellij.remoteServer;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.options.UnnamedConfigurable;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.remoteServer.configuration.RemoteServer;
|
||||
import com.intellij.remoteServer.configuration.ServerConfiguration;
|
||||
import com.intellij.remoteServer.configuration.deployment.DeploymentConfigurator;
|
||||
import com.intellij.remoteServer.runtime.ServerConnector;
|
||||
@@ -46,6 +47,11 @@ public abstract class ServerType<C extends ServerConfiguration> {
|
||||
@NotNull
|
||||
public abstract ServerConnector<?> createConnector(@NotNull C configuration, @NotNull ServerTaskExecutor asyncTasksExecutor);
|
||||
|
||||
@NotNull
|
||||
public ServerConnector<?> createConnector(@NotNull RemoteServer<C> server, @NotNull ServerTaskExecutor asyncTasksExecutor) {
|
||||
return createConnector(server.getConfiguration(), asyncTasksExecutor);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a non-null instance of {@link DebugConnector} if the server supports deployment in debug mode
|
||||
*/
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ public class ServerConnectionManagerImpl extends ServerConnectionManager {
|
||||
ServerConnection connection = myConnections.get(server);
|
||||
if (connection == null) {
|
||||
ServerTaskExecutorImpl executor = new ServerTaskExecutorImpl();
|
||||
connection = new ServerConnectionImpl(server, server.getType().createConnector(server.getConfiguration(), executor), this);
|
||||
connection = new ServerConnectionImpl(server, server.getType().createConnector(server, executor), this);
|
||||
myConnections.put(server, connection);
|
||||
myEventDispatcher.fireConnectionCreated(connection);
|
||||
}
|
||||
|
||||
+8
-16
@@ -18,22 +18,22 @@ package com.intellij.remoteServer.util.ssh;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.openapi.ui.ValidationInfo;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* @author michael.golubev
|
||||
*/
|
||||
public abstract class PublicSshKeyDialog extends DialogWrapper {
|
||||
public class PublicSshKeyDialog extends DialogWrapper {
|
||||
|
||||
private PublicSshKeyPanel myPanel;
|
||||
private PublicSshKeyFilePanel myPanel;
|
||||
|
||||
public PublicSshKeyDialog(@Nullable Project project, boolean textNotFile) {
|
||||
public PublicSshKeyDialog(@Nullable Project project) {
|
||||
super(project);
|
||||
setTitle("Upload Public SSH Key");
|
||||
myPanel = textNotFile ? new PublicSshKeyTextPanel() : new PublicSshKeyFilePanel();
|
||||
myPanel = new PublicSshKeyFilePanel();
|
||||
init();
|
||||
}
|
||||
|
||||
@@ -43,21 +43,13 @@ public abstract class PublicSshKeyDialog extends DialogWrapper {
|
||||
return myPanel.getMainPanel();
|
||||
}
|
||||
|
||||
private String getSshKey() {
|
||||
return myPanel.getSshKey();
|
||||
public File getSshKey() {
|
||||
return new File(myPanel.getSshKey());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected ValidationInfo doValidate() {
|
||||
return StringUtil.isEmpty(getSshKey()) ? new ValidationInfo("Specify public SSH key") : null;
|
||||
return getSshKey().isFile() ? null : new ValidationInfo("Public SSH key file does not exist");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doOKAction() {
|
||||
super.doOKAction();
|
||||
uploadSshKey(myPanel.getSshKey());
|
||||
}
|
||||
|
||||
protected abstract void uploadSshKey(String sskKey);
|
||||
}
|
||||
|
||||
+1
-3
@@ -25,7 +25,7 @@ import javax.swing.*;
|
||||
/**
|
||||
* @author michael.golubev
|
||||
*/
|
||||
public class PublicSshKeyFilePanel implements PublicSshKeyPanel {
|
||||
public class PublicSshKeyFilePanel {
|
||||
|
||||
private TextFieldWithBrowseButton myPublicSshKeyFileField;
|
||||
private HyperlinkLabel myPublicSshKeyTooltipHyperlink;
|
||||
@@ -36,7 +36,6 @@ public class PublicSshKeyFilePanel implements PublicSshKeyPanel {
|
||||
FileChooserDescriptorFactory.createSingleLocalFileDescriptor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSshKey() {
|
||||
return myPublicSshKeyFileField.getText();
|
||||
}
|
||||
@@ -47,7 +46,6 @@ public class PublicSshKeyFilePanel implements PublicSshKeyPanel {
|
||||
"Specify the location of your public SSH key file (.pub). You won’t be able to use the cloud unless you register you public key there. However, if you’ve already done that, you don’t have to specify the key now.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getMainPanel() {
|
||||
return myMainPanel;
|
||||
}
|
||||
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.remoteServer.util.ssh.PublicSshKeyTextPanel">
|
||||
<grid id="27dc6" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="2" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<constraints>
|
||||
<xy x="20" y="20" width="500" height="400"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<component id="dc5b7" class="com.intellij.ui.components.JBLabel">
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<labelFor value="b8838"/>
|
||||
<text value="Public SSH key:"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="1eb39" class="com.intellij.ui.HyperlinkLabel" binding="myPublicSshKeyTooltipHyperlink" custom-create="true">
|
||||
<constraints>
|
||||
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<iconTextGap value="2"/>
|
||||
</properties>
|
||||
</component>
|
||||
<vspacer id="b020d">
|
||||
<constraints>
|
||||
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
</vspacer>
|
||||
<component id="b379c" class="javax.swing.JTextField" binding="myPublicSshKeyTextField">
|
||||
<constraints>
|
||||
<grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
|
||||
<preferred-size width="150" height="-1"/>
|
||||
</grid>
|
||||
</constraints>
|
||||
<properties/>
|
||||
</component>
|
||||
</children>
|
||||
</grid>
|
||||
</form>
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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.remoteServer.util.ssh;
|
||||
|
||||
import com.intellij.remoteServer.util.TooltipUtil;
|
||||
import com.intellij.ui.HyperlinkLabel;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
/**
|
||||
* @author michael.golubev
|
||||
*/
|
||||
public class PublicSshKeyTextPanel implements PublicSshKeyPanel {
|
||||
|
||||
private HyperlinkLabel myPublicSshKeyTooltipHyperlink;
|
||||
private JTextField myPublicSshKeyTextField;
|
||||
private JPanel myMainPanel;
|
||||
|
||||
private void createUIComponents() {
|
||||
myPublicSshKeyTooltipHyperlink =
|
||||
TooltipUtil.createTooltip(
|
||||
"Copy your public SSH key here. You won’t be able to use the cloud unless you register you public key there. However, if you’ve already done that, you don’t have to specify the key now.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSshKey() {
|
||||
return myPublicSshKeyTextField.getText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getMainPanel() {
|
||||
return myMainPanel;
|
||||
}
|
||||
}
|
||||
+5
-3
@@ -15,18 +15,20 @@
|
||||
*/
|
||||
package com.intellij.remoteServer.util.ssh;
|
||||
|
||||
import com.intellij.remoteServer.configuration.ServerConfiguration;
|
||||
import com.intellij.remoteServer.configuration.RemoteServer;
|
||||
import com.intellij.remoteServer.util.CloudServerRuntimeInstance;
|
||||
import com.intellij.remoteServer.util.ServerRuntimeException;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* @author michael.golubev
|
||||
*/
|
||||
public interface SshKeyAwareServerRuntime {
|
||||
|
||||
void addSshKey(String sshKey) throws ServerRuntimeException;
|
||||
void addSshKey(File sshKey) throws ServerRuntimeException;
|
||||
|
||||
CloudServerRuntimeInstance asCloudServerRuntime();
|
||||
|
||||
ServerConfiguration getConfiguration();
|
||||
RemoteServer getServer();
|
||||
}
|
||||
|
||||
+10
-34
@@ -20,7 +20,6 @@ import com.intellij.notification.Notification;
|
||||
import com.intellij.notification.NotificationListener;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.MessageType;
|
||||
import com.intellij.remoteServer.configuration.ServerConfiguration;
|
||||
import com.intellij.remoteServer.runtime.ServerConnection;
|
||||
import com.intellij.remoteServer.runtime.ServerConnectionManager;
|
||||
import com.intellij.remoteServer.runtime.deployment.DeploymentLogManager;
|
||||
@@ -30,21 +29,15 @@ import com.intellij.remoteServer.runtime.ui.RemoteServersView;
|
||||
import com.intellij.remoteServer.util.*;
|
||||
import com.intellij.util.ParameterizedRunnable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.event.HyperlinkEvent;
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* @author michael.golubev
|
||||
*/
|
||||
public class SshKeyChecker {
|
||||
|
||||
private final boolean myTextNotFile;
|
||||
|
||||
public SshKeyChecker(boolean textNotFile) {
|
||||
myTextNotFile = textNotFile;
|
||||
}
|
||||
|
||||
private static boolean isSshKeyErrorMessage(String errorMessage) {
|
||||
return errorMessage.contains("Could not read from remote repository") || errorMessage.contains("The remote end hung up unexpectedly");
|
||||
}
|
||||
@@ -70,17 +63,6 @@ public class SshKeyChecker {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ServerConnection findServerConnection(SshKeyAwareServerRuntime serverRuntime) {
|
||||
ServerConfiguration configuration = serverRuntime.getConfiguration();
|
||||
for (ServerConnection connection : ServerConnectionManager.getInstance().getConnections()) {
|
||||
if (connection.getServer().getConfiguration() == configuration) {
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private class ServerHandler extends HandlerBase {
|
||||
|
||||
private final CloudNotifier myNotifier;
|
||||
@@ -114,7 +96,7 @@ public class SshKeyChecker {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void uploadKey(final String sskKey) {
|
||||
protected void uploadKey(final File sskKey) {
|
||||
new CloudConnectionTask(myProject, "Uploading SSH key", myConnectionTask.getServer()) {
|
||||
|
||||
@Override
|
||||
@@ -178,7 +160,7 @@ public class SshKeyChecker {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void uploadKey(final String sskKey) {
|
||||
protected void uploadKey(final File sskKey) {
|
||||
new CloudRuntimeTask(getProject(), "Uploading SSH key") {
|
||||
|
||||
@Override
|
||||
@@ -212,10 +194,7 @@ public class SshKeyChecker {
|
||||
}
|
||||
|
||||
private void redeploy() {
|
||||
final ServerConnection connection = findServerConnection(myServerRuntime);
|
||||
if (connection == null) {
|
||||
return;
|
||||
}
|
||||
final ServerConnection connection = ServerConnectionManager.getInstance().getOrCreateConnection(myServerRuntime.getServer());
|
||||
|
||||
final RemoteServersView view = RemoteServersView.getInstance(myDeploymentTask.getProject());
|
||||
view.showServerConnection(connection);
|
||||
@@ -231,19 +210,16 @@ public class SshKeyChecker {
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class HandlerBase {
|
||||
private static abstract class HandlerBase {
|
||||
|
||||
protected void chooseKey() {
|
||||
new PublicSshKeyDialog(getProject(), myTextNotFile) {
|
||||
|
||||
@Override
|
||||
protected void uploadSshKey(String sskKey) {
|
||||
uploadKey(sskKey);
|
||||
}
|
||||
}.show();
|
||||
PublicSshKeyDialog dialog = new PublicSshKeyDialog(getProject());
|
||||
if (dialog.showAndGet()) {
|
||||
uploadKey(dialog.getSshKey());
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void uploadKey(String sskKey);
|
||||
protected abstract void uploadKey(File sskKeyFile);
|
||||
|
||||
protected abstract Project getProject();
|
||||
}
|
||||
|
||||
@@ -17,9 +17,7 @@ package com.intellij.testFramework;
|
||||
|
||||
import com.intellij.ide.DataManager;
|
||||
import com.intellij.openapi.actionSystem.*;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.RangeMarker;
|
||||
import com.intellij.openapi.editor.*;
|
||||
import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
|
||||
import com.intellij.openapi.editor.actionSystem.EditorActionManager;
|
||||
import com.intellij.openapi.editor.actionSystem.TypedAction;
|
||||
@@ -39,6 +37,9 @@ import org.jetbrains.annotations.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
/**
|
||||
* User: Maxim.Mossienko
|
||||
* Date: 15.03.2010
|
||||
@@ -284,6 +285,35 @@ public class EditorTestUtil {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void verifyCaretAndSelectionState(Editor editor, CaretsState caretState) {
|
||||
CaretModel caretModel = editor.getCaretModel();
|
||||
List<com.intellij.openapi.editor.Caret> allCarets = new ArrayList<com.intellij.openapi.editor.Caret>(caretModel.getAllCarets());
|
||||
assertEquals("Unexpected number of carets", caretState.carets.size(), allCarets.size());
|
||||
for (int i = 0; i < caretState.carets.size(); i++) {
|
||||
String caretDescription = caretState.carets.size() == 1 ? "" : "caret " + (i + 1) + "/" + caretState.carets.size() + " ";
|
||||
com.intellij.openapi.editor.Caret currentCaret = allCarets.get(i);
|
||||
LogicalPosition actualCaretPosition = currentCaret.getLogicalPosition();
|
||||
LogicalPosition actualSelectionStart = editor.offsetToLogicalPosition(currentCaret.getSelectionStart());
|
||||
LogicalPosition actualSelectionEnd = editor.offsetToLogicalPosition(currentCaret.getSelectionEnd());
|
||||
EditorTestUtil.Caret expected = caretState.carets.get(i);
|
||||
if (expected.offset != null) {
|
||||
LogicalPosition expectedCaretPosition = editor.offsetToLogicalPosition(expected.offset);
|
||||
assertEquals(caretDescription + "unexpected caret position", expectedCaretPosition, actualCaretPosition);
|
||||
}
|
||||
if (expected.selection != null) {
|
||||
LogicalPosition expectedSelectionStart = editor.offsetToLogicalPosition(expected.selection.getStartOffset());
|
||||
LogicalPosition expectedSelectionEnd = editor.offsetToLogicalPosition(expected.selection.getEndOffset());
|
||||
|
||||
assertEquals(caretDescription + "unexpected selection start", expectedSelectionStart, actualSelectionStart);
|
||||
assertEquals(caretDescription + "unexpected selection end", expectedSelectionEnd, actualSelectionEnd);
|
||||
}
|
||||
else {
|
||||
assertFalse(caretDescription + "should has no selection, but was: (" + actualSelectionStart + ", " + actualSelectionEnd + ")",
|
||||
currentCaret.hasSelection());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void enableMultipleCarets() {
|
||||
Registry.get("editor.allow.multiple.carets").setValue(true);
|
||||
}
|
||||
|
||||
@@ -201,18 +201,13 @@ public abstract class ComparisonPolicy {
|
||||
}
|
||||
|
||||
private Object getWrapper(String line) {
|
||||
line = line.trim();
|
||||
char[] chars = new char[line.length()];
|
||||
line.getChars(0, line.length(), chars, 0);
|
||||
char[] result = new char[chars.length];
|
||||
int resultLength = 0;
|
||||
for (int i = 0; i < chars.length; i++) {
|
||||
char aChar = chars[i];
|
||||
if (Character.isWhitespace(aChar)) continue;
|
||||
result[resultLength] = aChar;
|
||||
resultLength++;
|
||||
StringBuilder builder = new StringBuilder(line.length());
|
||||
for (int i = 0; i < line.length(); i++) {
|
||||
char aChar = line.charAt(i);
|
||||
if (StringUtil.isWhiteSpace(aChar)) continue;
|
||||
builder.append(aChar);
|
||||
}
|
||||
return new String(result, 0, resultLength);
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -156,7 +156,9 @@ public class DiffFragmentBuilder {
|
||||
}
|
||||
|
||||
private static String concatenate(String[] strings, int start, int end) {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
int len = 0;
|
||||
for (int i = start - 1; i < end; i++) len += strings[i] == null ? 0 : strings[i].length();
|
||||
StringBuilder buffer = new StringBuilder(len);
|
||||
for (int i = start - 1; i < end; i++) buffer.append(strings[i]);
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
@@ -232,23 +232,33 @@ public class Util {
|
||||
}
|
||||
|
||||
public static DiffFragment concatenate(DiffFragment[] line, int from, int to) {
|
||||
StringBuffer buffer1 = new StringBuffer();
|
||||
StringBuffer buffer2 = new StringBuffer();
|
||||
String[] data1 = new String[to - from];
|
||||
String[] data2 = new String[to - from];
|
||||
|
||||
int len1 = 0;
|
||||
int len2 = 0;
|
||||
boolean isEqual = true;
|
||||
for (int j = from; j < to; j++) {
|
||||
DiffFragment fragment = line[j];
|
||||
isEqual &= fragment.isEqual();
|
||||
String text1 = fragment.getText1();
|
||||
String text2 = fragment.getText2();
|
||||
if (text1 != null) buffer1.append(text1);
|
||||
if (text2 != null) buffer2.append(text2);
|
||||
for (int i = 0; i < to - from; i++) {
|
||||
isEqual &= line[i + from].isEqual();
|
||||
data1[i] = line[i + from].getText1();
|
||||
data2[i] = line[i + from].getText2();
|
||||
len1 += data1[i] == null ? 0 : data1[i].length();
|
||||
len2 += data2[i] == null ? 0 : data2[i].length();
|
||||
}
|
||||
|
||||
StringBuilder buffer1 = new StringBuilder(len1);
|
||||
StringBuilder buffer2 = new StringBuilder(len2);
|
||||
for (int i = 0; i < to - from; i++) {
|
||||
if (data1[i] != null) buffer1.append(data1[i]);
|
||||
if (data2[i] != null) buffer2.append(data2[i]);
|
||||
}
|
||||
|
||||
String text1 = notEmptyContent(buffer1);
|
||||
String text2 = notEmptyContent(buffer2);
|
||||
return isEqual ? DiffFragment.unchanged(text1, text2) : new DiffFragment(text1, text2);
|
||||
}
|
||||
|
||||
private static String notEmptyContent(StringBuffer buffer) {
|
||||
private static String notEmptyContent(StringBuilder buffer) {
|
||||
return buffer.length() > 0 ? buffer.toString() : null;
|
||||
}
|
||||
|
||||
|
||||
@@ -199,8 +199,8 @@ public class ByWord implements DiffPolicy{
|
||||
int lastIndex = myFragments.size() - 1;
|
||||
DiffFragment prevFragment = myFragments.get(lastIndex);
|
||||
if (prevFragment.isEqual()) {
|
||||
prevFragment.appendText1(fragment.getText1());
|
||||
prevFragment.appendText2(fragment.getText2());
|
||||
prevFragment.appendText1(text1);
|
||||
prevFragment.appendText2(text2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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.diff.impl.processing;
|
||||
|
||||
import com.intellij.openapi.diff.impl.fragments.LineFragment;
|
||||
import com.intellij.openapi.diff.impl.util.TextDiffTypeEnum;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class DummyDiffFragmentsProcessor {
|
||||
public ArrayList<LineFragment> process(String text1, String text2) {
|
||||
ArrayList<LineFragment> lineFragments = new ArrayList<LineFragment>();
|
||||
|
||||
if (text1.isEmpty() && text2.isEmpty()) {
|
||||
return lineFragments;
|
||||
}
|
||||
|
||||
TextDiffTypeEnum type;
|
||||
if (text1.isEmpty()) {
|
||||
type = TextDiffTypeEnum.INSERT;
|
||||
}
|
||||
else if (text2.isEmpty()) {
|
||||
type = TextDiffTypeEnum.DELETED;
|
||||
}
|
||||
else {
|
||||
type = TextDiffTypeEnum.CHANGED;
|
||||
}
|
||||
lineFragments.add(new LineFragment(0, countLines(text1), 0, countLines(text2), type, new TextRange(0, text1.length()),
|
||||
new TextRange(0, text2.length())));
|
||||
|
||||
return lineFragments;
|
||||
}
|
||||
|
||||
private static int countLines(String text) {
|
||||
if (text == null || text.isEmpty()) return 0;
|
||||
int count = StringUtil.countNewLines(text);
|
||||
if (text.charAt(text.length() - 1) != '\n') count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.util;
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.io.URLUtil;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -38,11 +39,15 @@ public class ResourceUtil {
|
||||
}
|
||||
|
||||
public static URL getResource(@NotNull Class loaderClass, @NonNls @NotNull String basePath, @NonNls @NotNull String fileName) {
|
||||
if (basePath.endsWith("/")) basePath = basePath.substring(0, basePath.length() - 1);
|
||||
return getResource(loaderClass.getClassLoader(), basePath, fileName);
|
||||
}
|
||||
|
||||
final List<String> bundles = calculateBundleNames(basePath, Locale.getDefault());
|
||||
public static URL getResource(@NotNull ClassLoader loader, @NonNls @NotNull String basePath, @NonNls @NotNull String fileName) {
|
||||
String fixedPath = StringUtil.trimStart(StringUtil.trimEnd(basePath, "/"), "/");
|
||||
|
||||
List<String> bundles = calculateBundleNames(fixedPath, Locale.getDefault());
|
||||
for (String bundle : bundles) {
|
||||
URL url = loaderClass.getResource(bundle + "/" + fileName);
|
||||
URL url = loader.getResource(bundle + "/" + fileName);
|
||||
if (url == null) continue;
|
||||
|
||||
try {
|
||||
@@ -55,7 +60,7 @@ public class ResourceUtil {
|
||||
return url;
|
||||
}
|
||||
|
||||
return loaderClass.getResource(basePath + "/" + fileName);
|
||||
return loader.getResource(fixedPath + "/" + fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1970,15 +1970,20 @@ public class ContainerUtil extends ContainerUtilRt {
|
||||
* - slower modification in highly contented case (which is the kind of situation you shouldn't use COWAL anyway)
|
||||
*/
|
||||
@NotNull
|
||||
public static <T> ConcurrentList<T> createLockFreeCopyOnWriteList() {
|
||||
return new LockFreeCopyOnWriteArrayList<T>();
|
||||
public static <T> List<T> createLockFreeCopyOnWriteList() {
|
||||
return createConcurrentList();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static <T> ConcurrentList<T> createLockFreeCopyOnWriteList(@NotNull Collection<? extends T> c) {
|
||||
public static <T> List<T> createLockFreeCopyOnWriteList(@NotNull Collection<? extends T> c) {
|
||||
return new LockFreeCopyOnWriteArrayList<T>(c);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static <T> ConcurrentList<T> createConcurrentList() {
|
||||
return new LockFreeCopyOnWriteArrayList<T>();
|
||||
}
|
||||
|
||||
public static <T> void addIfNotNull(@Nullable T element, @NotNull Collection<T> result) {
|
||||
ContainerUtilRt.addIfNotNull(element, result);
|
||||
}
|
||||
|
||||
@@ -15,13 +15,11 @@
|
||||
*/
|
||||
package com.intellij.util.containers;
|
||||
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import gnu.trove.TObjectHashingStrategy;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.RandomAccess;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
// have to extend ArrayList because otherwise the spliterator() methods declared in Set and List are in conflict
|
||||
public class OrderedSet<T> extends ArrayList<T> implements Set<T>, RandomAccess {
|
||||
@@ -40,6 +38,33 @@ public class OrderedSet<T> extends ArrayList<T> implements Set<T>, RandomAccess
|
||||
myHashSet = new OpenTHashSet<T>(capacity, hashingStrategy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(@NotNull Collection<?> c) {
|
||||
boolean removed = false;
|
||||
for (Object o : c) {
|
||||
removed |= remove(o);
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(@NotNull Collection<?> c) {
|
||||
boolean removed = false;
|
||||
for (int i = size() - 1; i >= 0; i--) {
|
||||
Object o = get(i);
|
||||
if (!c.contains(o)) {
|
||||
removed |= remove(o);
|
||||
}
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<T> subList(int fromIndex, int toIndex) {
|
||||
throw new IncorrectOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return myHashSet.contains(o);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package com.intellij.util.diff;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.util.text.LineTokenizer;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.Enumerator;
|
||||
@@ -24,6 +25,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
|
||||
/**
|
||||
* @author dyoma
|
||||
@@ -49,15 +51,41 @@ public class Diff {
|
||||
final int startShift = getStartShift(objects1, objects2);
|
||||
final int endCut = getEndCut(objects1, objects2, startShift);
|
||||
|
||||
ChangeBuilder builder = new ChangeBuilder(startShift);
|
||||
int trimmedLength1 = objects1.length - startShift - endCut;
|
||||
int trimmedLength2 = objects2.length - startShift - endCut;
|
||||
if (trimmedLength1 == 0 || trimmedLength2 == 0) {
|
||||
if (trimmedLength1 != 0 || trimmedLength2 != 0) {
|
||||
builder.addChange(trimmedLength1, trimmedLength2);
|
||||
}
|
||||
return builder.getFirstChange();
|
||||
}
|
||||
|
||||
Enumerator<T> enumerator = new Enumerator<T>(objects1.length + objects2.length, ContainerUtil.<T>canonicalStrategy());
|
||||
int[] ints1 = enumerator.enumerate(objects1, startShift, endCut);
|
||||
int[] ints2 = enumerator.enumerate(objects2, startShift, endCut);
|
||||
Reindexer reindexer = new Reindexer();
|
||||
Reindexer reindexer = new Reindexer(); // discard unique elements, that have no chance to be matched
|
||||
int[][] discarded = reindexer.discardUnique(ints1, ints2);
|
||||
IntLCS intLCS = new IntLCS(discarded[0], discarded[1]);
|
||||
intLCS.execute();
|
||||
ChangeBuilder builder = new ChangeBuilder(startShift);
|
||||
reindexer.reindex(intLCS.getPaths(), builder);
|
||||
|
||||
if (discarded[0].length == 0 && discarded[1].length == 0) {
|
||||
// assert trimmedLength > 0
|
||||
builder.addChange(ints1.length, ints2.length);
|
||||
return builder.getFirstChange();
|
||||
}
|
||||
|
||||
BitSet[] changes;
|
||||
if (Registry.is("diff.patience.alg")) {
|
||||
PatienceIntLCS patienceIntLCS = new PatienceIntLCS(discarded[0], discarded[1]);
|
||||
patienceIntLCS.execute();
|
||||
changes = patienceIntLCS.getChanges();
|
||||
}
|
||||
else {
|
||||
IntLCS intLCS = new IntLCS(discarded[0], discarded[1]);
|
||||
intLCS.execute();
|
||||
changes = intLCS.getChanges();
|
||||
}
|
||||
|
||||
reindexer.reindex(changes, builder);
|
||||
return builder.getFirstChange();
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package com.intellij.util.diff;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.BitSet;
|
||||
|
||||
/**
|
||||
* @author dyoma
|
||||
@@ -24,6 +25,9 @@ class IntLCS {
|
||||
private final int[] myFirst;
|
||||
private final int[] mySecond;
|
||||
|
||||
private final int myStart1;
|
||||
private final int myStart2;
|
||||
|
||||
private final LinkedDiffPaths myPathsMatrix;
|
||||
private final int[] myPrevPathKey;
|
||||
private int[] myPrevEnds;
|
||||
@@ -31,11 +35,23 @@ class IntLCS {
|
||||
private final int myMaxX;
|
||||
private final int myMaxY;
|
||||
|
||||
private final BitSet myChanges1;
|
||||
private final BitSet myChanges2;
|
||||
|
||||
public IntLCS(int[] first, int[] second) {
|
||||
this(first, second, 0, first.length, 0, second.length, new BitSet(first.length), new BitSet(second.length));
|
||||
}
|
||||
|
||||
public IntLCS(int[] first, int[] second, int start1, int count1, int start2, int count2, BitSet changes1, BitSet changes2) {
|
||||
myFirst = first;
|
||||
mySecond = second;
|
||||
myMaxX = myFirst.length;
|
||||
myMaxY = mySecond.length;
|
||||
myStart1 = start1;
|
||||
myStart2 = start2;
|
||||
myMaxX = count1;
|
||||
myMaxY = count2;
|
||||
|
||||
myChanges1 = changes1;
|
||||
myChanges2 = changes2;
|
||||
|
||||
myPathsMatrix = new LinkedDiffPaths(myMaxX, myMaxY);
|
||||
myPrevPathKey = new int[myMaxX + myMaxY + 1];
|
||||
@@ -82,7 +98,10 @@ class IntLCS {
|
||||
}
|
||||
}
|
||||
myCurrentEnds[k + myMaxY] = end;
|
||||
if (k == myMaxX - myMaxY && end == myMaxX) return d;
|
||||
if (k == myMaxX - myMaxY && end == myMaxX) {
|
||||
myPathsMatrix.applyChanges(myStart1, myStart2, myChanges1, myChanges2);
|
||||
return d;
|
||||
}
|
||||
}
|
||||
int[] temps = myCurrentEnds;
|
||||
myCurrentEnds = myPrevEnds;
|
||||
@@ -91,8 +110,8 @@ class IntLCS {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
public LinkedDiffPaths getPaths() {
|
||||
return myPathsMatrix;
|
||||
public BitSet[] getChanges() {
|
||||
return new BitSet[]{myChanges1, myChanges2};
|
||||
}
|
||||
|
||||
private int findDiagonalEnd(int prevDiagonal, int prevEnd, boolean isVertical) {
|
||||
@@ -122,7 +141,7 @@ class IntLCS {
|
||||
|
||||
private int skipEquals(int x, int y) {
|
||||
int skipped = 0;
|
||||
while (x < myMaxX && y < myMaxY && myFirst[x] == mySecond[y]) {
|
||||
while (x < myMaxX && y < myMaxY && myFirst[myStart1 + x] == mySecond[myStart2 + y]) {
|
||||
skipped += 1;
|
||||
x++;
|
||||
y++;
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package com.intellij.util.diff;
|
||||
|
||||
import java.util.BitSet;
|
||||
|
||||
/**
|
||||
* @author dyoma
|
||||
*/
|
||||
@@ -30,10 +32,38 @@ final class LinkedDiffPaths {
|
||||
private static final int DISTANCE_MASK = ~VERTICAL_DIRECTION_FLAG;
|
||||
|
||||
public LinkedDiffPaths(int maxX, int maxY) {
|
||||
myMaxX = maxX-1;
|
||||
myMaxY = maxY-1;
|
||||
myMaxX = maxX;
|
||||
myMaxY = maxY;
|
||||
}
|
||||
|
||||
public void applyChanges(final int start1, final int start2, final BitSet changes1, final BitSet changes2) {
|
||||
decodePath(new LCSBuilder() {
|
||||
int x = myMaxX;
|
||||
int y = myMaxY;
|
||||
|
||||
@Override
|
||||
public void addEqual(int length) {
|
||||
x -= length;
|
||||
y -= length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addChange(int first, int second) {
|
||||
if (first > 0) {
|
||||
changes1.set(start1 + x - first, start1 + x);
|
||||
x -= first;
|
||||
}
|
||||
if (second > 0) {
|
||||
changes2.set(start2 + y - second, start2 + y);
|
||||
y -= second;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Path is decoded in reverse order (from the last change to the first)
|
||||
*/
|
||||
public <Builder extends LCSBuilder> Builder decodePath(Builder builder) {
|
||||
Decoder decoder = new Decoder(getXSize(), getYSize(), builder);
|
||||
int index = myCornerIndex;
|
||||
@@ -47,11 +77,11 @@ final class LinkedDiffPaths {
|
||||
}
|
||||
|
||||
public int getXSize() {
|
||||
return myMaxX + 1;
|
||||
return myMaxX;
|
||||
}
|
||||
|
||||
public int getYSize() {
|
||||
return myMaxY + 1;
|
||||
return myMaxY;
|
||||
}
|
||||
|
||||
public int encodeStep(int x, int y, int diagLength, boolean afterVertical, int prevIndex) throws FilesTooBigForDiffException {
|
||||
@@ -61,7 +91,7 @@ final class LinkedDiffPaths {
|
||||
|
||||
myPrevSteps[position] = prevIndex;
|
||||
mySteps[position] = encodedPath;
|
||||
if (x == myMaxX && y == myMaxY) myCornerIndex = position;
|
||||
if (x == myMaxX - 1 && y == myMaxY - 1) myCornerIndex = position;
|
||||
return position;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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.util.diff;
|
||||
|
||||
import java.util.BitSet;
|
||||
|
||||
public class PatienceIntLCS {
|
||||
private final int[] myFirst;
|
||||
private final int[] mySecond;
|
||||
|
||||
private final int myStart1;
|
||||
private final int myStart2;
|
||||
private final int myCount1;
|
||||
private final int myCount2;
|
||||
|
||||
private final BitSet myChanges1;
|
||||
private final BitSet myChanges2;
|
||||
|
||||
public PatienceIntLCS(int[] first, int[] second) {
|
||||
this(first, second, 0, first.length, 0, second.length, new BitSet(first.length), new BitSet(second.length));
|
||||
}
|
||||
|
||||
public PatienceIntLCS(int[] first, int[] second, int start1, int count1, int start2, int count2, BitSet changes1, BitSet changes2) {
|
||||
myFirst = first;
|
||||
mySecond = second;
|
||||
myStart1 = start1;
|
||||
myStart2 = start2;
|
||||
myCount1 = count1;
|
||||
myCount2 = count2;
|
||||
|
||||
myChanges1 = changes1;
|
||||
myChanges2 = changes2;
|
||||
}
|
||||
|
||||
public void execute() throws FilesTooBigForDiffException {
|
||||
if (myCount1 == 0 && myCount2 == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (myCount1 == 0 || myCount2 == 0) {
|
||||
addChange(myStart1, myCount1, myStart2, myCount2);
|
||||
return;
|
||||
}
|
||||
|
||||
int startOffset = matchForward(0, 0);
|
||||
int start1 = myStart1 + startOffset;
|
||||
int start2 = myStart2 + startOffset;
|
||||
|
||||
int endOffset = matchBackward(myStart1 + myCount1 - 1, myStart2 + myCount2 - 1, start1, start2);
|
||||
int count1 = myCount1 - startOffset - endOffset;
|
||||
int count2 = myCount2 - startOffset - endOffset;
|
||||
|
||||
if (count1 == 0 || count2 == 0) {
|
||||
addChange(start1, count1, start2, count2);
|
||||
}
|
||||
else {
|
||||
UniqueLCS uniqueLCS = new UniqueLCS(myFirst, mySecond, start1, count1, start2, count2);
|
||||
int[][] matching = uniqueLCS.execute();
|
||||
|
||||
if (matching == null) {
|
||||
IntLCS intLCS = new IntLCS(myFirst, mySecond, start1, count1, start2, count2, myChanges1, myChanges2);
|
||||
intLCS.execute();
|
||||
}
|
||||
else {
|
||||
int s1, s2, c1, c2;
|
||||
int matched = matching[0].length;
|
||||
assert matched > 0;
|
||||
|
||||
PatienceIntLCS patienceDiff =
|
||||
new PatienceIntLCS(myFirst, mySecond, start1, matching[0][0], start2, matching[1][0], myChanges1, myChanges2);
|
||||
patienceDiff.execute();
|
||||
|
||||
for (int i = 1; i < matching[0].length; i++) {
|
||||
s1 = matching[0][i - 1] + 1;
|
||||
s2 = matching[1][i - 1] + 1;
|
||||
|
||||
c1 = matching[0][i] - s1;
|
||||
c2 = matching[1][i] - s2;
|
||||
|
||||
if (c1 > 0 || c2 > 0) {
|
||||
patienceDiff = new PatienceIntLCS(myFirst, mySecond, start1 + s1, c1, start2 + s2, c2, myChanges1, myChanges2);
|
||||
patienceDiff.execute();
|
||||
}
|
||||
}
|
||||
|
||||
if (matching[0][matched - 1] == count1 - 1) {
|
||||
s1 = count1 - 1;
|
||||
c1 = 0;
|
||||
}
|
||||
else {
|
||||
s1 = matching[0][matched - 1] + 1;
|
||||
c1 = count1 - s1;
|
||||
}
|
||||
if (matching[1][matched - 1] == count2 - 1) {
|
||||
s2 = count2 - 1;
|
||||
c2 = 0;
|
||||
}
|
||||
else {
|
||||
s2 = matching[1][matched - 1] + 1;
|
||||
c2 = count2 - s2;
|
||||
}
|
||||
|
||||
patienceDiff = new PatienceIntLCS(myFirst, mySecond, start1 + s1, c1, start2 + s2, c2, myChanges1, myChanges2);
|
||||
patienceDiff.execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int matchForward(int offset1, int offset2) {
|
||||
final int size = Math.min(myCount1 - offset1, myCount2 - offset2);
|
||||
int idx = 0;
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (!(myFirst[offset1 + i] == mySecond[offset2 + i])) break;
|
||||
++idx;
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
private int matchBackward(int offset1, int offset2, int processedOffset1, int processedOffset2) {
|
||||
final int size = Math.min(offset1 - processedOffset1 - 1, offset2 - processedOffset2 - 1);
|
||||
int idx = 0;
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (!(myFirst[offset1 - i] == mySecond[offset2 - i])) break;
|
||||
++idx;
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
public void addChange(int start1, int count1, int start2, int count2) {
|
||||
myChanges1.set(start1, start1 + count1);
|
||||
myChanges2.set(start2, start2 + count2);
|
||||
}
|
||||
|
||||
public BitSet[] getChanges() {
|
||||
return new BitSet[]{myChanges1, myChanges2};
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package com.intellij.util.diff;
|
||||
import gnu.trove.TIntArrayList;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.BitSet;
|
||||
|
||||
/**
|
||||
* @author dyoma
|
||||
@@ -25,6 +26,7 @@ import java.util.Arrays;
|
||||
class Reindexer {
|
||||
private final int[][] myOldIndecies = new int[2][];
|
||||
private final int[] myOriginalLengths = new int[]{-1, -1};
|
||||
private final int[] myDiscardedLengths = new int[]{-1, -1};
|
||||
|
||||
public int[][] discardUnique(int[] ints1, int[] ints2) {
|
||||
int[] discarded1 = discard(ints2, ints1, 0);
|
||||
@@ -34,6 +36,8 @@ class Reindexer {
|
||||
void idInit(int length1, int length2) {
|
||||
myOriginalLengths[0] = length1;
|
||||
myOriginalLengths[1] = length2;
|
||||
myDiscardedLengths[0] = length1;
|
||||
myDiscardedLengths[1] = length2;
|
||||
for (int j = 0; j < 2; j++) {
|
||||
int originalLength = myOriginalLengths[j];
|
||||
myOldIndecies[j] = new int[originalLength];
|
||||
@@ -59,6 +63,7 @@ class Reindexer {
|
||||
}
|
||||
}
|
||||
myOldIndecies[arrayIndex] = oldIndecies.toNativeArray();
|
||||
myDiscardedLengths[arrayIndex] = discarded.size();
|
||||
return discarded.toNativeArray();
|
||||
}
|
||||
|
||||
@@ -69,67 +74,77 @@ class Reindexer {
|
||||
return sorted1;
|
||||
}
|
||||
|
||||
public void reindex(LinkedDiffPaths paths, LCSBuilder builder) {
|
||||
final boolean[] changes1 = new boolean[myOriginalLengths[0]];
|
||||
final boolean[] changes2 = new boolean[myOriginalLengths[1]];
|
||||
Arrays.fill(changes1, true);
|
||||
Arrays.fill(changes2, true);
|
||||
paths.decodePath(new LCSBuilder() {
|
||||
private int x = myOldIndecies[0].length - 1;
|
||||
private int y = myOldIndecies[1].length - 1;
|
||||
private int originalX = myOriginalLengths[0] - 1;
|
||||
private int originalY = myOriginalLengths[1] - 1;
|
||||
public void reindex(BitSet[] discardedChanges, LCSBuilder builder) {
|
||||
BitSet changes1 = new BitSet(myOriginalLengths[0]);
|
||||
BitSet changes2 = new BitSet(myOriginalLengths[1]);
|
||||
|
||||
@Override
|
||||
public void addChange(int first, int second) {
|
||||
x -= first;
|
||||
y -= second;
|
||||
originalX = markChanged(changes1, originalX, myOldIndecies[0], x);
|
||||
originalY = markChanged(changes2, originalY, myOldIndecies[1], y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addEqual(int length) {
|
||||
for (int i = length; i > 0; i--) {
|
||||
originalX = markChanged(changes1, originalX, myOldIndecies[0], x);
|
||||
originalY = markChanged(changes2, originalY, myOldIndecies[1], y);
|
||||
x--;
|
||||
y--;
|
||||
changes1[originalX] = false;
|
||||
changes2[originalY] = false;
|
||||
originalX--;
|
||||
originalY--;
|
||||
}
|
||||
}
|
||||
});
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
while (x < changes1.length && y < changes2.length) {
|
||||
while (x < myDiscardedLengths[0] || y < myDiscardedLengths[1]) {
|
||||
if ((x < myDiscardedLengths[0] && y < myDiscardedLengths[1]) && !discardedChanges[0].get(x) && !discardedChanges[1].get(y)) {
|
||||
x = increment(myOldIndecies[0], x, changes1, myOriginalLengths[0]);
|
||||
y = increment(myOldIndecies[1], y, changes2, myOriginalLengths[1]);
|
||||
continue;
|
||||
}
|
||||
if (discardedChanges[0].get(x)) {
|
||||
changes1.set(getOriginal(myOldIndecies[0], x));
|
||||
x = increment(myOldIndecies[0], x, changes1, myOriginalLengths[0]);
|
||||
continue;
|
||||
}
|
||||
if (discardedChanges[1].get(y)) {
|
||||
changes2.set(getOriginal(myOldIndecies[1], y));
|
||||
y = increment(myOldIndecies[1], y, changes2, myOriginalLengths[1]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (myDiscardedLengths[0] == 0) {
|
||||
changes1.set(0, myOriginalLengths[0]);
|
||||
}
|
||||
else {
|
||||
changes1.set(0, myOldIndecies[0][0]);
|
||||
}
|
||||
if (myDiscardedLengths[1] == 0) {
|
||||
changes2.set(0, myOriginalLengths[1]);
|
||||
}
|
||||
else {
|
||||
changes2.set(0, myOldIndecies[1][0]);
|
||||
}
|
||||
|
||||
x = 0;
|
||||
y = 0;
|
||||
while (x < myOriginalLengths[0] && y < myOriginalLengths[1]) {
|
||||
int startX = x;
|
||||
while (x < changes1.length && y < changes2.length && !changes1[x] && !changes2[y]) {
|
||||
while (x < myOriginalLengths[0] && y < myOriginalLengths[1] && !changes1.get(x) && !changes2.get(y)) {
|
||||
x++;
|
||||
y++;
|
||||
}
|
||||
if (x> startX) builder.addEqual(x - startX);
|
||||
int dx = 0;
|
||||
int dy = 0;
|
||||
while (x < changes1.length && changes1[x]) {
|
||||
while (x < myOriginalLengths[0] && changes1.get(x)) {
|
||||
dx++;
|
||||
x++;
|
||||
}
|
||||
while (y < changes2.length && changes2[y]) {
|
||||
while (y < myOriginalLengths[1] && changes2.get(y)) {
|
||||
dy++;
|
||||
y++;
|
||||
}
|
||||
if (dx != 0 || dy != 0) builder.addChange(dx, dy);
|
||||
}
|
||||
if (x != changes1.length || y != changes2.length)
|
||||
builder.addChange(changes1.length - x, changes2.length - y);
|
||||
if (x != myOriginalLengths[0] || y != myOriginalLengths[1]) builder.addChange(myOriginalLengths[0] - x, myOriginalLengths[1] - y);
|
||||
}
|
||||
|
||||
private int markChanged(final boolean[] changes, int from, int[] oldIndecies, int newTo) {
|
||||
int oldTo = newTo != -1 ? oldIndecies[newTo] : -1;
|
||||
for (int i = from; i > oldTo; i--) changes[i] = true;
|
||||
return oldTo;
|
||||
private int getOriginal(int[] indexes, int i) {
|
||||
return indexes[i];
|
||||
}
|
||||
|
||||
private int increment(int[] indexes, int i, BitSet set, int length) {
|
||||
if (i + 1 < indexes.length) {
|
||||
set.set(indexes[i] + 1, indexes[i + 1]);
|
||||
}
|
||||
else {
|
||||
set.set(indexes[i] + 1, length);
|
||||
}
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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.util.diff;
|
||||
|
||||
import gnu.trove.TIntIntHashMap;
|
||||
|
||||
public class UniqueLCS {
|
||||
private final int[] myFirst;
|
||||
private final int[] mySecond;
|
||||
|
||||
private final int myStart1;
|
||||
private final int myStart2;
|
||||
private final int myCount1;
|
||||
private final int myCount2;
|
||||
|
||||
public UniqueLCS(int[] first, int[] second) {
|
||||
this(first, second, 0, first.length, 0, second.length);
|
||||
}
|
||||
|
||||
public UniqueLCS(int[] first, int[] second, int start1, int count1, int start2, int count2) {
|
||||
myFirst = first;
|
||||
mySecond = second;
|
||||
myStart1 = start1;
|
||||
myStart2 = start2;
|
||||
myCount1 = count1;
|
||||
myCount2 = count2;
|
||||
}
|
||||
|
||||
public int[][] execute() {
|
||||
// map: key -> (offset1 + 1)
|
||||
// match: offset1 -> (offset2 + 1)
|
||||
TIntIntHashMap map = new TIntIntHashMap(myCount1 + myCount2);
|
||||
int[] match = new int[myCount1];
|
||||
|
||||
for (int i = 0; i < myCount1; i++) {
|
||||
int index = myStart1 + i;
|
||||
int val = map.get(myFirst[index]);
|
||||
|
||||
if (val == -1) continue;
|
||||
if (val == 0) {
|
||||
map.put(myFirst[index], i + 1);
|
||||
}
|
||||
else {
|
||||
map.put(myFirst[index], -1);
|
||||
}
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
for (int i = 0; i < myCount2; i++) {
|
||||
int index = myStart2 + i;
|
||||
int val = map.get(mySecond[index]);
|
||||
|
||||
if (val == 0 || val == -1) continue;
|
||||
if (match[val - 1] == 0) {
|
||||
match[val - 1] = i + 1;
|
||||
count++;
|
||||
}
|
||||
else {
|
||||
match[val - 1] = 0;
|
||||
map.put(mySecond[index], -1);
|
||||
count--;
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Largest increasing subsequence on unique elements
|
||||
int[] sequence = new int[count];
|
||||
int[] lastElement = new int[count];
|
||||
int[] predecessor = new int[myCount1];
|
||||
|
||||
int length = 0;
|
||||
for (int i = 0; i < myCount1; i++) {
|
||||
if (match[i] == 0) continue;
|
||||
|
||||
int j = binarySearch(sequence, match[i], length);
|
||||
if (j == length || match[i] < sequence[j]) {
|
||||
sequence[j] = match[i];
|
||||
lastElement[j] = i;
|
||||
predecessor[i] = j > 0 ? lastElement[j - 1] : -1;
|
||||
if (j == length) {
|
||||
length++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int[][] ret = new int[][]{new int[length], new int[length]};
|
||||
|
||||
int i = length - 1;
|
||||
int curr = lastElement[length - 1];
|
||||
while (curr != -1) {
|
||||
ret[0][i] = curr;
|
||||
ret[1][i] = match[curr] - 1;
|
||||
i--;
|
||||
curr = predecessor[curr];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// find max i: a[i] < val
|
||||
// return i + 1
|
||||
// assert a[i] != val
|
||||
private static int binarySearch(int[] sequence, int val, int length) {
|
||||
int left = -1;
|
||||
int right = length;
|
||||
|
||||
while (right - left > 1) {
|
||||
int middle = (left + right) / 2;
|
||||
if (sequence[middle] > val) {
|
||||
right = middle;
|
||||
}
|
||||
else {
|
||||
left = middle;
|
||||
}
|
||||
}
|
||||
return left + 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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.util.diff;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.BitSet;
|
||||
|
||||
public class IntLCSNewTest extends TestCase {
|
||||
public void testEqual() throws FilesTooBigForDiffException {
|
||||
BitSet[] change = buildChange(new int[]{1, 2, 3}, new int[]{1, 2, 3});
|
||||
checkChange(change, new int[]{0, 0, 0}, new int[]{0, 0, 0});
|
||||
}
|
||||
|
||||
public void testOneAtBegging() throws FilesTooBigForDiffException {
|
||||
BitSet[] change = buildChange(new int[]{1, 2}, new int[]{1, 3});
|
||||
checkChange(change, new int[]{0, 1}, new int[]{0, 1});
|
||||
}
|
||||
|
||||
public void testOneAntEnd() throws FilesTooBigForDiffException {
|
||||
BitSet[] change = buildChange(new int[]{1, 3}, new int[]{2, 3});
|
||||
checkChange(change, new int[]{1, 0}, new int[]{1, 0});
|
||||
}
|
||||
|
||||
public void testOneOverAtEnd() throws FilesTooBigForDiffException {
|
||||
BitSet[] change = buildChange(new int[]{1, 2}, new int[]{1, 2, 3});
|
||||
checkChange(change, new int[]{0, 0}, new int[]{0, 0, 1});
|
||||
}
|
||||
|
||||
public void testOneOverAtBegging() throws FilesTooBigForDiffException {
|
||||
BitSet[] change = buildChange(new int[]{1, 2, 3}, new int[]{2, 3});
|
||||
checkChange(change, new int[]{1, 0, 0}, new int[]{0, 0});
|
||||
}
|
||||
|
||||
public void testSingleMiddle() throws FilesTooBigForDiffException {
|
||||
BitSet[] change = buildChange(new int[]{1, 2, 3}, new int[]{4, 2, 5});
|
||||
checkChange(change, new int[]{1, 0, 1}, new int[]{1, 0, 1});
|
||||
}
|
||||
|
||||
public void testAbsolutelyDifferent() throws FilesTooBigForDiffException {
|
||||
BitSet[] change1 = buildChange(new int[]{1, 2}, new int[]{3, 4});
|
||||
checkChange(change1, new int[]{1, 1}, new int[]{1, 1});
|
||||
|
||||
BitSet[] change2 = buildChange(new int[]{1, 2, 3}, new int[]{4, 5, 6});
|
||||
checkChange(change2, new int[]{1, 1, 1}, new int[]{1, 1, 1});
|
||||
}
|
||||
|
||||
public void testSingleUniqueMoved() throws FilesTooBigForDiffException {
|
||||
BitSet[] change = buildChange(new int[]{1, 1, 2, 2, 10}, new int[]{10, 1, 1, 2, 2});
|
||||
checkChange(change, new int[]{0, 0, 0, 0, 1}, new int[]{1, 0, 0, 0, 0});
|
||||
}
|
||||
|
||||
public void testSingleFunctionMoved() throws FilesTooBigForDiffException {
|
||||
BitSet[] change = buildChange(new int[]{1, 2, 3, 4, 5, 6, 7, 8, 7, 9, 10, 2, 11, 12, 5, 13, 7, 8, 7},
|
||||
new int[]{10, 2, 11, 12, 5, 13, 7, 8, 7, 9, 1, 2, 3, 4, 5, 6, 7, 8, 7});
|
||||
checkChange(change, new int[]{1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1},
|
||||
new int[]{1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1});
|
||||
}
|
||||
|
||||
private static BitSet[] buildChange(int[] first, int[] second) throws FilesTooBigForDiffException {
|
||||
IntLCS intLCS = new IntLCS(first, second);
|
||||
intLCS.execute();
|
||||
return intLCS.getChanges();
|
||||
}
|
||||
|
||||
private static void checkChange(BitSet[] change, int[] expected1, int[] expected2) {
|
||||
for (int i = 0; i < expected1.length; i++) {
|
||||
assertEquals(change[0].get(i), expected1[i] == 1);
|
||||
}
|
||||
for (int i = 0; i < expected2.length; i++) {
|
||||
assertEquals(change[1].get(i), expected2[i] == 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,7 +68,7 @@ public class IntLCSTest extends TestCase {
|
||||
Reindexer reindexer = new Reindexer();
|
||||
reindexer.idInit(first.length, second.length);
|
||||
Diff.ChangeBuilder builder = new Diff.ChangeBuilder(0);
|
||||
reindexer.reindex(intLCS.getPaths(), builder);
|
||||
reindexer.reindex(intLCS.getChanges(), builder);
|
||||
return builder.getFirstChange();
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@ package com.intellij.util.diff;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.BitSet;
|
||||
|
||||
/**
|
||||
* @author dyoma
|
||||
*/
|
||||
@@ -67,10 +69,12 @@ public class LinkedDiffPathsTest extends TestCase {
|
||||
}
|
||||
|
||||
private Diff.Change decode(LinkedDiffPaths paths) {
|
||||
BitSet[] changes = new BitSet[]{new BitSet(), new BitSet()};
|
||||
paths.applyChanges(0, 0, changes[0], changes[1]);
|
||||
Reindexer reindexer = new Reindexer();
|
||||
reindexer.idInit(paths.getXSize(), paths.getYSize());
|
||||
Diff.ChangeBuilder builder = new Diff.ChangeBuilder(0);
|
||||
reindexer.reindex(paths, builder);
|
||||
reindexer.reindex(changes, builder);
|
||||
return builder.getFirstChange();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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.util.diff;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.BitSet;
|
||||
|
||||
public class PatienceIntLCSTest extends TestCase {
|
||||
public void testEqual() throws FilesTooBigForDiffException {
|
||||
BitSet[] change = buildChange(new int[]{1, 2, 3}, new int[]{1, 2, 3});
|
||||
checkChange(change, new int[]{0, 0, 0}, new int[]{0, 0, 0});
|
||||
}
|
||||
|
||||
public void testOneAtBegging() throws FilesTooBigForDiffException {
|
||||
BitSet[] change = buildChange(new int[]{1, 2}, new int[]{1, 3});
|
||||
checkChange(change, new int[]{0, 1}, new int[]{0, 1});
|
||||
}
|
||||
|
||||
public void testOneAntEnd() throws FilesTooBigForDiffException {
|
||||
BitSet[] change = buildChange(new int[]{1, 3}, new int[]{2, 3});
|
||||
checkChange(change, new int[]{1, 0}, new int[]{1, 0});
|
||||
}
|
||||
|
||||
public void testOneOverAtEnd() throws FilesTooBigForDiffException {
|
||||
BitSet[] change = buildChange(new int[]{1, 2}, new int[]{1, 2, 3});
|
||||
checkChange(change, new int[]{0, 0}, new int[]{0, 0, 1});
|
||||
}
|
||||
|
||||
public void testOneOverAtBegging() throws FilesTooBigForDiffException {
|
||||
BitSet[] change = buildChange(new int[]{1, 2, 3}, new int[]{2, 3});
|
||||
checkChange(change, new int[]{1, 0, 0}, new int[]{0, 0});
|
||||
}
|
||||
|
||||
public void testSingleMiddle() throws FilesTooBigForDiffException {
|
||||
BitSet[] change = buildChange(new int[]{1, 2, 3}, new int[]{4, 2, 5});
|
||||
checkChange(change, new int[]{1, 0, 1}, new int[]{1, 0, 1});
|
||||
}
|
||||
|
||||
public void testAbsolutelyDifferent() throws FilesTooBigForDiffException {
|
||||
BitSet[] change1 = buildChange(new int[]{1, 2}, new int[]{3, 4});
|
||||
checkChange(change1, new int[]{1, 1}, new int[]{1, 1});
|
||||
|
||||
BitSet[] change2 = buildChange(new int[]{1, 2, 3}, new int[]{4, 5, 6});
|
||||
checkChange(change2, new int[]{1, 1, 1}, new int[]{1, 1, 1});
|
||||
}
|
||||
|
||||
public void testSingleUniqueMoved() throws FilesTooBigForDiffException {
|
||||
BitSet[] change = buildChange(new int[]{1, 1, 2, 2, 10}, new int[]{10, 1, 1, 2, 2});
|
||||
checkChange(change, new int[]{1, 1, 1, 1, 0}, new int[]{0, 1, 1, 1, 1});
|
||||
}
|
||||
|
||||
public void testSingleFunctionMoved() throws FilesTooBigForDiffException {
|
||||
BitSet[] change = buildChange(new int[]{1, 2, 3, 4, 5, 6, 7, 8, 7, 9, 10, 2, 11, 12, 5, 13, 7, 8, 7},
|
||||
new int[]{10, 2, 11, 12, 5, 13, 7, 8, 7, 9, 1, 2, 3, 4, 5, 6, 7, 8, 7});
|
||||
checkChange(change, new int[]{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0},
|
||||
new int[]{0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1});
|
||||
}
|
||||
|
||||
public void testInnerChunks1() throws FilesTooBigForDiffException {
|
||||
BitSet[] change = buildChange(new int[]{0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0}, new int[]{1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 1, 7, 1});
|
||||
checkChange(change, new int[]{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}, new int[]{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1});
|
||||
}
|
||||
|
||||
public void testInnerChunks2() throws FilesTooBigForDiffException {
|
||||
BitSet[] change = buildChange(new int[]{0, 2, 3, 0, 4, 5, 0}, new int[]{1, 2, 1, 3, 4, 5, 1});
|
||||
checkChange(change, new int[]{1, 0, 0, 1, 0, 0, 1}, new int[]{1, 0, 1, 0, 0, 0, 1});
|
||||
}
|
||||
|
||||
public void testInnerChunks3() throws FilesTooBigForDiffException {
|
||||
BitSet[] change = buildChange(new int[]{15, 1, 2, 3, 1, 4, 5, 1, 15}, new int[]{13, 1, 2, 1, 3, 4, 5, 1, 13});
|
||||
checkChange(change, new int[]{1, 0, 0, 0, 1, 0, 0, 0, 1}, new int[]{1, 0, 0, 1, 0, 0, 0, 0, 1});
|
||||
}
|
||||
|
||||
public void testInnerChunks4() throws FilesTooBigForDiffException {
|
||||
BitSet[] change =
|
||||
buildChange(new int[]{15, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 15}, new int[]{13, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 13});
|
||||
checkChange(change, new int[]{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, new int[]{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1});
|
||||
}
|
||||
|
||||
private static BitSet[] buildChange(int[] first, int[] second) throws FilesTooBigForDiffException {
|
||||
PatienceIntLCS patienceIntLCS = new PatienceIntLCS(first, second);
|
||||
patienceIntLCS.execute();
|
||||
return patienceIntLCS.getChanges();
|
||||
}
|
||||
|
||||
private static void checkChange(BitSet[] change, int[] expected1, int[] expected2) {
|
||||
for (int i = 0; i < expected1.length; i++) {
|
||||
assertEquals(change[0].get(i), expected1[i] == 1);
|
||||
}
|
||||
for (int i = 0; i < expected2.length; i++) {
|
||||
assertEquals(change[1].get(i), expected2[i] == 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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.util.diff;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.BitSet;
|
||||
import java.util.Random;
|
||||
|
||||
public class ReindexerNewTest extends TestCase {
|
||||
public void testAllUnique() throws FilesTooBigForDiffException {
|
||||
checkCase(new int[]{1, 2, 3}, new int[]{4, 5});
|
||||
checkCase(new int[]{}, new int[]{4, 5});
|
||||
checkCase(new int[]{1, 2, 3}, new int[]{});
|
||||
}
|
||||
|
||||
public void testUniqueBeginning() throws FilesTooBigForDiffException {
|
||||
checkCase(new int[]{13, 15, 1, 2, 3}, new int[]{17, 1, 3});
|
||||
}
|
||||
|
||||
public void testUniqueEnd() throws FilesTooBigForDiffException {
|
||||
checkCase(new int[]{1, 2, 3, 13, 15}, new int[]{1, 3, 17});
|
||||
}
|
||||
|
||||
public void testSingleUniqueMoved() throws FilesTooBigForDiffException {
|
||||
checkCase(new int[]{1, 1, 2, 2, 10}, new int[]{10, 1, 1, 2, 2});
|
||||
}
|
||||
|
||||
public void testSingleFunctionMoved() throws FilesTooBigForDiffException {
|
||||
checkCase(new int[]{1, 2, 3, 4, 5, 6, 7, 8, 7, 9, 10, 2, 11, 12, 5, 13, 7, 8, 7},
|
||||
new int[]{10, 2, 11, 12, 5, 13, 7, 8, 7, 9, 1, 2, 3, 4, 5, 6, 7, 8, 7});
|
||||
}
|
||||
|
||||
public void testInnerChunks() throws FilesTooBigForDiffException {
|
||||
checkCase(new int[]{0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0}, new int[]{1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 1, 7, 1});
|
||||
checkCase(new int[]{0, 2, 3, 0, 4, 5, 0}, new int[]{1, 2, 1, 3, 4, 5, 1});
|
||||
checkCase(new int[]{15, 1, 2, 3, 1, 4, 5, 1, 15}, new int[]{13, 1, 2, 1, 3, 4, 5, 1, 13});
|
||||
checkCase(new int[]{15, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 15}, new int[]{13, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 13});
|
||||
}
|
||||
|
||||
public void testRandomExample() throws FilesTooBigForDiffException {
|
||||
int[] ints1 = new int[100];
|
||||
int[] ints2 = new int[100];
|
||||
Random rng = new Random();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (int j = 0; j < 100; j++) {
|
||||
ints1[j] = rng.nextInt(60);
|
||||
ints2[j] = rng.nextInt(60);
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("{");
|
||||
for (int j = 0; j < 100; j++) {
|
||||
builder.append(ints1[j]).append(", ");
|
||||
}
|
||||
builder.append("}\n{");
|
||||
for (int j = 0; j < 100; j++) {
|
||||
builder.append(ints2[j]).append(", ");
|
||||
}
|
||||
builder.append("}\n");
|
||||
|
||||
checkCase(ints1, ints2, builder.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkCase(int[] ints1, int[] ints2) throws FilesTooBigForDiffException {
|
||||
checkCase(ints1, ints2, null);
|
||||
}
|
||||
|
||||
public static void checkCase(int[] ints1, int[] ints2, String message) throws FilesTooBigForDiffException {
|
||||
final BitSet[] reindexChanges = new BitSet[]{new BitSet(), new BitSet()};
|
||||
LCSBuilder builder = new LCSBuilder() {
|
||||
private int myIndex1 = 0;
|
||||
private int myIndex2 = 0;
|
||||
|
||||
@Override
|
||||
public void addEqual(int length) {
|
||||
myIndex1 += length;
|
||||
myIndex2 += length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addChange(int first, int second) {
|
||||
reindexChanges[0].set(myIndex1, myIndex1 + first);
|
||||
reindexChanges[1].set(myIndex2, myIndex2 + second);
|
||||
|
||||
myIndex1 += first;
|
||||
myIndex2 += second;
|
||||
}
|
||||
};
|
||||
|
||||
IntLCS lcs = new IntLCS(ints1, ints2);
|
||||
lcs.execute();
|
||||
BitSet[] expectedChanges = lcs.getChanges();
|
||||
|
||||
Reindexer reindexer = new Reindexer();
|
||||
int[][] discarded = reindexer.discardUnique(ints1, ints2);
|
||||
lcs = new IntLCS(discarded[0], discarded[1]);
|
||||
lcs.execute();
|
||||
BitSet[] changes = lcs.getChanges();
|
||||
reindexer.reindex(changes, builder);
|
||||
|
||||
if (message != null) {
|
||||
assertEquals(message, expectedChanges[0], reindexChanges[0]);
|
||||
assertEquals(message, expectedChanges[1], reindexChanges[1]);
|
||||
}
|
||||
else {
|
||||
assertEquals(expectedChanges[0], reindexChanges[0]);
|
||||
assertEquals(expectedChanges[1], reindexChanges[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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.util.diff;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class UniqueLCSTest extends TestCase {
|
||||
public void testNoUnique() throws FilesTooBigForDiffException {
|
||||
int[][] change = buildChange(new int[]{1, 2, 3}, new int[]{4, 5, 6});
|
||||
assertNull(change);
|
||||
|
||||
change = buildChange(new int[]{1, 2, 1}, new int[]{1, 3, 1});
|
||||
assertNull(change);
|
||||
|
||||
change = buildChange(new int[]{1, 2, 3, 3, 2, 1}, new int[]{1, 2, 3});
|
||||
assertNull(change);
|
||||
|
||||
change = buildChange(new int[]{1, 2, 3}, new int[]{1, 2, 3, 3, 2, 1});
|
||||
assertNull(change);
|
||||
|
||||
change = buildChange(new int[]{1, 2, 3}, new int[]{1, 2, 3, 3, 2, 1});
|
||||
assertNull(change);
|
||||
|
||||
change = buildChange(new int[]{1, 2, 1}, new int[]{2, 1, 2});
|
||||
assertNull(change);
|
||||
|
||||
change = buildChange(new int[]{}, new int[]{2, 1, 2});
|
||||
assertNull(change);
|
||||
|
||||
change = buildChange(new int[]{1, 2, 2}, new int[]{});
|
||||
assertNull(change);
|
||||
|
||||
change = buildChange(new int[]{}, new int[]{});
|
||||
assertNull(change);
|
||||
}
|
||||
|
||||
public void testSingleUnique() throws FilesTooBigForDiffException {
|
||||
int[][] change = buildChange(new int[]{1}, new int[]{1});
|
||||
checkChange(change, new int[]{0}, new int[]{0});
|
||||
|
||||
change = buildChange(new int[]{5, 1}, new int[]{1});
|
||||
checkChange(change, new int[]{1}, new int[]{0});
|
||||
|
||||
change = buildChange(new int[]{1}, new int[]{2, 1});
|
||||
checkChange(change, new int[]{0}, new int[]{1});
|
||||
|
||||
change = buildChange(new int[]{1}, new int[]{1, 4});
|
||||
checkChange(change, new int[]{0}, new int[]{0});
|
||||
|
||||
change = buildChange(new int[]{1, 3}, new int[]{1});
|
||||
checkChange(change, new int[]{0}, new int[]{0});
|
||||
|
||||
change = buildChange(new int[]{5, 1, 3}, new int[]{2, 1, 4});
|
||||
checkChange(change, new int[]{1}, new int[]{1});
|
||||
}
|
||||
|
||||
public void testSingleSequence() throws FilesTooBigForDiffException {
|
||||
int[][] change = buildChange(new int[]{2, 4, 6, 1, 8, 3, 8, 2, 6, 5, 2, 4, 7, 11, 13}, new int[]{1, 10, 2, 3, 5, 7, 11, 13, 12});
|
||||
checkChange(change, new int[]{3, 5, 9, 12, 13, 14}, new int[]{0, 3, 4, 5, 6, 7});
|
||||
}
|
||||
|
||||
public void testLargestLCS1() throws FilesTooBigForDiffException {
|
||||
checkMaxSequence(new int[]{1, 2, 3}, new int[]{1, 2, 3});
|
||||
}
|
||||
|
||||
public void testLargestLCS2() throws FilesTooBigForDiffException {
|
||||
checkMaxSequence(new int[]{5, 6, 1, 2, 3}, new int[]{1, 2, 3});
|
||||
}
|
||||
|
||||
public void testLargestLCS3() throws FilesTooBigForDiffException {
|
||||
checkMaxSequence(new int[]{0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 7, 15}, new int[]{0, 2, 6, 9, 13, 15});
|
||||
}
|
||||
|
||||
public void testLargestLCS4() throws FilesTooBigForDiffException {
|
||||
checkMaxSequence(new int[]{1, 9, 3, 8, 11, 4, 5, 6, 19, 7}, new int[]{1, 3, 4, 5, 6, 7});
|
||||
}
|
||||
|
||||
private static int[][] buildChange(int[] first, int[] second) throws FilesTooBigForDiffException {
|
||||
UniqueLCS uniqueLCS = new UniqueLCS(first, second);
|
||||
return uniqueLCS.execute();
|
||||
}
|
||||
|
||||
private static void checkChange(int[][] change, int[] expected1, int[] expected2) {
|
||||
for (int i = 0; i < expected1.length; i++) {
|
||||
assertEquals(change[0][i], expected1[i]);
|
||||
}
|
||||
for (int i = 0; i < expected2.length; i++) {
|
||||
assertEquals(change[1][i], expected2[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkMaxSequence(int[] sequence, int[] expected) throws FilesTooBigForDiffException {
|
||||
int max = 0;
|
||||
for (int i = 0; i < sequence.length; i++) {
|
||||
max = Math.max(sequence[i] + 1, max);
|
||||
}
|
||||
|
||||
int[] first = new int[sequence.length];
|
||||
int[] second = new int[max + 2];
|
||||
|
||||
for (int i = 0; i < sequence.length; i++) {
|
||||
assertEquals("Elements in sequence should be unique", second[sequence[i]], 0);
|
||||
first[i] = i + 1;
|
||||
second[sequence[i]] = i + 1;
|
||||
}
|
||||
|
||||
int[][] result = buildChange(first, second);
|
||||
|
||||
assertEquals(result[0].length, result[1].length);
|
||||
assertEquals(result[0].length, expected.length);
|
||||
for (int i = 0; i < expected.length; i++) {
|
||||
assertEquals(expected[i], sequence[result[0][i]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -155,6 +155,13 @@ public interface XDebugSession extends AbstractDebuggerSession {
|
||||
|
||||
void setPauseActionSupported(boolean isSupported);
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
@Deprecated
|
||||
/**
|
||||
* @deprecated to remove in IDEA 14
|
||||
*/
|
||||
void setAutoInitBreakpoints(boolean value);
|
||||
|
||||
void rebuildViews();
|
||||
|
||||
<V extends XSmartStepIntoVariant> void smartStepInto(XSmartStepIntoHandler<V> handler, V variant);
|
||||
|
||||
@@ -114,6 +114,7 @@ public class XDebugSessionImpl implements XDebugSession {
|
||||
private final Icon myIcon;
|
||||
|
||||
private volatile boolean breakpointsInitialized;
|
||||
private boolean autoInitBreakpoints = true;
|
||||
|
||||
public XDebugSessionImpl(final @NotNull ExecutionEnvironment env, final @NotNull ProgramRunner runner,
|
||||
XDebuggerManagerImpl debuggerManager) {
|
||||
@@ -156,6 +157,11 @@ public class XDebugSessionImpl implements XDebugSession {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAutoInitBreakpoints(boolean value) {
|
||||
autoInitBreakpoints = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPauseActionSupported(final boolean isSupported) {
|
||||
myPauseActionSupported = isSupported;
|
||||
@@ -230,7 +236,7 @@ public class XDebugSessionImpl implements XDebugSession {
|
||||
myDebugProcess = process;
|
||||
mySessionData = sessionData;
|
||||
|
||||
if (myDebugProcess.checkCanInitBreakpoints()) {
|
||||
if (autoInitBreakpoints && myDebugProcess.checkCanInitBreakpoints()) {
|
||||
initBreakpoints();
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
</extensionPoints>
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<tipAndTrick file="CreateTargetTag.html" />
|
||||
|
||||
<projectService serviceInterface="com.intellij.lang.ant.config.AntConfiguration" serviceImplementation="com.intellij.lang.ant.config.impl.AntConfigurationImpl"/>
|
||||
<projectService serviceInterface="com.intellij.lang.ant.config.impl.AntWorkspaceConfiguration" serviceImplementation="com.intellij.lang.ant.config.impl.AntWorkspaceConfiguration"/>
|
||||
|
||||
|
||||
+1
-1
@@ -121,7 +121,7 @@ public class GradleModuleBuilder extends AbstractExternalModuleBuilder<GradlePro
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ModuleWizardStep getCustomOptionsStep(Disposable parentDisposable) {
|
||||
public ModuleWizardStep getCustomOptionsStep(WizardContext context, Disposable parentDisposable) {
|
||||
if (!myWizardContext.isCreatingNewProject()) return null;
|
||||
final GradleProjectSettingsControl settingsControl = new GradleProjectSettingsControl(getExternalProjectSettings());
|
||||
return new ExternalModuleSettingsStep<GradleProjectSettings>(this, settingsControl);
|
||||
|
||||
+18
-1
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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 org.jetbrains.plugins.groovy.gpp;
|
||||
|
||||
import com.intellij.codeInsight.generation.OverrideImplementUtil;
|
||||
@@ -18,7 +33,9 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpres
|
||||
import org.jetbrains.plugins.groovy.lang.psi.expectedTypes.GroovyExpectedTypesProvider;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.GrTupleType;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.signatures.GrClosureSignatureUtil;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.typeEnhancers.AbstractClosureParameterEnhancer;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
|
||||
|
||||
import java.util.*;
|
||||
@@ -124,7 +141,7 @@ public class GppClosureParameterTypeProvider extends AbstractClosureParameterEnh
|
||||
|
||||
@Nullable
|
||||
public static PsiType[] findSingleAbstractMethodSignature(@Nullable PsiType type) {
|
||||
if (type instanceof PsiClassType) {
|
||||
if (type instanceof PsiClassType && !(TypesUtil.isClassType(type, GroovyCommonClassNames.GROOVY_LANG_CLOSURE))) {
|
||||
List<Pair<PsiMethod, PsiSubstitutor>> result = getMethodsToOverrideImplementInInheritor((PsiClassType)type, true);
|
||||
if (result.size() == 1) {
|
||||
return getParameterTypes(result.get(0));
|
||||
|
||||
+8
@@ -735,6 +735,14 @@ public class GrClosureSignatureUtil {
|
||||
innerArgs.add(new InnerArg(TypeConversionUtil.erasure(closureArgument.getType()), closureArgument));
|
||||
}
|
||||
|
||||
return mapParametersToArguments(signature, innerArgs, hasNamedArgs, partial, context);
|
||||
}
|
||||
|
||||
private static ArgInfo<PsiElement>[] mapParametersToArguments(@NotNull GrClosureSignature signature,
|
||||
@NotNull List<InnerArg> innerArgs,
|
||||
boolean hasNamedArgs,
|
||||
boolean partial,
|
||||
@NotNull PsiElement context) {
|
||||
final ArgInfo<InnerArg>[] innerMap = mapParametersToArguments(signature, innerArgs.toArray(new InnerArg[innerArgs.size()]), new Function<InnerArg, PsiType>() {
|
||||
@Override
|
||||
public PsiType fun(InnerArg o) {
|
||||
|
||||
+17
-4
@@ -24,6 +24,7 @@ import com.intellij.psi.presentation.java.JavaPresentationUtil;
|
||||
import com.intellij.psi.search.SearchScope;
|
||||
import com.intellij.psi.stubs.IStubElementType;
|
||||
import com.intellij.ui.LayeredIcon;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import icons.JetgroovyIcons;
|
||||
@@ -43,6 +44,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpres
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinitionBody;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrAccessorMethod;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.dataFlow.types.TypeInferenceHelper;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrAccessorMethodImpl;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.stubs.GrFieldStub;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.typeEnhancers.GrVariableEnhancer;
|
||||
@@ -140,12 +142,23 @@ public class GrFieldImpl extends GrVariableBaseImpl<GrFieldStub> implements GrFi
|
||||
|
||||
@Override
|
||||
public PsiType getTypeGroovy() {
|
||||
if (getDeclaredType() == null && getInitializerGroovy() == null) {
|
||||
final PsiType type = GrVariableEnhancer.getEnhancedType(this);
|
||||
if (type != null) {
|
||||
return type;
|
||||
PsiType type = TypeInferenceHelper.getCurrentContext().getExpressionType(this, new Function<GrFieldImpl, PsiType>() {
|
||||
@Override
|
||||
public PsiType fun(GrFieldImpl field) {
|
||||
if (getDeclaredType() == null && getInitializerGroovy() == null) {
|
||||
final PsiType type = GrVariableEnhancer.getEnhancedType(field);
|
||||
if (type != null) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
if (type != null) {
|
||||
return type;
|
||||
}
|
||||
|
||||
return super.getTypeGroovy();
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -460,7 +460,7 @@ public class CompleteReferenceExpression {
|
||||
myMethodPointerOperator = place.getDotTokenType() == GroovyTokenTypes.mMEMBER_POINTER;
|
||||
myIsMap = isMap(place);
|
||||
final PsiType thisType = GrReferenceResolveUtil.getQualifierType(place);
|
||||
mySubstitutorComputer = new SubstitutorComputer(thisType, PsiType.EMPTY_ARRAY, PsiType.EMPTY_ARRAY, true, place, place.getParent());
|
||||
mySubstitutorComputer = new SubstitutorComputer(thisType, PsiType.EMPTY_ARRAY, PsiType.EMPTY_ARRAY, place, place.getParent());
|
||||
}
|
||||
|
||||
private static boolean shouldSkipPackages(@NotNull GrReferenceExpression place) {
|
||||
|
||||
+3
-26
@@ -24,7 +24,6 @@ import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.groovy.codeInspection.untypedUnresolvedAccess.GrUnresolvedAccessInspection;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
|
||||
@@ -36,7 +35,6 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrRefere
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMember;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatement;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiManager;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyResolveResultImpl;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil;
|
||||
@@ -44,8 +42,6 @@ import org.jetbrains.plugins.groovy.lang.psi.typeEnhancers.ClosureParameterEnhan
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.GdkMethodUtil;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
|
||||
import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
|
||||
import org.jetbrains.plugins.groovy.lang.resolve.processors.CompletionProcessor;
|
||||
import org.jetbrains.plugins.groovy.lang.resolve.processors.MethodResolverProcessor;
|
||||
import org.jetbrains.plugins.groovy.lang.resolve.processors.ResolverProcessor;
|
||||
|
||||
import java.util.List;
|
||||
@@ -63,18 +59,11 @@ public class GrReferenceResolveUtil {
|
||||
static boolean resolveImpl(ResolverProcessor processor, GrReferenceExpression place) {
|
||||
GrExpression qualifier = place.getQualifier();
|
||||
if (qualifier == null) {
|
||||
if (processor instanceof MethodResolverProcessor || processor instanceof CompletionProcessor) {
|
||||
processStaticImports(place.getContainingFile(), processor, ResolveState.initial(), place);
|
||||
if (processor instanceof MethodResolverProcessor && ((MethodResolverProcessor)processor).hasApplicableCandidates()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ResolveUtil.treeWalkUp(place, processor, true)) return false;
|
||||
if (!processor.hasCandidates()) {
|
||||
qualifier = PsiImplUtil.getRuntimeQualifier(place);
|
||||
if (qualifier != null) {
|
||||
if (!processQualifier(processor, qualifier, place)) return false;
|
||||
GrExpression runtimeQualifier = PsiImplUtil.getRuntimeQualifier(place);
|
||||
if (runtimeQualifier != null) {
|
||||
if (!processQualifier(processor, runtimeQualifier, place)) return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -102,18 +91,6 @@ public class GrReferenceResolveUtil {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean processStaticImports(PsiFile file, ResolverProcessor processor, ResolveState state, PsiElement place) {
|
||||
if (file instanceof GroovyFile) {
|
||||
GrImportStatement[] imports = ((GroovyFile)file).getImportStatements();
|
||||
for (GrImportStatement anImport : imports) {
|
||||
if (anImport.isStatic()) {
|
||||
if (!anImport.processDeclarations(processor, state, null, place)) return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean processIfJavaLangClass(ResolverProcessor processor,
|
||||
@Nullable PsiType type,
|
||||
GroovyPsiElement resolveContext,
|
||||
|
||||
+22
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2013 JetBrains s.r.o.
|
||||
* Copyright 2000-2014 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.
|
||||
@@ -16,11 +16,14 @@
|
||||
package org.jetbrains.plugins.groovy.lang.psi.impl.synthetic;
|
||||
|
||||
import com.intellij.navigation.NavigationItem;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.RecursionManager;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiIntersectionType;
|
||||
import com.intellij.psi.PsiType;
|
||||
import com.intellij.psi.search.LocalSearchScope;
|
||||
import com.intellij.psi.search.SearchScope;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -28,12 +31,24 @@ import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.dataFlow.types.TypeInferenceHelper;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.typeEnhancers.GrVariableEnhancer;
|
||||
|
||||
/**
|
||||
* @author ven
|
||||
*/
|
||||
public class ClosureSyntheticParameter extends GrLightParameter implements NavigationItem, GrRenameableLightElement {
|
||||
private static final Function<ClosureSyntheticParameter,PsiType> TYPES_CALCULATOR = new Function<ClosureSyntheticParameter, PsiType>() {
|
||||
@Override
|
||||
public PsiType fun(ClosureSyntheticParameter parameter) {
|
||||
PsiType typeGroovy = GrVariableEnhancer.getEnhancedType(parameter);
|
||||
if (typeGroovy instanceof PsiIntersectionType) {
|
||||
return ((PsiIntersectionType)typeGroovy).getRepresentative();
|
||||
}
|
||||
return typeGroovy;
|
||||
}
|
||||
};
|
||||
|
||||
private final GrClosableBlock myClosure;
|
||||
|
||||
public ClosureSyntheticParameter(GrClosableBlock closure) {
|
||||
@@ -54,11 +69,12 @@ public class ClosureSyntheticParameter extends GrLightParameter implements Navig
|
||||
public PsiType getTypeGroovy() {
|
||||
assert isValid();
|
||||
|
||||
PsiType typeGroovy = GrVariableEnhancer.getEnhancedType(this);
|
||||
if (typeGroovy instanceof PsiIntersectionType) {
|
||||
return ((PsiIntersectionType)typeGroovy).getRepresentative();
|
||||
}
|
||||
return typeGroovy;
|
||||
return RecursionManager.doPreventingRecursion(this, false, new Computable<PsiType>() {
|
||||
@Override
|
||||
public PsiType compute() {
|
||||
return TypeInferenceHelper.getCurrentContext().getExpressionType(ClosureSyntheticParameter.this, TYPES_CALCULATOR);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
+45
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2013 JetBrains s.r.o.
|
||||
* Copyright 2000-2014 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.
|
||||
@@ -16,9 +16,11 @@
|
||||
package org.jetbrains.plugins.groovy.lang.psi.typeEnhancers;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.util.InheritanceUtil;
|
||||
import com.intellij.psi.util.MethodSignature;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.containers.hash.HashMap;
|
||||
import com.intellij.util.containers.hash.HashSet;
|
||||
@@ -35,6 +37,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrGd
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.GrRangeType;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.GrTupleType;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.signatures.GrClosureSignatureUtil;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.GdkMethodUtil;
|
||||
import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
|
||||
@@ -129,6 +132,12 @@ public class ClosureParameterEnhancer extends AbstractClosureParameterEnhancer {
|
||||
if (!(parent instanceof GrMethodCall)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
PsiType fromSam = inferParameterTypeFromSAM((GrMethodCall)parent, closure, index);
|
||||
if (fromSam != null) {
|
||||
return fromSam;
|
||||
}
|
||||
|
||||
String methodName = findMethodName((GrMethodCall)parent);
|
||||
|
||||
GrExpression expression = ((GrMethodCall)parent).getInvokedExpression();
|
||||
@@ -255,6 +264,41 @@ public class ClosureParameterEnhancer extends AbstractClosureParameterEnhancer {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiType inferParameterTypeFromSAM(@NotNull GrMethodCall methodCall, @NotNull GrClosableBlock closure, int index) {
|
||||
if (!ClosureToSamConverter.isSamConversionAllowed(methodCall)) return null;
|
||||
|
||||
GroovyResolveResult resolveResult = methodCall.advancedResolve();
|
||||
PsiElement resolved = resolveResult.getElement();
|
||||
|
||||
if (!(resolved instanceof PsiMethod)) return null;
|
||||
|
||||
|
||||
|
||||
Map<GrExpression,Pair<PsiParameter,PsiType>> map = GrClosureSignatureUtil.mapArgumentsToParameters(resolveResult, methodCall, false, true,
|
||||
methodCall.getNamedArguments(),
|
||||
methodCall.getExpressionArguments(),
|
||||
methodCall.getClosureArguments());
|
||||
if (map == null) return null;
|
||||
|
||||
Pair<PsiParameter, PsiType> samParameter = map.get(closure);
|
||||
assert samParameter != null;
|
||||
|
||||
PsiType samTypeSubstituted = samParameter.getSecond();
|
||||
if (!(samTypeSubstituted instanceof PsiClassType)) return null;
|
||||
|
||||
MethodSignature samSignature = ClosureToSamConverter.findSAMSignature(samTypeSubstituted);
|
||||
if (samSignature == null) return null;
|
||||
|
||||
PsiType[] parameterTypes = samSignature.getParameterTypes();
|
||||
|
||||
if (index >= parameterTypes.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return parameterTypes[index];
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
private static PsiType getEntryForMap(@Nullable PsiType map, @NotNull final Project project, @NotNull final GlobalSearchScope scope) {
|
||||
|
||||
+16
-1
@@ -27,6 +27,8 @@ import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.signatures.GrSignature;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.GrClosureType;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.signatures.GrClosureSignatureUtil;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames;
|
||||
import org.jetbrains.plugins.groovy.util.LightCacheKey;
|
||||
|
||||
import java.util.Collection;
|
||||
@@ -44,12 +46,19 @@ public class ClosureToSamConverter extends GrTypeConverter {
|
||||
|
||||
@Override
|
||||
public Boolean isConvertible(@NotNull PsiType ltype, @NotNull PsiType rtype, @NotNull final GroovyPsiElement context) {
|
||||
if (rtype instanceof GrClosureType && ltype instanceof PsiClassType && GroovyConfigUtils.getInstance().isVersionAtLeast(context, GroovyConfigUtils.GROOVY2_2)) {
|
||||
if (rtype instanceof GrClosureType &&
|
||||
ltype instanceof PsiClassType &&
|
||||
isSamConversionAllowed(context) &&
|
||||
!TypesUtil.isClassType(ltype, GroovyCommonClassNames.GROOVY_LANG_CLOSURE)) {
|
||||
MethodSignature signature = findSAMSignature(ltype);
|
||||
if (signature != null) {
|
||||
final PsiType[] samParameterTypes = signature.getParameterTypes();
|
||||
|
||||
GrSignature closureSignature = ((GrClosureType)rtype).getSignature();
|
||||
|
||||
boolean raw = ((PsiClassType)ltype).isRaw();
|
||||
if (raw) return true;
|
||||
|
||||
if (GrClosureSignatureUtil.isSignatureApplicable(closureSignature, samParameterTypes, context)) {
|
||||
return true;
|
||||
}
|
||||
@@ -59,6 +68,10 @@ public class ClosureToSamConverter extends GrTypeConverter {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean isSamConversionAllowed(PsiElement context) {
|
||||
return GroovyConfigUtils.getInstance().isVersionAtLeast(context, GroovyConfigUtils.GROOVY2_2);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static MethodSignature findSingleAbstractMethod(@NotNull PsiClass aClass, @NotNull PsiSubstitutor substitutor) {
|
||||
MethodSignature signature;
|
||||
@@ -98,6 +111,8 @@ public class ClosureToSamConverter extends GrTypeConverter {
|
||||
@Nullable
|
||||
public static MethodSignature findSAMSignature(@Nullable PsiType type) {
|
||||
if (type instanceof PsiClassType) {
|
||||
if (TypesUtil.isClassType(type, GroovyCommonClassNames.GROOVY_LANG_CLOSURE)) return null;
|
||||
|
||||
PsiClassType.ClassResolveResult result = ((PsiClassType)type).resolveGenerics();
|
||||
PsiClass aClass = result.getElement();
|
||||
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2013 JetBrains s.r.o.
|
||||
* Copyright 2000-2014 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.
|
||||
@@ -50,7 +50,7 @@ public class AccessorResolverProcessor extends MethodResolverProcessor {
|
||||
myPropertyName = propertyName;
|
||||
|
||||
mySearchForGetter = searchForGetter;
|
||||
mySubstitutorComputer = byShape ? null : new SubstitutorComputer(thisType, PsiType.EMPTY_ARRAY, typeArguments, false, place, myPlace);
|
||||
mySubstitutorComputer = byShape ? null : new SubstitutorComputer(thisType, PsiType.EMPTY_ARRAY, typeArguments, place, myPlace);
|
||||
}
|
||||
|
||||
public boolean execute(@NotNull PsiElement element, @NotNull ResolveState state) {
|
||||
|
||||
+1
-1
@@ -88,7 +88,7 @@ public class MethodResolverProcessor extends ResolverProcessor implements GrMeth
|
||||
myAllVariants = allVariants;
|
||||
myByShape = byShape;
|
||||
|
||||
mySubstitutorComputer = new SubstitutorComputer(myThisType, myArgumentTypes, typeArguments, myAllVariants, myPlace, myPlace.getParent());
|
||||
mySubstitutorComputer = new SubstitutorComputer(myThisType, myArgumentTypes, typeArguments, myPlace, myPlace.getParent());
|
||||
myTypedContext = GppTypeConverter.hasTypedContext(myPlace);
|
||||
}
|
||||
|
||||
|
||||
+9
-10
@@ -23,7 +23,6 @@ import com.intellij.util.containers.hash.HashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils;
|
||||
import org.jetbrains.plugins.groovy.config.GroovyConfigUtils;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GrControlFlowOwner;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.signatures.GrClosureSignature;
|
||||
@@ -59,8 +58,6 @@ public class SubstitutorComputer {
|
||||
@Nullable private final PsiType[] myArgumentTypes;
|
||||
private final PsiType[] myTypeArguments;
|
||||
|
||||
private final boolean myAllVariants;
|
||||
|
||||
private final GrControlFlowOwner myFlowOwner;
|
||||
private final PsiElement myPlaceToInferContext;
|
||||
private PsiResolveHelper myHelper;
|
||||
@@ -69,13 +66,11 @@ public class SubstitutorComputer {
|
||||
public SubstitutorComputer(PsiType thisType,
|
||||
@Nullable PsiType[] argumentTypes,
|
||||
PsiType[] typeArguments,
|
||||
boolean allVariants,
|
||||
PsiElement place,
|
||||
PsiElement placeToInferContext) {
|
||||
myThisType = thisType;
|
||||
myArgumentTypes = argumentTypes;
|
||||
myTypeArguments = typeArguments;
|
||||
myAllVariants = allVariants;
|
||||
myPlace = place;
|
||||
myPlaceToInferContext = placeToInferContext;
|
||||
|
||||
@@ -164,8 +159,7 @@ public class SubstitutorComputer {
|
||||
final GrClosureSignature signature = GrClosureSignatureUtil.createSignature(method, partialSubstitutor);
|
||||
final GrClosureParameter[] params = signature.getParameters();
|
||||
|
||||
final GrClosureSignatureUtil.ArgInfo<PsiType>[] argInfos =
|
||||
GrClosureSignatureUtil.mapArgTypesToParameters(erasedSignature, argTypes, myPlace, myAllVariants);
|
||||
final GrClosureSignatureUtil.ArgInfo<PsiType>[] argInfos = GrClosureSignatureUtil.mapArgTypesToParameters(erasedSignature, argTypes, myPlace, true);
|
||||
if (argInfos == null) return partialSubstitutor;
|
||||
|
||||
int max = Math.max(params.length, argTypes.length);
|
||||
@@ -208,12 +202,15 @@ public class SubstitutorComputer {
|
||||
|
||||
@Nullable
|
||||
private PsiType handleConversion(@Nullable PsiType paramType, @Nullable PsiType argType) {
|
||||
if (GroovyConfigUtils.getInstance().isVersionAtLeast(myPlace, GroovyConfigUtils.GROOVY2_2_2) &&
|
||||
InheritanceUtil.isInheritor(argType, GroovyCommonClassNames.GROOVY_LANG_CLOSURE)) {
|
||||
if (ClosureToSamConverter.isSamConversionAllowed(myPlace) &&
|
||||
InheritanceUtil.isInheritor(argType, GroovyCommonClassNames.GROOVY_LANG_CLOSURE) &&
|
||||
!TypesUtil.isClassType(paramType, GroovyCommonClassNames.GROOVY_LANG_CLOSURE)) {
|
||||
PsiType converted = handleConversionOfSAMType(paramType, (PsiClassType)argType);
|
||||
if (converted != null) {
|
||||
return converted;
|
||||
}
|
||||
|
||||
return argType;
|
||||
}
|
||||
|
||||
if (!TypesUtil.isAssignable(TypeConversionUtil.erasure(paramType), argType, myPlace) &&
|
||||
@@ -242,7 +239,9 @@ public class SubstitutorComputer {
|
||||
closureArgs,
|
||||
LanguageLevel.JDK_1_7);
|
||||
|
||||
return JavaPsiFacade.getElementFactory(myPlace.getProject()).createType(samClass, substitutor);
|
||||
if (!substitutor.getSubstitutionMap().isEmpty()) {
|
||||
return JavaPsiFacade.getElementFactory(myPlace.getProject()).createType(samClass, substitutor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2013 JetBrains s.r.o.
|
||||
* Copyright 2000-2014 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.
|
||||
@@ -157,7 +157,8 @@ class GroovyStressPerformanceTest extends LightGroovyTestCase {
|
||||
text = "foo(it) { $text }"
|
||||
}
|
||||
myFixture.enableInspections(new MissingReturnInspection())
|
||||
measureHighlighting("def <T> foo(T t, Closure cl) {}\n" + text, 10000)
|
||||
|
||||
measureHighlighting("def <T> void foo(T t, Closure cl) {}\n$text", 10000)
|
||||
}
|
||||
|
||||
public void testDeeplyNestedClosuresInGenericCalls2() {
|
||||
@@ -167,7 +168,7 @@ class GroovyStressPerformanceTest extends LightGroovyTestCase {
|
||||
text = "foo(it) { $text }"
|
||||
}
|
||||
myFixture.enableInspections(new MissingReturnInspection())
|
||||
measureHighlighting("def <T> foo(T t, Closure<T> cl) {}\n" + text, 10000)
|
||||
measureHighlighting("def <T> void foo(T t, Closure<T> cl) {}\n$text", 10000)
|
||||
}
|
||||
|
||||
public void testManyAnnotatedScriptVariables() {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user