Merge remote-tracking branch 'origin/master'

This commit is contained in:
Ekaterina Tuzova
2014-08-18 19:38:49 +04:00
63 changed files with 1526 additions and 760 deletions
@@ -326,19 +326,36 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV
}
@Override
public void computeSourcePosition(@NotNull XNavigatable navigatable) {
if (myValueDescriptor instanceof FieldDescriptorImpl) {
SourcePosition position = ((FieldDescriptorImpl)myValueDescriptor).getSourcePosition(getProject(), getDebuggerContext());
if (position != null) {
navigatable.setSourcePosition(DebuggerUtilsEx.toXSourcePosition(position));
public void computeSourcePosition(@NotNull final XNavigatable navigatable) {
if (myEvaluationContext.getSuspendContext().isResumed()) return;
myEvaluationContext.getDebugProcess().getManagerThread().schedule(new SuspendContextCommandImpl(myEvaluationContext.getSuspendContext()) {
@Override
public Priority getPriority() {
return Priority.NORMAL;
}
}
if (myValueDescriptor instanceof LocalVariableDescriptorImpl) {
SourcePosition position = ((LocalVariableDescriptorImpl)myValueDescriptor).getSourcePosition(getProject(), getDebuggerContext());
if (position != null) {
navigatable.setSourcePosition(DebuggerUtilsEx.toXSourcePosition(position));
@Override
public void contextAction() throws Exception {
ApplicationManager.getApplication().runReadAction(new Runnable() {
@Override
public void run() {
if (myValueDescriptor instanceof FieldDescriptorImpl) {
SourcePosition position = ((FieldDescriptorImpl)myValueDescriptor).getSourcePosition(getProject(), getDebuggerContext());
if (position != null) {
navigatable.setSourcePosition(DebuggerUtilsEx.toXSourcePosition(position));
}
}
if (myValueDescriptor instanceof LocalVariableDescriptorImpl) {
SourcePosition position =
((LocalVariableDescriptorImpl)myValueDescriptor).getSourcePosition(getProject(), getDebuggerContext());
if (position != null) {
navigatable.setSourcePosition(DebuggerUtilsEx.toXSourcePosition(position));
}
}
}
});
}
}
});
}
private DebuggerContextImpl getDebuggerContext() {
@@ -69,7 +69,7 @@ public class JavaGenerateMemberCompletionContributor {
for (PsiField field : parent.getFields()) {
if (!(field instanceof PsiEnumConstant)) {
Collections.addAll(prototypes, GetterSetterPrototypeProvider.generateGetterSetters(field, true));
Collections.addAll(prototypes, GetterSetterPrototypeProvider.generateGetterSetters(field, true));
Collections.addAll(prototypes, GetterSetterPrototypeProvider.generateGetterSetters(field, false));
}
}
for (final PsiMethod prototype : prototypes) {
@@ -81,7 +81,7 @@ public class OverrideImplementTest extends LightCodeInsightTestCase {
CodeStyleSettings codeStyleSettings = CodeStyleSettingsManager.getSettings(getProject()).clone();
try {
CommonCodeStyleSettings javaSettings = codeStyleSettings.getCommonSettings(JavaLanguage.INSTANCE);
codeStyleSettings.RIGHT_MARGIN = 80;
javaSettings.RIGHT_MARGIN = 80;
javaSettings.KEEP_LINE_BREAKS = true;
codeStyleSettings.GENERATE_FINAL_PARAMETERS = true;
javaSettings.METHOD_PARAMETERS_WRAP = CommonCodeStyleSettings.WRAP_ON_EVERY_ITEM;
@@ -97,7 +97,7 @@ public class OverrideImplementTest extends LightCodeInsightTestCase {
CodeStyleSettings codeStyleSettings = CodeStyleSettingsManager.getSettings(getProject()).clone();
try {
CommonCodeStyleSettings javaSettings = codeStyleSettings.getCommonSettings(JavaLanguage.INSTANCE);
codeStyleSettings.RIGHT_MARGIN = 80;
javaSettings.RIGHT_MARGIN = 80;
javaSettings.KEEP_LINE_BREAKS = false;
codeStyleSettings.GENERATE_FINAL_PARAMETERS = false;
javaSettings.METHOD_PARAMETERS_WRAP = CommonCodeStyleSettings.WRAP_ON_EVERY_ITEM;
@@ -165,6 +165,10 @@ public final class ModuleBuildTarget extends JVMModuleBuildTarget<JavaSourceRoot
int fingerprint = getDependenciesFingerprint();
for (JavaSourceRootDescriptor root : pd.getBuildRootIndex().getTargetRoots(this, null)) {
fingerprint += FileUtil.fileHashCode(root.getRootFile());
}
final LanguageLevel level = JpsJavaExtensionService.getInstance().getLanguageLevel(module);
if (level != null) {
fingerprint += level.name().hashCode();
@@ -38,8 +38,4 @@ public class OutgoingResult {
public List<? extends VcsFullCommitDetails> getCommits() {
return myCommits;
}
public boolean hasErrors() {
return !myErrors.isEmpty();
}
}
@@ -15,9 +15,12 @@
*/
package com.intellij.dvcs.push;
import org.jetbrains.annotations.NotNull;
/**
* Source to push from. For example, local branch for git or branch/bookmark for mercurial.
*/
public interface PushSource {
@NotNull
String getPresentation();
}
@@ -198,14 +198,15 @@ public class PushController implements Disposable {
@Override
public void onSuccess() {
OutgoingResult outgoing = result.get();
if (outgoing.hasErrors()) {
List<VcsError> errors = outgoing.getErrors();
if (!errors.isEmpty()) {
final CommitLoader loader = new CommitLoader() {
@Override
public void reloadCommits() {
loadCommits(model, node, false);
}
};
myPushLog.setChildren(node, ContainerUtil.map(outgoing.getErrors(), new Function<VcsError, DefaultMutableTreeNode>() {
myPushLog.setChildren(node, ContainerUtil.map(errors, new Function<VcsError, DefaultMutableTreeNode>() {
@Override
public DefaultMutableTreeNode fun(final VcsError error) {
VcsLinkedText errorLinkText = new VcsLinkedText(error.getText(), new VcsLinkListener() {
@@ -347,13 +348,13 @@ public class PushController implements Disposable {
return additionalPanels;
}
private boolean hasRepoForPushSupport(@NotNull PushSupport support) {
for (MyRepoModel model : myView2Model.values()) {
if (support.equals(model.getSupport())) {
return true;
private boolean hasRepoForPushSupport(@NotNull final PushSupport support) {
return ContainerUtil.exists(myView2Model.values(), new Condition<MyRepoModel>() {
@Override
public boolean value(MyRepoModel model) {
return support.equals(model.getSupport());
}
}
return false;
});
}
private static class MyRepoModel {
@@ -34,13 +34,10 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
/**
* @author Nadya Zabrodina
*/
public class BranchActionGroupPopup extends PopupFactoryImpl.ActionGroupPopup {
public BranchActionGroupPopup(@NotNull String title, @NotNull Project project,
@NotNull Condition<AnAction> preselectActionCondition, @NotNull ActionGroup actions) {
super(title, actions, SimpleDataContext.getProjectContext(project), false, false, false, false, null, -1,
super(title, actions, SimpleDataContext.getProjectContext(project), false, false, true, false, null, -1,
preselectActionCondition, null);
}
@@ -25,9 +25,6 @@ import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* @author Nadya Zabrodina
*/
public abstract class NewBranchAction<T extends Repository> extends DumbAwareAction {
protected final List<T> myRepositories;
protected Project myProject;
@@ -43,7 +40,7 @@ public abstract class NewBranchAction<T extends Repository> extends DumbAwareAct
public void update(AnActionEvent e) {
if (DvcsUtil.anyRepositoryIsFresh(myRepositories)) {
e.getPresentation().setEnabled(false);
e.getPresentation().setDescription("Checkout of a new branch is not possible before the first commit.");
e.getPresentation().setDescription("Checkout of a new branch is not possible before the first commit");
}
}
@@ -89,11 +89,23 @@ public class PsiNamesElementSignatureProvider extends AbstractElementSignaturePr
return candidate instanceof PsiComment ? candidate : null;
}
else if (CODE_BLOCK_MARKER.equals(elementMarker)) {
int index = 0;
if (tokenizer.hasMoreTokens()) {
String indexStr = tokenizer.nextToken();
try {
index = Integer.parseInt(indexStr);
}
catch (NumberFormatException e) {
if (processingInfoStorage != null) {
processingInfoStorage.append("Invalid block index: ").append(indexStr).append("\n");
}
}
}
for (PsiElement child = parent.getFirstChild(); child != null; child = child.getNextSibling()) {
PsiElement firstChild = child.getFirstChild();
PsiElement lastChild = child.getLastChild();
if (firstChild != null && lastChild != null && "{".equals(firstChild.getText()) && "}".equals(lastChild.getText())) {
return child;
if (isBlockElement(child)) {
if (--index < 0) {
return child;
}
}
}
return null;
@@ -212,18 +224,39 @@ public class PsiNamesElementSignatureProvider extends AbstractElementSignaturePr
PsiElement parent = element.getParent();
if (parent instanceof PsiNamedElement && !(parent instanceof PsiFile)) {
PsiElement firstChild = element.getFirstChild();
PsiElement lastChild = element.getLastChild();
if (firstChild != null && "{".equals(firstChild.getText()) && lastChild != null && "}".equals(lastChild.getText())) {
if (isBlockElement(element)) {
int index = getBlockElementIndex(element);
StringBuilder bufferToUse = buffer;
if (bufferToUse == null) {
bufferToUse = new StringBuilder();
}
bufferToUse.append(TYPE_MARKER).append(ELEMENT_TOKENS_SEPARATOR).append(CODE_BLOCK_MARKER);
if (index > 0) {
bufferToUse.append(ELEMENT_TOKENS_SEPARATOR).append(index);
}
return bufferToUse;
}
}
return null;
}
private static boolean isBlockElement(@NotNull PsiElement element) {
PsiElement firstChild = element.getFirstChild();
PsiElement lastChild = element.getLastChild();
return firstChild != null && "{".equals(firstChild.getText()) && lastChild != null && "}".equals(lastChild.getText());
}
private static int getBlockElementIndex(@NotNull PsiElement element) {
int i = 0;
for (PsiElement sibling : element.getParent().getChildren()) {
if (element.equals(sibling)) {
return i;
}
if (isBlockElement(sibling)) {
i++;
}
}
throw new RuntimeException("Malformed PSI");
}
}
@@ -49,6 +49,7 @@ import com.intellij.ui.navigation.History;
import com.intellij.ui.navigation.Place;
import com.intellij.ui.speedSearch.ElementFilter;
import com.intellij.ui.treeStructure.SimpleNode;
import com.intellij.ui.treeStructure.filtered.FilteringTreeBuilder;
import com.intellij.util.ui.UIUtil;
import com.intellij.util.ui.update.Activatable;
import com.intellij.util.ui.update.MergingUpdateQueue;
@@ -87,6 +88,7 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat
private final History myHistory = new History(this);
private final OptionsTree myTree;
private final SettingsTreeView myTreeView;
private final MySearchField mySearch;
private final Splitter myMainSplitter;
//[back/forward] JComponent myToolbarComponent;
@@ -126,7 +128,12 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat
mySearch = new MySearchField() {
@Override
protected void onTextKeyEvent(final KeyEvent e) {
myTree.processTextEvent(e);
if (myTreeView != null) {
myTreeView.myTree.processKeyEvent(e);
}
else {
myTree.processTextEvent(e);
}
}
};
@@ -144,12 +151,12 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat
}
});
myTree = new OptionsTree(myProject, groups, getContext()) {
final KeyListener listener = new KeyListener() {
@Override
protected void onTreeKeyEvent(final KeyEvent e) {
public void keyTyped(KeyEvent event) {
myFilterDocumentWasChanged = false;
try {
mySearch.keyEventToTextField(e);
mySearch.keyEventToTextField(event);
}
finally {
if (myFilterDocumentWasChanged && !isFilterFieldVisible()) {
@@ -157,10 +164,33 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat
}
}
}
};
getContext().addColleague(myTree);
Disposer.register(this, myTree);
@Override
public void keyPressed(KeyEvent event) {
keyTyped(event);
}
@Override
public void keyReleased(KeyEvent event) {
keyTyped(event);
}
};
if (Registry.is("ide.file.settings.tree.new")) {
myTreeView = new SettingsTreeView(listener, getContext(), groups);
myTree = null;
}
else {
myTreeView = null;
myTree = new OptionsTree(myProject, groups, getContext()) {
@Override
protected void onTreeKeyEvent(final KeyEvent e) {
listener.keyTyped(e);
}
};
}
getContext().addColleague(myTreeView != null ? myTreeView : myTree);
Disposer.register(this, myTreeView != null ? myTreeView : myTree);
mySearch.addDocumentListener(new DocumentAdapter() {
@Override
protected void textChanged(DocumentEvent e) {
@@ -198,7 +228,8 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat
@Override
public Dimension getMinimumSize() {
Dimension dimension = super.getMinimumSize();
dimension.width = Math.max(myTree.getMinimumSize().width, mySearchWrapper.getPreferredSize().width);
JComponent component = myTreeView != null ? myTreeView : myTree;
dimension.width = Math.max(component.getMinimumSize().width, mySearchWrapper.getPreferredSize().width);
return dimension;
}
};
@@ -211,7 +242,7 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat
*/
myLeftSide.add(mySearchWrapper, BorderLayout.NORTH);
myLeftSide.add(myTree, BorderLayout.CENTER);
myLeftSide.add(myTreeView != null ? myTreeView : myTree, BorderLayout.CENTER);
setLayout(new BorderLayout());
@@ -233,9 +264,19 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat
mySpotlightUpdate = new MergingUpdateQueue("OptionsSpotlight", 200, false, this, this, this);
if (preselectedConfigurable != null) {
myTree.select(preselectedConfigurable);
if (myTreeView != null) {
myTreeView.select(preselectedConfigurable);
}
else {
myTree.select(preselectedConfigurable);
}
} else {
myTree.selectFirst();
if (myTreeView != null) {
myTreeView.selectFirst();
}
else {
myTree.selectFirst();
}
}
Toolkit.getDefaultToolkit().addAWTEventListener(this,
@@ -295,12 +336,16 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat
@Deprecated
@Nullable
public <T extends Configurable> T findConfigurable(Class<T> configurableClass) {
return myTree.findConfigurable(configurableClass);
return myTreeView != null
? myTreeView.findConfigurable(configurableClass)
: myTree.findConfigurable(configurableClass);
}
@Nullable
public SearchableConfigurable findConfigurableById(@NotNull String configurableId) {
return myTree.findConfigurableById(configurableId);
return myTreeView != null
? myTreeView.findConfigurableById(configurableId)
: myTree.findConfigurableById(configurableId);
}
public ActionCallback clearSearchAndSelect(Configurable configurable) {
@@ -318,7 +363,9 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat
public ActionCallback select(Configurable configurable, final String text) {
myFilter.refilterFor(text, false, true);
return myTree.select(configurable);
return myTreeView != null
? myTreeView.select(configurable)
: myTree.select(configurable);
}
private float readProportion(final float defaultValue, final String propertyName) {
@@ -367,7 +414,10 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat
myOwnDetails.setContent(myContentWrapper);
myOwnDetails.setBannerMinHeight(mySearchWrapper.getHeight());
myOwnDetails.setText(getBannerText(configurable));
if (Registry.is("ide.file.settings.order.new")) {
if (myTreeView != null) {
myOwnDetails.forProject(myTreeView.findConfigurableProject(configurable));
}
else if (Registry.is("ide.file.settings.order.new")) {
myOwnDetails.forProject(myTree.getConfigurableProject(configurable));
}
@@ -385,7 +435,8 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat
checkModified(oldConfigurable);
checkModified(configurable);
if (myTree.myBuilder.getSelectedElements().size() == 0) {
FilteringTreeBuilder builder = myTreeView != null ? myTreeView.myBuilder : myTree.myBuilder;
if (builder.getSelectedElements().size() == 0) {
select(configurable).notify(result);
} else {
result.setDone();
@@ -507,6 +558,9 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat
}
private String[] getBannerText(Configurable configurable) {
if (myTreeView != null) {
return myTreeView.getPathNames(configurable);
}
final List<Configurable> list = myTree.getPathToRoot(configurable);
final String[] result = new String[list.size()];
int add = 0;
@@ -795,7 +849,12 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat
getContext().fireErrorsChanged(errors, null);
if (!errors.isEmpty()) {
myTree.select(errors.keySet().iterator().next());
if (myTreeView != null) {
myTreeView.select(errors.keySet().iterator().next());
}
else {
myTree.select(errors.keySet().iterator().next());
}
}
}
@@ -835,7 +894,7 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat
return myFiltered.contains(node.getConfigurable()) || isChildOfNameHit(node);
}
return true;
return SettingsTreeView.isFiltered(myFiltered, myHits, value);
}
private boolean isChildOfNameHit(OptionsTree.EditorNode node) {
@@ -929,7 +988,8 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat
myLastSelected = current;
}
final ActionCallback callback = fireUpdate(adjustSelection ? myTree.findNodeFor(toSelect) : null, adjustSelection, now);
SimpleNode node = !adjustSelection ? null : myTreeView != null ? myTreeView.findNode(toSelect) : myTree.findNodeFor(toSelect);
final ActionCallback callback = fireUpdate(node, adjustSelection, now);
myFilterDocumentWasChanged = true;
@@ -965,7 +1025,12 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat
myFilter.refilterFor(filter, false, true).doWhenDone(new Runnable() {
@Override
public void run() {
myTree.select(config).notifyWhenDone(result);
if (myTreeView != null) {
myTreeView.select(config).notifyWhenDone(result);
}
else {
myTree.select(config).notifyWhenDone(result);
}
}
});
@@ -0,0 +1,864 @@
/*
* 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.options.newEditor;
import com.intellij.icons.AllIcons;
import com.intellij.ide.ui.search.ConfigurableHit;
import com.intellij.ide.util.treeView.NodeDescriptor;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.options.*;
import com.intellij.openapi.options.ex.ConfigurableWrapper;
import com.intellij.openapi.options.ex.NodeConfigurable;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.ActionCallback;
import com.intellij.openapi.util.Disposer;
import com.intellij.ui.*;
import com.intellij.ui.treeStructure.*;
import com.intellij.ui.treeStructure.filtered.FilteringTreeBuilder;
import com.intellij.ui.treeStructure.filtered.FilteringTreeStructure;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ui.GraphicsUtil;
import com.intellij.util.ui.UIUtil;
import com.intellij.util.ui.tree.TreeUtil;
import com.intellij.util.ui.update.MergingUpdateQueue;
import com.intellij.util.ui.update.Update;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;
import javax.swing.*;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeExpansionListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.plaf.TreeUI;
import javax.swing.plaf.basic.BasicTreeUI;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
/**
* @author Sergey.Malenkov
*/
final class SettingsTreeView extends JComponent implements Disposable, OptionsEditorColleague {
final SimpleTree myTree;
final FilteringTreeBuilder myBuilder;
private final OptionsEditorContext myContext;
private final MyRoot myRoot;
private final JScrollPane myScroller;
private JLabel mySeparator;
private final MyRenderer myRenderer = new MyRenderer();
private final IdentityHashMap<Configurable, MyNode> myConfigurableToNodeMap = new IdentityHashMap<Configurable, MyNode>();
private final MergingUpdateQueue myQueue = new MergingUpdateQueue("OptionsTree", 150, false, this, this, this).setRestartTimerOnAdd(true);
private Configurable myQueuedConfigurable;
SettingsTreeView(final KeyListener listener, OptionsEditorContext context, ConfigurableGroup... groups) {
myContext = context;
myRoot = new MyRoot(groups);
myTree = new MyTree();
myTree.getInputMap().clear();
TreeUtil.installActions(myTree);
myTree.setOpaque(true);
myTree.setBorder(BorderFactory.createEmptyBorder(0, 1, 0, 0));
myTree.setRowHeight(-1);
myTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
myTree.setCellRenderer(myRenderer);
myTree.setRootVisible(false);
myTree.setShowsRootHandles(false);
myScroller = ScrollPaneFactory.createScrollPane(myTree);
myScroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
add(myScroller);
myTree.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
myBuilder.revalidateTree();
}
@Override
public void componentMoved(ComponentEvent e) {
myBuilder.revalidateTree();
}
@Override
public void componentShown(ComponentEvent e) {
myBuilder.revalidateTree();
}
});
myTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent event) {
MyNode node = extractNode(event.getNewLeadSelectionPath());
select(node == null ? null : node.myConfigurable);
}
});
myTree.addKeyListener(new KeyListener() {
public void keyTyped(KeyEvent event) {
if (listener != null && isValid(event)) {
listener.keyTyped(event);
}
}
public void keyPressed(KeyEvent event) {
if (listener != null && isValid(event)) {
listener.keyPressed(event);
}
}
public void keyReleased(KeyEvent event) {
if (listener != null && isValid(event)) {
listener.keyReleased(event);
}
}
private boolean isValid(KeyEvent event) {
return null == myTree.getInputMap().get(KeyStroke.getKeyStrokeForEvent(event));
}
});
myBuilder = new MyBuilder(new SimpleTreeStructure.Impl(myRoot));
myBuilder.setFilteringMerge(300, null);
Disposer.register(this, myBuilder);
}
@NotNull
String[] getPathNames(Configurable configurable) {
ArrayDeque<String> path = new ArrayDeque<String>();
MyNode node = myConfigurableToNodeMap.get(configurable);
while (node != null) {
path.push(node.myDisplayName);
SimpleNode parent = node.getParent();
node = parent instanceof MyNode
? (MyNode)parent
: null;
}
return ArrayUtil.toStringArray(path);
}
@Nullable
SimpleNode findNode(Configurable toSelect) {
return myConfigurableToNodeMap.get(toSelect);
}
@Nullable
SearchableConfigurable findConfigurableById(@NotNull String id) {
for (Configurable configurable : myConfigurableToNodeMap.keySet()) {
if (configurable instanceof SearchableConfigurable) {
SearchableConfigurable searchable = (SearchableConfigurable)configurable;
if (id.equals(searchable.getId())) {
return searchable;
}
}
}
return null;
}
@Nullable
<T extends UnnamedConfigurable> T findConfigurable(@NotNull Class<T> type) {
for (UnnamedConfigurable configurable : myConfigurableToNodeMap.keySet()) {
if (configurable instanceof ConfigurableWrapper) {
ConfigurableWrapper wrapper = (ConfigurableWrapper)configurable;
configurable = wrapper.getConfigurable();
}
if (type.isInstance(configurable)) {
return type.cast(configurable);
}
}
return null;
}
@Nullable
Project findConfigurableProject(@Nullable Configurable configurable) {
if (configurable instanceof ConfigurableWrapper) {
ConfigurableWrapper wrapper = (ConfigurableWrapper)configurable;
return wrapper.getExtensionPoint().getProject();
}
return findConfigurableProject(myConfigurableToNodeMap.get(configurable));
}
@Nullable
private static Project findConfigurableProject(@Nullable MyNode node) {
if (node != null) {
Configurable configurable = node.myConfigurable;
if (configurable instanceof ConfigurableWrapper) {
ConfigurableWrapper wrapper = (ConfigurableWrapper)configurable;
return wrapper.getExtensionPoint().getProject();
}
SimpleNode parent = node.getParent();
if (parent instanceof MyNode) {
return findConfigurableProject((MyNode)parent);
}
}
return null;
}
@Nullable
private ConfigurableGroup findConfigurableGroupAt(int x, int y) {
TreePath path = myTree.getClosestPathForLocation(x - myTree.getX(), y - myTree.getY());
while (path != null) {
MyNode node = extractNode(path);
if (node == null) {
return null;
}
if (node.myComposite instanceof ConfigurableGroup) {
return (ConfigurableGroup)node.myComposite;
}
path = path.getParentPath();
}
return null;
}
@Nullable
private static MyNode extractNode(@Nullable Object object) {
if (object instanceof TreePath) {
TreePath path = (TreePath)object;
object = path.getLastPathComponent();
}
if (object instanceof DefaultMutableTreeNode) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode)object;
object = node.getUserObject();
}
if (object instanceof FilteringTreeStructure.FilteringNode) {
FilteringTreeStructure.FilteringNode node = (FilteringTreeStructure.FilteringNode)object;
object = node.getDelegate();
}
return object instanceof MyNode
? (MyNode)object
: null;
}
static boolean isFiltered(Set<Configurable> configurables, ConfigurableHit hits, SimpleNode value) {
if (value instanceof MyNode && !configurables.contains(((MyNode)value).myConfigurable)) {
if (hits != null) {
configurables = hits.getNameFullHits();
while (value != null) {
if (value instanceof MyNode) {
if (configurables.contains(((MyNode)value).myConfigurable)) {
return true;
}
}
value = value.getParent();
}
}
return false;
}
return true;
}
@Override
public void doLayout() {
myScroller.setBounds(0, 0, getWidth(), getHeight());
}
@Override
public void paint(Graphics g) {
super.paint(g);
if (mySeparator == null) {
mySeparator = new JLabel();
mySeparator.setFont(UIUtil.getLabelFont());
mySeparator.setFont(getFont().deriveFont(Font.BOLD));
}
ConfigurableGroup group = findConfigurableGroupAt(0, 5 + mySeparator.getFont().getSize());
if (group != null && group == findConfigurableGroupAt(0, -5)) {
int offset = UIUtil.isUnderNativeMacLookAndFeel() ? 1 : 3;
mySeparator.setBorder(BorderFactory.createEmptyBorder(offset, 18, offset, 3));
mySeparator.setText(group.getDisplayName());
Rectangle bounds = myScroller.getViewport().getBounds();
int height = mySeparator.getPreferredSize().height;
if (bounds.height > height) {
bounds.height = height;
}
g.setColor(myTree.getBackground());
if (g instanceof Graphics2D) {
int h = bounds.height / 4;
int y = bounds.y + bounds.height - h;
g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height - h);
((Graphics2D)g).setPaint(UIUtil.getGradientPaint(
0, y, g.getColor(),
0, y + h, ColorUtil.toAlpha(g.getColor(), 0)));
g.fillRect(bounds.x, y, bounds.width, h + h);
}
else {
g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
}
mySeparator.setSize(bounds.width - 1, bounds.height);
mySeparator.paint(g.create(bounds.x + 1, bounds.y, bounds.width - 1, bounds.height));
}
}
void selectFirst() {
for (ConfigurableGroup eachGroup : myRoot.myGroups) {
Configurable[] kids = eachGroup.getConfigurables();
if (kids.length > 0) {
select(kids[0]);
return;
}
}
}
ActionCallback select(@Nullable final Configurable configurable) {
if (myBuilder.isSelectionBeingAdjusted()) {
return new ActionCallback.Rejected();
}
final ActionCallback callback = new ActionCallback();
myQueuedConfigurable = configurable;
myQueue.queue(new Update(this) {
public void run() {
if (configurable == myQueuedConfigurable) {
if (configurable == null) {
fireSelected(null, callback);
}
else {
myBuilder.getReady(this).doWhenDone(new Runnable() {
@Override
public void run() {
if (configurable != myQueuedConfigurable) return;
MyNode editorNode = myConfigurableToNodeMap.get(configurable);
FilteringTreeStructure.FilteringNode editorUiNode = myBuilder.getVisibleNodeFor(editorNode);
if (editorUiNode == null) return;
if (!myBuilder.getSelectedElements().contains(editorUiNode)) {
myBuilder.select(editorUiNode, new Runnable() {
public void run() {
fireSelected(configurable, callback);
}
});
}
else {
myBuilder.scrollSelectionToVisible(new Runnable() {
public void run() {
fireSelected(configurable, callback);
}
}, false);
}
}
});
}
}
}
@Override
public void setRejected() {
super.setRejected();
callback.setRejected();
}
});
return callback;
}
private void fireSelected(Configurable configurable, ActionCallback callback) {
myContext.fireSelected(configurable, this).doWhenProcessed(callback.createSetDoneRunnable());
}
@Override
public void dispose() {
myQueuedConfigurable = null;
}
@Override
public ActionCallback onSelected(@Nullable Configurable configurable, Configurable oldConfigurable) {
return select(configurable);
}
@Override
public ActionCallback onModifiedAdded(Configurable configurable) {
myTree.repaint();
return new ActionCallback.Done();
}
@Override
public ActionCallback onModifiedRemoved(Configurable configurable) {
myTree.repaint();
return new ActionCallback.Done();
}
@Override
public ActionCallback onErrorsChanged() {
return new ActionCallback.Done();
}
private final class MyRoot extends CachingSimpleNode {
private final ConfigurableGroup[] myGroups;
private MyRoot(ConfigurableGroup[] groups) {
super(null);
myGroups = groups;
}
@Override
protected SimpleNode[] buildChildren() {
if (myGroups == null || myGroups.length == 0) {
return NO_CHILDREN;
}
SimpleNode[] result = new SimpleNode[myGroups.length];
for (int i = 0; i < myGroups.length; i++) {
result[i] = new MyNode(this, myGroups[i]);
}
return result;
}
}
private final class MyNode extends CachingSimpleNode {
private final Configurable.Composite myComposite;
private final Configurable myConfigurable;
private final String myDisplayName;
private MyNode(CachingSimpleNode parent, Configurable configurable) {
super(parent);
myComposite = configurable instanceof Configurable.Composite ? (Configurable.Composite)configurable : null;
myConfigurable = configurable;
String name = configurable.getDisplayName();
myDisplayName = name != null ? name.replace("\n", " ") : "{ " + configurable.getClass().getSimpleName() + " }";
myConfigurableToNodeMap.put(configurable, this);
}
private MyNode(CachingSimpleNode parent, ConfigurableGroup group) {
super(parent);
myComposite = group;
myConfigurable = null;
String name = group.getDisplayName();
myDisplayName = name != null ? name.replace("\n", " ") : "{ " + group.getClass().getSimpleName() + " }";
}
@Override
protected SimpleNode[] buildChildren() {
if (myComposite == null) {
return NO_CHILDREN;
}
Configurable[] configurables = myComposite.getConfigurables();
if (configurables == null || configurables.length == 0) {
return NO_CHILDREN;
}
SimpleNode[] result = new SimpleNode[configurables.length];
for (int i = 0; i < configurables.length; i++) {
result[i] = new MyNode(this, configurables[i]);
if (myConfigurable != null) {
myContext.registerKid(myConfigurable, configurables[i]);
}
}
return result;
}
@Override
public boolean isAlwaysLeaf() {
return myComposite == null;
}
@Override
public int getWeight() {
return WeightBasedComparator.UNDEFINED_WEIGHT;
}
}
private final class MyRenderer extends GroupedElementsRenderer.Tree {
private JLabel myNodeIcon;
private JLabel myProjectIcon;
protected JComponent createItemComponent() {
myTextLabel = new ErrorLabel();
return myTextLabel;
}
@Override
protected void layout() {
myNodeIcon = new JLabel(" ", SwingConstants.RIGHT);
myProjectIcon = new JLabel(" ", SwingConstants.LEFT);
myProjectIcon.setOpaque(true);
myRendererComponent.add(BorderLayout.NORTH, mySeparatorComponent);
myRendererComponent.add(BorderLayout.CENTER, myComponent);
myRendererComponent.add(BorderLayout.WEST, myNodeIcon);
myRendererComponent.add(BorderLayout.EAST, myProjectIcon);
}
public Component getTreeCellRendererComponent(JTree tree,
Object value,
boolean selected,
boolean expanded,
boolean leaf,
int row,
boolean focused) {
myTextLabel.setOpaque(selected);
myTextLabel.setFont(UIUtil.getLabelFont());
String text;
boolean hasSeparatorAbove = false;
int preferredForcedWidth = -1;
MyNode node = extractNode(value);
if (node == null) {
text = value.toString();
}
else {
text = node.myDisplayName;
// show groups in bold
if (myRoot == node.getParent()) {
hasSeparatorAbove = node != myRoot.getChildAt(0);
myTextLabel.setFont(myTextLabel.getFont().deriveFont(Font.BOLD));
}
TreePath path = tree.getPathForRow(row);
if (path == null) {
if (value instanceof DefaultMutableTreeNode) {
path = new TreePath(((DefaultMutableTreeNode)value).getPath());
}
}
int forcedWidth = 2000;
if (path != null && tree.isVisible()) {
Rectangle visibleRect = tree.getVisibleRect();
int nestingLevel = tree.isRootVisible() ? path.getPathCount() - 1 : path.getPathCount() - 2;
int left = UIUtil.getTreeLeftChildIndent();
int right = UIUtil.getTreeRightChildIndent();
Insets treeInsets = tree.getInsets();
int indent = (left + right) * nestingLevel + (treeInsets != null ? treeInsets.left + treeInsets.right : 0);
forcedWidth = visibleRect.width > 0 ? visibleRect.width - indent : forcedWidth;
}
preferredForcedWidth = forcedWidth - 4;
}
Component result = configureComponent(text, null, null, null, selected, hasSeparatorAbove, null, preferredForcedWidth);
// update font color for modified configurables
if (!selected && node != null) {
Configurable configurable = node.myConfigurable;
if (configurable != null) {
if (myContext.getErrors().containsKey(configurable)) {
myTextLabel.setForeground(JBColor.RED);
}
else if (myContext.getModified().contains(configurable)) {
myTextLabel.setForeground(JBColor.BLUE);
}
}
}
// configure project icon
Project project = null;
if (node != null) {
SimpleNode parent = node.getParent();
if (parent instanceof MyNode) {
if (myRoot == parent.getParent()) {
project = findConfigurableProject(node); // show icon for top-level nodes
if (node.myConfigurable instanceof NodeConfigurable) { // special case for custom subgroups (build.tools)
Configurable[] configurables = ((NodeConfigurable)node.myConfigurable).getConfigurables();
if (configurables != null) { // assume that all configurables have the same project
project = findConfigurableProject(configurables[0]);
}
}
}
else if (((MyNode)parent).myConfigurable instanceof NodeConfigurable) {
if (((MyNode)node.getParent()).myConfigurable instanceof NodeConfigurable) {
project = findConfigurableProject(node); // special case for custom subgroups
}
}
}
}
if (project != null) {
myProjectIcon.setIcon(selected
? AllIcons.General.ProjectConfigurableSelected
: AllIcons.General.ProjectConfigurable);
myProjectIcon.setToolTipText(OptionsBundle.message(project.isDefault()
? "configurable.default.project.tooltip"
: "configurable.current.project.tooltip"));
myProjectIcon.setBackground(myTextLabel.getBackground());
myProjectIcon.setVisible(true);
}
else {
myProjectIcon.setVisible(false);
}
// configure node icon
if (value instanceof DefaultMutableTreeNode) {
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)value;
TreePath treePath = new TreePath(treeNode.getPath());
myNodeIcon.setIcon(myTree.getHandleIcon(treeNode, treePath));
}
else {
myNodeIcon.setIcon(null);
}
return result;
}
public boolean isUnderHandle(Point point) {
Point handlePoint = SwingUtilities.convertPoint(myRendererComponent, point, myNodeIcon);
Rectangle bounds = myNodeIcon.getBounds();
return bounds.x < handlePoint.x && bounds.getMaxX() >= handlePoint.x;
}
}
private final class MyTree extends SimpleTree {
@Override
public String getToolTipText(MouseEvent event) {
if (event != null) {
Component component = getDeepestRendererComponentAt(event.getX(), event.getY());
if (component instanceof JLabel) {
JLabel label = (JLabel)component;
if (label.getIcon() != null) {
String text = label.getToolTipText();
if (text != null) {
return text;
}
}
}
}
return super.getToolTipText(event);
}
@Override
protected boolean paintNodes() {
return false;
}
@Override
protected boolean highlightSingleNode() {
return false;
}
@Override
public void setUI(TreeUI ui) {
TreeUI actualUI = ui;
if (!(ui instanceof MyTreeUi)) {
actualUI = new MyTreeUi();
}
super.setUI(actualUI);
}
@Override
protected boolean isCustomUI() {
return true;
}
@Override
protected void configureUiHelper(TreeUIHelper helper) {
}
@Override
public boolean getScrollableTracksViewportWidth() {
return true;
}
@Override
public void processKeyEvent(KeyEvent e) {
TreePath path = myTree.getSelectionPath();
if (path != null) {
if (e.getKeyCode() == KeyEvent.VK_LEFT) {
if (isExpanded(path)) {
collapsePath(path);
return;
}
}
else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
if (isCollapsed(path)) {
expandPath(path);
return;
}
}
}
super.processKeyEvent(e);
}
@Override
protected void processMouseEvent(MouseEvent e) {
MyTreeUi ui = (MyTreeUi)myTree.getUI();
boolean toggleNow = MouseEvent.MOUSE_RELEASED == e.getID()
&& UIUtil.isActionClick(e, MouseEvent.MOUSE_RELEASED)
&& !ui.isToggleEvent(e);
if (toggleNow || MouseEvent.MOUSE_PRESSED == e.getID()) {
TreePath path = getPathForLocation(e.getX(), e.getY());
if (path != null) {
Rectangle bounds = getPathBounds(path);
if (bounds != null && path.getLastPathComponent() instanceof DefaultMutableTreeNode) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent();
boolean selected = isPathSelected(path);
boolean expanded = isExpanded(path);
Component comp =
myRenderer.getTreeCellRendererComponent(this, node, selected, expanded, node.isLeaf(), getRowForPath(path), isFocusOwner());
comp.setBounds(bounds);
comp.validate();
Point point = new Point(e.getX() - bounds.x, e.getY() - bounds.y);
if (myRenderer.isUnderHandle(point)) {
if (toggleNow) {
ui.toggleExpandState(path);
}
e.consume();
return;
}
}
}
}
super.processMouseEvent(e);
}
private final class MyTreeUi extends BasicTreeUI {
@Override
public void toggleExpandState(TreePath path) {
super.toggleExpandState(path);
}
@Override
public boolean isToggleEvent(MouseEvent event) {
return super.isToggleEvent(event);
}
@Override
protected boolean shouldPaintExpandControl(TreePath path,
int row,
boolean isExpanded,
boolean hasBeenExpanded,
boolean isLeaf) {
return false;
}
@Override
protected void paintHorizontalPartOfLeg(Graphics g,
Rectangle clipBounds,
Insets insets,
Rectangle bounds,
TreePath path,
int row,
boolean isExpanded,
boolean hasBeenExpanded,
boolean isLeaf) {
}
@Override
protected void paintVerticalPartOfLeg(Graphics g, Rectangle clipBounds, Insets insets, TreePath path) {
}
@Override
public void paint(Graphics g, JComponent c) {
GraphicsUtil.setupAntialiasing(g);
super.paint(g, c);
}
}
}
private final class MyBuilder extends FilteringTreeBuilder {
List<Object> myToExpandOnResetFilter;
boolean myRefilteringNow;
boolean myWasHoldingFilter;
public MyBuilder(SimpleTreeStructure structure) {
super(myTree, myContext.getFilter(), structure, new WeightBasedComparator(false));
myTree.addTreeExpansionListener(new TreeExpansionListener() {
public void treeExpanded(TreeExpansionEvent event) {
invalidateExpansions();
}
public void treeCollapsed(TreeExpansionEvent event) {
invalidateExpansions();
}
});
}
private void invalidateExpansions() {
if (!myRefilteringNow) {
myToExpandOnResetFilter = null;
}
}
@Override
protected boolean isSelectable(Object object) {
return object instanceof MyNode;
}
@Override
public boolean isAutoExpandNode(NodeDescriptor nodeDescriptor) {
return myContext.isHoldingFilter();
}
@Override
public boolean isToEnsureSelectionOnFocusGained() {
return false;
}
@Override
protected ActionCallback refilterNow(Object preferredSelection, boolean adjustSelection) {
final List<Object> toRestore = new ArrayList<Object>();
if (myContext.isHoldingFilter() && !myWasHoldingFilter && myToExpandOnResetFilter == null) {
myToExpandOnResetFilter = myBuilder.getUi().getExpandedElements();
}
else if (!myContext.isHoldingFilter() && myWasHoldingFilter && myToExpandOnResetFilter != null) {
toRestore.addAll(myToExpandOnResetFilter);
myToExpandOnResetFilter = null;
}
myWasHoldingFilter = myContext.isHoldingFilter();
ActionCallback result = super.refilterNow(preferredSelection, adjustSelection);
myRefilteringNow = true;
return result.doWhenDone(new Runnable() {
public void run() {
myRefilteringNow = false;
if (!myContext.isHoldingFilter() && getSelectedElements().isEmpty()) {
restoreExpandedState(toRestore);
}
}
});
}
private void restoreExpandedState(List<Object> toRestore) {
TreePath[] selected = myTree.getSelectionPaths();
if (selected == null) {
selected = new TreePath[0];
}
List<TreePath> toCollapse = new ArrayList<TreePath>();
for (int eachRow = 0; eachRow < myTree.getRowCount(); eachRow++) {
if (!myTree.isExpanded(eachRow)) continue;
TreePath eachVisiblePath = myTree.getPathForRow(eachRow);
if (eachVisiblePath == null) continue;
Object eachElement = myBuilder.getElementFor(eachVisiblePath.getLastPathComponent());
if (toRestore.contains(eachElement)) continue;
for (TreePath eachSelected : selected) {
if (!eachVisiblePath.isDescendant(eachSelected)) {
toCollapse.add(eachVisiblePath);
}
}
}
for (TreePath each : toCollapse) {
myTree.collapsePath(each);
}
}
}
}
@@ -394,6 +394,7 @@ console.too.much.text.buffer.ratio.description=Used for disabling of console pro
when there is too much of text to process.\n\
The ratio is used against the console cycle buffer size (idea.cycle.buffer.size/theRatio=maxTextLength).
ide.file.settings.order.new=false
ide.file.settings.tree.new=false
ide.new.project.settings=true
ide.new.project.settings.description=Temporary key for new project settings dialog UI
@@ -21,6 +21,7 @@ import com.intellij.execution.ui.ConsoleView;
import com.intellij.execution.ui.RunContentDescriptor;
import com.intellij.execution.ui.RunnerLayoutUi;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.DataKey;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.MessageType;
import com.intellij.xdebugger.breakpoints.XBreakpoint;
@@ -45,6 +46,7 @@ import javax.swing.event.HyperlinkListener;
* @author nik
*/
public interface XDebugSession extends AbstractDebuggerSession {
DataKey<XDebugSession> DATA_KEY = DataKey.create("XDebugSessionTab.XDebugSession");
@NotNull
Project getProject();
@@ -170,6 +170,7 @@ public class XDebugSessionImpl implements XDebugSession {
myPauseActionSupported = isSupported;
}
@NotNull
public List<AnAction> getRestartActions() {
return myRestartActions;
}
@@ -180,6 +181,7 @@ public class XDebugSessionImpl implements XDebugSession {
}
}
@NotNull
public List<AnAction> getExtraActions() {
return myExtraActions;
}
@@ -22,7 +22,6 @@ import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.ui.content.ContentManager;
import com.intellij.util.SingleAlarm;
import com.intellij.xdebugger.XDebugSession;
import com.intellij.xdebugger.impl.ui.XDebugSessionTab;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -71,11 +70,11 @@ public abstract class XDebugView implements Disposable {
ViewContext viewContext = ViewContext.CONTEXT_KEY.getData(dataContext);
ContentManager contentManager = viewContext == null ? null : viewContext.getContentManager();
if (contentManager != null) {
XDebugSession session = XDebugSessionTab.SESSION_KEY.getData(DataManager.getInstance().getDataContext(contentManager.getComponent()));
XDebugSession session = XDebugSession.DATA_KEY.getData(DataManager.getInstance().getDataContext(contentManager.getComponent()));
if (session != null) {
return session;
}
}
return XDebugSessionTab.SESSION_KEY.getData(dataContext);
return XDebugSession.DATA_KEY.getData(dataContext);
}
}
@@ -56,7 +56,6 @@ import java.util.List;
public class XDebugSessionTab extends DebuggerSessionTabBase {
private static final DataKey<XDebugSessionTab> TAB_KEY = DataKey.create("XDebugSessionTab");
public static final DataKey<XDebugSession> SESSION_KEY = DataKey.create("XDebugSessionTab.XDebugSession");
private XWatchesViewImpl myWatchesView;
private final List<XDebugView> myViews = new ArrayList<XDebugView>();
@@ -146,7 +145,7 @@ public class XDebugSessionTab extends DebuggerSessionTabBase {
}
if (mySession != null) {
if (SESSION_KEY.is(dataId)) {
if (XDebugSession.DATA_KEY.is(dataId)) {
return mySession;
}
else if (LangDataKeys.CONSOLE_VIEW.is(dataId)) {
@@ -416,20 +416,6 @@ unstash.unstashing=Unstashing...
unstash.view.tooltip=View selected stash
unstash.view=&View
unstashing.title=UnStashing changes...
update.locally.modified.files.tooltip=Locally modified files.
update.locally.modified.files=&Files:
update.locally.modified.git.root=Git Root:
update.locally.modified.message=<html><p>The following files under this root are locally modified. <br/>\
Possible reasons: uncommitted changes; a problem with crlf conversion; {0} configuration file auto-save.</p>\
<p><ul>\
<li>Press <b>Revert Files</b> to discard these local changes and continue the update process.</li>\
<li>Press <b>Cancel</b> to cancel the update process. <br/> Use Auto-Stash option to stash local changes before update and restore them after it.</li>\
</ul></p> \
</html>
update.locally.modified.rescan.tooltip=<html>Rescan the repository to check for locally modified files again.<br/>Use this button if you have resolved the problem manually.</html>
update.locally.modified.rescan=Re&scan
update.locally.modified.revert=Revert Files
update.locally.modified.title=Locally modified files are detected
update.options.display.name=Git Update Settings
update.options.no.commit=No &Commit
update.options.save.before.update=Clean working tree before update
@@ -30,6 +30,7 @@ import git4idea.commands.*;
import git4idea.merge.GitConflictResolver;
import git4idea.update.GitUpdateResult;
import git4idea.util.GitUIUtil;
import git4idea.util.LocalChangesWouldBeOverwrittenHelper;
import git4idea.util.StringScanner;
import git4idea.util.UntrackedFilesNotifier;
import org.jetbrains.annotations.NotNull;
@@ -43,6 +44,8 @@ import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import static git4idea.commands.GitLocalChangesWouldBeOverwrittenDetector.Operation.CHECKOUT;
/**
* @author Kirill Likhodedov
*/
@@ -77,7 +80,9 @@ public class GitRebaser {
final GitRebaseProblemDetector rebaseConflictDetector = new GitRebaseProblemDetector();
rebaseHandler.addLineListener(rebaseConflictDetector);
GitUntrackedFilesOverwrittenByOperationDetector untrackedFilesDetector = new GitUntrackedFilesOverwrittenByOperationDetector(root);
GitLocalChangesWouldBeOverwrittenDetector localChangesDetector = new GitLocalChangesWouldBeOverwrittenDetector(root, CHECKOUT);
rebaseHandler.addLineListener(untrackedFilesDetector);
rebaseHandler.addLineListener(localChangesDetector);
String progressTitle = "Rebasing";
GitTask rebaseTask = new GitTask(myProject, rebaseHandler, progressTitle);
@@ -108,7 +113,7 @@ public class GitRebaser {
});
if (failure.get()) {
updateResult.set(handleRebaseFailure(rebaseHandler, root, rebaseConflictDetector, untrackedFilesDetector));
updateResult.set(handleRebaseFailure(rebaseHandler, root, rebaseConflictDetector, untrackedFilesDetector, localChangesDetector));
}
}
finally {
@@ -333,19 +338,27 @@ public class GitRebaser {
}
@NotNull
public GitUpdateResult handleRebaseFailure(@NotNull GitLineHandler handler, @NotNull VirtualFile root,
public GitUpdateResult handleRebaseFailure(@NotNull GitLineHandler handler,
@NotNull VirtualFile root,
@NotNull GitRebaseProblemDetector rebaseConflictDetector,
@NotNull GitMessageWithFilesDetector untrackedWouldBeOverwrittenDetector) {
@NotNull GitMessageWithFilesDetector untrackedWouldBeOverwrittenDetector,
@NotNull GitLocalChangesWouldBeOverwrittenDetector localChangesDetector) {
if (rebaseConflictDetector.isMergeConflict()) {
LOG.info("handleRebaseFailure merge conflict");
final boolean allMerged = new GitRebaser.ConflictResolver(myProject, myGit, root, this).merge();
return allMerged ? GitUpdateResult.SUCCESS_WITH_RESOLVED_CONFLICTS : GitUpdateResult.INCOMPLETE;
} else if (untrackedWouldBeOverwrittenDetector.wasMessageDetected()) {
}
else if (untrackedWouldBeOverwrittenDetector.wasMessageDetected()) {
LOG.info("handleRebaseFailure: untracked files would be overwritten by checkout");
UntrackedFilesNotifier.notifyUntrackedFilesOverwrittenBy(myProject, root,
untrackedWouldBeOverwrittenDetector.getRelativeFilePaths(), "rebase", null);
return GitUpdateResult.ERROR;
} else {
}
else if (localChangesDetector.wasMessageDetected()) {
LocalChangesWouldBeOverwrittenHelper.showErrorNotification(myProject, root, "rebase", localChangesDetector.getRelativeFilePaths());
return GitUpdateResult.ERROR;
}
else {
LOG.info("handleRebaseFailure error " + handler.errors());
GitUIUtil.notifyImportantError(myProject, "Rebase error", GitUIUtil.stringifyErrors(handler.errors()));
return GitUpdateResult.ERROR;
@@ -51,6 +51,7 @@ import git4idea.merge.GitConflictResolver;
import git4idea.repo.GitRepository;
import git4idea.stash.GitStashUtils;
import git4idea.util.GitUIUtil;
import git4idea.util.LocalChangesWouldBeOverwrittenHelper;
import git4idea.util.UntrackedFilesNotifier;
import git4idea.validators.GitBranchNameValidator;
import org.jetbrains.annotations.NotNull;
@@ -68,6 +69,8 @@ import java.util.HashSet;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import static git4idea.commands.GitLocalChangesWouldBeOverwrittenDetector.Operation.MERGE;
/**
* The unstash dialog
*/
@@ -349,7 +352,9 @@ public class GitUnstashDialog extends DialogWrapper {
}
});
GitUntrackedFilesOverwrittenByOperationDetector untrackedFilesDetector = new GitUntrackedFilesOverwrittenByOperationDetector(root);
GitLocalChangesWouldBeOverwrittenDetector localChangesDetector = new GitLocalChangesWouldBeOverwrittenDetector(root, MERGE);
h.addLineListener(untrackedFilesDetector);
h.addLineListener(localChangesDetector);
GitUtil.workingTreeChangeStarted(myProject);
try {
@@ -371,6 +376,8 @@ public class GitUnstashDialog extends DialogWrapper {
} else if (untrackedFilesDetector.wasMessageDetected()) {
UntrackedFilesNotifier.notifyUntrackedFilesOverwrittenBy(myProject, root, untrackedFilesDetector.getRelativeFilePaths(),
"unstash", null);
} else if (localChangesDetector.wasMessageDetected()) {
LocalChangesWouldBeOverwrittenHelper.showErrorDialog(myProject, root, "unstash", localChangesDetector.getRelativeFilePaths());
} else if (!res.success()) {
GitUIUtil.showOperationErrors(myProject, h.errors(), h.printableCommandLine());
}
@@ -45,14 +45,8 @@ import javax.swing.event.HyperlinkEvent;
import java.util.List;
/**
* <p>
* The popup which allows to quickly switch and control Git branches.
* </p>
* <p>
* Use {@link #asListPopup()} to achieve the {@link ListPopup} itself.
* </p>
*
* @author Kirill Likhodedov
* <p/>
*/
class GitBranchPopup {
@@ -160,32 +154,28 @@ class GitBranchPopup {
}
private void notifyAboutSyncedBranches() {
VcsNotifier.getInstance(myProject).notifyImportantInfo("Synchronous branch control enabled",
"You have several Git roots in the project and they all are checked out at the same branch. " +
"We've enabled synchronous branch control for the project. <br/>" +
"If you wish to control branches in different roots separately, you may <a href='settings'>disable</a> the setting.",
new NotificationListener() {
@Override
public void hyperlinkUpdate(@NotNull Notification notification,
@NotNull HyperlinkEvent event) {
if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
ShowSettingsUtil.getInstance().showSettingsDialog(myProject, myVcs
.getConfigurable().getDisplayName());
if (myVcsSettings.getSyncSetting() == GitBranchSyncSetting.DONT) {
notification.expire();
}
}
}
}
);
String description = "You have several Git roots in the project and they all are checked out at the same branch. " +
"We've enabled synchronous branch control for the project. <br/>" +
"If you wish to control branches in different roots separately, " +
"you may <a href='settings'>disable</a> the setting.";
NotificationListener listener = new NotificationListener() {
@Override
public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) {
if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
ShowSettingsUtil.getInstance().showSettingsDialog(myProject, myVcs.getConfigurable().getDisplayName());
if (myVcsSettings.getSyncSetting() == GitBranchSyncSetting.DONT) {
notification.expire();
}
}
}
};
VcsNotifier.getInstance(myProject).notifyImportantInfo("Synchronous branch control enabled", description, listener);
}
private ActionGroup createActions() {
DefaultActionGroup popupGroup = new DefaultActionGroup(null, false);
GitRepositoryManager repositoryManager = myRepositoryManager;
if (repositoryManager.moreThanOneRoot()) {
if (userWantsSyncControl()) {
fillWithCommonRepositoryActions(popupGroup, repositoryManager);
}
@@ -196,7 +186,6 @@ class GitBranchPopup {
else {
fillPopupWithCurrentRepositoryActions(popupGroup, null);
}
popupGroup.addSeparator();
return popupGroup;
}
@@ -36,10 +36,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
*
* @author Kirill Likhodedov
*/
class GitBranchPopupActions {
private final Project myProject;
@@ -123,7 +119,7 @@ class GitBranchPopupActions {
public void update(AnActionEvent e) {
if (myRepository.isFresh()) {
e.getPresentation().setEnabled(false);
e.getPresentation().setDescription("Checkout is not possible before the first commit.");
e.getPresentation().setDescription("Checkout is not possible before the first commit");
}
}
}
@@ -18,12 +18,10 @@ package git4idea.update;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.VcsNotifier;
import com.intellij.openapi.vcs.update.UpdatedFiles;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ui.UIUtil;
import git4idea.GitBranch;
import git4idea.GitUtil;
import git4idea.branch.GitBranchPair;
@@ -78,42 +76,12 @@ public class GitRebaseUpdater extends GitUpdater {
return dest.getName();
}
// TODO
//if (!checkLocallyModified(myRoot)) {
// cancel();
// updateSucceeded.set(false);
//}
// TODO: show at any case of update successfullibility, also don't show here but for all roots
//if (mySkippedCommits.size() > 0) {
// GitSkippedCommits.showSkipped(myProject, mySkippedCommits);
//}
public void cancel() {
myRebaser.abortRebase(myRoot);
myProgressIndicator.setText2("Refreshing files for the root " + myRoot.getPath());
myRoot.refresh(false, true);
}
/**
* Check and process locally modified files
*
* @param root the project root
* @param ex the exception holder
*/
protected boolean checkLocallyModified(final VirtualFile root) throws VcsException {
final Ref<Boolean> cancelled = new Ref<Boolean>(false);
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
public void run() {
if (!GitUpdateLocallyModifiedDialog.showIfNeeded(myProject, root)) {
cancelled.set(true);
}
}
});
return !cancelled.get();
}
@Override
public String toString() {
return "Rebase updater";
@@ -1,71 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="git4idea.update.GitUpdateLocallyModifiedDialog">
<grid id="27dc6" binding="myRootPanel" layout-manager="GridLayoutManager" row-count="3" 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="514" height="322"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="62c84" class="javax.swing.JLabel">
<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>
<text resource-bundle="git4idea/i18n/GitBundle" key="update.locally.modified.git.root"/>
</properties>
</component>
<component id="24443" class="javax.swing.JLabel" binding="myGitRoot">
<constraints>
<grid row="0" column="1" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value=""/>
</properties>
</component>
<component id="e43af" class="javax.swing.JLabel" binding="myDescriptionLabel">
<constraints>
<grid row="1" column="0" row-span="1" col-span="3" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value=""/>
</properties>
</component>
<scrollpane class="com.intellij.ui.components.JBScrollPane" id="bde6e">
<constraints>
<grid row="2" column="1" row-span="1" col-span="1" vsize-policy="7" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="5d604" class="com.intellij.ui.components.JBList" binding="myFilesList" default-binding="true">
<constraints/>
<properties>
<toolTipText resource-bundle="git4idea/i18n/GitBundle" key="update.locally.modified.files.tooltip"/>
</properties>
</component>
</children>
</scrollpane>
<component id="8f763" class="javax.swing.JButton" binding="myRescanButton" default-binding="true">
<constraints>
<grid row="2" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="1" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="git4idea/i18n/GitBundle" key="update.locally.modified.rescan"/>
<toolTipText resource-bundle="git4idea/i18n/GitBundle" key="update.locally.modified.rescan.tooltip"/>
</properties>
</component>
<component id="ab434" class="javax.swing.JLabel">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="9" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<labelFor value="bde6e"/>
<text resource-bundle="git4idea/i18n/GitBundle" key="update.locally.modified.files"/>
<verticalAlignment value="0"/>
</properties>
</component>
</children>
</grid>
</form>
@@ -1,211 +0,0 @@
/*
* Copyright 2000-2009 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 git4idea.update;
import com.intellij.openapi.application.ApplicationNamesInfo;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ui.UIUtil;
import com.intellij.vcsUtil.VcsUtil;
import git4idea.GitUtil;
import git4idea.commands.GitCommand;
import git4idea.commands.GitSimpleHandler;
import git4idea.util.StringScanner;
import git4idea.i18n.GitBundle;
import git4idea.rollback.GitRollbackEnvironment;
import git4idea.util.GitUIUtil;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* The dialog that displays locally modified files during update process
*/
public class GitUpdateLocallyModifiedDialog extends DialogWrapper {
/**
* The rescan button
*/
private JButton myRescanButton;
/**
* The list of files to revert
*/
private JList myFilesList;
private JLabel myDescriptionLabel;
/**
* The git root label
*/
private JLabel myGitRoot;
/**
* The root panel
*/
private JPanel myRootPanel;
/**
* The collection with locally modified files
*/
private final List<String> myLocallyModifiedFiles;
/**
* The constructor
*
* @param project the current project
* @param root the vcs root
* @param locallyModifiedFiles the collection of locally modified files to use
*/
protected GitUpdateLocallyModifiedDialog(final Project project, final VirtualFile root, List<String> locallyModifiedFiles) {
super(project, true);
myLocallyModifiedFiles = locallyModifiedFiles;
setTitle(GitBundle.getString("update.locally.modified.title"));
myGitRoot.setText(root.getPresentableUrl());
myFilesList.setModel(new DefaultListModel());
setOKButtonText(GitBundle.getString("update.locally.modified.revert"));
syncListModel();
myRescanButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
myLocallyModifiedFiles.clear();
try {
scanFiles(project, root, myLocallyModifiedFiles);
}
catch (VcsException ex) {
GitUIUtil.showOperationError(project, ex, "Checking for locally modified files");
}
}
});
myDescriptionLabel
.setText(GitBundle.message("update.locally.modified.message", ApplicationNamesInfo.getInstance().getFullProductName()));
init();
}
/**
* Refresh list model according to the current content of the collection
*/
private void syncListModel() {
DefaultListModel listModel = (DefaultListModel)myFilesList.getModel();
listModel.removeAllElements();
for (String p : myLocallyModifiedFiles) {
listModel.addElement(p);
}
}
/**
* {@inheritDoc}
*/
@Override
protected JComponent createCenterPanel() {
return myRootPanel;
}
/**
* {@inheritDoc}
*/
@Override
protected String getDimensionServiceKey() {
return getClass().getName();
}
/**
* Scan working tree and detect locally modified files
*
* @param project the project to scan
* @param root the root to scan
* @param files the collection with files
* @throws VcsException if there problem with running git or working tree is dirty in unsupported way
*/
private static void scanFiles(Project project, VirtualFile root, List<String> files) throws VcsException {
String rootPath = root.getPath();
GitSimpleHandler h = new GitSimpleHandler(project, root, GitCommand.DIFF);
h.addParameters("--name-status");
h.setSilent(true);
h.setStdoutSuppressed(true);
StringScanner s = new StringScanner(h.run());
while (s.hasMoreData()) {
if (s.isEol()) {
s.line();
continue;
}
if (s.tryConsume("M\t")) {
String path = rootPath + "/" + GitUtil.unescapePath(s.line());
files.add(path);
}
else {
throw new VcsException("Working tree is dirty in unsupported way: " + s.line());
}
}
}
/**
* Show the dialog if needed
*
* @param project the project
* @param root the vcs root
* @return true if showing is not needed or operation completed successfully
*/
public static boolean showIfNeeded(final Project project, final VirtualFile root) {
final ArrayList<String> files = new ArrayList<String>();
try {
scanFiles(project, root, files);
final AtomicBoolean rc = new AtomicBoolean(true);
if (!files.isEmpty()) {
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
public void run() {
GitUpdateLocallyModifiedDialog d = new GitUpdateLocallyModifiedDialog(project, root, files);
d.show();
rc.set(d.isOK());
}
});
if (rc.get()) {
if (!files.isEmpty()) {
revertFiles(project, root, files);
}
}
}
return rc.get();
}
catch (final VcsException e) {
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
public void run() {
GitUIUtil.showOperationError(project, e, "Checking for locally modified files");
}
});
return false;
}
}
/**
* Revert files from the list
*
* @param project the project
* @param root the vcs root
* @param files the files to revert
*/
private static void revertFiles(Project project, VirtualFile root, ArrayList<String> files) throws VcsException {
// TODO consider deleted files
GitRollbackEnvironment rollback = GitRollbackEnvironment.getInstance(project);
ArrayList<FilePath> list = new ArrayList<FilePath>(files.size());
for (String p : files) {
list.add(VcsUtil.getFilePath(p));
}
rollback.revert(root, list);
}
}
@@ -35,12 +35,12 @@ import java.util.List;
public class LocalChangesWouldBeOverwrittenHelper {
@NotNull
public static String getErrorNotificationDescription() {
private static String getErrorNotificationDescription() {
return getErrorDescription(true);
}
@NotNull
public static String getErrorDialogDescription() {
private static String getErrorDialogDescription() {
return getErrorDescription(false);
}
@@ -56,7 +56,7 @@ public class LocalChangesWouldBeOverwrittenHelper {
}
}
public static void showErrorNotification(@NotNull final Project project, @NotNull VirtualFile root, @NotNull final String operationName,
public static void showErrorNotification(@NotNull final Project project, @NotNull final VirtualFile root, @NotNull final String operationName,
@NotNull final Collection<String> relativeFilePaths) {
final Collection<String> absolutePaths = GitUtil.toAbsolute(root, relativeFilePaths);
final List<Change> changes = GitUtil.findLocalChangesForPaths(project, root, absolutePaths, false);
@@ -66,20 +66,33 @@ public class LocalChangesWouldBeOverwrittenHelper {
@Override
protected void hyperlinkActivated(@NotNull Notification notification,
@NotNull HyperlinkEvent e) {
String title = "Local Changes Prevent from " + StringUtil.capitalize(operationName);
String description = getErrorDialogDescription();
if (changes.isEmpty()) {
GitUtil.showPathsInDialog(project, absolutePaths, title, description);
}
else {
DialogBuilder builder = new DialogBuilder(project);
builder.setNorthPanel(new MultiLineLabel(description));
builder.setCenterPanel(new ChangesBrowserWithRollback(project, changes));
builder.addOkAction();
builder.setTitle(title);
builder.show();
}
showErrorDialog(project, operationName, changes, absolutePaths);
}
});
}
public static void showErrorDialog(@NotNull Project project, @NotNull VirtualFile root, @NotNull String operationName,
@NotNull Collection<String> relativeFilePaths) {
Collection<String> absolutePaths = GitUtil.toAbsolute(root, relativeFilePaths);
List<Change> changes = GitUtil.findLocalChangesForPaths(project, root, absolutePaths, false);
showErrorDialog(project, operationName, changes, absolutePaths);
}
private static void showErrorDialog(@NotNull Project project, @NotNull String operationName, @NotNull List<Change> changes,
@NotNull Collection<String> absolutePaths) {
String title = "Local Changes Prevent from " + StringUtil.capitalize(operationName);
String description = getErrorDialogDescription();
if (changes.isEmpty()) {
GitUtil.showPathsInDialog(project, absolutePaths, title, description);
}
else {
DialogBuilder builder = new DialogBuilder(project);
builder.setNorthPanel(new MultiLineLabel(description));
builder.setCenterPanel(new ChangesBrowserWithRollback(project, changes));
builder.addOkAction();
builder.setTitle(title);
builder.show();
}
}
}
@@ -48,6 +48,7 @@ import org.jetbrains.plugins.github.util.*;
import java.io.IOException;
import java.util.Collections;
import static git4idea.commands.GitLocalChangesWouldBeOverwrittenDetector.Operation.CHECKOUT;
import static org.jetbrains.plugins.github.util.GithubUtil.setVisibleEnabled;
/**
@@ -256,7 +257,9 @@ public class GithubRebaseAction extends DumbAwareAction {
final GitUntrackedFilesOverwrittenByOperationDetector untrackedFilesDetector =
new GitUntrackedFilesOverwrittenByOperationDetector(root);
final GitLocalChangesWouldBeOverwrittenDetector localChangesDetector = new GitLocalChangesWouldBeOverwrittenDetector(root, CHECKOUT);
handler.addLineListener(untrackedFilesDetector);
handler.addLineListener(localChangesDetector);
GitTask pullTask = new GitTask(project, handler, "Rebasing from upstream/master");
pullTask.setProgressIndicator(indicator);
@@ -271,7 +274,8 @@ public class GithubRebaseAction extends DumbAwareAction {
@Override
protected void onFailure() {
GitUpdateResult result = rebaser.handleRebaseFailure(handler, root, rebaseConflictDetector, untrackedFilesDetector);
GitUpdateResult result = rebaser.handleRebaseFailure(handler, root, rebaseConflictDetector,
untrackedFilesDetector, localChangesDetector);
repositoryManager.updateRepository(root);
if (result == GitUpdateResult.NOTHING_TO_UPDATE ||
result == GitUpdateResult.SUCCESS ||
@@ -16,6 +16,7 @@
package org.jetbrains.plugins.groovy.lang.formatter
import com.intellij.psi.codeStyle.CommonCodeStyleSettings
import org.jetbrains.plugins.groovy.GroovyLanguage
/**
* @author Max Medvedev
@@ -24,7 +25,7 @@ class WrappingTest extends GroovyFormatterTestCase {
@Override
protected void setUp() throws Exception {
super.setUp()
myTempSettings.RIGHT_MARGIN = 10
myTempSettings.setRightMargin(GroovyLanguage.INSTANCE, 10);
}
void testWrapChainedMethodCalls() {
@@ -1,126 +0,0 @@
/*
* Copyright 2000-2011 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.zmlx.hg4idea;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.VcsNotifier;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.Nullable;
import org.zmlx.hg4idea.action.HgCommandResultNotifier;
import org.zmlx.hg4idea.command.HgPushCommand;
import org.zmlx.hg4idea.execution.HgCommandResult;
import org.zmlx.hg4idea.execution.HgCommandResultHandler;
import org.zmlx.hg4idea.ui.HgPushDialog;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class HgPusher {
private static final Logger LOG = Logger.getInstance(HgPusher.class);
private static Pattern PUSH_COMMITS_PATTERN = Pattern.compile(".*added (\\d+) changesets.*");
// hg push command has definite exit values for some cases:
// mercurial returns 0 if push was successful, 1 if nothing to push. see hg push --help
private static int PUSH_SUCCEEDED_EXIT_VALUE = 0;
private static int NOTHING_TO_PUSH_EXIT_VALUE = 1;
private final Project myProject;
public HgPusher(Project project) {
myProject = project;
}
/* public void showDialogAndPush(@NotNull List<? extends Repository> selectedRepositories) {
if (selectedRepositories.isEmpty()) {
VcsBalloonProblemNotifier.showOverChangesView(myProject, "No Mercurial repositories in the project", MessageType.ERROR);
return;
}
final AtomicReference<HgPushCommand> pushCommand = new AtomicReference<HgPushCommand>();
final HgPushDialog dialog = new HgPushDialog(myProject, repositories, selectedRepo);
dialog.show();
if (d2.isOK()) {
pushCommand.set(preparePushCommand(myProject, dialog));
new Task.Backgroundable(myProject, "Pushing...", false) {
@Override
public void run(@NotNull ProgressIndicator indicator) {
if (pushCommand.get() != null) {
push(myProject, pushCommand.get());
}
}
}.queue();
}
}*/
public static void push(final Project project, HgPushCommand command) {
final VirtualFile repo = command.getRepo();
command.execute(new HgCommandResultHandler() {
@Override
public void process(@Nullable HgCommandResult result) {
if (result == null) {
return;
}
if (result.getExitValue() == PUSH_SUCCEEDED_EXIT_VALUE) {
int commitsNum = getNumberOfPushedCommits(result);
String successTitle = "Pushed successfully";
String successDescription = String.format("Pushed %d %s [%s]", commitsNum, StringUtil.pluralize("commit", commitsNum),
repo.getPresentableName());
VcsNotifier.getInstance(project).notifySuccess(successTitle, successDescription);
}
else if (result.getExitValue() == NOTHING_TO_PUSH_EXIT_VALUE) {
VcsNotifier.getInstance(project).notifySuccess("Nothing to push");
}
else {
new HgCommandResultNotifier(project).notifyError(result, "Push failed",
"Failed to push to [" + repo.getPresentableName() + "]");
}
}
});
}
private static HgPushCommand preparePushCommand(Project project, HgPushDialog dialog) {
final HgPushCommand command = new HgPushCommand(project, dialog.getRepository().getRoot(), dialog.getTarget());
command.setRevision(dialog.getRevision());
command.setForce(dialog.isForce());
command.setBranchName(dialog.getBranch());
command.setBookmarkName(dialog.getBookmarkName());
command.setIsNewBranch(dialog.isNewBranch());
return command;
}
private static int getNumberOfPushedCommits(HgCommandResult result) {
int numberOfCommitsInAllSubrepos = 0;
final List<String> outputLines = result.getOutputLines();
for (String outputLine : outputLines) {
outputLine = outputLine.trim();
final Matcher matcher = PUSH_COMMITS_PATTERN.matcher(outputLine);
if (matcher.matches()) {
try {
numberOfCommitsInAllSubrepos += Integer.parseInt(matcher.group(1));
}
catch (NumberFormatException e) {
LOG.error("getNumberOfPushedCommits ", e);
return -1;
}
}
}
return numberOfCommitsInAllSubrepos;
}
}
@@ -71,7 +71,7 @@ public class HgOutgoingCommitsProvider extends OutgoingCommitsProvider {
if (HgErrorUtil.isAbortLine(error)) {
if (HgErrorUtil.isAuthorizationError(error)) {
VcsError authorizationError =
new VcsError(error + "<a href='authentificate'>" + LOGIN_AND_REFRESH_LINK + "</a>", new VcsErrorHandler() {
new VcsError(error + "<a href='authenticate'>" + LOGIN_AND_REFRESH_LINK + "</a>", new VcsErrorHandler() {
public void handleError(@NotNull CommitLoader commitLoader) {
commitLoader.reloadCommits();
}
@@ -16,16 +16,23 @@
package org.zmlx.hg4idea.push;
import com.intellij.dvcs.push.PushSource;
import org.jetbrains.annotations.NotNull;
public class HgSource implements PushSource {
String mySource;
public class HgPushSource implements PushSource {
@NotNull private String myBranch;
public HgSource(String branch) {
mySource = branch;
public HgPushSource(@NotNull String branch) {
myBranch = branch;
}
@NotNull
@Override
public String getPresentation() {
return mySource;
return myBranch;
}
@NotNull
public String getBranch() {
return myBranch; // presentation may differ from branch
}
}
@@ -80,9 +80,9 @@ public class HgPushSupport extends PushSupport<HgRepository> {
@NotNull
@Override
public HgSource getSource(@NotNull HgRepository repository) {
public HgPushSource getSource(@NotNull HgRepository repository) {
String localBranch = HgUtil.getActiveBranchName(repository);
return new HgSource(localBranch);
return new HgPushSource(localBranch);
}
@Override
@@ -19,16 +19,34 @@ import com.intellij.dvcs.push.PushSpec;
import com.intellij.dvcs.push.Pusher;
import com.intellij.dvcs.push.VcsPushOptionValue;
import com.intellij.dvcs.repo.Repository;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.VcsNotifier;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.zmlx.hg4idea.action.HgCommandResultNotifier;
import org.zmlx.hg4idea.command.HgPushCommand;
import org.zmlx.hg4idea.execution.HgCommandResult;
import org.zmlx.hg4idea.execution.HgCommandResultHandler;
import org.zmlx.hg4idea.repo.HgRepository;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class HgPusher extends Pusher {
private static final Logger LOG = Logger.getInstance(HgPusher.class);
private static final String ONE = "one";
private static Pattern PUSH_COMMITS_PATTERN = Pattern.compile(".*(?:added|pushed) (\\d+|" + ONE + ") changeset.*");
// hg push command has definite exit values for some cases:
// mercurial returns 0 if push was successful, 1 if nothing to push. see hg push --help
private static int PUSH_SUCCEEDED_EXIT_VALUE = 0;
private static int NOTHING_TO_PUSH_EXIT_VALUE = 1;
@Override
public void push(@NotNull Map<Repository, PushSpec> pushSpecs, @Nullable VcsPushOptionValue vcsPushOptionValue, boolean force) {
for (Map.Entry<Repository, PushSpec> entry : pushSpecs.entrySet()) {
@@ -39,23 +57,71 @@ public class HgPusher extends Pusher {
if (destination == null) {
continue;
}
HgSource source = (HgSource)hgSpec.getSource();
HgPushSource source = (HgPushSource)hgSpec.getSource();
Project project = repository.getProject();
final HgPushCommand pushCommand = new HgPushCommand(project, repository.getRoot(), destination.myTarget);
pushCommand.setIsNewBranch(true); // set always true, because it just allow mercurial to create a new one if needed
pushCommand.setForce(force);
if (source.mySource.equals(hgRepository.getCurrentBookmark())) {
String branchName = source.getBranch();
if (branchName.equals(hgRepository.getCurrentBookmark())) {
if (vcsPushOptionValue == HgVcsPushOptionValue.Current) {
pushCommand.setBookmarkName(source.mySource);
pushCommand.setBookmarkName(branchName);
}
else {
pushCommand.setRevision(source.mySource);
pushCommand.setRevision(branchName);
}
}
else {
pushCommand.setBranchName(source.mySource);
pushCommand.setBranchName(branchName);
}
org.zmlx.hg4idea.HgPusher.push(project, pushCommand);
push(project, pushCommand);
}
}
public static void push(@NotNull final Project project, @NotNull HgPushCommand command) {
final VirtualFile repo = command.getRepo();
command.execute(new HgCommandResultHandler() {
@Override
public void process(@Nullable HgCommandResult result) {
if (result == null) {
return;
}
if (result.getExitValue() == PUSH_SUCCEEDED_EXIT_VALUE) {
int commitsNum = getNumberOfPushedCommits(result);
String successTitle = "Pushed successfully";
String successDescription = String.format("Pushed %d %s [%s]", commitsNum, StringUtil.pluralize("commit", commitsNum),
repo.getPresentableName());
VcsNotifier.getInstance(project).notifySuccess(successTitle, successDescription);
}
else if (result.getExitValue() == NOTHING_TO_PUSH_EXIT_VALUE) {
VcsNotifier.getInstance(project).notifySuccess("Nothing to push");
}
else {
new HgCommandResultNotifier(project).notifyError(result, "Push failed",
"Failed to push to [" + repo.getPresentableName() + "]");
}
}
});
}
private static int getNumberOfPushedCommits(@NotNull HgCommandResult result) {
int numberOfCommitsInAllSubrepos = 0;
final List<String> outputLines = result.getOutputLines();
for (String outputLine : outputLines) {
outputLine = outputLine.trim();
final Matcher matcher = PUSH_COMMITS_PATTERN.matcher(outputLine);
if (matcher.matches()) {
try {
String numberOfCommits = matcher.group(1);
numberOfCommitsInAllSubrepos += ONE.equals(numberOfCommits) ? 1 : Integer.parseInt(numberOfCommits);
}
catch (NumberFormatException e) {
LOG.error("getNumberOfPushedCommits ", e);
return -1;
}
}
}
return numberOfCommitsInAllSubrepos;
}
}
@@ -11,11 +11,13 @@ import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.fileEditor.*;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.VerticalFlowLayout;
import com.intellij.openapi.util.UserDataHolderBase;
import com.intellij.openapi.vfs.ReadonlyStatusHandler;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.HyperlinkLabel;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.util.ExceptionUtil;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -42,8 +44,10 @@ public class SceneBuilderEditor extends UserDataHolderBase implements FileEditor
private final CardLayout myLayout = new CardLayout();
private final JPanel myPanel = new JPanel(myLayout);
private final JPanel myErrorPanel = new JPanel(new VerticalFlowLayout(VerticalFlowLayout.TOP, 10, 5, true, false));
//private final JPanel myErrorPanel = new JPanel(new VerticalFlowLayout(VerticalFlowLayout.TOP, 10, 5, true, false));
private final JPanel myErrorPanel = new JPanel(new BorderLayout());
private final HyperlinkLabel myErrorLabel = new HyperlinkLabel();
private JTextArea myErrorStack;
private final Document myDocument;
private final ExternalChangeListener myChangeListener;
@@ -72,11 +76,19 @@ public class SceneBuilderEditor extends UserDataHolderBase implements FileEditor
}
});
myErrorPanel.add(myErrorLabel);
myErrorStack = new JTextArea(50, 20);
myErrorStack.setEditable(false);
myErrorPanel.add(myErrorLabel, BorderLayout.NORTH);
myErrorPanel.add(ScrollPaneFactory.createScrollPane(myErrorStack), BorderLayout.CENTER);
myPanel.add(myErrorPanel);
}
private void showErrorPage(State state, Throwable e) {
if (e != null) {
LOG.info(e);
}
removeSceneBuilder();
if (e == null) {
@@ -93,12 +105,21 @@ public class SceneBuilderEditor extends UserDataHolderBase implements FileEditor
}
myErrorLabel.setIcon(Messages.getWarningIcon());
}
myErrorStack.setText(null);
myErrorStack.setVisible(false);
}
else {
myErrorLabel.setHyperlinkText("Error: " + e.getMessage(), "", "");
String message = e.getMessage();
if (message == null) {
message = e.getClass().getName();
}
myErrorLabel.setHyperlinkText("Error: " + message, "", "");
myErrorLabel.setIcon(Messages.getErrorIcon());
LOG.info(e);
myErrorStack.setText(ExceptionUtil.getThrowableText(e));
myErrorStack.setVisible(true);
}
myLayout.show(myPanel, ERROR_CARD);
}
@@ -139,8 +160,12 @@ public class SceneBuilderEditor extends UserDataHolderBase implements FileEditor
}
@Override
public void handleError(Throwable e) {
showErrorPage(null, e);
public void handleError(final Throwable e) {
UIUtil.invokeLaterIfNeeded(new Runnable() {
public void run() {
showErrorPage(null, e);
}
});
}
private void initSceneBuilder(boolean choosePathIfEmpty) {
@@ -11,6 +11,8 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
@@ -33,7 +35,8 @@ import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.util.Map;
import java.util.*;
import java.util.List;
public class StudyCheckAction extends DumbAwareAction {
@@ -111,13 +114,26 @@ public class StudyCheckAction extends DumbAwareAction {
if (openedFile != null) {
StudyTaskManager taskManager = StudyTaskManager.getInstance(project);
final TaskFile selectedTaskFile = taskManager.getTaskFile(openedFile);
List<VirtualFile> filesToDelete = new ArrayList<VirtualFile>();
if (selectedTaskFile != null) {
VirtualFile windowsDescription = StudyUtils.flushWindows(selectedEditor.getDocument(), selectedTaskFile, openedFile);
FileDocumentManager.getInstance().saveAllDocuments();
final VirtualFile taskDir = openedFile.getParent();
Task currentTask = selectedTaskFile.getTask();
StudyStatus oldStatus = currentTask.getStatus();
Map<String, TaskFile> taskFiles = selectedTaskFile.getTask().getTaskFiles();
for (Map.Entry<String, TaskFile> entry : taskFiles.entrySet()) {
String name = entry.getKey();
TaskFile taskFile = entry.getValue();
VirtualFile virtualFile = taskDir.findChild(name);
if (virtualFile == null) {
continue;
}
VirtualFile windowFile = StudyUtils.flushWindows(FileDocumentManager.getInstance().getDocument(virtualFile), taskFile, virtualFile);
filesToDelete.add(windowFile);
FileDocumentManager.getInstance().saveAllDocuments();
}
StudyRunAction runAction = (StudyRunAction)ActionManager.getInstance().getAction(StudyRunAction.ACTION_ID);
if (runAction != null) {
if (runAction != null && currentTask.getTaskFiles().size() == 1) {
runAction.run(project);
}
final StudyTestRunner testRunner = new StudyTestRunner(currentTask, taskDir);
@@ -131,36 +147,68 @@ public class StudyCheckAction extends DumbAwareAction {
if (testProcess != null) {
String failedMessage = testRunner.getPassedTests(testProcess);
if (failedMessage.equals(StudyTestRunner.TEST_OK)) {
currentTask.setStatus(StudyStatus.Solved);
currentTask.setStatus(StudyStatus.Solved, oldStatus);
StudyUtils.updateStudyToolWindow(project);
selectedTaskFile.drawAllWindows(selectedEditor);
ProjectView.getInstance(project).refresh();
for (VirtualFile file:filesToDelete) {
try {
file.delete(this);
}
catch (IOException e) {
LOG.error(e);
}
}
createTestResultPopUp("Congratulations!", JBColor.GREEN, project);
return;
}
final TaskFile taskFileCopy = new TaskFile();
final VirtualFile copyWithAnswers = getCopyWithAnswers(taskDir, openedFile, selectedTaskFile, taskFileCopy);
for (final TaskWindow taskWindow : taskFileCopy.getTaskWindows()) {
if (!taskWindow.isValid(selectedEditor.getDocument())) {
for (Map.Entry<String, TaskFile> entry : taskFiles.entrySet()) {
String name = entry.getKey();
TaskFile taskFile = entry.getValue();
TaskFile answerTaskFile = new TaskFile();
VirtualFile virtualFile = taskDir.findChild(name);
if (virtualFile == null) {
continue;
}
check(project, taskWindow, copyWithAnswers, taskFileCopy, selectedTaskFile, selectedEditor.getDocument(), testRunner,
openedFile);
VirtualFile answerFile = getCopyWithAnswers(taskDir, virtualFile, taskFile, answerTaskFile);
for (TaskWindow taskWindow : answerTaskFile.getTaskWindows()) {
Document document = FileDocumentManager.getInstance().getDocument(virtualFile);
if (document == null) {
continue;
}
if (!taskWindow.isValid(document)) {
continue;
}
check(project, taskWindow, answerFile, answerTaskFile, taskFile, document, testRunner, virtualFile);
}
FileEditor fileEditor = FileEditorManager.getInstance(project).getSelectedEditor(virtualFile);
Editor editor = null;
if (fileEditor instanceof StudyEditor) {
StudyEditor studyEditor = (StudyEditor) fileEditor;
editor = studyEditor.getEditor();
}
if (editor != null) {
taskFile.drawAllWindows(editor);
StudyUtils.synchronize();
}
try {
answerFile.delete(this);
}
catch (IOException e) {
LOG.error(e);
}
}
try {
copyWithAnswers.delete(this);
for (VirtualFile file:filesToDelete) {
try {
file.delete(this);
}
catch (IOException e) {
LOG.error(e);
}
}
catch (IOException e) {
LOG.error(e);
}
try {
windowsDescription.delete(this);
}
catch (IOException e) {
LOG.error("failed to delete windows description", e);
}
selectedTaskFile.drawAllWindows(selectedEditor);
currentTask.setStatus(StudyStatus.Failed, oldStatus);
StudyUtils.updateStudyToolWindow(project);
createTestResultPopUp(failedMessage, JBColor.RED, project);
}
}
@@ -183,7 +231,7 @@ public class StudyCheckAction extends DumbAwareAction {
VirtualFile openedFile) {
try {
VirtualFile windowCopy = answerFile.copy(this, answerFile.getParent(), "window" + taskWindow.getIndex() + ".py");
VirtualFile windowCopy = answerFile.copy(this, answerFile.getParent(), answerFile.getNameWithoutExtension() + "_window" + taskWindow.getIndex() + ".py");
final FileDocumentManager documentManager = FileDocumentManager.getInstance();
final Document windowDocument = documentManager.getDocument(windowCopy);
if (windowDocument != null) {
@@ -216,7 +264,7 @@ public class StudyCheckAction extends DumbAwareAction {
VirtualFile fileWindows = StudyUtils.flushWindows(windowDocument, windowTaskFile, windowCopy);
Process smartTestProcess = testRunner.launchTests(project, windowCopy.getPath());
boolean res = testRunner.getPassedTests(smartTestProcess).equals(StudyTestRunner.TEST_OK);
userTaskWindow.setStatus(res ? StudyStatus.Solved : StudyStatus.Failed);
userTaskWindow.setStatus(res ? StudyStatus.Solved : StudyStatus.Failed, StudyStatus.Unchecked);
windowCopy.delete(this);
fileWindows.delete(this);
if (!resourceFile.delete()) {
@@ -240,7 +288,7 @@ public class StudyCheckAction extends DumbAwareAction {
VirtualFile copy = null;
try {
copy = file.copy(this, taskDir, "answers.py");
copy = file.copy(this, taskDir, file.getNameWithoutExtension() +"_answers.py");
final FileDocumentManager documentManager = FileDocumentManager.getInstance();
final Document document = documentManager.getDocument(copy);
if (document != null) {
@@ -81,13 +81,8 @@ public class StudyRefreshTaskAction extends DumbAwareAction {
document.setText(patternText);
StudyStatus oldStatus = currentTask.getStatus();
LessonInfo lessonInfo = currentTask.getLesson().getLessonInfo();
if (oldStatus == StudyStatus.Failed) {
lessonInfo.setTaskFailed(lessonInfo.getTaskFailed() - 1);
}
if (oldStatus == StudyStatus.Solved) {
lessonInfo.setTaskSolved(lessonInfo.getTaskSolved() - 1);
}
lessonInfo.setTaskUnchecked(lessonInfo.getTaskUnchecked() + 1);
lessonInfo.update(oldStatus, -1);
lessonInfo.update(StudyStatus.Unchecked, +1);
StudyUtils.updateStudyToolWindow(project);
for (TaskWindow taskWindow : selectedTaskFile.getTaskWindows()) {
taskWindow.reset();
@@ -67,12 +67,21 @@ abstract public class StudyTaskNavigationAction extends DumbAwareAction {
if (taskDir == null) {
return;
}
for (String name : nextTaskFiles.keySet()) {
VirtualFile shouldBeActive = null;
for (Map.Entry<String, TaskFile> entry : nextTaskFiles.entrySet()) {
String name = entry.getKey();
TaskFile taskFile = entry.getValue();
VirtualFile vf = taskDir.findChild(name);
if (vf != null) {
FileEditorManager.getInstance(project).openFile(vf, true);
if (!taskFile.getTaskWindows().isEmpty()) {
shouldBeActive = vf;
}
}
}
if (shouldBeActive != null) {
FileEditorManager.getInstance(project).openFile(shouldBeActive, true);
}
}
protected abstract JButton getButton(StudyEditor selectedStudyEditor);
@@ -33,9 +33,9 @@ public class Lesson implements Stateful{
}
@Override
public void setStatus(StudyStatus status) {
public void setStatus(StudyStatus status, StudyStatus oldStatus) {
for (Task task : taskList) {
task.setStatus(status);
task.setStatus(status, oldStatus);
}
}
@@ -40,4 +40,21 @@ public class LessonInfo {
public void setTaskUnchecked(int taskUnchecked) {
myTaskUnchecked = taskUnchecked;
}
public void update(StudyStatus status, int delta) {
switch (status) {
case Solved: {
myTaskSolved += delta;
break;
}
case Failed: {
myTaskFailed += delta;
break;
}
case Unchecked: {
myTaskUnchecked += delta;
break;
}
}
}
}
@@ -2,5 +2,5 @@ package com.jetbrains.python.edu.course;
public interface Stateful {
StudyStatus getStatus();
void setStatus(StudyStatus status);
void setStatus(StudyStatus status, StudyStatus oldStatus);
}
@@ -55,21 +55,14 @@ public class Task implements Stateful{
this.name = name;
}
public void setStatus(@NotNull final StudyStatus status) {
public void setStatus(@NotNull final StudyStatus status, @NotNull final StudyStatus oldStatus) {
LessonInfo lessonInfo = myLesson.getLessonInfo();
StudyStatus oldStatus = getStatus();
if (status != oldStatus) {
if (status == StudyStatus.Failed) {
lessonInfo.setTaskFailed(lessonInfo.getTaskFailed() + 1);
lessonInfo.setTaskUnchecked(lessonInfo.getTaskUnchecked() - 1);
}
if (status == StudyStatus.Solved) {
lessonInfo.setTaskSolved(lessonInfo.getTaskSolved() + 1);
lessonInfo.setTaskUnchecked(lessonInfo.getTaskUnchecked() - 1);
}
for (TaskFile taskFile : taskFiles.values()) {
taskFile.setStatus(status);
}
lessonInfo.update(oldStatus, -1);
lessonInfo.update(status, +1);
}
for (TaskFile taskFile : taskFiles.values()) {
taskFile.setStatus(status, oldStatus);
}
}
@@ -200,9 +200,9 @@ public class TaskFile implements Stateful{
this.taskWindows = taskWindows;
}
public void setStatus(@NotNull final StudyStatus status) {
public void setStatus(@NotNull final StudyStatus status, @NotNull final StudyStatus oldStatus) {
for (TaskWindow taskWindow : taskWindows) {
taskWindow.setStatus(status);
taskWindow.setStatus(status, oldStatus);
}
}
@@ -34,7 +34,7 @@ public class TaskWindow implements Comparable, Stateful {
return myStatus;
}
public void setStatus(StudyStatus status) {
public void setStatus(StudyStatus status, StudyStatus oldStatus) {
myStatus = status;
}
@@ -10,8 +10,6 @@ common.elem.phrasing |= math
SVG.foreignObject.content |=
( math
| html.elem
| body.elem
| common.inner.flow
)
@@ -377,6 +377,7 @@ datatypes w = "http://whattf.org/datatype-draft"
| common.attrs.aria.role.combobox
| common.attrs.aria.role.dialog
| common.attrs.aria.role.directory
| common.attrs.aria.role.group
| common.attrs.aria.role.heading
| common.attrs.aria.role.img
| common.attrs.aria.role.link
@@ -425,7 +426,16 @@ datatypes w = "http://whattf.org/datatype-draft"
( common.attrs
& ( common.attrs.aria.role.presentation
| common.attrs.aria.role.menuitem
| common.attrs.aria.role.button
)?
)
summary.inner =
( common.inner.phrasing )
( common.inner.phrasing
| h1.elem
| h2.elem
| h3.elem
| h4.elem
| h5.elem
| h6.elem
| hgroup.elem
)
@@ -133,15 +133,7 @@ common.attrs.aria.implicit.toolbar |=
& aria.prop.activedescendant?
)
common.attrs.aria.implicit.columnheader |=
( aria.prop.sort?
& aria.prop.readonly?
& aria.prop.required?
& aria.state.selected?
& aria.state.expanded?
)
common.attrs.aria.implicit.rowheader |=
common.attrs.aria.implicit.column-or-row-header |=
( aria.prop.sort?
& aria.prop.readonly?
& aria.prop.required?
@@ -112,6 +112,7 @@ datatypes w = "http://whattf.org/datatype-draft"
& ol.attrs.reversed?
& ol.attrs.type?
& ( ( common.attrs.aria.role.directory
| common.attrs.aria.role.group
| common.attrs.aria.role.list
| common.attrs.aria.role.listbox
| common.attrs.aria.role.menu
@@ -113,9 +113,7 @@ common.attrs =
)
common.attrs.basic =
( ( common.attrs.id
| common.attrs.xml-id
)? # REVISIT assuming only either one is allowed
( common.attrs.id?
& common.attrs.class?
& common.attrs.title?
& common.attrs.base?
@@ -125,10 +123,6 @@ common.attrs.basic =
attribute id {
common.data.id
}
common.attrs.xml-id =
attribute xml:id {
xsd:NCName
} & XMLonly
common.attrs.class =
attribute class {
common.data.tokens
@@ -387,6 +381,10 @@ common.attrs.other =
common.data.keylabellist =
w:keylabellist
## List of Source Sizes
common.data.source.size.list =
w:source-size-list
## Microdata Properties
common.data.microdata-properties =
list { w:microdata-property+ }
@@ -466,7 +464,7 @@ common.attrs.aria.implicit.article = ( notAllowed )
common.attrs.aria.implicit.banner = ( notAllowed )
common.attrs.aria.implicit.button = ( notAllowed )
common.attrs.aria.implicit.checkbox = ( notAllowed )
common.attrs.aria.implicit.columnheader = ( notAllowed )
common.attrs.aria.implicit.column-or-row-header = ( notAllowed )
common.attrs.aria.implicit.combobox = ( notAllowed )
common.attrs.aria.implicit.complementary = ( notAllowed )
common.attrs.aria.implicit.contentinfo = ( notAllowed )
@@ -486,7 +484,6 @@ common.attrs.aria.implicit.option = ( notAllowed )
common.attrs.aria.implicit.progressbar = ( notAllowed )
common.attrs.aria.implicit.radio = ( notAllowed )
common.attrs.aria.implicit.region = ( notAllowed )
common.attrs.aria.implicit.rowheader = ( notAllowed )
common.attrs.aria.implicit.section = ( notAllowed )
common.attrs.aria.implicit.select = ( notAllowed )
common.attrs.aria.implicit.slider = ( notAllowed )
@@ -15,6 +15,7 @@ datatypes w = "http://whattf.org/datatype-draft"
( common.attrs
& script.attrs.type?
& script.attrs.language? # restricted in Schematron
& embedded.content.attrs.crossorigin?
& ( common.attrs.aria.role.presentation
| common.attrs.aria.role.menuitem
)?
@@ -29,6 +30,7 @@ datatypes w = "http://whattf.org/datatype-draft"
& script.attrs.type?
& script.attrs.charset?
& script.attrs.language? # restricted in Schematron
& embedded.content.attrs.crossorigin?
& ( common.attrs.aria.role.presentation
| common.attrs.aria.role.menuitem
)?
@@ -15,12 +15,15 @@ namespace local = ""
img.attrs =
( common.attrs
& img.attrs.src
& img.attrs.srcset?
& img.attrs.sizes?
& img.attrs.alt? # ARIA: if alt empty, only allowed role value is "presentation"; check in assertions
& img.attrs.height?
& img.attrs.width?
& img.attrs.usemap?
& img.attrs.ismap?
& img.attrs.border? # obsolete
& embedded.content.attrs.crossorigin?
& ( common.attrs.aria.implicit.img
| common.attrs.aria
)?
@@ -29,6 +32,14 @@ namespace local = ""
attribute src {
common.data.uri.non-empty
}
img.attrs.srcset =
attribute srcset {
string
} & v5only
img.attrs.sizes =
attribute sizes {
common.data.source.size.list
} & v5only
img.attrs.alt =
attribute alt {
text
@@ -58,6 +69,54 @@ namespace local = ""
common.elem.phrasing |= img.elem
## Image with multiple sources: <picture>
picture.elem =
element picture { picture.inner & picture.attrs }
& v5only
picture.attrs =
( common.attrs )
picture.inner =
( ( source.picture.elem*
& common.elem.script-supporting*
),
( img.elem
& common.elem.script-supporting*
)
)
common.elem.phrasing |= picture.elem
## Picture source: <source srcset>
source.picture.elem =
element source { source.picture.inner & source.picture.attrs }
source.picture.attrs =
( common.attrs
& source.picture.attrs.media?
& source.picture.attrs.srcset
& source.picture.attrs.sizes?
& source.picture.attrs.type?
)
source.picture.attrs.media =
attribute media {
common.data.mediaquery
}
source.picture.attrs.srcset =
attribute srcset {
string
}
source.picture.attrs.sizes =
attribute sizes {
common.data.source.size.list
}
source.picture.attrs.type =
attribute type {
common.data.mimetype
}
source.picture.inner =
( empty )
## Plug-ins: <embed>
embed.elem =
@@ -72,6 +131,7 @@ namespace local = ""
& ( common.attrs.aria.landmark.application
| common.attrs.aria.landmark.document
| common.attrs.aria.role.img
| common.attrs.aria.role.presentation
)?
)
embed.attrs.src =
@@ -292,6 +352,7 @@ namespace local = ""
& ( common.attrs.aria.landmark.application
| common.attrs.aria.landmark.document
| common.attrs.aria.role.img
| common.attrs.aria.role.presentation
)?
)
object.attrs.data =
@@ -370,6 +431,7 @@ namespace local = ""
& ( common.attrs.aria.landmark.application
| common.attrs.aria.landmark.document
| common.attrs.aria.role.img
| common.attrs.aria.role.presentation
)?
)
iframe.attrs.src =
@@ -548,7 +610,7 @@ namespace local = ""
w:string "allowfullscreen" | w:string ""
} & v5only
iframe.inner =
( text )
( ( text & HTMLonly ) | empty )
common.elem.phrasing |= iframe.elem
@@ -655,3 +717,10 @@ namespace local = ""
( empty )
common.elem.phrasing |= area.elem
## Attributes Common to Embedded Content
embedded.content.attrs.crossorigin =
attribute crossorigin {
w:string "anonymous" | w:string "use-credentials" | w:string ""
} & v5only
@@ -812,6 +812,12 @@ datatypes w = "http://whattf.org/datatype-draft"
}
object.attrs &= object.attrs.border?
table.attrs.border =
attribute border {
string
}
table.attrs &= table.attrs.border?
## cellpadding attribute
table.attrs.cellpadding =
@@ -14,6 +14,7 @@ datatypes w = "http://whattf.org/datatype-draft"
& media.attrs.loop?
& media.attrs.mediagroup?
& media.attrs.muted?
& embedded.content.attrs.crossorigin?
)
media.attrs.autoplay =
attribute autoplay {
@@ -48,7 +49,6 @@ datatypes w = "http://whattf.org/datatype-draft"
( common.attrs
& source.attrs.src
& source.attrs.type?
& source.attrs.media?
& ( common.attrs.aria.role.presentation
| common.attrs.aria.role.menuitem
)?
@@ -61,10 +61,6 @@ datatypes w = "http://whattf.org/datatype-draft"
attribute type {
common.data.mimetype
}
source.attrs.media =
attribute media {
common.data.mediaquery
}
source.inner =
( empty )
@@ -164,6 +164,7 @@ datatypes w = "http://whattf.org/datatype-draft"
& shared-hyperlink.attrs.type?
& link.attrs.sizes?
# link.attrs.title included in common.attrs
& embedded.content.attrs.crossorigin?
& ( common.attrs.aria.role.link
| common.attrs.aria.role.presentation
| common.attrs.aria.role.menuitem
@@ -25,7 +25,7 @@ common.attrs.microdata =
}
common.attrs.microdata.itemtype =
attribute itemtype {
common.data.uri.absolute
list { common.data.uri.absolute+ }
}
common.attrs.microdata.itemid =
attribute itemid {
@@ -58,6 +58,7 @@ base.attrs &= common.attrs.microdata
& shared-hyperlink.attrs.type?
& link.attrs.sizes?
# link.attrs.title included in common.attrs
& embedded.content.attrs.crossorigin?
& ( common.attrs.aria.role.link
| common.attrs.aria.role.presentation
| common.attrs.aria.role.menuitem
@@ -1,5 +1,5 @@
nonRDFaLite = empty
# #####################################################################
# #####################################################################
## RELAX NG Schema for HTML 5: RDFa 1.1 and RDFa Lite 1.1 #
# #####################################################################
@@ -9,12 +9,12 @@ nonRDFaLite = empty
common.data.rdfa.safecurie =
xsd:string {
pattern = "\[(([\i-[:]][\c-[:]]*)?:)?[^\s]+\]"
minLength = "3"
pattern = "\[(([\i-[:]][\c-[:]]*)?:?)[^\s]*\]"
minLength = "2"
}
common.data.rdfa.curie =
xsd:string {
pattern = "(([\i-[:]][\c-[:]]*)?:)?[^\s]+"
pattern = "(([\i-[:]][\c-[:]]*)?:)[^\s]*"
minLength = "1"
}
common.data.rdfa.term =
@@ -182,15 +182,11 @@ link.rdfa.attrs.metadata =
& common.attrs.present
& common.attrs.other
& ( ( common.attrs.rdfa.property
& ( link.attrs.rel
| common.attrs.rdfa.rel
)?
& link.attrs.rel?
)
|
( common.attrs.rdfa.property?
& ( link.attrs.rel
| common.attrs.rdfa.rel
)
& link.attrs.rel
)
)
& link.attrs.href
@@ -208,6 +204,11 @@ link.rdfa.attrs.metadata =
& shared-hyperlink.attrs.type?
& link.attrs.sizes?
# link.attrs.title included in common.attrs
& embedded.content.attrs.crossorigin?
& ( common.attrs.aria.role.link
| common.attrs.aria.role.presentation
| common.attrs.aria.role.menuitem
)?
)
link.rdfa.attrs.phrasing =
( common.attrs.basic
@@ -215,9 +216,7 @@ link.rdfa.attrs.phrasing =
& common.attrs.present
& common.attrs.other
& common.attrs.rdfa.property
& ( link.attrs.rel
| common.attrs.rdfa.rel
)?
& link.attrs.rel?
& ( ( common.attrs.rdfa.resource
& link.attrs.href?
)
@@ -239,6 +238,11 @@ link.rdfa.attrs.phrasing =
& shared-hyperlink.attrs.type?
& link.attrs.sizes?
# link.attrs.title included in common.attrs
& embedded.content.attrs.crossorigin?
& ( common.attrs.aria.role.link
| common.attrs.aria.role.presentation
| common.attrs.aria.role.menuitem
)?
)
common.elem.metadata |= link.rdfa.elem.metadata
common.elem.phrasing |= link.rdfa.elem.phrasing
@@ -50,13 +50,8 @@ datatypes w = "http://whattf.org/datatype-draft"
element table { table.inner & table.attrs }
table.attrs =
( common.attrs
& table.attrs.border?
& common.attrs.aria?
)
table.attrs.border =
attribute border {
string
}
table.inner =
( caption.elem?
, common.elem.script-supporting*
@@ -84,9 +79,7 @@ datatypes w = "http://whattf.org/datatype-draft"
element caption { caption.inner & caption.attrs }
caption.attrs =
( common.attrs
& ( common.attrs.aria.role.presentation
| common.attrs.aria.role.menuitem
)?
& common.attrs.aria?
)
caption.inner =
( common.inner.flow )
@@ -100,9 +93,7 @@ datatypes w = "http://whattf.org/datatype-draft"
element colgroup { colgroup.inner & colgroup.attrs }
colgroup.attrs =
( common.attrs
& ( common.attrs.aria.role.presentation
| common.attrs.aria.role.menuitem
)?
& common.attrs.aria?
)
colgroup.attrs.span =
attribute span {
@@ -122,9 +113,7 @@ datatypes w = "http://whattf.org/datatype-draft"
col.attrs =
( common.attrs
& col.attrs.span?
& ( common.attrs.aria.role.presentation
| common.attrs.aria.role.menuitem
)?
& common.attrs.aria?
)
col.attrs.span =
attribute span {
@@ -139,9 +128,7 @@ datatypes w = "http://whattf.org/datatype-draft"
element thead { thead.inner & thead.attrs }
thead.attrs =
( common.attrs
& ( common.attrs.aria.role.presentation
| common.attrs.aria.role.menuitem
)?
& common.attrs.aria?
)
thead.inner =
( tr.elem*
@@ -154,9 +141,7 @@ datatypes w = "http://whattf.org/datatype-draft"
element tfoot { tfoot.inner & tfoot.attrs }
tfoot.attrs =
( common.attrs
& ( common.attrs.aria.role.presentation
| common.attrs.aria.role.menuitem
)?
& common.attrs.aria?
)
tfoot.inner =
( tr.elem*
@@ -169,9 +154,7 @@ datatypes w = "http://whattf.org/datatype-draft"
element tbody { tbody.inner & tbody.attrs }
tbody.attrs =
( common.attrs
& ( common.attrs.aria.role.presentation
| common.attrs.aria.role.menuitem
)?
& common.attrs.aria?
)
tbody.inner =
( tr.elem*
@@ -253,7 +236,9 @@ datatypes w = "http://whattf.org/datatype-draft"
& tables.attrs.scope?
& tables.attrs.headers?
# & tables.attrs.alignment
& common.attrs.aria?
& ( common.attrs.aria?
| common.attrs.aria.implicit.column-or-row-header
)
)
th.inner =
( common.inner.flow )
@@ -812,6 +812,12 @@ datatypes w = "http://whattf.org/datatype-draft"
}
object.attrs &= object.attrs.border?
table.attrs.border =
attribute border {
string
}
table.attrs &= table.attrs.border?
## cellpadding attribute
table.attrs.cellpadding =
@@ -97,7 +97,7 @@ grammar {
attribute overline-thickness { Number.datatype }?
a:documentation [ "\x{a}" ~ " glyph: Glyph Element\x{a}" ~ " " ]
SVG.glyph.class = notAllowed
SVG.glyph.content = SVG.Description.class*, SVG.glyph.class*
SVG.glyph.content = SVG.Description.class* | SVG.glyph.class*
glyph = element glyph { attlist.glyph, SVG.glyph.content }
attlist.glyph &=
SVG.Core.attrib,
@@ -120,7 +120,7 @@ grammar {
]
SVG.missing-glyph.class = notAllowed
SVG.missing-glyph.content =
SVG.Description.class*, SVG.missing-glyph.class*
SVG.Description.class* | SVG.missing-glyph.class*
missing-glyph =
element missing-glyph {
attlist.missing-glyph, SVG.missing-glyph.content
@@ -62,8 +62,7 @@ grammar {
| SVG.Conditional.class
| SVG.Image.class
| SVG.Shape.class
| SVG.Hyperlink.class
| SVG.Extensibility.class)*
| SVG.Hyperlink.class)*
switch = element switch { attlist.switch, SVG.switch.content }
attlist.switch &=
SVG.Core.attrib,
@@ -57,3 +57,14 @@ foreignElement =
| text
| foreignElement)*
}
SVG.a.content &= SVG.Extensibility.class*
SVG.defs.content &= SVG.Extensibility.class*
SVG.glyph.content &= SVG.Extensibility.class*
SVG.g.content &= SVG.Extensibility.class*
SVG.marker.content &= SVG.Extensibility.class*
SVG.mask.content &= SVG.Extensibility.class*
SVG.missing-glyph.content &= SVG.Extensibility.class*
SVG.pattern.content &= SVG.Extensibility.class*
SVG.svg.content &= SVG.Extensibility.class*
SVG.switch.content &= SVG.Extensibility.class*
SVG.symbol.content &= SVG.Extensibility.class*
@@ -7,7 +7,7 @@
- shared-hyperlink.attrs.ping =
- attribute ping {
- common.data.uris
- } & v5only & nonW3C
- } & v5only
## Emphatic Stress: <em>
@@ -1,7 +1,8 @@
diff -r dd84d714a0da build.py
--- a/build.py Fri Oct 25 13:00:39 2013 +0900
+++ b/build.py Fri Jan 24 13:53:06 2014 +0400
@@ -147,8 +147,8 @@
diff --git a/build.py b/build.py
index ac7ecb3..db7de9c 100755
--- a/build.py
+++ b/build.py
@@ -153,13 +153,13 @@ dependencyJars = runDependencyJars + buildOnlyDependencyJars
moduleNames = [
"syntax",
@@ -10,9 +11,18 @@ diff -r dd84d714a0da build.py
+ # "util",
+ # "xmlparser",
"validator",
- "jing-trang",
- "htmlparser",
- "nu-validator-site",
- "tests",
+ # "jing-trang",
+ # "htmlparser",
+ # "nu-validator-site",
+ # "tests",
]
@@ -875,19 +875,19 @@
javaSafeNamePat = re.compile(r'[^a-zA-Z0-9]')
@@ -926,19 +926,19 @@ def downloadDependencies():
downloadDependency(url, md5sum)
def buildAll():
@@ -42,36 +52,5 @@ diff -r dd84d714a0da build.py
+ # buildXmlParser()
+ # buildValidator()
def hgCloneOrUpdate(mod, baseUrl):
def gitCloneOrUpdate(mod, baseUrl):
if os.path.exists(mod):
@@ -925,18 +925,18 @@
# XXX root dir
for mod in moduleNames:
hgCloneOrUpdate(mod, hgRoot)
- gitCloneOrUpdate("nu-validator-site", gitRoot)
- runCmd('"%s" co http://jing-trang.googlecode.com/svn/branches/validator-nu jing-trang' % (svnCmd))
- hgCloneOrUpdate("htmlparser", parserHgRoot)
- testsRemote = "https://github.com/validator/tests.git"
- testsBranch = "master"
- testsDir = "tests"
- if os.path.exists(testsDir):
- os.chdir(testsDir)
- runCmd('"%s" pull %s %s' % (gitCmd, testsRemote, testsBranch))
- os.chdir("..")
- else:
- runCmd('"%s" clone %s %s' % (gitCmd, testsRemote, testsDir))
+ # gitCloneOrUpdate("nu-validator-site", gitRoot)
+ # runCmd('"%s" co http://jing-trang.googlecode.com/svn/branches/validator-nu jing-trang' % (svnCmd))
+ # hgCloneOrUpdate("htmlparser", parserHgRoot)
+ # testsRemote = "https://github.com/validator/tests.git"
+ # testsBranch = "master"
+ # testsDir = "tests"
+ # if os.path.exists(testsDir):
+ # os.chdir(testsDir)
+ # runCmd('"%s" pull %s %s' % (gitCmd, testsRemote, testsBranch))
+ # os.chdir("..")
+ # else:
+ # runCmd('"%s" clone %s %s' % (gitCmd, testsRemote, testsDir))
def selfUpdate():
hgCloneOrUpdate("build", hgRoot)
@@ -11,9 +11,9 @@ mkdir temp
cd temp
echo ">>>>> Preparing validator build"
hg clone https://bitbucket.org/validator/build build
git clone https://github.com/validator/build build
cd build
hg import "$PATCHES/patch_build.patch" --no-commit
git apply "$PATCHES/patch_build.patch"
cd ..
echo