Merge remote branch 'origin/master'

This commit is contained in:
irengrig
2012-03-02 15:08:24 +04:00
22 changed files with 707 additions and 172 deletions
@@ -2065,10 +2065,6 @@ public class CompileDriver {
try {
for (FileProcessingCompiler.ProcessingItem item : items) {
final VirtualFile file = item.getFile();
if (file == null) {
LOG.error("FileProcessingCompiler.ProcessingItem.getFile() must not return null: compiler " + adapter.getCompiler().getDescription());
continue;
}
final String url = file.getUrl();
allUrls.add(url);
if (!forceCompile && cache.getTimestamp(url) == file.getTimeStamp()) {
@@ -28,22 +28,18 @@ import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiShortNamesCache;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
/**
* Created by IntelliJ IDEA.
* User: Irina.Chernushina
* Date: 8/5/11
* Time: 8:36 PM
* To change this template use File | Settings | File Templates.
*/
* User: Irina.Chernushina
* Date: 8/5/11
* Time: 8:36 PM
*/
public class ExceptionWorker {
@NonNls private static final String AT = "at";
private static final String AT_PREFIX = AT + " ";
private static final TextAttributes HYPERLINK_ATTRIBUTES = EditorColorsManager
.getInstance().getGlobalScheme().getAttributes(CodeInsightColors.HYPERLINK_ATTRIBUTES);
@@ -54,7 +50,7 @@ public class ExceptionWorker {
private PsiClass myClass;
private PsiFile myFile;
private String myMethod;
private Trinity<TextRange,TextRange,TextRange> myInfo;
private Trinity<TextRange, TextRange, TextRange> myInfo;
public ExceptionWorker(Project project, final GlobalSearchScope searchScope) {
myProject = project;
@@ -71,31 +67,32 @@ public class ExceptionWorker {
myMethod = myInfo.getSecond().substring(line);
String className = myInfo.first.substring(line).trim();
final int dollarIndex = className.indexOf('$');
if (dollarIndex >= 0){
if (dollarIndex >= 0) {
className = className.substring(0, dollarIndex);
}
final int lparenthIndex = myInfo.third.getStartOffset();
final int rparenthIndex = myInfo.third.getEndOffset();
final String fileAndLine = line.substring(lparenthIndex + 1, rparenthIndex).trim();
final String fileAndLine = line.substring(lparenthIndex + 1, rparenthIndex);
final int colonIndex = fileAndLine.lastIndexOf(':');
if (colonIndex < 0) return;
if (colonIndex <= 0) return;
final String lineString = fileAndLine.substring(colonIndex + 1);
try{
final String lineString = fileAndLine.substring(colonIndex + 1).trim();
try {
final int lineNumber = Integer.parseInt(lineString);
final PsiManager manager = PsiManager.getInstance(myProject);
final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(manager.getProject());
myClass = psiFacade.findClass(className, mySearchScope);
if (myClass == null) {
myClass = psiFacade.findClass(className, GlobalSearchScope.allScope(myProject));
/*if (myClass == null) {//try to find class according to all dollars in package name
myClass = psiFacade.findClass(className, GlobalSearchScope.allScope(myProject));
}*/
if (myClass == null) return;
myClass = myClass != null ? myClass : psiFacade.findClass(className, GlobalSearchScope.allScope(myProject));
myFile = myClass == null ? null : (PsiFile)myClass.getContainingFile().getNavigationElement();
if (myFile == null) {
// try find the file with the required name
PsiFile[] files = PsiShortNamesCache.getInstance(myProject).getFilesByName(fileAndLine.substring(0, colonIndex).trim());
if (files.length > 0) {
myFile = files[0];
}
}
myFile = (PsiFile) myClass.getContainingFile().getNavigationElement();
if (myFile == null) return;
/*
@@ -120,7 +117,7 @@ public class ExceptionWorker {
}
myResult = new Filter.Result(highlightStartOffset, highlightEndOffset, linkInfo, attributes);
}
catch(NumberFormatException e){
catch (NumberFormatException e) {
//
}
}
@@ -147,34 +144,27 @@ public class ExceptionWorker {
@Nullable
static Trinity<TextRange, TextRange, TextRange> parseExceptionLine(final String line) {
int atIndex = line.indexOf(AT_PREFIX);
if (atIndex < 0) return null;
int lparenIdx = line.indexOf('(');
int rparenIdx = line.indexOf(')', lparenIdx + 1);
if (lparenIdx < 0 || rparenIdx <= lparenIdx) return null;
final int lparenthIndex = line.indexOf('(', atIndex);
if (lparenthIndex < 0) return null;
final int lastDotIndex = line.lastIndexOf('.', lparenthIndex);
if (lastDotIndex < 0 || lastDotIndex < atIndex) return null;
final int rparenthIndex = line.indexOf(')', lparenthIndex);
if (rparenthIndex < 0) return null;
int dotIdx = line.lastIndexOf('.', lparenIdx);
int startIdx = handleSpaces(line, dotIdx, -1, false);
if (startIdx == dotIdx) return null;
// class, method, link
return Trinity.create(adjustedRange(line, atIndex + AT_PREFIX.length(), lastDotIndex),
adjustedRange(line, lastDotIndex + 1, lparenthIndex), new TextRange(lparenthIndex, rparenthIndex));
return Trinity.create(new TextRange(startIdx + 1, handleSpaces(line, dotIdx, -1, true)),
new TextRange(handleSpaces(line, dotIdx + 1, 1, true), handleSpaces(line, lparenIdx + 1, -1, true)),
new TextRange(lparenIdx, rparenIdx));
}
private static TextRange adjustedRange(final String line, final int start, final int end) {
String sub = line.substring(start, end);
return new TextRange(start, end - spacesEnd(sub));
}
private static int spacesEnd(final String s) {
int cnt = 0;
for (int i = s.length() - 1; i >= 0; i--) {
final char c = s.charAt(i);
if (! Character.isSpaceChar(c)) return cnt;
++ cnt;
private static int handleSpaces(String line, int pos, int delta, boolean skip) {
int len = line.length();
while (pos >= 0 && pos < len) {
final char c = line.charAt(pos);
if (skip != Character.isSpaceChar(c)) break;
pos += delta;
}
return 0;
return pos;
}
}
@@ -313,7 +313,7 @@ class ServerState {
lib.setClasspath(library.getPaths());
}
else {
LOG.info("Failed to load global library " + lib.getName());
LOG.info("Failed to load global library " + library.getName());
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
* Copyright 2000-2012 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.
@@ -104,6 +104,8 @@ public interface ActionToolbar extends SwitchProvider, QuickActionProvider {
*/
void updateActionsImmediately();
boolean hasVisibleActions();
/**
* @param component will be used for datacontext computations
*/
@@ -870,6 +870,11 @@ public class ActionToolbarImpl extends JPanel implements ActionToolbar {
}
}
@Override
public boolean hasVisibleActions() {
return !myVisibleActions.isEmpty();
}
public void setTargetComponent(final JComponent component) {
myTargetComponent = component;
@@ -0,0 +1,66 @@
/*
* Copyright 2000-2012 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.roots;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
/**
* @author Kirill Likhodedov
*/
class GitRootError {
private final Type myType;
@NotNull private final VirtualFile myRoot;
enum Type {
EXTRA_ROOT,
UNREGISTERED_ROOT
}
GitRootError(@NotNull Type type, @NotNull VirtualFile root) {
myType = type;
myRoot = root;
}
@Override
public String toString() {
return "GitRootError{" +
"myType=" + myType +
", myRoot=" + myRoot +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
GitRootError error = (GitRootError)o;
if (!myRoot.equals(error.myRoot)) return false;
if (myType != error.myType) return false;
return true;
}
@Override
public int hashCode() {
int result = myType != null ? myType.hashCode() : 0;
result = 31 * result + myRoot.hashCode();
return result;
}
}
@@ -0,0 +1,62 @@
/*
* Copyright 2000-2012 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.roots;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.intellij.openapi.vfs.VirtualFile;
import git4idea.PlatformFacade;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
/**
* Detects actual Git roots and compares them to the ones registered in Settings | Version Control.
*
* @author Kirill Likhodedov
*/
class GitRootErrorsFinder {
@NotNull private final Project myProject;
private final PlatformFacade myPlatformFacade;
GitRootErrorsFinder(@NotNull Project project, @NotNull PlatformFacade platformFacade) {
myProject = project;
myPlatformFacade = platformFacade;
}
@NotNull
Collection<GitRootError> find() {
ProjectLevelVcsManager vcsManager = myPlatformFacade.getVcsManager(myProject);
Collection<VirtualFile> vcsRoots = Arrays.asList(vcsManager.getRootsUnderVcs(myPlatformFacade.getVcs(myProject)));
Collection<VirtualFile> gitRoots = new GitRootDetector(myProject).detect().getRoots();
Collection<GitRootError> errors = new ArrayList<GitRootError>();
for (VirtualFile vcsRoot : vcsRoots) {
if (!gitRoots.contains(vcsRoot)) {
errors.add(new GitRootError(GitRootError.Type.EXTRA_ROOT, vcsRoot));
}
}
for (VirtualFile gitRoot : gitRoots) {
if (!vcsRoots.contains(gitRoot)) {
errors.add(new GitRootError(GitRootError.Type.UNREGISTERED_ROOT, gitRoot));
}
}
return errors;
}
}
@@ -107,13 +107,15 @@ public class GitMergeUpdater extends GitUpdater {
final GitMerger merger,
GitLineHandler mergeHandler) {
final MergeError error = mergeLineListener.getMergeError();
LOG.info("doUpdate merge error: " + error);
LOG.info("merge error: " + error);
if (error == MergeError.CONFLICT) {
LOG.info("Conflict detected");
final boolean allMerged =
new MyConflictResolver(myProject, merger, myRoot).merge();
return allMerged ? GitUpdateResult.SUCCESS : GitUpdateResult.INCOMPLETE;
}
else if (error == MergeError.LOCAL_CHANGES) {
LOG.info("Local changes would be overwritten by merge");
final List<FilePath> paths = getFilesOverwrittenByMerge(mergeLineListener.getOutput());
final Collection<Change> changes = getLocalChangesFilteredByFiles(paths);
final ChangeListViewerDialog dialog = new ChangeListViewerDialog(myProject, changes, false) {
@@ -129,12 +131,16 @@ public class GitMergeUpdater extends GitUpdater {
}
});
return GitUpdateResult.ERROR;
} else if (untrackedFilesWouldBeOverwrittenByMergeDetector.wasMessageDetected()) {
}
else if (untrackedFilesWouldBeOverwrittenByMergeDetector.wasMessageDetected()) {
LOG.info("handleMergeFailure: untracked files would be overwritten by merge");
UntrackedFilesNotifier.notifyUntrackedFilesOverwrittenBy(myProject, untrackedFilesWouldBeOverwrittenByMergeDetector.getFiles(), "merge");
return GitUpdateResult.ERROR;
} else {
GitUIUtil.notifyImportantError(myProject, "Error merging", GitUIUtil.stringifyErrors(mergeHandler.errors()));
}
else {
String errors = GitUIUtil.stringifyErrors(mergeHandler.errors());
LOG.info("Unknown error: " + errors);
GitUIUtil.notifyImportantError(myProject, "Error merging", errors);
return GitUpdateResult.ERROR;
}
}
@@ -193,11 +193,12 @@ public class GitUpdateProcess {
notifyImportantError(myProject, "Error updating " + rootName,
"Updating " + rootName + " failed with an error: " + e.getLocalizedMessage());
} finally {
if (!incomplete) {
restoreLocalChanges(context);
} else {
if (incomplete || !success) {
mySaver.notifyLocalChangesAreNotRestored();
}
else {
restoreLocalChanges(context);
}
}
return success;
}
@@ -0,0 +1,88 @@
/*
* Copyright 2000-2012 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.roots
import git4idea.test.GitMockVirtualFile
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.io.FileUtil
/**
*
* @author Kirill Likhodedov
*/
class AbstractGitRootTest {
/**
* Creates the necessary temporary directories in the filesystem with empty ".git" directories for given roots.
* And creates an instance of the project.
* @param gitRoots path to actual .git roots, relative to the project dir.
*/
Project initProject(Collection<String> gitRoots, Collection<String> projectStructure) {
String projectDir = createDirs(gitRoots)
Project project = [
getBaseDir: { new GitMockVirtualFile(projectDir) }
] as Project
createProjectStructure(project, projectStructure);
project
}
void createProjectStructure(Project project, Collection<String> paths) {
paths.each { String path ->
File file = new File(project.baseDir.path + "/" + path)
file.mkdir()
}
}
/**
* @return path to the project
*/
private static String createDirs(Collection<String> gitRoots) {
if (gitRoots.empty) {
return FileUtil.createTempDirectory("grdt", null);
}
File baseDir = createBaseTempDir()
int maxDepth = findMaxDepthAboveProject(gitRoots)
File projectDir = createChild(baseDir, maxDepth)
gitRoots.each { String path ->
File file = new File(projectDir.path + "/" + path)
file.mkdirs()
file.deleteOnExit()
File gitDir = new File(file, ".git")
gitDir.mkdir()
gitDir.deleteOnExit()
}
return projectDir.path
}
private static File createBaseTempDir() {
FileUtil.createTempDirectory("pref", null)
}
private static File createChild(File base, int depth) {
File dir = base
depth.times { dir = FileUtil.createTempDirectory(dir, "grdt", null)}
dir
}
// Assuming that there are no ".." inside the path - only in the beginning
static int findMaxDepthAboveProject(Collection<String> paths) {
def len = { String path -> path.split("/").count("..") }
len(paths.max(len))
}
}
@@ -58,13 +58,13 @@ class GitIntegrationEnablerTest {
}
@Test
void "Simplest case - 1 root for the whole project => just add VCS root"() {
void "1 root for the whole project, then just add VCS root"() {
doTest given(["."]),
expect(git_init: [])
}
@Test
void "No Git roots => init & notify"() {
void "No Git roots, then init & notify"() {
doTest given( [], false ),
expect( git_init: ["."],
vcs_roots: ["."],
@@ -72,28 +72,28 @@ class GitIntegrationEnablerTest {
}
@Test
void "Below Git, no inside => notify"() {
void "Below Git, no inside, then notify"() {
doTest given([".."], true, true),
expect( git_init: [],
notification("Added Git root: ${path("..")}"))
}
@Test
void "Git for project, some inside => notify"() {
void "Git for project, some inside, then notify"() {
doTest given( [".", "community"]),
expect(git_init: [],
notification("Added Git roots: ${path(".")}, ${path("community")}"))
}
@Test
void "Below Git, some inside => notify"() {
void "Below Git, some inside, then notify"() {
doTest given(["..", "community"], true, true),
expect(git_init: [],
notification("Added Git roots: ${path("..")}, ${path("community")}"))
}
@Test
void "Not under Git, some inside => notify"() {
void "Not under Git, some inside, then notify"() {
doTest given( ["community", "contrib"], false, false),
expect(git_init: [],
notification("Added Git roots: ${path("community")}, ${path("contrib")}"))
@@ -16,20 +16,18 @@
package git4idea.roots
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.vfs.VirtualFile
import git4idea.test.GitMockVirtualFile
import git4idea.test.GitGTestUtil
import org.junit.Before
import org.junit.Test
import static org.junit.Assert.assertEquals
import static org.junit.Assert.fail
import git4idea.test.GitGTestUtil
/**
* @author Kirill Likhodedov
*/
class GitRootDetectorTest {
class GitRootDetectorTest extends AbstractGitRootTest {
Project myProject
@@ -126,7 +124,7 @@ class GitRootDetectorTest {
* @param map
*/
private void doTest(Map map) {
initProject(map.gits, map.project)
myProject = initProject(map.gits, map.project)
testInfo empty: map.expected.empty,
full : map.full,
roots: map.expected,
@@ -144,65 +142,6 @@ class GitRootDetectorTest {
below: false
}
/**
* Creates the necessary temporary directories in the filesystem with empty ".git" directories for given roots.
* And creates an instance of the project.
* @param gitRoots path to actual .git roots, relative to the project dir.
*/
private void initProject(Collection<String> gitRoots, Collection<String> projectStructure) {
String projectDir = createDirs(gitRoots)
myProject = [
getBaseDir: { new GitMockVirtualFile(projectDir) }
] as Project
createProjectStructure(projectStructure);
}
void createProjectStructure(Collection<String> paths) {
paths.each { String path ->
File file = new File(myProject.baseDir.path + "/" + path)
file.mkdir()
}
}
/**
* @return path to the project
*/
private static String createDirs(Collection<String> gitRoots) {
if (gitRoots.empty) {
return FileUtil.createTempDirectory("grdt", null);
}
File baseDir = createBaseTempDir()
int maxDepth = findMaxDepthAboveProject(gitRoots)
File projectDir = createChild(baseDir, maxDepth)
gitRoots.each { String path ->
File file = new File(projectDir.path + "/" + path)
file.mkdirs()
file.deleteOnExit()
File gitDir = new File(file, ".git")
gitDir.mkdir()
gitDir.deleteOnExit()
}
return projectDir.path
}
private static File createBaseTempDir() {
FileUtil.createTempDirectory("pref", null)
}
private static File createChild(File base, int depth) {
File dir = base
depth.times { dir = FileUtil.createTempDirectory(dir, "grdt", null)}
dir
}
// Assuming that there are no ".." inside the path - only in the beginning
static int findMaxDepthAboveProject(Collection<String> paths) {
def len = { String path -> path.split("/").count("..") }
len(paths.max(len))
}
void testInfo(Map expected) {
assertInfo(expected, detect())
}
@@ -0,0 +1,136 @@
/*
* Copyright 2000-2012 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.roots
import com.intellij.openapi.project.Project
import git4idea.test.GitMockVcsManager
import git4idea.test.GitTestPlatformFacade
import org.junit.Before
import org.junit.Test
import static git4idea.test.GitGTestUtil.toAbsolute
import static junit.framework.Assert.assertEquals
import git4idea.test.GitMockVirtualFile
/**
*
* @author Kirill Likhodedov
*/
class GitRootErrorsFinderTest extends AbstractGitRootTest {
Project myProject
GitTestPlatformFacade myPlatformFacade
GitMockVcsManager myVcsManager
@Before
void setUp() {
myPlatformFacade = new GitTestPlatformFacade()
}
@Test
void "No roots => no errors"() {
doTest git: [],
roots: [],
errors: []
}
@Test
void "Same 1 root in both => no errors"() {
doTest git: ["."],
roots: ["."],
errors: []
}
@Test
void "Same 2 roots in both => no errors"() {
doTest git: ["..", "community"],
roots: ["..", "community"],
errors: []
}
@Test
void "One git, no VCS roots => error"() {
doTest git: ["."],
roots: [],
errors: [unreg : ["."]]
}
@Test
void "One VCS root, no gits => error"() {
doTest git: [],
roots: ["."],
errors: [extra: ["."]]
}
@Test
void "One root, but different => 2 errors"() {
doTest git: ["."],
roots: ["community"],
errors: [unreg: ["."], extra: ["community"]]
}
@Test
void "Two roots, one matching, one different => 2 errors"() {
doTest git: [".", "community"],
roots: [".", "contrib"],
errors: [unreg: ["community"], extra: ["contrib"]]
}
@Test
void "Two roots in git, one matching in VCS => error"() {
doTest git: [".", "community"],
roots: ["."],
errors: [unreg: ["community"]]
}
@Test
void "Two roots, both not matching => 4 errors"() {
doTest git: ["..", "community"],
roots: [".", "contrib"],
errors: [unreg: ["..", "community"], extra: [".", "contrib"]]
}
private void doTest(Map map) {
myProject = initProject(map.git, [])
myVcsManager = (GitMockVcsManager) myPlatformFacade.getVcsManager(myProject)
addVcsRoots(map.roots)
Collection<GitRootError> expected = new ArrayList<GitRootError>();
expected.addAll map.errors.unreg.collect { unreg(it) }
expected.addAll map.errors.extra.collect { extra(it) }
Collection<GitRootError> actual = new GitRootErrorsFinder(myProject, myPlatformFacade).find()
assertEquals expected.toSet(), actual.toSet()
}
void addVcsRoots(Collection<String> relativeRoots) {
relativeRoots.each {
String root = toAbsolute(it, myProject)
myVcsManager.addRoots(root)
}
}
GitRootError unreg(String path) {
return new GitRootError(GitRootError.Type.UNREGISTERED_ROOT, new GitMockVirtualFile(toAbsolute(path, myProject)))
}
GitRootError extra(String path) {
return new GitRootError(GitRootError.Type.EXTRA_ROOT, new GitMockVirtualFile(toAbsolute(path, myProject)))
}
}
@@ -34,6 +34,10 @@ public class GitMockVcsManager extends ProjectLevelVcsManager {
Collection<String> myRoots = []
void addRoots(String... roots) {
roots.each { myRoots << it }
}
@Override
VirtualFile[] getRootsUnderVcs(AbstractVcs vcs) {
myRoots.collect { new GitMockVirtualFile(it) }
@@ -122,4 +122,15 @@ class GitMockVirtualFile extends VirtualFile {
myPath;
}
boolean equals(o) {
if (this.is(o)) return true
if (getClass() != o.class) return false
GitMockVirtualFile file = (GitMockVirtualFile)o
if (myPath != file.myPath) return false
return true
}
int hashCode() {
return (myPath != null ? myPath.hashCode() : 0)
}
}
@@ -0,0 +1,116 @@
package org.jetbrains.plugins.gradle.action;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.gradle.task.GradleTaskManager;
import org.jetbrains.plugins.gradle.task.GradleTaskType;
import org.jetbrains.plugins.gradle.ui.GradleDataKeys;
import org.jetbrains.plugins.gradle.ui.GradleProjectStructureNode;
import org.jetbrains.plugins.gradle.util.GradleConstants;
import java.util.*;
/**
* Common super class for actions that are invoked on 'sync project structures' tree nodes.
* <p/>
* Not thread-safe.
*
* @author Denis Zhdanov
* @since 2/29/12 4:15 PM
*/
public abstract class AbstractGradleSyncTreeNodeAction extends AnAction {
private static final Map<String, Helper> HELPERS = new HashMap<String, Helper>();
static {
HELPERS.put(GradleConstants.SYNC_TREE_CONTEXT_MENU_PLACE, new ContextMenuHelper());
HELPERS.put(GradleConstants.SYNC_TREE_FLOATING_TOOLBAR_PLACE, new FloatingToolbarHelper());
}
@SuppressWarnings("unchecked")
@Override
public void update(AnActionEvent e) {
final Project project = PlatformDataKeys.PROJECT.getData(e.getDataContext());
final Helper helper = HELPERS.get(e.getPlace());
if (project == null || helper == null) {
e.getPresentation().setEnabled(false);
e.getPresentation().setVisible(false);
return;
}
Collection<GradleProjectStructureNode<?>> nodes = helper.getTargetNodes(e);
if (nodes != null) {
filterNodes(nodes);
}
helper.updatePresentation(nodes, e.getPresentation());
if (e.getPresentation().isEnabled()) {
final GradleTaskManager taskManager = ServiceManager.getService(GradleTaskManager.class);
if (taskManager != null && taskManager.hasTaskOfTypeInProgress(GradleTaskType.RESOLVE_PROJECT)) {
e.getPresentation().setEnabled(false);
}
}
}
@Override
public void actionPerformed(AnActionEvent e) {
final Project project = PlatformDataKeys.PROJECT.getData(e.getDataContext());
final Helper helper = HELPERS.get(e.getPlace());
if (project == null || helper == null) {
return;
}
final Collection<GradleProjectStructureNode<?>> nodes = helper.getTargetNodes(e);
if (nodes == null || nodes.isEmpty()) {
return;
}
doActionPerformed(nodes, project);
}
protected abstract void doActionPerformed(@NotNull Collection<GradleProjectStructureNode<?>> nodes, @NotNull Project project);
protected void filterNodes(@NotNull Collection<GradleProjectStructureNode<?>> nodes) {
}
private interface Helper {
@Nullable
Collection<GradleProjectStructureNode<?>> getTargetNodes(@NotNull AnActionEvent e);
void updatePresentation(@Nullable Collection<GradleProjectStructureNode<?>> nodes, @NotNull Presentation presentation);
}
private static class ContextMenuHelper implements Helper {
@Nullable
@Override
public Collection<GradleProjectStructureNode<?>> getTargetNodes(@NotNull AnActionEvent e) {
return GradleDataKeys.SYNC_TREE_SELECTED_NODE.getData(e.getDataContext());
}
@Override
public void updatePresentation(@Nullable Collection<GradleProjectStructureNode<?>> nodes, @NotNull Presentation presentation) {
presentation.setVisible(true);
presentation.setEnabled(nodes != null && !nodes.isEmpty());
}
}
private static class FloatingToolbarHelper implements Helper {
@SuppressWarnings("unchecked")
@Nullable
@Override
public Collection<GradleProjectStructureNode<?>> getTargetNodes(@NotNull AnActionEvent e) {
List<GradleProjectStructureNode<?>> result = new ArrayList<GradleProjectStructureNode<?>>();
final GradleProjectStructureNode<?> node = GradleDataKeys.SYNC_TREE_NODE_UNDER_MOUSE.getData(e.getDataContext());
if (node != null) {
result.add(node);
}
return result;
}
@Override
public void updatePresentation(@Nullable Collection<GradleProjectStructureNode<?>> nodes, @NotNull Presentation presentation) {
boolean active = nodes != null && !nodes.isEmpty();
presentation.setVisible(active);
presentation.setEnabled(active);
}
}
}
@@ -1,21 +1,15 @@
package org.jetbrains.plugins.gradle.action;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.gradle.config.GradleTextAttributes;
import org.jetbrains.plugins.gradle.importing.GradleLocalNodeImportHelper;
import org.jetbrains.plugins.gradle.ui.GradleDataKeys;
import org.jetbrains.plugins.gradle.ui.GradleProjectStructureNode;
import org.jetbrains.plugins.gradle.util.GradleBundle;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
@@ -26,46 +20,32 @@ import java.util.List;
* @author Denis Zhdanov
* @since 2/7/12 10:32 AM
*/
public class GradleImportEntityAction extends AnAction {
public class GradleImportEntityAction extends AbstractGradleSyncTreeNodeAction {
private static final Logger LOG = Logger.getInstance("#" + GradleImportEntityAction.class.getName());
public GradleImportEntityAction() {
getTemplatePresentation().setText(GradleBundle.message("gradle.action.import.entity.text"));
getTemplatePresentation().setDescription(GradleBundle.message("gradle.action.import.entity.description"));
}
@Override
public void update(AnActionEvent e) {
final Collection<GradleProjectStructureNode<?>> nodes = getInterestedNodes(e.getDataContext());
e.getPresentation().setEnabled(!nodes.isEmpty());
protected void filterNodes(@NotNull Collection<GradleProjectStructureNode<?>> nodes) {
for (Iterator<GradleProjectStructureNode<?>> iterator = nodes.iterator(); iterator.hasNext(); ) {
GradleProjectStructureNode<?> node = iterator.next();
if (node.getDescriptor().getAttributes() != GradleTextAttributes.GRADLE_LOCAL_CHANGE) {
iterator.remove();
}
}
}
@Override
public void actionPerformed(AnActionEvent e) {
final Project project = PlatformDataKeys.PROJECT.getData(e.getDataContext());
if (project == null) {
LOG.warn("Can't import gradle-local entities. Reason: target intellij project is undefined");
return;
}
final Collection<GradleProjectStructureNode<?>> nodes = getInterestedNodes(e.getDataContext());
protected void doActionPerformed(@NotNull Collection<GradleProjectStructureNode<?>> nodes, @NotNull Project project) {
final GradleLocalNodeImportHelper importHelper = project.getComponent(GradleLocalNodeImportHelper.class);
importHelper.importNodes(nodes);
}
@NotNull
private static Collection<GradleProjectStructureNode<?>> getInterestedNodes(@NotNull DataContext context) {
final Collection<GradleProjectStructureNode<?>> selectedNodes = GradleDataKeys.SYNC_TREE_NODE.getData(context);
if (selectedNodes == null) {
return Collections.emptyList();
}
List<GradleProjectStructureNode<?>> result = new ArrayList<GradleProjectStructureNode<?>>();
for (GradleProjectStructureNode<?> node : selectedNodes) {
final List<GradleProjectStructureNode<?>> interestedNodes = new ArrayList<GradleProjectStructureNode<?>>();
for (GradleProjectStructureNode<?> node : nodes) {
if (node.getDescriptor().getAttributes() == GradleTextAttributes.GRADLE_LOCAL_CHANGE) {
result.add(node);
interestedNodes.add(node);
}
}
return result;
importHelper.importNodes(interestedNodes);
}
}
@@ -39,6 +39,6 @@ public class GradleRefreshProjectAction extends AbstractGradleLinkedProjectActio
@Override
protected void doActionPerformed(@NotNull final Project project, @NotNull final String linkedProjectPath) {
GradleUtil.refreshProject(project);
GradleUtil.refreshProject(project);
}
}
@@ -29,7 +29,7 @@ import java.util.concurrent.atomic.AtomicLong;
*/
public class GradleProjectStructureChangesDetector extends AbstractProjectComponent {
private static final int REFRESH_DELAY_MILLIS = (int)TimeUnit.SECONDS.toMillis(2);
private static final int REFRESH_DELAY_MILLIS = (int)500;
private final Alarm myAlarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD);
private final AtomicLong myStartRefreshTime = new AtomicLong();
@@ -78,7 +78,7 @@ public class GradleProjectStructureChangesDetector extends AbstractProjectCompon
}
myStartRefreshTime.set(System.currentTimeMillis() + REFRESH_DELAY_MILLIS);
myAlarm.cancelAllRequests();
myAlarm.addRequest(myRequest, REFRESH_DELAY_MILLIS + 16);
myAlarm.addRequest(myRequest, REFRESH_DELAY_MILLIS);
}
private class RefreshRequest implements Runnable {
@@ -1,8 +1,16 @@
package org.jetbrains.plugins.gradle.sync;
import com.intellij.ide.ui.customization.CustomizationUtil;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.actionSystem.ex.ActionManagerEx;
import com.intellij.openapi.actionSystem.ex.AnActionListener;
import com.intellij.openapi.project.Project;
import com.intellij.ui.ColoredSideBorder;
import com.intellij.ui.HintHint;
import com.intellij.ui.HintListener;
import com.intellij.ui.LightweightHint;
import com.intellij.ui.treeStructure.Tree;
import com.intellij.util.Alarm;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NonNls;
@@ -19,8 +27,10 @@ import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EventObject;
import java.util.List;
/**
@@ -30,10 +40,14 @@ import java.util.List;
* @since 11/3/11 3:58 PM
*/
public class GradleProjectStructureChangesPanel extends GradleToolWindowPanel {
private final Alarm myToolbarAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD);
private Tree myTree;
private GradleProjectStructureTreeModel myTreeModel;
private GradleProjectStructureContext myContext;
private Object myNodeUnderMouse;
private LightweightHint myHint;
public GradleProjectStructureChangesPanel(@NotNull Project project, @NotNull GradleProjectStructureContext context) {
super(project, GradleConstants.TOOL_WINDOW_TOOLBAR_PLACE);
@@ -75,7 +89,8 @@ public class GradleProjectStructureChangesPanel extends GradleToolWindowPanel {
result.add(myTree, constraints);
result.setBackground(myTree.getBackground());
CustomizationUtil.installPopupHandler(myTree, GradleConstants.ACTION_GROUP_SYNC_TREE, GradleConstants.SYNC_TREE_PLACE);
CustomizationUtil.installPopupHandler(myTree, GradleConstants.ACTION_GROUP_SYNC_TREE, GradleConstants.SYNC_TREE_CONTEXT_MENU_PLACE);
setupActionHint();
return result;
}
@@ -85,10 +100,119 @@ public class GradleProjectStructureChangesPanel extends GradleToolWindowPanel {
int i = 1;
}
private void setupActionHint() {
final ActionManagerEx actionManager = ActionManagerEx.getInstanceEx();
actionManager.addAnActionListener(new AnActionListener.Adapter() {
@Override
public void afterActionPerformed(AnAction action, DataContext dataContext, AnActionEvent event) {
if (event != null && GradleConstants.SYNC_TREE_FLOATING_TOOLBAR_PLACE.equals(event.getPlace())) {
hideHind();
}
}
}, getProject());
final ActionGroup actionGroup = (ActionGroup)actionManager.getAction(GradleConstants.ACTION_GROUP_SYNC_TREE);
final ActionToolbar toolbar = actionManager.createActionToolbar(GradleConstants.SYNC_TREE_FLOATING_TOOLBAR_PLACE, actionGroup, true);
toolbar.setTargetComponent(this);
final JComponent toolbarComponent = toolbar.getComponent();
toolbarComponent.setOpaque(true);
final Color foreground = myTree.getForeground();
toolbarComponent.setForeground(foreground);
toolbarComponent.setBackground(myTree.getBackground());
toolbarComponent.setBorder(new ColoredSideBorder(foreground, foreground, foreground, foreground, 1));
myTree.addMouseMotionListener(new MouseMotionAdapter() {
Object activeNode;
@Override
public void mouseMoved(MouseEvent e) {
final TreePath path = myTree.getPathForLocation(e.getX(), e.getY());
if (path == null) {
return;
}
final Object node = path.getLastPathComponent();
myNodeUnderMouse = node;
LightweightHint hint = myHint;
if (node == activeNode && hint.isVisible()) {
return;
}
hideHind();
if (!actionManager.isActionPopupStackEmpty()) {
return;
}
toolbar.updateActionsImmediately();
if (!toolbar.hasVisibleActions()) {
activeNode = null;
return;
}
activeNode = node;
final LightweightHint lightweightHint = new LightweightHint(toolbarComponent);
lightweightHint.addHintListener(new HintListener() {
@Override
public void hintHidden(EventObject event) {
activeNode = null;
}
});
final Rectangle bounds = myTree.getPathBounds(path);
if (bounds == null) {
assert false;
return;
}
final Icon icon = ((GradleProjectStructureNode)node).getDescriptor().getOpenIcon();
int xAdjustment = 0;
if (icon != null) {
xAdjustment = icon.getIconWidth();
}
lightweightHint.show(myTree, bounds.x + xAdjustment, bounds.y + bounds.height, myTree, new HintHint(e));
myHint = lightweightHint;
startToolbarTracking();
}
});
myTree.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
// Hide hint on context menu opening.
if (e.isPopupTrigger()) {
hideHind();
}
}
});
}
private void startToolbarTracking() {
myToolbarAlarm.cancelAllRequests();
final int delayMillis = 300;
myToolbarAlarm.addRequest(new Runnable() {
@Override
public void run() {
if (myHint == null) {
return;
}
final Point location = MouseInfo.getPointerInfo().getLocation();
SwingUtilities.convertPointFromScreen(location, GradleProjectStructureChangesPanel.this);
if (GradleProjectStructureChangesPanel.this.contains(location)) {
myToolbarAlarm.addRequest(this, delayMillis);
}
else {
hideHind();
}
}
}, delayMillis);
}
private void hideHind() {
final LightweightHint hint = myHint;
if (hint != null && hint.isVisible()) {
hint.hide();
myToolbarAlarm.cancelAllRequests();
}
}
@Nullable
@Override
public Object getData(@NonNls String dataId) {
if (GradleDataKeys.SYNC_TREE_NODE.is(dataId)) {
if (GradleDataKeys.SYNC_TREE_SELECTED_NODE.is(dataId)) {
TreePath[] paths = myTree.getSelectionPaths();
if (paths == null) {
return null;
@@ -99,6 +223,9 @@ public class GradleProjectStructureChangesPanel extends GradleToolWindowPanel {
}
return result;
}
else if (GradleDataKeys.SYNC_TREE_NODE_UNDER_MOUSE.is(dataId)) {
return myNodeUnderMouse;
}
else {
return super.getData(dataId);
}
@@ -11,7 +11,12 @@ import java.util.Collection;
public class GradleDataKeys {
/** Key for obtaining currently selected nodes at the gradle 'sync project structure' tree. */
public static final DataKey<Collection<GradleProjectStructureNode<?>>> SYNC_TREE_NODE = DataKey.create("gradle.sync.tree.node");
public static final DataKey<Collection<GradleProjectStructureNode<?>>> SYNC_TREE_SELECTED_NODE
= DataKey.create("gradle.sync.tree.node.selected");
/** Key for obtaining node under mouse cursor at the gradle 'sync project structure' tree. */
public static final DataKey<GradleProjectStructureNode<?>> SYNC_TREE_NODE_UNDER_MOUSE
= DataKey.create("gradle.sync.tree.node.under.mouse");
private GradleDataKeys() {
}
@@ -16,8 +16,9 @@ public class GradleConstants {
@NonNls public static final String EXTENSION = "gradle";
@NonNls public static final String DEFAULT_SCRIPT_NAME = "build.gradle";
@NonNls public static final String TOOL_WINDOW_TOOLBAR_PLACE = "GRADLE_SYNC_CHANGES_TOOLBAR";
@NonNls public static final String SYNC_TREE_PLACE = "GRADLE_SYNC_TREE_PLACE";
@NonNls public static final String TOOL_WINDOW_TOOLBAR_PLACE = "GRADLE_SYNC_CHANGES_TOOLBAR";
@NonNls public static final String SYNC_TREE_CONTEXT_MENU_PLACE = "GRADLE_SYNC_TREE_CONTEXT_MENU_PLACE";
@NonNls public static final String SYNC_TREE_FLOATING_TOOLBAR_PLACE = "GRADLE_SYNC_TREE_FLOATING_TOOLBAR_PLACE";
@NonNls public static final String ACTION_GROUP_SYNC_TREE = "Gradle.SyncTreeGroup";