diff --git a/java/execution/impl/src/com/intellij/compiler/options/CompileStepBeforeRun.java b/java/execution/impl/src/com/intellij/compiler/options/CompileStepBeforeRun.java index 4c2a775ccd7e..e7891d8930de 100644 --- a/java/execution/impl/src/com/intellij/compiler/options/CompileStepBeforeRun.java +++ b/java/execution/impl/src/com/intellij/compiler/options/CompileStepBeforeRun.java @@ -48,6 +48,7 @@ public class CompileStepBeforeRun extends BeforeRunTaskProvider ID = Key.create("Make"); private static final Key RUN_CONFIGURATION = Key.create("RUN_CONFIGURATION"); + private static final Key RUN_CONFIGURATION_TYPE_ID = Key.create("RUN_CONFIGURATION_TYPE_ID"); @NonNls protected static final String MAKE_PROJECT_ON_RUN_KEY = "makeProjectOnRun"; @@ -148,6 +149,7 @@ public class CompileStepBeforeRun extends BeforeRunTaskProvider subtypeOf = u.isSubtypeOf(mm.type, m.type); + final int file = myClassToSourceFile.get(cc.name); - if (Difference.weakerAccess(mm.access, m.access) || - ((m.access & Opcodes.ACC_STATIC) > 0 && (mm.access & Opcodes.ACC_STATIC) == 0) || - ((m.access & Opcodes.ACC_STATIC) == 0 && (mm.access & Opcodes.ACC_STATIC) > 0) || - ((m.access & Opcodes.ACC_FINAL) > 0) || - !m.exceptions.equals(mm.exceptions) || - (subtypeOf.isValue() && subtypeOf.value()) || - (subtypeOf.isNone() || !subtypeOf.value()) || - !empty(mm.signature) || !empty(m.signature)) { - final int file = myClassToSourceFile.get(cc.name); - - if (file > 0) { - final String f = myContext.getValue(file); - debug("Complex condition is satisfied, affecting file ", f); - affectedFiles.add(new File(f)); - } + if (file > 0) { + final String f = myContext.getValue(file); + debug("Affecting file ", f); + affectedFiles.add(new File(f)); } } else { @@ -2052,7 +2041,7 @@ public class Mappings { myDebugS.debug(comment, s); } - public void toStream (final PrintStream stream) { + public void toStream(final PrintStream stream) { final Streamable[] data = { myClassToSubclasses, myClassToClassDependency, @@ -2071,7 +2060,7 @@ public class Mappings { "ClassToSourceFile" }; - for (int i = 0; i= 0; + } + public static String command2string(@NotNull Collection command) { final StringBuilder builder = new StringBuilder(); for (Iterator it = command.iterator(); it.hasNext(); ) { diff --git a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidJpsUtil.java b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidJpsUtil.java index f9c7c1b682fa..03c2faeaf491 100644 --- a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidJpsUtil.java +++ b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidJpsUtil.java @@ -435,7 +435,8 @@ class AndroidJpsUtil { } public static boolean isLightBuild(@NotNull CompileContext context) { - return Boolean.parseBoolean(context.getBuilderParameter(AndroidCommonUtils.LIGHT_BUILD_OPTION)); + final String typeId = context.getBuilderParameter("RUN_CONFIGURATION_TYPE_ID"); + return typeId != null && AndroidCommonUtils.isTestConfiguration(typeId); } public static boolean isReleaseBuild(@NotNull CompileContext context) { diff --git a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidPackagingBuilder.java b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidPackagingBuilder.java index 298ffaf8cb02..c0469285bb6c 100644 --- a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidPackagingBuilder.java +++ b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidPackagingBuilder.java @@ -138,8 +138,7 @@ public class AndroidPackagingBuilder extends ProjectLevelBuilder { private static boolean runPngCaching(@NotNull CompileContext context, @NotNull Module module, @NotNull AndroidFileSetStorage storage, - @Nullable AndroidFileSetState state) - throws IOException { + @Nullable AndroidFileSetState state) throws IOException { final AndroidFileSetState savedState = storage.getState(module.getName()); if (context.isMake() && savedState != null && savedState.equalsTo(state)) { return true; @@ -165,6 +164,14 @@ public class AndroidPackagingBuilder extends ProjectLevelBuilder { final File resCacheDir = AndroidJpsUtil.getResourcesCacheDir(context, module); + if (context.isProjectRebuild() && resCacheDir.exists()) { + if (!FileUtil.delete(resCacheDir)) { + context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, + "Cannot delete directory " + resCacheDir.getPath())); + return false; + } + } + if (!resCacheDir.exists()) { if (!resCacheDir.mkdirs()) { context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, diff --git a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidSourceGeneratingBuilder.java b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidSourceGeneratingBuilder.java index b7a70d8a9418..a153b7fa1e12 100644 --- a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidSourceGeneratingBuilder.java +++ b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidSourceGeneratingBuilder.java @@ -18,6 +18,8 @@ import org.jetbrains.android.util.*; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.jps.ClasspathItem; +import org.jetbrains.jps.ClasspathKind; import org.jetbrains.jps.Module; import org.jetbrains.jps.ModuleChunk; import org.jetbrains.jps.incremental.*; @@ -46,6 +48,9 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder { @NonNls private static final String PERMISSION_GROUP_TAG = "permission-group"; @NonNls private static final String NAME_ATTRIBUTE = "name"; + private static final int MIN_PLATFORM_TOOLS_REVISION = 11; + private static final int MIN_SDK_TOOLS_REVISION = 19; + public AndroidSourceGeneratingBuilder() { super(BuilderCategory.SOURCE_GENERATOR); } @@ -70,6 +75,16 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder { } private static ModuleLevelBuilder.ExitCode doBuild(CompileContext context, ModuleChunk chunk) throws IOException { + final Map moduleDataMap = computeModuleDatas(chunk.getModules(), context); + if (moduleDataMap == null || moduleDataMap.size() == 0) { + return ExitCode.ABORT; + } + + if (!checkVersions(moduleDataMap, context)) { + return ExitCode.ABORT; + } + checkAndroidDependencies(moduleDataMap, context); + final Map idlFilesToCompile = new HashMap(); final Map rsFilesToCompile = new HashMap(); @@ -93,12 +108,6 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder { return true; } }); - - final Map moduleDataMap = computeModuleDatas(chunk.getModules(), context); - - if (moduleDataMap == null || moduleDataMap.size() == 0) { - return ExitCode.ABORT; - } boolean success = true; if (context.isProjectRebuild()) { @@ -154,6 +163,75 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder { return success ? ExitCode.OK : ExitCode.ABORT; } + private static boolean checkVersions(@NotNull Map dataMap, @NotNull CompileContext context) { + for (Map.Entry entry : dataMap.entrySet()) { + final Module module = entry.getKey(); + final AndroidPlatform platform = entry.getValue().getPlatform(); + + boolean success = true; + + final int platformToolsRevision = platform.getPlatformToolsRevision(); + if (platformToolsRevision >= 0 && platformToolsRevision < MIN_PLATFORM_TOOLS_REVISION) { + final String message = '[' + + module.getName() + + "] Incompatible version of Android SDK Platform-tools package. Min version is " + + MIN_PLATFORM_TOOLS_REVISION + + ". Please, update it though SDK manager"; + context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, message)); + success = false; + } + + final int sdkToolsRevision = platform.getSdkToolsRevision(); + if (sdkToolsRevision >= 0 && sdkToolsRevision < MIN_SDK_TOOLS_REVISION) { + final String message = '[' + + module.getName() + + "] Incompatible version of Android SDK Tools package. Min version is " + + MIN_SDK_TOOLS_REVISION + + ". Please, update it though SDK manager"; + context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, message)); + success = false; + } + + // show error message only for first module, because all modules usualy have the same sdk specified + if (!success) { + return false; + } + } + return true; + } + + private static void checkAndroidDependencies(@NotNull Map moduleDataMap, @NotNull CompileContext context) { + for (Map.Entry entry : moduleDataMap.entrySet()) { + final Module module = entry.getKey(); + final MyModuleData moduleData = entry.getValue(); + final AndroidFacet facet = moduleData.getFacet(); + + if (facet.isLibrary()) { + continue; + } + + for (ClasspathItem item : module.getClasspath(ClasspathKind.PRODUCTION_RUNTIME, false)) { + if (item instanceof Module) { + final Module depModule = (Module)item; + final AndroidFacet depFacet = AndroidJpsUtil.getFacet(depModule); + + if (depFacet != null && !depFacet.isLibrary()) { + String message = "Suspicious module dependency " + + module.getName() + + " -> " + + depModule.getName() + + ": Android application module depends on other application module. Possibly, you should "; + if (AndroidJpsUtil.isMavenizedModule(depModule)) { + message += "change packaging type of module " + depModule.getName() + " to 'apklib' in pom.xml file or "; + } + message += "change dependency scope to 'Provided'."; + context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.WARNING, message)); + } + } + } + } + } + private static boolean runBuildConfigGeneration(@NotNull CompileContext context, @NotNull Map moduleDataMap, @NotNull AndroidBuildConfigStateStorage storage) { @@ -276,7 +354,7 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder { continue; } - final IAndroidTarget target = moduleData.getAndroidTarget(); + final IAndroidTarget target = moduleData.getPlatform().getTarget(); try { final File[] sourceRoots = AndroidJpsUtil.getSourceRootsForModuleAndDependencies(module); @@ -353,8 +431,9 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder { continue; } - final IAndroidTarget target = moduleData.getAndroidTarget(); - final String sdkLocation = moduleData.getSdkLocation(); + final AndroidPlatform platform = moduleData.getPlatform(); + final IAndroidTarget target = platform.getTarget(); + final String sdkLocation = platform.getSdk().getSdkPath(); final String filePath = file.getPath(); File tmpOutputDirectory = null; @@ -414,7 +493,7 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder { final File generatedSourcesDir = AndroidJpsUtil.getGeneratedSourcesStorage(module); final File aptOutputDirectory = new File(generatedSourcesDir, AndroidJpsUtil.AAPT_GENERATED_SOURCE_ROOT_NAME); - final IAndroidTarget target = moduleData.getAndroidTarget(); + final IAndroidTarget target = moduleData.getPlatform().getTarget(); try { if (!needToRunAaptCompilation(facet)) { @@ -777,8 +856,6 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder { success = false; continue; } - final AndroidSdk androidSdk = platform.getSdk(); - final IAndroidTarget target = platform.getTarget(); final File manifestFile = AndroidJpsUtil.getManifestFileForCompilationPath(facet); if (manifestFile == null || !manifestFile.exists()) { @@ -796,7 +873,7 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder { continue; } - moduleDataMap.put(module, new MyModuleData(androidSdk.getSdkPath(), target, facet, manifestFile, packageName)); + moduleDataMap.put(module, new MyModuleData(platform, facet, manifestFile, packageName)); } return success ? moduleDataMap : null; @@ -883,32 +960,24 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder { } private static class MyModuleData { - private final String mySdkLocation; - private final IAndroidTarget myAndroidTarget; + private final AndroidPlatform myPlatform; private final AndroidFacet myFacet; private final File myManifestFileForCompiler; private final String myPackage; - private MyModuleData(@NotNull String sdkLocation, - @NotNull IAndroidTarget androidTarget, + private MyModuleData(@NotNull AndroidPlatform platform, @NotNull AndroidFacet facet, @NotNull File manifestFileForCompiler, @NotNull String aPackage) { - mySdkLocation = sdkLocation; - myAndroidTarget = androidTarget; + myPlatform = platform; myFacet = facet; myManifestFileForCompiler = manifestFileForCompiler; myPackage = aPackage; } @NotNull - public IAndroidTarget getAndroidTarget() { - return myAndroidTarget; - } - - @NotNull - public String getSdkLocation() { - return mySdkLocation; + public AndroidPlatform getPlatform() { + return myPlatform; } @NotNull diff --git a/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java b/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java index 351fad3391b2..9ada61a6e078 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java +++ b/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java @@ -24,7 +24,6 @@ import com.intellij.compiler.impl.ModuleCompileScope; import com.intellij.compiler.options.CompileStepBeforeRun; import com.intellij.compiler.progress.CompilerTask; import com.intellij.execution.configurations.RunConfiguration; -import com.intellij.execution.junit.JUnitConfiguration; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.compiler.*; @@ -88,10 +87,6 @@ public class AndroidCompileUtil { @NonNls public static final String PROGUARD_CFG_FILE_NAME = "proguard-project.txt"; @NonNls public static final String OLD_PROGUARD_CFG_FILE_NAME = "proguard.cfg"; - @NonNls - private static final String[] SCALA_TEST_CONFIGURATIONS = - {"ScalaTestRunConfiguration", "SpecsRunConfiguration", "Specs2RunConfiguration"}; - private AndroidCompileUtil() { } @@ -622,23 +617,8 @@ public class AndroidCompileUtil { } public static boolean isFullBuild(@NotNull CompileContext context) { - final RunConfiguration runConfiguration = CompileStepBeforeRun.getRunConfiguration(context); - - if (runConfiguration == null) { - return true; - } - - if (runConfiguration instanceof JUnitConfiguration) { - return false; - } - - for (AndroidLightBuildProvider provider : AndroidLightBuildProvider.EP_NAME.getExtensions()) { - if (provider.toPerformLightBuild(runConfiguration)) { - return false; - } - } - final String id = runConfiguration.getType().getId(); - return ArrayUtil.find(SCALA_TEST_CONFIGURATIONS, id) < 0; + final RunConfiguration c = CompileStepBeforeRun.getRunConfiguration(context); + return c == null || !AndroidCommonUtils.isTestConfiguration(c.getType().getId()); } public static boolean isReleaseBuild(@NotNull CompileContext context) { diff --git a/plugins/android/src/org/jetbrains/android/compiler/AndroidPrecompileTask.java b/plugins/android/src/org/jetbrains/android/compiler/AndroidPrecompileTask.java index 7539aa2329a4..e4d6b8b2295f 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/AndroidPrecompileTask.java +++ b/plugins/android/src/org/jetbrains/android/compiler/AndroidPrecompileTask.java @@ -32,7 +32,6 @@ import com.intellij.openapi.roots.DependencyScope; import com.intellij.openapi.roots.ModuleOrderEntry; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.roots.OrderEntry; -import com.intellij.openapi.util.Key; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; @@ -41,7 +40,6 @@ import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.facet.AndroidRootUtil; import org.jetbrains.android.maven.AndroidMavenUtil; import org.jetbrains.android.sdk.AndroidPlatform; -import org.jetbrains.android.util.AndroidCommonUtils; import org.jetbrains.annotations.NotNull; import java.io.File; @@ -54,7 +52,6 @@ import java.util.Set; */ public class AndroidPrecompileTask implements CompileTask { private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.compiler.AndroidPrecompileTask"); - private static final Key LIGHT_BUILD_KEY = Key.create(AndroidCommonUtils.LIGHT_BUILD_OPTION); @Override public boolean execute(CompileContext context) { @@ -97,6 +94,7 @@ public class AndroidPrecompileTask implements CompileTask { excludeAllSourceRoots(module, configuration, addedEntries); } else { + // todo: support this by project converter to use on compile-server unexcludeAllSourceRoots(facet, configuration); } } @@ -106,10 +104,6 @@ public class AndroidPrecompileTask implements CompileTask { LOG.debug("Files excluded by Android: " + addedEntries.size()); CompilerManager.getInstance(project).addCompilationStatusListener(new MyCompilationStatusListener(project, addedEntries), project); } - - if (!AndroidCompileUtil.isFullBuild(context)) { - context.getCompileScope().putUserData(LIGHT_BUILD_KEY, Boolean.toString(true)); - } return true; } diff --git a/plugins/git4idea/src/git4idea/GitBranch.java b/plugins/git4idea/src/git4idea/GitBranch.java index a6d01112aa55..11b06afcdabe 100644 --- a/plugins/git4idea/src/git4idea/GitBranch.java +++ b/plugins/git4idea/src/git4idea/GitBranch.java @@ -101,7 +101,9 @@ public class GitBranch extends GitReference { */ public static Pair splitNameOfRemoteBranch(String branchName) { int firstSlash = branchName.indexOf('/'); - return Pair.create(branchName.substring(0, firstSlash), branchName.substring(firstSlash + 1)); + String remoteName = firstSlash > -1 ? branchName.substring(0, firstSlash) : branchName; + String remoteBranchName = branchName.substring(firstSlash + 1); + return Pair.create(remoteName, remoteBranchName); } /** diff --git a/plugins/git4idea/src/git4idea/GitUtil.java b/plugins/git4idea/src/git4idea/GitUtil.java index 9d21bab8482c..8556b790634b 100644 --- a/plugins/git4idea/src/git4idea/GitUtil.java +++ b/plugins/git4idea/src/git4idea/GitUtil.java @@ -114,6 +114,14 @@ public class GitUtil { else { pathToDir = content; } + + if (!FileUtil.isAbsolute(pathToDir)) { + String canonicalPath = FileUtil.toCanonicalPath(FileUtil.join(rootDir.getPath(), pathToDir)); + if (canonicalPath == null) { + return null; + } + pathToDir = FileUtil.toSystemIndependentName(canonicalPath); + } return VcsUtil.getVirtualFile(pathToDir); } diff --git a/plugins/git4idea/src/git4idea/actions/GitStash.java b/plugins/git4idea/src/git4idea/actions/GitStash.java index 38abfd3f8d34..39339ba71d14 100644 --- a/plugins/git4idea/src/git4idea/actions/GitStash.java +++ b/plugins/git4idea/src/git4idea/actions/GitStash.java @@ -48,9 +48,11 @@ public class GitStash extends GitRepositoryAction { if (!d.isOK()) { return; } - affectedRoots.add(d.getGitRoot()); + VirtualFile root = d.getGitRoot(); + affectedRoots.add(root); final GitLineHandler h = d.handler(); GitHandlerUtil.doSynchronously(h, GitBundle.getString("stashing.title"), h.printableCommandLine()); + root.refresh(true, true); } /** diff --git a/plugins/git4idea/src/git4idea/changes/GitChangeUtils.java b/plugins/git4idea/src/git4idea/changes/GitChangeUtils.java index 80ff6e9cb3dc..4e5f4af077cc 100644 --- a/plugins/git4idea/src/git4idea/changes/GitChangeUtils.java +++ b/plugins/git4idea/src/git4idea/changes/GitChangeUtils.java @@ -15,6 +15,7 @@ */ package git4idea.changes; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; @@ -50,6 +51,8 @@ public class GitChangeUtils { */ public static final String COMMITTED_CHANGELIST_FORMAT = "%ct%n%H%n%P%n%an%x20%x3C%ae%x3E%n%cn%x20%x3C%ce%x3E%n%s%n%x03%n%b%n%x03"; + private static final Logger LOG = Logger.getInstance(GitChangeUtils.class); + /** * A private constructor for utility class */ @@ -194,11 +197,17 @@ public class GitChangeUtils { handler.endOptions(); handler.setNoSSH(true); handler.setSilent(true); - //handler.setSilent(true); String output = handler.run(); StringTokenizer stk = new StringTokenizer(output, "\n\r \t", false); if (!stk.hasMoreTokens()) { - throw new VcsException("The string '" + revisionNumber + "' does not represents a revision number. Output: [" + output + "]"); + GitSimpleHandler dh = new GitSimpleHandler(project, vcsRoot, GitCommand.LOG); + dh.addParameters("-1", "HEAD"); + dh.setNoSSH(true); + dh.setSilent(true); + String out = dh.run(); + LOG.info("Diagnostic output from 'git log -1 HEAD': [" + out + "]"); + throw new VcsException(String.format("The string '%s' does not represent a revision number. Output: [%s]\n Root: %s", + revisionNumber, output, vcsRoot)); } Date timestamp = GitUtil.parseTimestampWithNFEReport(stk.nextToken(), handler, output); return new GitRevisionNumber(stk.nextToken(), timestamp); diff --git a/plugins/git4idea/src/git4idea/checkin/GitCheckinEnvironment.java b/plugins/git4idea/src/git4idea/checkin/GitCheckinEnvironment.java index 18de6c810cb3..a2e21ae6f5ec 100644 --- a/plugins/git4idea/src/git4idea/checkin/GitCheckinEnvironment.java +++ b/plugins/git4idea/src/git4idea/checkin/GitCheckinEnvironment.java @@ -557,6 +557,12 @@ public class GitCheckinEnvironment implements CheckinEnvironment { myDirtyScopeManager.dirDirtyRecursively(root); } + public void reset() { + myNextCommitAmend = false; + myNextCommitAuthor = null; + myNextCommitIsPushed = null; + } + /** * Checkin options for git */ @@ -642,9 +648,8 @@ public class GitCheckinEnvironment implements CheckinEnvironment { */ public void refresh() { myAuthor.setSelectedItem(""); - myNextCommitAuthor = null; myAmend.setSelected(false); - myNextCommitIsPushed = null; + reset(); } /** diff --git a/plugins/git4idea/src/git4idea/history/browser/CherryPicker.java b/plugins/git4idea/src/git4idea/history/browser/CherryPicker.java index 85f5b378deb4..13491fd91270 100644 --- a/plugins/git4idea/src/git4idea/history/browser/CherryPicker.java +++ b/plugins/git4idea/src/git4idea/history/browser/CherryPicker.java @@ -34,6 +34,7 @@ import com.intellij.util.ArrayUtil; import com.intellij.util.Consumer; import com.intellij.util.WaitForProgressToShow; import git4idea.PlatformFacade; +import git4idea.checkin.GitCheckinEnvironment; import git4idea.commands.Git; import git4idea.commands.GitCommandResult; import git4idea.commands.GitMessageWithFilesDetector; @@ -59,6 +60,12 @@ import static git4idea.commands.GitSimpleEventDetector.Event.LOCAL_CHANGES_OVERW public class CherryPicker { + /** + * Name of the {@code .git/CHERRY_PICK_HEAD} file which is stored under {@code .git} when cherry-pick is in progress, + * and contains the hash of the commit being cherry-picked. + */ + private static final String CHERRY_PICK_HEAD_FILE = "CHERRY_PICK_HEAD"; + private static final Logger LOG = Logger.getInstance(CherryPicker.class); @NotNull private final Project myProject; @@ -254,7 +261,7 @@ public class CherryPicker { } private void removeCherryPickHead(@NotNull GitRepository repository) { - File cherryPickHeadFile = new File(repository.getGitDir().getPath(), "CHERRY_PICK_HEAD"); + File cherryPickHeadFile = new File(repository.getGitDir().getPath(), CHERRY_PICK_HEAD_FILE); final VirtualFile cherryPickHead = myPlatformFacade.getLocalFileSystem().refreshAndFindFileByIoFile(cherryPickHeadFile); if (cherryPickHead != null && cherryPickHead.exists()) { @@ -486,8 +493,9 @@ public class CherryPicker { final Collection committingDocs = markCommittingDocs(); try { CheckinEnvironment ce = myPlatformFacade.getVcs(myProject).getCheckinEnvironment(); - if (ce != null) { + if (ce != null && ce instanceof GitCheckinEnvironment) { try { + ((GitCheckinEnvironment)ce).reset(); List exceptions = ce.commit(myChanges, myCommitMessage); VcsDirtyScopeManager.getInstance(myProject).filePathsDirty(ChangesUtil.getPaths(myChanges), null); if (exceptions != null && !exceptions.isEmpty()) { diff --git a/plugins/git4idea/src/git4idea/jgit/GitHttpAdapter.java b/plugins/git4idea/src/git4idea/jgit/GitHttpAdapter.java index 30b272cd363b..325fdb5f98e3 100644 --- a/plugins/git4idea/src/git4idea/jgit/GitHttpAdapter.java +++ b/plugins/git4idea/src/git4idea/jgit/GitHttpAdapter.java @@ -146,7 +146,7 @@ public final class GitHttpAdapter { } private static void logException(GitRepository repository, String remoteName, String remoteUrl, Exception e, String operation) { - LOG.info("Exception while " + operation + " " + remoteName + "(" + remoteUrl + ")" + " in " + repository.toLogString(), e); + LOG.error("Exception while " + operation + " " + remoteName + "(" + remoteUrl + ")" + " in " + repository.toLogString(), e); } private static GitFetchResult.Type convertToFetchResultType(GeneralResult result) { @@ -301,6 +301,18 @@ public final class GitHttpAdapter { rememberPassword(provider); return GeneralResult.SUCCESS; } + catch (InvalidRemoteException e) { + if (!noRemoteWithoutGitErrorFixTried && isNoRemoteWithoutDotGitError(e, url)) { + url += ".git"; + command.setUrl(url); + provider.setUrl(url); + noRemoteWithoutGitErrorFixTried = true; + // don't "eat" one password entering attempt + //noinspection AssignmentToForLoopParameter + i--; + command.cleanup(); + } + } catch (JGitInternalException e) { if (authError(e)) { if (provider.wasCancelled()) { // if user cancels the dialog, just return diff --git a/plugins/git4idea/src/git4idea/merge/GitMergeProvider.java b/plugins/git4idea/src/git4idea/merge/GitMergeProvider.java index 779546da35e0..fcc5fc72b39c 100644 --- a/plugins/git4idea/src/git4idea/merge/GitMergeProvider.java +++ b/plugins/git4idea/src/git4idea/merge/GitMergeProvider.java @@ -19,6 +19,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.history.VcsRevisionNumber; import com.intellij.openapi.vcs.merge.MergeData; import com.intellij.openapi.vcs.merge.MergeProvider2; import com.intellij.openapi.vcs.merge.MergeSession; @@ -37,6 +38,7 @@ import git4idea.commands.GitSimpleHandler; import git4idea.util.StringScanner; import git4idea.i18n.GitBundle; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.util.HashMap; @@ -119,12 +121,7 @@ public class GitMergeProvider implements MergeProvider2 { } mergeData.CURRENT = loadRevisionCatchingErrors(current); mergeData.LAST = loadRevisionCatchingErrors(last); - try { - mergeData.LAST_REVISION_NUMBER = GitRevisionNumber.resolve(myProject, root, myReverse ? "HEAD" : "MERGE_HEAD"); - } - catch (VcsException e) { - // ignore exception, the null value will be used - } + mergeData.LAST_REVISION_NUMBER = findLastRevisionNumber(root); } catch (IOException e) { throw new IllegalStateException("Failed to load file content", e); @@ -135,6 +132,33 @@ public class GitMergeProvider implements MergeProvider2 { return mergeData; } + @Nullable + private VcsRevisionNumber findLastRevisionNumber(@NotNull VirtualFile root) { + if (myReverse) { + try { + return GitRevisionNumber.resolve(myProject, root, "HEAD"); + } + catch (VcsException e) { + log.error("Couldn't resolve the HEAD in " + root, e); + } + } + else { + try { + return GitRevisionNumber.resolve(myProject, root, "MERGE_HEAD"); + } + catch (VcsException e) { + log.info("Couldn't resolved the MERGE_HEAD in " + root, e); // this may be not a bug, just cherry-pick + try { + return GitRevisionNumber.resolve(myProject, root, "CHERRY_PICK_HEAD"); + } + catch (VcsException e1) { + log.error("Couldn't resolve neither MERGE_HEAD, nor the CHERRY_PICK_HEAD in " + root, e1); + } + } + } + return null; + } + private byte[] loadRevisionCatchingErrors(final GitFileRevision revision) throws VcsException, IOException { try { return revision.getContent(); diff --git a/plugins/git4idea/src/git4idea/rebase/GitRebaseDialog.java b/plugins/git4idea/src/git4idea/rebase/GitRebaseDialog.java index e08d4a890787..2dff5fc4aa6e 100644 --- a/plugins/git4idea/src/git4idea/rebase/GitRebaseDialog.java +++ b/plugins/git4idea/src/git4idea/rebase/GitRebaseDialog.java @@ -76,7 +76,7 @@ public class GitRebaseDialog extends DialogWrapper { /** * If selected, rebase is interactive */ - private JCheckBox myInteractiveCheckBox; + protected JCheckBox myInteractiveCheckBox; /** * No merges are performed if selected. */ diff --git a/plugins/git4idea/src/git4idea/rebase/GitRebaseLineListener.java b/plugins/git4idea/src/git4idea/rebase/GitRebaseLineListener.java index b5ab7ceee4a9..62aabee3a3bc 100644 --- a/plugins/git4idea/src/git4idea/rebase/GitRebaseLineListener.java +++ b/plugins/git4idea/src/git4idea/rebase/GitRebaseLineListener.java @@ -61,7 +61,7 @@ public class GitRebaseLineListener extends GitLineHandlerAdapter { assert myStatus == null; myStatus = Status.FINISHED; } - else if (line.startsWith("Automatic cherry-pick failed. ")) { + else if (line.startsWith("Automatic cherry-pick failed") || line.startsWith("When you have resolved this problem")) { assert myStatus == null || myStatus == Status.ERROR; myStatus = Status.CONFLICT; } @@ -69,7 +69,7 @@ public class GitRebaseLineListener extends GitLineHandlerAdapter { assert myStatus == null; myStatus = myProgressLine == null ? Status.CANCELLED : Status.ERROR; } - else if (line.startsWith("fatal") || line.startsWith("error: ")) { + else if (line.startsWith("fatal") || line.startsWith("error: ") || line.startsWith("Cannot rebase")) { if (myStatus != Status.CONFLICT) { myStatus = Status.ERROR; } @@ -97,7 +97,7 @@ public class GitRebaseLineListener extends GitLineHandlerAdapter { else { total = current = 0; } - return new Result(myStatus == null ? Status.ERROR : myStatus, total, current); + return new Result(myStatus == null ? Status.FINISHED : myStatus, total, current); } /** diff --git a/plugins/git4idea/src/git4idea/ui/GitUnstashDialog.java b/plugins/git4idea/src/git4idea/ui/GitUnstashDialog.java index e6accdb510cd..61ce16bfe79a 100644 --- a/plugins/git4idea/src/git4idea/ui/GitUnstashDialog.java +++ b/plugins/git4idea/src/git4idea/ui/GitUnstashDialog.java @@ -406,7 +406,8 @@ public class GitUnstashDialog extends DialogWrapper { if (!d.isOK()) { return; } - affectedRoots.add(d.getGitRoot()); + VirtualFile root = d.getGitRoot(); + affectedRoots.add(root); GitLineHandler h = d.handler(); final AtomicBoolean conflict = new AtomicBoolean(); @@ -418,9 +419,9 @@ public class GitUnstashDialog extends DialogWrapper { } }); int rc = GitHandlerUtil.doSynchronously(h, GitBundle.getString("unstash.unstashing"), h.printableCommandLine(), false); + root.refresh(true, true); if (conflict.get()) { - VirtualFile root = d.getGitRoot(); boolean conflictsResolved = new UnstashConflictResolver(project, root, d.getSelectedStash()).merge(); LOG.info("loadRoot " + root + ", conflictsResolved: " + conflictsResolved); } else if (rc != 0) { diff --git a/plugins/git4idea/src/git4idea/update/GitFetchResult.java b/plugins/git4idea/src/git4idea/update/GitFetchResult.java index 9ed1559588c7..1138bbc9ba6f 100644 --- a/plugins/git4idea/src/git4idea/update/GitFetchResult.java +++ b/plugins/git4idea/src/git4idea/update/GitFetchResult.java @@ -65,6 +65,11 @@ public final class GitFetchResult { public static GitFetchResult error(Exception error) { return error(Collections.singletonList(error)); } + + @NotNull + public static GitFetchResult error(@NotNull String errorMessage) { + return error(new Exception(errorMessage)); + } public boolean isSuccess() { return myType == Type.SUCCESS; diff --git a/plugins/git4idea/src/git4idea/update/GitFetcher.java b/plugins/git4idea/src/git4idea/update/GitFetcher.java index bf01ef78ab95..e7628d655175 100644 --- a/plugins/git4idea/src/git4idea/update/GitFetcher.java +++ b/plugins/git4idea/src/git4idea/update/GitFetcher.java @@ -98,6 +98,30 @@ public class GitFetcher { return fetchResult; } + @NotNull + public GitFetchResult fetch(@NotNull VirtualFile root, @NotNull String remoteName) { + GitRepository repository = myRepositoryManager.getRepositoryForRoot(root); + if (repository == null) { + return logError("Repository can't be null for " + root, myRepositoryManager.toString()); + } + GitRemote remote = GitUtil.findRemoteByName(repository, remoteName); + if (remote == null) { + return logError("Couldn't find remote with the name " + remoteName, null); + } + String url = remote.getFirstUrl(); + if (url == null) { + return logError("URL is null for remote " + remote.getName(), null); + } + return fetchRemote(repository, remote, url); + } + + private static GitFetchResult logError(@NotNull String message, @Nullable String additionalInfo) { + String addInfo = additionalInfo != null ? "\n" + additionalInfo : ""; + LOG.error(message + addInfo); + return GitFetchResult.error(message); + } + + @NotNull private GitFetchResult fetchCurrentRemote(@NotNull GitRepository repository) { FetchParams fetchParams = getFetchParams(repository); if (fetchParams.isError()) { @@ -106,7 +130,11 @@ public class GitFetcher { GitRemote remote = fetchParams.getRemote(); String url = fetchParams.getUrl(); + return fetchRemote(repository, remote, url); + } + @NotNull + private GitFetchResult fetchRemote(@NotNull GitRepository repository, @NotNull GitRemote remote, @NotNull String url) { if (GitHttpAdapter.shouldUseJGit(url)) { return GitHttpAdapter.fetch(repository, remote, url, null); } @@ -299,7 +327,9 @@ public class GitFetcher { additionalInfo.put(root, ai); } if (!result.isSuccess()) { - displayFetchResult(myProject, result, errorNotificationTitle, getErrors()); + Collection errors = new ArrayList(getErrors()); + errors.addAll(result.getErrors()); + displayFetchResult(myProject, result, errorNotificationTitle, errors); return false; } } diff --git a/plugins/git4idea/src/git4idea/util/GitFileUtils.java b/plugins/git4idea/src/git4idea/util/GitFileUtils.java index 4a89f87c4daa..63f040651847 100644 --- a/plugins/git4idea/src/git4idea/util/GitFileUtils.java +++ b/plugins/git4idea/src/git4idea/util/GitFileUtils.java @@ -30,10 +30,7 @@ import git4idea.repo.GitRepository; import git4idea.repo.GitRepositoryManager; import org.jetbrains.annotations.NotNull; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; +import java.util.*; /** * File utilities for the git @@ -179,7 +176,7 @@ public class GitFileUtils { String output = handler.run(); List nonIgnoredFiles = new ArrayList(paths.size()); - List ignoredPaths = Arrays.asList(StringUtil.splitByLines(output)); + Set ignoredPaths = new HashSet(Arrays.asList(StringUtil.splitByLines(output))); for (String pathToCheck : paths) { if (!ignoredPaths.contains(pathToCheck)) { nonIgnoredFiles.add(pathToCheck); diff --git a/plugins/github/src/org/jetbrains/plugins/github/GithubRebaseAction.java b/plugins/github/src/org/jetbrains/plugins/github/GithubRebaseAction.java index 29a06253faef..5967e49727e3 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/GithubRebaseAction.java +++ b/plugins/github/src/org/jetbrains/plugins/github/GithubRebaseAction.java @@ -19,7 +19,9 @@ import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; @@ -35,8 +37,13 @@ import git4idea.commands.GitSimpleHandler; import git4idea.repo.GitRemote; import git4idea.repo.GitRepository; import git4idea.repo.GitRepositoryManager; +import git4idea.update.GitFetchResult; +import git4idea.update.GitFetcher; +import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.github.ui.GithubLoginDialog; +import java.util.concurrent.atomic.AtomicReference; + /** * Created by IntelliJ IDEA. * @@ -141,7 +148,7 @@ public class GithubRebaseAction extends DumbAwareAction { final Ref remoteForForkParentRepo = new Ref(); for (GitRemote gitRemote : gitRepository.getRemotes()) { for (String url : gitRemote.getUrls()) { - if (url.endsWith(parent + ".git")) { + if (url.endsWith("/" + parent + ".git")) { remoteForForkParentRepo.set(gitRemote.getName()); break; } @@ -194,6 +201,10 @@ public class GithubRebaseAction extends DumbAwareAction { }); } + boolean fetchedSuccessfully = fetchParentOrNotifyError(project, gitRepository, remoteForForkParentRepo.get()); + if (!fetchedSuccessfully) { + return; + } BasicAction.saveAll(); final GithubRebase action = (GithubRebase) ActionManager.getInstance().getAction("Github.Rebase.Internal"); action.setRebaseOrigin(remoteForForkParentRepo.get()); @@ -202,6 +213,23 @@ public class GithubRebaseAction extends DumbAwareAction { action.actionPerformed(actionEvent); } + private static boolean fetchParentOrNotifyError(@NotNull final Project project, @NotNull final GitRepository repository, + @NotNull final String remote) { + final AtomicReference fetchResult = new AtomicReference(); + ProgressManager.getInstance().run(new Task.Modal(project, "Fetching " + remote, false) { + @Override + public void run(@NotNull ProgressIndicator indicator) { + fetchResult.set(new GitFetcher(project, indicator, false).fetch(repository.getRoot(), remote)); + } + }); + GitFetchResult result = fetchResult.get(); + if (!result.isSuccess()) { + GitFetcher.displayFetchResult(project, result, null, result.getErrors()); + return false; + } + return true; + } + private void showErrorMessageInEDT(final Project project, final String message) { UIUtil.invokeLaterIfNeeded(new Runnable(){ @Override diff --git a/plugins/github/src/org/jetbrains/plugins/github/ui/GithubRebaseDialog.java b/plugins/github/src/org/jetbrains/plugins/github/ui/GithubRebaseDialog.java index 0b9b571509a9..b6c8e0085261 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/ui/GithubRebaseDialog.java +++ b/plugins/github/src/org/jetbrains/plugins/github/ui/GithubRebaseDialog.java @@ -41,6 +41,7 @@ public class GithubRebaseDialog extends GitRebaseDialog { public void configure(final String originName) { setTitle("Rebase GitHub"); + myInteractiveCheckBox.setSelected(false); myShowRemoteBranchesCheckBox.setSelected(true); myShowRemoteBranchesCheckBox.getParent().remove(myShowRemoteBranchesCheckBox); myGitRootComboBox.setEnabled(false); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/AlignmentProvider.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/AlignmentProvider.java index b4326d6dd936..d1df472da142 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/AlignmentProvider.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/AlignmentProvider.java @@ -35,16 +35,20 @@ class AlignmentProvider { private final Map> myTree = new HashMap>(); private final Map, Alignment> myAlignments = new HashMap, Alignment>(); + private final Map, Boolean> myAllowBackwardShift = new HashMap, Boolean>(); - public void addPair(PsiElement e1, PsiElement e2) { + public void addPair(PsiElement e1, PsiElement e2, Boolean allowBackwardShift) { LOG.assertTrue(e1 != e2); final Set set1 = myTree.get(e1); final Set set2 = myTree.get(e2); - LOG.assertTrue(set1 == null || set2 == null || !myAlignments.containsKey(set1) || !myAlignments.containsKey(set2)); - if (set1 != null && set2 != null) { + LOG.assertTrue(!myAlignments.containsKey(set1) || !myAlignments.containsKey(set2)); + LOG.assertTrue(myAllowBackwardShift.get(set1).booleanValue() == myAllowBackwardShift.get(set2).booleanValue()); + if (allowBackwardShift != null) { + LOG.assertTrue(myAllowBackwardShift.get(set1).booleanValue() == allowBackwardShift.booleanValue()); + } if (myAlignments.containsKey(set2)) { for (Iterator iterator = set1.iterator(); iterator.hasNext(); ) { PsiElement element = iterator.next(); @@ -64,15 +68,23 @@ class AlignmentProvider { } } else if (set1 != null) { + if (allowBackwardShift != null) { + LOG.assertTrue(myAllowBackwardShift.get(set1).booleanValue() == allowBackwardShift.booleanValue()); + } addInternal(set1, e2); } else if (set2 != null) { + if (allowBackwardShift != null) { + LOG.assertTrue(myAllowBackwardShift.get(set2).booleanValue() == allowBackwardShift.booleanValue()); + } addInternal(set2, e1); } else { final HashSet set = createHashSet(); addInternal(set, e1); addInternal(set, e2); + myAllowBackwardShift.put(set, allowBackwardShift); + } } @@ -92,16 +104,17 @@ class AlignmentProvider { }; } - public void addPair(ASTNode node1, ASTNode node2) { - addPair(node1.getPsi(), node2.getPsi()); + public void addPair(ASTNode node1, ASTNode node2, boolean allowBackwardShift) { + addPair(node1.getPsi(), node2.getPsi(), allowBackwardShift); } - private void add(PsiElement element) { + private void add(PsiElement element, boolean allowBackwardShift) { if (myTree.get(element) != null) return; final HashSet set = createHashSet(); set.add(element); myTree.put(element, set); + myAllowBackwardShift.put(set, allowBackwardShift); } @Nullable @@ -114,17 +127,19 @@ class AlignmentProvider { Alignment alignment = myAlignments.get(set); if (alignment != null) return alignment; - alignment = Alignment.createAlignment(true); + alignment = Alignment.createAlignment(myAllowBackwardShift.get(set)); myAlignments.put(set, alignment); return alignment; } - public Aligner createAligner(PsiElement expression) { - return new Aligner(expression); + public Aligner createAligner(PsiElement expression, boolean allowBackwardShift) { + Aligner aligner = new Aligner(allowBackwardShift); + aligner.append(expression); + return aligner; } - public Aligner createAligner() { - return new Aligner(); + public Aligner createAligner(boolean allowBackwardShift) { + return new Aligner(allowBackwardShift); } /** @@ -135,12 +150,10 @@ class AlignmentProvider { */ class Aligner { private PsiElement myRef = null; + private boolean allowBackwardShift = true; - private Aligner() { - } - - private Aligner(PsiElement initial) { - myRef = initial; + Aligner(boolean allowBackwardShift) { + this.allowBackwardShift = allowBackwardShift; } void append(@Nullable PsiElement element) { @@ -148,10 +161,10 @@ class AlignmentProvider { if (myRef == null) { myRef = element; - add(element); + add(element, allowBackwardShift); } else { - addPair(myRef, element); + addPair(myRef, element, allowBackwardShift); } } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/GeeseUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/GeeseUtil.java index cf080d546e77..30115c2fe6f2 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/GeeseUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/GeeseUtil.java @@ -139,7 +139,7 @@ public class GeeseUtil { break; } } - alignments.addPair(rBrace, cur); + alignments.addPair(rBrace, cur, true); } public static boolean isClosureContainLF(PsiElement rBrace) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/GroovyBlockGenerator.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/GroovyBlockGenerator.java index b42bf1cceec7..80aabd5f05a9 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/GroovyBlockGenerator.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/GroovyBlockGenerator.java @@ -49,7 +49,9 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrLabeledStatement; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariableDeclaration; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentLabel; import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument; import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrCodeBlock; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssignmentExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrBinaryExpression; @@ -137,7 +139,7 @@ public class GroovyBlockGenerator implements GroovyElementTypes { if (childNode.getTextRange().getLength() > 0) { final Indent indent = GroovyIndentProcessor.getChildIndent(myBlock, childNode); if (myAlignment != null) { - myAlignmentProvider.addPair(myNode, childNode); + myAlignmentProvider.addPair(myNode, childNode, true); } subBlocks.add(new GroovyBlock(childNode, indent, myWrap, mySettings, myGroovySettings, myAlignmentProvider)); } @@ -148,18 +150,32 @@ public class GroovyBlockGenerator implements GroovyElementTypes { // chained properties, calls, indexing, etc if (NESTED.contains(myNode.getElementType()) && blockPsi.getParent() != null && !NESTED.contains(blockPsi.getParent().getNode().getElementType())) { final List subBlocks = new ArrayList(); - AlignmentProvider.Aligner dotsAligner = mySettings.ALIGN_MULTILINE_CHAINED_METHODS ? myAlignmentProvider.createAligner() : null; + AlignmentProvider.Aligner dotsAligner = mySettings.ALIGN_MULTILINE_CHAINED_METHODS ? myAlignmentProvider.createAligner(true) : null; addNestedChildren(myNode.getPsi(), subBlocks, dotsAligner, true); return subBlocks; } + if (blockPsi instanceof GrListOrMap && ((GrListOrMap)blockPsi).isMap() && myGroovySettings.ALIGN_NAMED_ARGS_IN_MAP) { + AlignmentProvider.Aligner labels = myAlignmentProvider.createAligner(false); + AlignmentProvider.Aligner exprs = myAlignmentProvider.createAligner(true); + GrNamedArgument[] namedArgs = ((GrListOrMap)blockPsi).getNamedArguments(); + for (GrNamedArgument arg : namedArgs) { + GrArgumentLabel label = arg.getLabel(); + if (label != null) labels.append(label); + + PsiElement colon = arg.getColon(); + if (colon == null) colon = arg.getExpression(); + if (colon != null) exprs.append(colon); + } + } + // For Parameter lists if (isListLikeClause(blockPsi)) { final ArrayList subBlocks = new ArrayList(); List astNodes = visibleChildren(myNode); if (mustAlign(blockPsi, astNodes)) { - final AlignmentProvider.Aligner aligner = myAlignmentProvider.createAligner(); + final AlignmentProvider.Aligner aligner = myAlignmentProvider.createAligner(false); for (ASTNode node : astNodes) { if (!isKeyword(node)) aligner.append(node.getPsi()); } @@ -178,7 +194,7 @@ public class GroovyBlockGenerator implements GroovyElementTypes { final ArrayList subBlocks = new ArrayList(); if (classLevel && myAlignment != null) { - final AlignmentProvider.Aligner aligner = myAlignmentProvider.createAligner(); + final AlignmentProvider.Aligner aligner = myAlignmentProvider.createAligner(true); for (ASTNode child : children) { aligner.append(child.getPsi()); } @@ -212,7 +228,7 @@ public class GroovyBlockGenerator implements GroovyElementTypes { else { currentGroup = new ArrayList(); for (LeafPsiElement expression : table) { - currentGroup.add(myAlignmentProvider.createAligner(expression)); + currentGroup.add(myAlignmentProvider.createAligner(expression, true)); } } } @@ -228,9 +244,9 @@ public class GroovyBlockGenerator implements GroovyElementTypes { if (variables.length > 0) { if (!classLevel || currentGroup == null || fieldGroupEnded(psi)) { currentGroup = new ArrayList(); - currentGroup.add(myAlignmentProvider.createAligner()); - currentGroup.add(myAlignmentProvider.createAligner()); - currentGroup.add(myAlignmentProvider.createAligner()); + currentGroup.add(myAlignmentProvider.createAligner(true)); + currentGroup.add(myAlignmentProvider.createAligner(true)); + currentGroup.add(myAlignmentProvider.createAligner(true)); } AlignmentProvider.Aligner varName = currentGroup.get(1); @@ -304,8 +320,10 @@ public class GroovyBlockGenerator implements GroovyElementTypes { // println 'xxx' // }) if (blockPsi instanceof GrArgumentList && mySettings.ALIGN_MULTILINE_PARAMETERS_IN_CALLS) { - return children.size() != 3 || children.get(0).getElementType() != mLPAREN - || children.get(1).getElementType() != CLOSABLE_BLOCK || children.get(2).getElementType() != mRPAREN; + return !(children.size() == 3 && + children.get(0).getElementType() == mLPAREN && + (children.get(1).getElementType() == CLOSABLE_BLOCK || children.get(1).getElementType() == LIST_OR_MAP) && + children.get(2).getElementType() == mRPAREN); } if (blockPsi instanceof GrAssignmentExpression && ((GrAssignmentExpression)blockPsi).getRValue() instanceof GrAssignmentExpression) { @@ -426,7 +444,7 @@ public class GroovyBlockGenerator implements GroovyElementTypes { private List generateForBinaryExpr() { final ArrayList subBlocks = new ArrayList(); AlignmentProvider.Aligner - alignment = mySettings.ALIGN_MULTILINE_BINARY_OPERATION ? myAlignmentProvider.createAligner() : null; + alignment = mySettings.ALIGN_MULTILINE_BINARY_OPERATION ? myAlignmentProvider.createAligner(true) : null; GrBinaryExpression binary = (GrBinaryExpression)myNode.getPsi(); LOG.assertTrue(binary != null); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/GroovyCodeStyleSettings.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/GroovyCodeStyleSettings.java index 2869eb12bcf2..a1b4bef01e24 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/GroovyCodeStyleSettings.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/GroovyCodeStyleSettings.java @@ -44,6 +44,7 @@ public class GroovyCodeStyleSettings extends CustomCodeStyleSettings { public boolean SPACE_IN_NAMED_ARGUMENT = true; public boolean ALIGN_MULTILINE_LIST_OR_MAP = false; public boolean SPACE_WITHIN_LIST_OR_MAP = false; + public boolean ALIGN_NAMED_ARGS_IN_MAP = false; public GroovyCodeStyleSettings(CodeStyleSettings container) { super("GroovyCodeStyleSettings", container); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/GroovyLanguageCodeStyleSettingsProvider.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/GroovyLanguageCodeStyleSettingsProvider.java index a93b3cf1549b..e0166e4369ad 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/GroovyLanguageCodeStyleSettingsProvider.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/GroovyLanguageCodeStyleSettingsProvider.java @@ -43,8 +43,10 @@ public class GroovyLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSe @NotNull SettingsType settingsType) { if (settingsType == SettingsType.WRAPPING_AND_BRACES_SETTINGS) { consumer.showAllStandardOptions(); - consumer.showCustomOption(GroovyCodeStyleSettings.class, "USE_FLYING_GEESE_BRACES", "Use flying geese braces", CodeStyleSettingsCustomizable.WRAPPING_BRACES); + consumer.showCustomOption(GroovyCodeStyleSettings.class, "USE_FLYING_GEESE_BRACES", "Use flying geese braces", + CodeStyleSettingsCustomizable.WRAPPING_BRACES); consumer.showCustomOption(GroovyCodeStyleSettings.class, "ALIGN_MULTILINE_LIST_OR_MAP", "Align when multiple", "List and map literals"); + consumer.showCustomOption(GroovyCodeStyleSettings.class, "ALIGN_NAMED_ARGS_IN_MAP", "Align named arguments", "List and map literals"); return; } if (settingsType == SettingsType.SPACING_SETTINGS) { @@ -268,6 +270,10 @@ public class GroovyLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSe " label: " + " if (2 < 3) return else if (2 > 3) return else return\n" + " for (int i = 0; i < 0xFFFFFF; i += 2) System.out.println(i)\n" + + " print([\n" + + " l1: expr1,\n" + + " label2: expr2\n" + + " ])\n" + " while (x < 50000) x++\n" + " switch (a) {\n" + " case 0:\n" + diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/processors/GroovyIndentProcessor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/processors/GroovyIndentProcessor.java index a2d41525e024..aea939055570 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/processors/GroovyIndentProcessor.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/processors/GroovyIndentProcessor.java @@ -138,12 +138,6 @@ public abstract class GroovyIndentProcessor implements GroovyElementTypes { } } - if (psiParent instanceof GrListOrMap) { - if (child.getElementType() != mLBRACK && child.getElementType() != mRBRACK /*&& child.getElementType() != mCOMMA*/) { - return Indent.getContinuationWithoutFirstIndent(); - } - } - if ((psiParent instanceof GrDocComment && child.getElementType() != mGDOC_COMMENT_START) || psiParent instanceof GrDocTag && diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/arguments/GrNamedArgument.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/arguments/GrNamedArgument.java index e7feced34096..3d5d6a1c0f63 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/arguments/GrNamedArgument.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/arguments/GrNamedArgument.java @@ -16,6 +16,7 @@ package org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments; +import com.intellij.psi.PsiElement; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; @@ -34,4 +35,7 @@ public interface GrNamedArgument extends GroovyPsiElement { @Nullable String getLabelName(); + + @Nullable + PsiElement getColon(); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/arguments/GrNamedArgumentImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/arguments/GrNamedArgumentImpl.java index 5f592e5bf1a5..a118ae2af69e 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/arguments/GrNamedArgumentImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/arguments/GrNamedArgumentImpl.java @@ -17,8 +17,10 @@ package org.jetbrains.plugins.groovy.lang.psi.impl.statements.arguments; import com.intellij.lang.ASTNode; +import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes; import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor; import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentLabel; @@ -59,4 +61,10 @@ public class GrNamedArgumentImpl extends GroovyPsiElementImpl implements GrNamed final GrArgumentLabel label = getLabel(); return label == null ? null : label.getName(); } + + @Nullable + @Override + public PsiElement getColon() { + return findChildByType(GroovyTokenTypes.mCOLON); + } } \ No newline at end of file diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/formatter/FormatterTest.java b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/formatter/FormatterTest.java index 12b386232606..ef7b27a79f5a 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/formatter/FormatterTest.java +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/formatter/FormatterTest.java @@ -235,6 +235,12 @@ public class FormatterTest extends GroovyFormatterTestCase { public void testGeese7() {doGeeseTest();} public void testGeese8() {doGeeseTest();} + public void testMapInArgumentList() {doTest();} + public void testMapInArgumentList2() { + myTempSettings.getCustomSettings(GroovyCodeStyleSettings.class).ALIGN_NAMED_ARGS_IN_MAP = true; + doTest(); + } + private void doGeeseTest() { GroovyCodeStyleSettings customSettings = myTempSettings.getCustomSettings(GroovyCodeStyleSettings.class); boolean oldvalue = customSettings.USE_FLYING_GEESE_BRACES; diff --git a/plugins/groovy/testdata/groovy/formatter/MapInArgumentList2.test b/plugins/groovy/testdata/groovy/formatter/MapInArgumentList2.test new file mode 100644 index 000000000000..8101230b11be --- /dev/null +++ b/plugins/groovy/testdata/groovy/formatter/MapInArgumentList2.test @@ -0,0 +1,13 @@ +test( + changesSorter: [ + (GradleMismatchedLibraryPathChange): 2, + (GradleLibraryDependencyPresenceChange): 1 + ] +) +----- +test( + changesSorter: [ + (GradleMismatchedLibraryPathChange) : 2, + (GradleLibraryDependencyPresenceChange): 1 + ] +) \ No newline at end of file diff --git a/plugins/groovy/testdata/groovy/formatter/mapInArgumentList.test b/plugins/groovy/testdata/groovy/formatter/mapInArgumentList.test new file mode 100644 index 000000000000..ea87e82c46f3 --- /dev/null +++ b/plugins/groovy/testdata/groovy/formatter/mapInArgumentList.test @@ -0,0 +1,13 @@ +test( + changesSorter: [ + (GradleMismatchedLibraryPathChange): 2, + (GradleLibraryDependencyPresenceChange): 1 + ] +) +----- +test( + changesSorter: [ + (GradleMismatchedLibraryPathChange): 2, + (GradleLibraryDependencyPresenceChange): 1 + ] +) \ No newline at end of file