diff --git a/build/lib/gant/jps-sources.zip b/build/lib/gant/jps-sources.zip index 156b692f32b0..fdd201988892 100644 Binary files a/build/lib/gant/jps-sources.zip and b/build/lib/gant/jps-sources.zip differ diff --git a/build/lib/gant/lib/jps.jar b/build/lib/gant/lib/jps.jar index 258ade38632c..4a5b749b50e8 100644 Binary files a/build/lib/gant/lib/jps.jar and b/build/lib/gant/lib/jps.jar differ diff --git a/build/scripts/dist.gant b/build/scripts/dist.gant index 07e8849d3af9..2921df30192a 100644 --- a/build/scripts/dist.gant +++ b/build/scripts/dist.gant @@ -136,10 +136,12 @@ private def layoutWin(Map args, String home, Paths paths) { patchPropertiesFile(paths.distWin) ant.echo(file: "$paths.distWin/bin/idea.exe.vmoptions", message: args.vmoptions.replace(' ', '\n')) - ant.zip(zipfile: "$paths.artifacts/idea${args.buildNumber}.win.zip") { + def winZipPath = "$paths.artifacts/idea${args.buildNumber}.win.zip" + ant.zip(zipfile: winZipPath) { fileset(dir: paths.distAll) fileset(dir: paths.distWin) } + notifyArtifactBuilt(winZipPath) } private def layoutMac(Map args, String home, Paths paths) { @@ -170,7 +172,8 @@ private def layoutMac(Map args, String home, Paths paths) { def root = isEap() ? "${version}-${args.buildNumber}.app" : "IntelliJ IDEA ${version} CE.app" - ant.zip(zipfile: "$paths.artifacts/idea${args.buildNumber}.mac.zip") { + def macZipPath = "$paths.artifacts/idea${args.buildNumber}.mac.zip" + ant.zip(zipfile: macZipPath) { [paths.distAll, paths.distMac].each { tarfileset(dir: it, prefix: root) { exclude(name: "bin/*.sh") @@ -183,6 +186,7 @@ private def layoutMac(Map args, String home, Paths paths) { include(name: "Contents/MacOS/idea") } } + notifyArtifactBuilt(macZipPath) } def layoutLinux(Map args, String home, Paths paths) { @@ -216,7 +220,9 @@ def layoutLinux(Map args, String home, Paths paths) { } } - ant.gzip(src: tarPath, zipfile: "${tarPath}.gz") + def gzPath = "${tarPath}.gz" + ant.gzip(src: tarPath, zipfile: gzPath) ant.delete(file: tarPath) + notifyArtifactBuilt(gzPath) } diff --git a/build/scripts/utils.gant b/build/scripts/utils.gant index 8be15353d412..5f46552722dc 100644 --- a/build/scripts/utils.gant +++ b/build/scripts/utils.gant @@ -88,3 +88,11 @@ binding.setVariable("loadProject", { requireProperty("home", guessHome()) project.builder.buildInfoPrinter = new org.jetbrains.jps.teamcity.TeamcityBuildInfoPrinter() + +binding.setVariable("notifyArtifactBuilt", { String artifactPath -> + if (!artifactPath.startsWith(home)) { + project.error("Artifact path $artifactPath should start with $home") + } + def relativePath = artifactPath.substring(home.length()) + project.info("##teamcity[publishArtifacts '$relativePath']") +}) \ No newline at end of file diff --git a/java/compiler/impl/src/com/intellij/compiler/CompilerManagerImpl.java b/java/compiler/impl/src/com/intellij/compiler/CompilerManagerImpl.java index 1f5fbdf98164..273514938618 100644 --- a/java/compiler/impl/src/com/intellij/compiler/CompilerManagerImpl.java +++ b/java/compiler/impl/src/com/intellij/compiler/CompilerManagerImpl.java @@ -29,7 +29,6 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.packaging.impl.compiler.IncrementalArtifactsCompiler; import com.intellij.util.ArrayUtil; import com.intellij.util.Chunk; import com.intellij.util.containers.ContainerUtil; @@ -68,7 +67,6 @@ public class CompilerManagerImpl extends CompilerManager { addTranslatingCompiler(new JavaCompiler(project), new HashSet(Arrays.asList(StdFileTypes.JAVA)), new HashSet(Arrays.asList(StdFileTypes.CLASS))); addCompiler(new ResourceCompiler(project, compilerConfiguration)); addCompiler(new RmicCompiler()); - addCompiler(new IncrementalArtifactsCompiler()); for(Compiler compiler: Extensions.getExtensions(Compiler.EP_NAME, myProject)) { addCompiler(compiler); diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java index 230c9e999ec9..b3a7fb1ce535 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java @@ -24,6 +24,7 @@ package com.intellij.compiler.impl; import com.intellij.CommonBundle; import com.intellij.analysis.AnalysisScope; import com.intellij.compiler.*; +import com.intellij.compiler.impl.newApi.NewCompiler; import com.intellij.compiler.make.CacheCorruptedException; import com.intellij.compiler.make.CacheUtils; import com.intellij.compiler.make.DependencyCache; @@ -560,7 +561,7 @@ public class CompileDriver { return CompilerBundle.message("status.compilation.completed.successfully.with.warnings.and.errors", errorCount, warningCount); } - private static class ExitStatus { + static class ExitStatus { private final String myName; private ExitStatus(@NonNls String name) { @@ -577,10 +578,10 @@ public class CompileDriver { public static final ExitStatus UP_TO_DATE = new ExitStatus("UP_TO_DATE"); } - private static class ExitException extends Exception { + static class ExitException extends Exception { private final ExitStatus myStatus; - private ExitException(ExitStatus status) { + ExitException(ExitStatus status) { myStatus = status; } @@ -724,7 +725,7 @@ public class CompileDriver { boolean didSomething = false; final CompilerManager compilerManager = CompilerManager.getInstance(myProject); - + NewCompilerRunner runner = new NewCompilerRunner(context, compilerManager, forceCompile, onlyCheckStatus); try { didSomething |= generateSources(compilerManager, context, forceCompile, onlyCheckStatus); @@ -746,19 +747,23 @@ public class CompileDriver { didSomething |= invokeFileProcessingCompilers(compilerManager, context, ClassInstrumentingCompiler.class, FILE_PROCESSING_COMPILER_ADAPTER_FACTORY, isRebuild, false, onlyCheckStatus); + didSomething |= runner.invokeCompilers(NewCompiler.CompileOrderPlace.CLASS_INSTRUMENTING); // explicitly passing forceCompile = false because in scopes that is narrower than ProjectScope it is impossible // to understand whether the class to be processed is in scope or not. Otherwise compiler may process its items even if // there were changes in completely independent files. didSomething |= invokeFileProcessingCompilers(compilerManager, context, ClassPostProcessingCompiler.class, FILE_PROCESSING_COMPILER_ADAPTER_FACTORY, isRebuild, false, onlyCheckStatus); + didSomething |= runner.invokeCompilers(NewCompiler.CompileOrderPlace.CLASS_POST_PROCESSING); didSomething |= invokeFileProcessingCompilers(compilerManager, context, PackagingCompiler.class, FILE_PACKAGING_COMPILER_ADAPTER_FACTORY, isRebuild, false, onlyCheckStatus); + didSomething |= runner.invokeCompilers(NewCompiler.CompileOrderPlace.PACKAGING); didSomething |= invokeFileProcessingCompilers(compilerManager, context, Validator.class, FILE_PROCESSING_COMPILER_ADAPTER_FACTORY, forceCompile, true, onlyCheckStatus); + didSomething |= runner.invokeCompilers(NewCompiler.CompileOrderPlace.VALIDATING); } catch (ExitException e) { if (LOG.isDebugEnabled()) { @@ -954,10 +959,10 @@ public class CompileDriver { final TranslatorsOutputSink sink = new TranslatorsOutputSink(context, translators); final Set generatedTypes = new HashSet(); Collection chunkFiles = chunkMap.get(currentChunk); + final Set filesToRecompile = new HashSet(); + final Set allDependent = new HashSet(); try { int round = 0; - final Set filesToRecompile = new HashSet(); - final Set allDependent = new HashSet(); Collection dependentFiles = Collections.emptyList(); final Function>, Pair>> dependencyFilter = new DependentClassesCumulativeFilter(); @@ -968,7 +973,7 @@ public class CompileDriver { if (context.getProgressIndicator().isCanceled()) { throw new ExitException(ExitStatus.CANCELLED); } - + context.getProgressIndicator().setText("Preparing sources for " + compiler.getDescription()); DumbService.getInstance(myProject).waitForSmartMode(); if (round == 0) { @@ -1087,19 +1092,18 @@ public class CompileDriver { indicator.popState(); } } - - if (context.getMessageCount(CompilerMessageCategory.ERROR) != 0) { - filesToRecompile.addAll(allDependent); - } - if (filesToRecompile.size() > 0) { - sink.add(null, Collections.emptyList(), VfsUtil.toVirtualFileArray(filesToRecompile)); - } } catch (CacheCorruptedException e) { LOG.info(e); context.requestRebuildNextTime(e.getMessage()); } finally { + if (context.getMessageCount(CompilerMessageCategory.ERROR) != 0) { + filesToRecompile.addAll(allDependent); + } + if (filesToRecompile.size() > 0) { + sink.add(null, Collections.emptyList(), VfsUtil.toVirtualFileArray(filesToRecompile)); + } if (context.getMessageCount(CompilerMessageCategory.ERROR) == 0) { // perform update only if there were no errors, so it is guaranteed that the file was processd by all neccesary compilers sink.flushPostponedItems(); diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompilerCacheManager.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompilerCacheManager.java index c99dc6f64924..4afce35c12ac 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/CompilerCacheManager.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompilerCacheManager.java @@ -15,6 +15,8 @@ */ package com.intellij.compiler.impl; +import com.intellij.compiler.impl.newApi.NewCompiler; +import com.intellij.compiler.impl.newApi.NewCompilerCache; import com.intellij.openapi.Disposable; import com.intellij.openapi.compiler.*; import com.intellij.openapi.compiler.Compiler; @@ -42,6 +44,7 @@ import java.util.Map; public class CompilerCacheManager implements ProjectComponent { private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.CompilerCacheManager"); private final Map myCompilerToCacheMap = new HashMap(); + private final Map, NewCompilerCache> myNewCachesMap = new HashMap, NewCompilerCache>(); private final List myCacheDisposables = new ArrayList(); private final File myCachesRoot; private final Runnable myShutdownTask = new Runnable() { @@ -49,8 +52,10 @@ public class CompilerCacheManager implements ProjectComponent { flushCaches(); } }; + private final Project myProject; public CompilerCacheManager(Project project) { + myProject = project; myCachesRoot = CompilerPaths.getCacheStoreDirectory(project); } @@ -85,6 +90,23 @@ public class CompilerCacheManager implements ProjectComponent { return dir; } + public synchronized NewCompilerCache getNewCompilerCache(NewCompiler compiler) throws IOException { + NewCompilerCache cache = myNewCachesMap.get(compiler); + if (cache == null) { + final NewCompilerCache newCache = new NewCompilerCache(compiler, NewCompilerRunner.getNewCompilerCacheDir(myProject, compiler)); + myNewCachesMap.put(compiler, newCache); + myCacheDisposables.add(new Disposable() { + @Override + public void dispose() { + newCache.close(); + } + }); + cache = newCache; + } + //noinspection unchecked + return (NewCompilerCache)cache; + } + public synchronized FileProcessingCompilerStateCache getFileProcessingCompilerCache(FileProcessingCompiler compiler) throws IOException { Object cache = myCompilerToCacheMap.get(compiler); if (cache == null) { @@ -149,6 +171,7 @@ public class CompilerCacheManager implements ProjectComponent { } } myCacheDisposables.clear(); + myNewCachesMap.clear(); myCompilerToCacheMap.clear(); } diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/NewCompilerRunner.java b/java/compiler/impl/src/com/intellij/compiler/impl/NewCompilerRunner.java new file mode 100644 index 000000000000..a5dc5e2ffc24 --- /dev/null +++ b/java/compiler/impl/src/com/intellij/compiler/impl/NewCompilerRunner.java @@ -0,0 +1,246 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.compiler.impl; + +import com.intellij.compiler.impl.newApi.*; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.compiler.*; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.project.DumbService; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.Ref; +import com.intellij.util.CommonProcessors; +import com.intellij.util.Processor; +import com.intellij.util.io.KeyDescriptor; +import gnu.trove.THashSet; +import gnu.trove.TObjectHashingStrategy; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.IOException; +import java.util.*; + +/** + * @author nik + */ +public class NewCompilerRunner { + private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.NewCompilerRunner"); + private CompileContext myContext; + private final boolean myForceCompile; + private final boolean myOnlyCheckStatus; + private final NewCompiler[] myCompilers; + private final Project myProject; + + public NewCompilerRunner(CompileContext context, CompilerManager compilerManager, boolean forceCompile, boolean onlyCheckStatus) { + myContext = context; + myForceCompile = forceCompile; + myOnlyCheckStatus = onlyCheckStatus; + myCompilers = compilerManager.getCompilers(NewCompiler.class); + myProject = myContext.getProject(); + } + + public boolean invokeCompilers(NewCompiler.CompileOrderPlace place) throws CompileDriver.ExitException { + boolean didSomething = false; + try { + for (NewCompiler compiler : myCompilers) { + if (compiler.getOrderPlace().equals(place)) { + didSomething = invokeCompiler(compiler); + } + } + } + catch (IOException e) { + LOG.info(e); + myContext.requestRebuildNextTime(e.getMessage()); + throw new CompileDriver.ExitException(CompileDriver.ExitStatus.ERRORS); + } + catch (CompileDriver.ExitException e) { + throw e; + } + catch (ProcessCanceledException e) { + throw e; + } + catch (Exception e) { + LOG.info(e); + myContext.addMessage(CompilerMessageCategory.ERROR, CompilerBundle.message("compiler.error.exception", e.getMessage()), null, -1, -1); + } + return didSomething; + } + + private boolean invokeCompiler(NewCompiler compiler) throws IOException, CompileDriver.ExitException { + return invokeCompiler(compiler, compiler.createInstance(myContext)); + } + + private , Key, State> + boolean invokeCompiler(NewCompiler compiler, CompilerInstance instance) throws IOException, CompileDriver.ExitException { + NewCompilerCache cache = CompilerCacheManager.getInstance(myProject).getNewCompilerCache(compiler); + NewCompilerPersistentData data = new NewCompilerPersistentData(getNewCompilerCacheDir(myProject, compiler), compiler.getVersion()); + if (data.isVersionChanged()) { + LOG.info("Clearing cache for " + compiler.getDescription()); + cache.wipe(); + } + + Set targetsToRemove = new HashSet(data.getAllTargets()); + for (T target : instance.getAllTargets()) { + targetsToRemove.remove(target.getId()); + } + if (!myOnlyCheckStatus) { + for (String target : targetsToRemove) { + int id = data.removeId(target); + if (LOG.isDebugEnabled()) { + LOG.debug("Removing obsolete target '" + target + "' (id=" + id + ")"); + } + List keys = new ArrayList(); + cache.processSources(id, new CommonProcessors.CollectProcessor(keys)); + List> obsoleteSources = new ArrayList>(); + for (Key key : keys) { + final State state = cache.getState(id, key); + obsoleteSources.add(Pair.create(key, state)); + } + instance.processObsoleteTarget(target, obsoleteSources); + if (myContext.getMessageCount(CompilerMessageCategory.ERROR) > 0) { + return true; + } + for (Key key : keys) { + cache.remove(id, key); + } + } + } + + boolean didSomething = false; + for (T target : instance.getSelectedTargets()) { + int id = data.getId(target.getId()); + didSomething |= processTarget(target, id, compiler, instance, cache); + } + + data.save(); + return didSomething; + } + + public static File getNewCompilerCacheDir(Project project, NewCompiler compiler) { + return new File(CompilerPaths.getCacheStoreDirectory(project), compiler.getId()); + } + + private , Key, State> + boolean processTarget(T target, final int targetId, final NewCompiler compiler, final CompilerInstance instance, + final NewCompilerCache cache) throws IOException, CompileDriver.ExitException { + if (LOG.isDebugEnabled()) { + LOG.debug("Processing target '" + target + "' (id=" + targetId + ")"); + } + final List items = instance.getItems(target); + if (myContext.getMessageCount(CompilerMessageCategory.ERROR) > 0) return true; + + final List> toProcess = new ArrayList>(); + final THashSet keySet = new THashSet(new SourceItemHashingStrategy(compiler)); + final Ref exception = Ref.create(null); + DumbService.getInstance(myProject).waitForSmartMode(); + ApplicationManager.getApplication().runReadAction(new Runnable() { + @Override + public void run() { + try { + for (Item item : items) { + final Key key = item.getKey(); + keySet.add(key); + State output = cache.getState(targetId, key); + if (myForceCompile || output == null || !item.isUpToDate(output)) { + toProcess.add(Pair.create(item, output)); + } + } + } + catch (IOException e) { + exception.set(e); + } + } + }); + if (!exception.isNull()) { + throw exception.get(); + } + + final List toRemove = new ArrayList(); + cache.processSources(targetId, new Processor() { + @Override + public boolean process(Key key) { + if (!keySet.contains(key)) { + toRemove.add(key); + } + return true; + } + }); + + if (LOG.isDebugEnabled()) { + LOG.debug(toProcess.size() + " items will be processed, " + toRemove.size() + " items will be removed"); + } + + if (toProcess.isEmpty() && toRemove.isEmpty()) { + return false; + } + + if (myOnlyCheckStatus) { + throw new CompileDriver.ExitException(CompileDriver.ExitStatus.CANCELLED); + } + + List> obsoleteItems = new ArrayList>(); + for (Key key : toRemove) { + obsoleteItems.add(Pair.create(key, cache.getState(targetId, key))); + } + + final List processedItems = new ArrayList(); + final List toRefresh = new ArrayList(); + instance.processItems(target, toProcess, obsoleteItems, new CompilerInstance.OutputConsumer() { + @Override + public void addFileToRefresh(@NotNull File file) { + toRefresh.add(file); + } + @Override + public void addProcessedItem(@NotNull Item sourceItem) { + processedItems.add(sourceItem); + } + }); + if (myContext.getMessageCount(CompilerMessageCategory.ERROR) > 0) { + return true; + } + + for (Key key : toRemove) { + cache.remove(targetId, key); + } + CompilerUtil.refreshIOFiles(toRefresh); + for (Item item : processedItems) { + cache.putOutput(targetId, item.getKey(), item.computeState()); + } + + return true; + + } + + private class SourceItemHashingStrategy implements TObjectHashingStrategy { + private KeyDescriptor myKeyDescriptor; + + public SourceItemHashingStrategy(NewCompiler compiler) { + myKeyDescriptor = compiler.getItemKeyDescriptor(); + } + + @Override + public int computeHashCode(S object) { + return myKeyDescriptor.getHashCode(object); + } + + @Override + public boolean equals(S o1, S o2) { + return myKeyDescriptor.isEqual(o1, o2); + } + } +} diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/AnnotationProcessingCompiler.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/AnnotationProcessingCompiler.java index 00357520f65b..3fcac756823b 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/AnnotationProcessingCompiler.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/AnnotationProcessingCompiler.java @@ -76,6 +76,10 @@ public class AnnotationProcessingCompiler implements TranslatingCompiler{ final String path = CompilerPaths.getAnnotationProcessorsGenerationPath(module); return path != null? lfs.findFileByPath(path) : null; } + + public VirtualFile getModuleOutputDirectoryForTests(Module module) { + return getModuleOutputDirectory(module); + } }; final JavacCompiler javacCompiler = getBackEndCompiler(); final boolean processorMode = javacCompiler.setAnnotationProcessorMode(true); diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/newApi/BuildTarget.java b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/BuildTarget.java new file mode 100644 index 000000000000..a53ffc1ff9c0 --- /dev/null +++ b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/BuildTarget.java @@ -0,0 +1,39 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.compiler.impl.newApi; + +import org.jetbrains.annotations.NotNull; + +/** + * @author nik + */ +public abstract class BuildTarget { + public static final BuildTarget DEFAULT = new BuildTarget() { + @NotNull + @Override + public String getId() { + return ""; + } + }; + + @NotNull + public abstract String getId(); + + @Override + public String toString() { + return "Build Target: " + getId(); + } +} diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/newApi/CompileItem.java b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/CompileItem.java new file mode 100644 index 000000000000..fb941ca3e4b2 --- /dev/null +++ b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/CompileItem.java @@ -0,0 +1,31 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.compiler.impl.newApi; + +import org.jetbrains.annotations.NotNull; + +/** + * @author nik + */ +public abstract class CompileItem { + @NotNull + public abstract Key getKey(); + + public abstract boolean isUpToDate(@NotNull State state); + + @NotNull + public abstract State computeState(); +} diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/newApi/CompilerInstance.java b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/CompilerInstance.java new file mode 100644 index 000000000000..2e9f9cf59593 --- /dev/null +++ b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/CompilerInstance.java @@ -0,0 +1,60 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.compiler.impl.newApi; + +import com.intellij.openapi.compiler.CompileContext; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.util.List; + +/** + * @author nik + */ +public abstract class CompilerInstance, Key, State> { + protected final CompileContext myContext; + + protected CompilerInstance(CompileContext context) { + myContext = context; + } + + protected Project getProject() { + return myContext.getProject(); + } + + @NotNull + public abstract List getAllTargets(); + + @NotNull + public abstract List getSelectedTargets(); + + public abstract void processObsoleteTarget(@NotNull String targetId, @NotNull List> obsoleteItems); + + + @NotNull + public abstract List getItems(@NotNull T target); + + public abstract void processItems(@NotNull T target, @NotNull List> changedItems, @NotNull List> obsoleteItems, + @NotNull OutputConsumer consumer); + + public interface OutputConsumer> { + void addFileToRefresh(@NotNull File file); + + void addProcessedItem(@NotNull Item sourceItem); + } +} diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/newApi/NewCompiler.java b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/NewCompiler.java new file mode 100644 index 000000000000..b82b8bf9d84c --- /dev/null +++ b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/NewCompiler.java @@ -0,0 +1,62 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.compiler.impl.newApi; + +import com.intellij.openapi.compiler.CompileContext; +import com.intellij.openapi.compiler.Compiler; +import com.intellij.util.io.DataExternalizer; +import com.intellij.util.io.KeyDescriptor; +import org.jetbrains.annotations.NotNull; + +/** + * @author nik + */ +public abstract class NewCompiler implements Compiler { + private final String myId; + private final int myVersion; + private final CompileOrderPlace myOrderPlace; + + protected NewCompiler(@NotNull String id, int version, @NotNull CompileOrderPlace orderPlace) { + myId = id; + myVersion = version; + myOrderPlace = orderPlace; + } + + @NotNull + public abstract KeyDescriptor getItemKeyDescriptor(); + @NotNull + public abstract DataExternalizer getItemStateExternalizer(); + + @NotNull + public abstract CompilerInstance, Key, State> createInstance(@NotNull CompileContext context); + + public final String getId() { + return myId; + } + + public final int getVersion() { + return myVersion; + } + + public CompileOrderPlace getOrderPlace() { + return myOrderPlace; + } + + public static enum CompileOrderPlace { + CLASS_INSTRUMENTING, CLASS_POST_PROCESSING, PACKAGING, VALIDATING + } + +} diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/newApi/NewCompilerCache.java b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/NewCompilerCache.java new file mode 100644 index 000000000000..0b6ea2ecc3e0 --- /dev/null +++ b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/NewCompilerCache.java @@ -0,0 +1,132 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.compiler.impl.newApi; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.util.Processor; +import com.intellij.util.io.KeyDescriptor; +import com.intellij.util.io.PersistentHashMap; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.File; +import java.io.IOException; + +/** + * @author nik + */ +public class NewCompilerCache { + private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.newApi.NewCompilerCache"); + private PersistentHashMap, State> myPersistentMap; + private File myCacheFile; + private final NewCompiler myCompiler; + + public NewCompilerCache(NewCompiler compiler, final File compilerCacheDir) throws IOException { + myCompiler = compiler; + myCacheFile = new File(compilerCacheDir, "timestamps"); + createMap(); + } + + private void createMap() throws IOException { + myPersistentMap = new PersistentHashMap, State>(myCacheFile, new SourceItemDataDescriptor(myCompiler.getItemKeyDescriptor()), + myCompiler.getItemStateExternalizer()); + } + + private KeyAndTargetData getKeyAndTargetData(Key key, int target) { + KeyAndTargetData data = new KeyAndTargetData(); + data.myTarget = target; + data.myKey = key; + return data; + } + + public void wipe() throws IOException { + try { + myPersistentMap.close(); + } + catch (IOException ignored) { + } + PersistentHashMap.deleteFilesStartingWith(myCacheFile); + createMap(); + } + + public void close() { + try { + myPersistentMap.close(); + } + catch (IOException e) { + LOG.info(e); + } + } + + public void remove(int targetId, Key key) throws IOException { + myPersistentMap.remove(getKeyAndTargetData(key, targetId)); + } + + public State getState(int targetId, Key key) throws IOException { + return myPersistentMap.get(getKeyAndTargetData(key, targetId)); + } + + public void processSources(final int targetId, final Processor processor) throws IOException { + myPersistentMap.processKeys(new Processor>() { + @Override + public boolean process(KeyAndTargetData data) { + return targetId == data.myTarget ? processor.process(data.myKey) : true; + } + }); + } + + public void putOutput(int targetId, Key key, State outputItem) throws IOException { + myPersistentMap.put(getKeyAndTargetData(key, targetId), outputItem); + } + + + private static class KeyAndTargetData { + public int myTarget; + public Key myKey; + } + + private class SourceItemDataDescriptor implements KeyDescriptor> { + private final KeyDescriptor myKeyDescriptor; + + public SourceItemDataDescriptor(KeyDescriptor keyDescriptor) { + myKeyDescriptor = keyDescriptor; + } + + @Override + public boolean isEqual(KeyAndTargetData val1, KeyAndTargetData val2) { + return val1.myTarget == val2.myTarget; + } + + @Override + public int getHashCode(KeyAndTargetData value) { + return value.myTarget + 239 * myKeyDescriptor.getHashCode(value.myKey); + } + + @Override + public void save(DataOutput out, KeyAndTargetData value) throws IOException { + out.writeInt(value.myTarget); + myKeyDescriptor.save(out, value.myKey); + } + + + @Override + public KeyAndTargetData read(DataInput in) throws IOException { + int target = in.readInt(); + final Key item = myKeyDescriptor.read(in); + return getKeyAndTargetData(item, target); + } + } +} diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/newApi/NewCompilerPersistentData.java b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/NewCompilerPersistentData.java new file mode 100644 index 000000000000..1746c334cf77 --- /dev/null +++ b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/NewCompilerPersistentData.java @@ -0,0 +1,119 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.compiler.impl.newApi; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.util.io.IOUtil; +import gnu.trove.TIntHashSet; +import org.jetbrains.annotations.NotNull; + +import java.io.*; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * @author nik + */ +public class NewCompilerPersistentData { + private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.newApi.NewCompilerPersistentData"); + private static final int VERSION = 0; + private File myFile; + private Map myTarget2Id = new HashMap(); + private TIntHashSet myUsedIds = new TIntHashSet(); + private boolean myVersionChanged; + private final int myCompilerVersion; + + public NewCompilerPersistentData(File cacheStoreDirectory, int compilerVersion) throws IOException { + myCompilerVersion = compilerVersion; + myFile = new File(cacheStoreDirectory, "info"); + if (!myFile.exists()) { + LOG.info("Compiler info file doesn't exists: " + myFile.getAbsolutePath()); + myVersionChanged = true; + return; + } + + DataInputStream input = new DataInputStream(new FileInputStream(myFile)); + try { + final int dataVersion = input.readInt(); + if (dataVersion != VERSION) { + LOG.info("Version of compiler info file (" + myFile.getAbsolutePath() + ") changed: " + dataVersion + " -> " + VERSION); + myVersionChanged = true; + return; + } + + final int savedCompilerVersion = input.readInt(); + if (savedCompilerVersion != compilerVersion) { + LOG.info("Compiler caches version changed (" + myFile.getAbsolutePath() + "): " + savedCompilerVersion + " -> " + compilerVersion); + myVersionChanged = true; + return; + } + + int size = input.readInt(); + while (size-- > 0) { + final String target = IOUtil.readString(input); + final int id = input.readInt(); + myTarget2Id.put(target, id); + myUsedIds.add(id); + } + } + finally { + input.close(); + } + } + + public boolean isVersionChanged() { + return myVersionChanged; + } + + public void save() throws IOException { + final DataOutputStream output = new DataOutputStream(new FileOutputStream(myFile)); + try { + output.writeInt(VERSION); + output.writeInt(myCompilerVersion); + output.writeInt(myTarget2Id.size()); + + for (Map.Entry entry : myTarget2Id.entrySet()) { + IOUtil.writeString(entry.getKey(), output); + output.writeInt(entry.getValue()); + } + } + finally { + output.close(); + } + } + + public int getId(@NotNull String target) { + if (myTarget2Id.containsKey(target)) { + return myTarget2Id.get(target); + } + int id = 0; + while (myUsedIds.contains(id)) { + id++; + } + myTarget2Id.put(target, id); + myUsedIds.add(id); + return id; + } + + public Set getAllTargets() { + return myTarget2Id.keySet(); + } + + public int removeId(String target) { + return myTarget2Id.remove(target); + } +} diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/newApi/SingleTargetCompilerInstance.java b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/SingleTargetCompilerInstance.java new file mode 100644 index 000000000000..359366e70bb4 --- /dev/null +++ b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/SingleTargetCompilerInstance.java @@ -0,0 +1,48 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.compiler.impl.newApi; + +import com.intellij.openapi.compiler.CompileContext; +import com.intellij.openapi.util.Pair; +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.List; + +/** + * @author nik + */ +public abstract class SingleTargetCompilerInstance, S, O> extends CompilerInstance { + protected SingleTargetCompilerInstance(CompileContext context) { + super(context); + } + + @NotNull + @Override + public List getAllTargets() { + return Collections.singletonList(BuildTarget.DEFAULT); + } + + @NotNull + @Override + public List getSelectedTargets() { + return getAllTargets(); + } + + @Override + public void processObsoleteTarget(@NotNull String targetId, @NotNull List> obsoleteItems) { + } +} diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/newApi/VirtualFileCompileItem.java b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/VirtualFileCompileItem.java new file mode 100644 index 000000000000..a2c0b7055c9f --- /dev/null +++ b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/VirtualFileCompileItem.java @@ -0,0 +1,55 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.compiler.impl.newApi; + +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.io.EnumeratorStringDescriptor; +import com.intellij.util.io.KeyDescriptor; +import org.jetbrains.annotations.NotNull; + +/** + * @author nik + */ +public abstract class VirtualFileCompileItem extends CompileItem { + public static final KeyDescriptor KEY_DESCRIPTOR = new EnumeratorStringDescriptor(); + protected final VirtualFile myFile; + + public VirtualFileCompileItem(@NotNull VirtualFile file) { + myFile = file; + } + + @NotNull + public VirtualFile getFile() { + return myFile; + } + + @Override + public final boolean isUpToDate(@NotNull State state) { + if (myFile.getTimeStamp() != state.getSourceTimestamp()) { + return false; + } + return isStateUpToDate(state); + } + + protected abstract boolean isStateUpToDate(State state); + + @NotNull + @Override + public String getKey() { + return myFile.getUrl(); + } + +} diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/newApi/VirtualFilePersistentState.java b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/VirtualFilePersistentState.java new file mode 100644 index 000000000000..9f30a9182e99 --- /dev/null +++ b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/VirtualFilePersistentState.java @@ -0,0 +1,31 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.compiler.impl.newApi; + +/** +* @author nik +*/ +public class VirtualFilePersistentState { + private final long mySourceTimestamp; + + public VirtualFilePersistentState(long sourceTimestamp) { + mySourceTimestamp = sourceTimestamp; + } + + public final long getSourceTimestamp() { + return mySourceTimestamp; + } +} diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/newApi/VirtualFileStateExternalizer.java b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/VirtualFileStateExternalizer.java new file mode 100644 index 000000000000..6ca460108979 --- /dev/null +++ b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/VirtualFileStateExternalizer.java @@ -0,0 +1,44 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.compiler.impl.newApi; + +import com.intellij.util.io.DataExternalizer; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + +/** +* @author nik +*/ +public abstract class VirtualFileStateExternalizer implements DataExternalizer { + protected abstract void doSave(DataOutput out, State value) throws IOException; + + protected abstract State doRead(DataInput in, long sourceTimestamp) throws IOException; + + @Override + public final void save(DataOutput out, State value) throws IOException { + out.writeLong(value.getSourceTimestamp()); + doSave(out, value); + } + + @Override + public final State read(DataInput in) throws IOException { + final long sourceTimestamp = in.readLong(); + return doRead(in, sourceTimestamp); + } + +} diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/JarDestinationInfo.java b/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/JarDestinationInfo.java index 8e0e53a29ff9..5cc45c19278e 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/JarDestinationInfo.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/JarDestinationInfo.java @@ -16,9 +16,9 @@ package com.intellij.compiler.impl.packagingCompiler; -import com.intellij.openapi.deployment.DeploymentUtil; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.JarFileSystem; /** * @author nik @@ -29,12 +29,18 @@ public class JarDestinationInfo extends DestinationInfo { private final JarInfo myJarInfo; public JarDestinationInfo(final String pathInJar, final JarInfo jarInfo, DestinationInfo jarDestination) { - super(DeploymentUtil.appendToPath(jarDestination.getOutputPath(), pathInJar), jarDestination.getOutputFile(), jarDestination.getOutputFilePath()); + super(appendPathInJar(jarDestination.getOutputPath(), pathInJar), jarDestination.getOutputFile(), jarDestination.getOutputFilePath()); LOG.assertTrue(!pathInJar.startsWith(".."), pathInJar); myPathInJar = StringUtil.startsWithChar(pathInJar, '/') ? pathInJar : "/" + pathInJar; myJarInfo = jarInfo; } + private static String appendPathInJar(String outputPath, String pathInJar) { + LOG.assertTrue(outputPath.length() > 0 && outputPath.charAt(outputPath.length() - 1) != '/'); + LOG.assertTrue(pathInJar.length() > 0 && pathInJar.charAt(0) != '/'); + return outputPath + JarFileSystem.JAR_SEPARATOR + pathInJar; + } + public String getPathInJar() { return myPathInJar; } diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactAdditionalCompileScopeProvider.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactAdditionalCompileScopeProvider.java index 6fdbb1d3f33d..78f9c1221117 100644 --- a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactAdditionalCompileScopeProvider.java +++ b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactAdditionalCompileScopeProvider.java @@ -35,7 +35,7 @@ public class ArtifactAdditionalCompileScopeProvider extends AdditionalCompileSco if (ArtifactCompileScope.getArtifacts(baseScope) != null) { return null; } - final IncrementalArtifactsCompiler compiler = IncrementalArtifactsCompiler.getInstance(project); + final ArtifactsCompiler compiler = ArtifactsCompiler.getInstance(project); if (compiler == null || !filter.acceptCompiler(compiler)) { return null; } diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactBuildTarget.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactBuildTarget.java new file mode 100644 index 000000000000..41aeba8c8516 --- /dev/null +++ b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactBuildTarget.java @@ -0,0 +1,41 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.packaging.impl.compiler; + +import com.intellij.compiler.impl.newApi.BuildTarget; +import com.intellij.packaging.artifacts.Artifact; +import org.jetbrains.annotations.NotNull; + +/** + * @author nik + */ +public class ArtifactBuildTarget extends BuildTarget { + private Artifact myArtifact; + + public ArtifactBuildTarget(Artifact artifact) { + myArtifact = artifact; + } + + public Artifact getArtifact() { + return myArtifact; + } + + @NotNull + @Override + public String getId() { + return myArtifact.getName(); + } +} diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactCompilerCompileItem.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactCompilerCompileItem.java new file mode 100644 index 000000000000..517bef7e89d3 --- /dev/null +++ b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactCompilerCompileItem.java @@ -0,0 +1,86 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.packaging.impl.compiler; + +import com.intellij.compiler.impl.newApi.VirtualFileCompileItem; +import com.intellij.compiler.impl.packagingCompiler.DestinationInfo; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.SmartList; +import com.intellij.util.io.DataExternalizer; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * @author nik + */ +public class ArtifactCompilerCompileItem extends VirtualFileCompileItem { + public static final DataExternalizer OUTPUT_EXTERNALIZER = new ArtifactPackagingItemExternalizer(); + private final List myDestinations = new SmartList(); + + public ArtifactCompilerCompileItem(VirtualFile file) { + super(file); + } + + public void addDestination(DestinationInfo info) { + myDestinations.add(info); + } + + public List getDestinations() { + return myDestinations; + } + + @NotNull + @Override + public ArtifactPackagingItemOutputState computeState() { + final SmartList> pairs = new SmartList>(); + for (DestinationInfo destination : myDestinations) { + destination.update(); + final VirtualFile outputFile = destination.getOutputFile(); + long timestamp = outputFile != null ? outputFile.getTimeStamp() : -1; + pairs.add(Pair.create(destination.getOutputPath(), timestamp)); + } + return new ArtifactPackagingItemOutputState(myFile.getTimeStamp(), pairs); + } + + @Override + public boolean isStateUpToDate(ArtifactPackagingItemOutputState state) { + final SmartList> cachedDestinations = state.myDestinations; + if (cachedDestinations.size() != myDestinations.size()) { + return false; + } + + for (DestinationInfo info : myDestinations) { + final VirtualFile outputFile = info.getOutputFile(); + long timestamp = outputFile != null ? outputFile.getTimeStamp() : -1; + final String path = info.getOutputPath(); + boolean found = false; + //todo[nik] use map if list contains many items + for (Pair cachedDestination : cachedDestinations) { + if (cachedDestination.first.equals(path)) { + if (cachedDestination.second != timestamp) return false; + found = true; + break; + } + } + if (!found) return false; + } + + return true; + } + +} diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactPackagingItemExternalizer.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactPackagingItemExternalizer.java new file mode 100644 index 000000000000..f1adc4c406e1 --- /dev/null +++ b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactPackagingItemExternalizer.java @@ -0,0 +1,54 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.packaging.impl.compiler; + +import com.intellij.compiler.impl.newApi.VirtualFileStateExternalizer; +import com.intellij.openapi.util.Pair; +import com.intellij.util.SmartList; +import com.intellij.util.io.IOUtil; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + +/** +* @author nik +*/ +public class ArtifactPackagingItemExternalizer + extends VirtualFileStateExternalizer { + private byte[] myBuffer = IOUtil.allocReadWriteUTFBuffer(); + + @Override + protected void doSave(DataOutput out, ArtifactPackagingItemOutputState value) throws IOException { + out.writeInt(value.myDestinations.size()); + for (Pair pair : value.myDestinations) { + IOUtil.writeUTFFast(myBuffer, out, pair.getFirst()); + out.writeLong(pair.getSecond()); + } + } + + @Override + protected ArtifactPackagingItemOutputState doRead(DataInput in, long sourceTimestamp) throws IOException { + int size = in.readInt(); + SmartList> destinations = new SmartList>(); + while (size-- > 0) { + String path = IOUtil.readUTFFast(myBuffer, in); + long outputTimestamp = in.readLong(); + destinations.add(Pair.create(path, outputTimestamp)); + } + return new ArtifactPackagingItemOutputState(sourceTimestamp, destinations); + } +} diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactPackagingItemOutputState.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactPackagingItemOutputState.java new file mode 100644 index 000000000000..e93390d6bc72 --- /dev/null +++ b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactPackagingItemOutputState.java @@ -0,0 +1,32 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.packaging.impl.compiler; + +import com.intellij.compiler.impl.newApi.VirtualFilePersistentState; +import com.intellij.openapi.util.Pair; +import com.intellij.util.SmartList; + +/** +* @author nik +*/ +public class ArtifactPackagingItemOutputState extends VirtualFilePersistentState { + public final SmartList> myDestinations; + + public ArtifactPackagingItemOutputState(long timestamp, SmartList> destinations) { + super(timestamp); + myDestinations = destinations; + } +} diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactPackagingItemValidityState.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactPackagingItemValidityState.java deleted file mode 100644 index 2dbf9cca5dea..000000000000 --- a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactPackagingItemValidityState.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2000-2009 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.intellij.packaging.impl.compiler; - -import com.intellij.compiler.impl.packagingCompiler.DestinationInfo; -import com.intellij.openapi.compiler.ValidityState; -import com.intellij.openapi.util.Comparing; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.SmartList; -import com.intellij.util.StringSetSpinAllocator; -import com.intellij.util.io.IOUtil; -import org.jetbrains.annotations.Nullable; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; -import java.util.List; -import java.util.Set; - -/** - * @author nik - */ -public class ArtifactPackagingItemValidityState implements ValidityState { - private final SmartList> myDestinations; - - public ArtifactPackagingItemValidityState(List destinationInfos, boolean sourceFileModified, - @Nullable ArtifactPackagingItemValidityState oldState) { - myDestinations = new SmartList>(); - final Set paths = StringSetSpinAllocator.alloc(); - try { - for (DestinationInfo info : destinationInfos) { - final VirtualFile outputFile = info.getOutputFile(); - long timestamp = outputFile != null ? outputFile.getTimeStamp() : -1; - final String path = info.getOutputPath(); - myDestinations.add(Pair.create(path, timestamp)); - paths.add(path); - } - - if (!sourceFileModified && oldState != null) { - for (Pair pair : oldState.myDestinations) { - if (!paths.contains(pair.getFirst())) { - myDestinations.add(pair); - } - } - } - } - finally { - StringSetSpinAllocator.dispose(paths); - } - } - - public ArtifactPackagingItemValidityState(DataInput input) throws IOException { - int size = input.readInt(); - myDestinations = new SmartList>(); - while (size-- > 0) { - String path = IOUtil.readString(input); - long timestamp = input.readLong(); - myDestinations.add(Pair.create(path, timestamp)); - } - } - - public boolean equalsTo(final ValidityState otherState) { - if (!(otherState instanceof ArtifactPackagingItemValidityState)) { - return false; - } - - final SmartList> otherDestinations = ((ArtifactPackagingItemValidityState)otherState).myDestinations; - if (otherDestinations.size() != myDestinations.size()) { - return false; - } - - if (myDestinations.size() == 1) { - return myDestinations.get(0).equals(otherDestinations.get(0)); - } - - return Comparing.haveEqualElements(myDestinations, otherDestinations); - } - - - public void save(final DataOutput output) throws IOException { - output.writeInt(myDestinations.size()); - for (Pair pair : myDestinations) { - IOUtil.writeString(pair.getFirst(), output); - output.writeLong(pair.getSecond()); - } - } -} \ No newline at end of file diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactPackagingProcessingItem.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactPackagingProcessingItem.java deleted file mode 100644 index 2054b9978af6..000000000000 --- a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactPackagingProcessingItem.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2000-2009 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.intellij.packaging.impl.compiler; - -import com.intellij.compiler.impl.FileProcessingCompilerStateCache; -import com.intellij.compiler.impl.packagingCompiler.DestinationInfo; -import com.intellij.openapi.compiler.FileProcessingCompiler; -import com.intellij.openapi.compiler.ValidityState; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.SmartList; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.IOException; -import java.util.List; - -/** - * @author nik - */ -public class ArtifactPackagingProcessingItem implements FileProcessingCompiler.ProcessingItem { - private final VirtualFile mySourceFile; - private final List> myDestinations = new SmartList>(); - private List myEnabledDestinations; - private boolean mySourceFileModified; - private ArtifactPackagingItemValidityState myOldState; - - public ArtifactPackagingProcessingItem(final VirtualFile sourceFile) { - mySourceFile = sourceFile; - } - - @NotNull - public VirtualFile getFile() { - return mySourceFile; - } - - public void addDestination(DestinationInfo info, boolean enabled) { - for (int i = 0; i < myDestinations.size(); i++) { - Pair pair = myDestinations.get(i); - if (info.getOutputPath().equals(pair.getFirst().getOutputPath())) { - if (enabled && !pair.getSecond()) { - myDestinations.set(i, Pair.create(info, true)); - } - return; - } - } - myDestinations.add(Pair.create(info, enabled)); - } - - public List> getDestinations() { - return myDestinations; - } - - public void init(FileProcessingCompilerStateCache cache) throws IOException { - final String url = mySourceFile.getUrl(); - myOldState = (ArtifactPackagingItemValidityState)cache.getExtState(url); - mySourceFileModified = cache.getTimestamp(url) != mySourceFile.getTimeStamp(); - } - - public void setProcessed() { - for (DestinationInfo destination : myEnabledDestinations) { - destination.update(); - } - } - - public List getEnabledDestinations() { - if (myEnabledDestinations == null) { - myEnabledDestinations = new SmartList(); - for (Pair destination : myDestinations) { - if (destination.getSecond()) { - myEnabledDestinations.add(destination.getFirst()); - } - } - } - return myEnabledDestinations; - } - - @Nullable - public ValidityState getValidityState() { - return new ArtifactPackagingItemValidityState(getEnabledDestinations(), mySourceFileModified, myOldState); - } -} diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactsCompiler.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactsCompiler.java new file mode 100644 index 000000000000..b410fe456a78 --- /dev/null +++ b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactsCompiler.java @@ -0,0 +1,88 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.packaging.impl.compiler; + +import com.intellij.compiler.impl.newApi.CompileItem; +import com.intellij.compiler.impl.newApi.CompilerInstance; +import com.intellij.compiler.impl.newApi.NewCompiler; +import com.intellij.compiler.impl.newApi.VirtualFileCompileItem; +import com.intellij.openapi.compiler.CompileContext; +import com.intellij.openapi.compiler.CompileScope; +import com.intellij.openapi.compiler.CompilerManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Key; +import com.intellij.packaging.artifacts.Artifact; +import com.intellij.util.io.DataExternalizer; +import com.intellij.util.io.KeyDescriptor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Set; + +/** + * @author nik + */ +public class ArtifactsCompiler extends NewCompiler { + static final Key> WRITTEN_PATHS_KEY = Key.create("artifacts_written_paths"); + static final Key> AFFECTED_ARTIFACTS = Key.create("affected_artifacts"); + + public ArtifactsCompiler() { + super("artifacts_compiler", 0, NewCompiler.CompileOrderPlace.PACKAGING); + } + + @Nullable + public static ArtifactsCompiler getInstance(@NotNull Project project) { + final ArtifactsCompiler[] compilers = CompilerManager.getInstance(project).getCompilers(ArtifactsCompiler.class); + return compilers.length == 1 ? compilers[0] : null; + } + + @NotNull + @Override + public KeyDescriptor getItemKeyDescriptor() { + return VirtualFileCompileItem.KEY_DESCRIPTOR; + } + + @NotNull + @Override + public DataExternalizer getItemStateExternalizer() { + return ArtifactCompilerCompileItem.OUTPUT_EXTERNALIZER; + } + + @NotNull + @Override + public CompilerInstance, String, ArtifactPackagingItemOutputState> createInstance( + @NotNull CompileContext context) { + return new ArtifactsCompilerInstance(context); + } + + public boolean validateConfiguration(final CompileScope scope) { + return true; + } + + @NotNull + public String getDescription() { + return "Artifacts Packaging Compiler"; + } + + public static Set getAffectedArtifacts(final CompileContext compileContext) { + return compileContext.getUserData(AFFECTED_ARTIFACTS); + } + + @Nullable + public static Set getWrittenPaths(@NotNull CompileContext context) { + return context.getUserData(WRITTEN_PATHS_KEY); + } +} diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactsCompilerInstance.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactsCompilerInstance.java new file mode 100644 index 000000000000..f2746de16b45 --- /dev/null +++ b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactsCompilerInstance.java @@ -0,0 +1,389 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.packaging.impl.compiler; + +import com.intellij.compiler.CompilerManagerImpl; +import com.intellij.compiler.impl.CompilerUtil; +import com.intellij.compiler.impl.newApi.CompilerInstance; +import com.intellij.compiler.impl.packagingCompiler.*; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ReadAction; +import com.intellij.openapi.application.Result; +import com.intellij.openapi.compiler.CompileContext; +import com.intellij.openapi.compiler.CompilerBundle; +import com.intellij.openapi.compiler.CompilerMessageCategory; +import com.intellij.openapi.compiler.make.BuildParticipant; +import com.intellij.openapi.compiler.make.BuildParticipantProvider; +import com.intellij.openapi.deployment.DeploymentUtil; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.JarFileSystem; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.packaging.artifacts.Artifact; +import com.intellij.packaging.artifacts.ArtifactManager; +import com.intellij.packaging.artifacts.ArtifactProperties; +import com.intellij.packaging.artifacts.ArtifactPropertiesProvider; +import com.intellij.packaging.elements.CompositePackagingElement; +import com.intellij.packaging.elements.PackagingElementResolvingContext; +import com.intellij.packaging.impl.artifacts.ArtifactValidationUtil; +import com.intellij.util.ThrowableRunnable; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.text.CaseInsensitiveStringHashingStrategy; +import gnu.trove.THashSet; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.FileFilter; +import java.io.IOException; +import java.util.*; + +/** + * @author nik + */ +public class ArtifactsCompilerInstance extends CompilerInstance { + private static final Logger LOG = Logger.getInstance("#com.intellij.packaging.impl.compiler.ArtifactsCompilerInstance"); + private ArtifactsProcessingItemsBuilderContext myBuilderContext; + + public ArtifactsCompilerInstance(CompileContext context) { + super(context); + } + + @NotNull + @Override + public List getAllTargets() { + return getArtifactTargets(false); + } + + @NotNull + @Override + public List getSelectedTargets() { + return getArtifactTargets(true); + } + + private List getArtifactTargets(final boolean selectedOnly) { + final List targets = new ArrayList(); + new ReadAction() { + protected void run(final Result result) { + final Set artifacts; + if (selectedOnly) { + artifacts = ArtifactCompileScope.getArtifactsToBuild(getProject(), myContext.getCompileScope()); + } + else { + artifacts = new HashSet(Arrays.asList(ArtifactManager.getInstance(getProject()).getArtifacts())); + } + List additionalArtifacts = new ArrayList(); + for (BuildParticipantProvider provider : BuildParticipantProvider.EXTENSION_POINT_NAME.getExtensions()) { + for (Module module : ModuleManager.getInstance(getProject()).getModules()) { + final Collection participants = provider.getParticipants(module); + for (BuildParticipant participant : participants) { + ContainerUtil.addIfNotNull(participant.createArtifact(myContext), additionalArtifacts); + } + } + } + if (LOG.isDebugEnabled() && !additionalArtifacts.isEmpty()) { + LOG.debug("additional artifacts to build: " + additionalArtifacts); + } + artifacts.addAll(additionalArtifacts); + + for (Artifact artifact : artifacts) { + targets.add(new ArtifactBuildTarget(artifact)); + } + if (selectedOnly) { + myContext.putUserData(ArtifactsCompiler.AFFECTED_ARTIFACTS, artifacts); + } + } + }.execute(); + return targets; + } + + @Override + public void processObsoleteTarget(@NotNull String targetId, @NotNull List> obsoleteItems) { + deleteFiles(obsoleteItems, Collections.>emptyList()); + } + + @NotNull + @Override + public List getItems(@NotNull ArtifactBuildTarget target) { + myBuilderContext = new ArtifactsProcessingItemsBuilderContext(myContext); + final Artifact artifact = target.getArtifact(); + + final Set selfIncludingArtifacts = new ReadAction>() { + protected void run(final Result> result) { + result.setResult(ArtifactValidationUtil.getInstance(getProject()).getSelfIncludingArtifacts()); + } + }.execute().getResultObject(); + if (selfIncludingArtifacts.contains(artifact)) { + myContext.addMessage(CompilerMessageCategory.ERROR, "Artifact '" + artifact.getName() + "' includes itself in the output layout", null, -1, -1); + return Collections.emptyList(); + } + + final String outputPath = artifact.getOutputPath(); + if (outputPath == null || outputPath.length() == 0) { + myContext.addMessage(CompilerMessageCategory.ERROR, "Cannot build '" + artifact.getName() + "' artifact: output path is not specified", + null, -1, -1); + return Collections.emptyList(); + } + + new ReadAction() { + protected void run(final Result result) { + collectItems(artifact, outputPath); + } + }.execute(); + return new ArrayList(myBuilderContext.getProcessingItems()); + } + + private void collectItems(@NotNull Artifact artifact, @NotNull String outputPath) { + final CompositePackagingElement rootElement = artifact.getRootElement(); + final VirtualFile outputFile = LocalFileSystem.getInstance().findFileByPath(outputPath); + final CopyToDirectoryInstructionCreator instructionCreator = new CopyToDirectoryInstructionCreator(myBuilderContext, outputPath, outputFile); + final PackagingElementResolvingContext resolvingContext = ArtifactManager.getInstance(getProject()).getResolvingContext(); + rootElement.computeIncrementalCompilerInstructions(instructionCreator, resolvingContext, myBuilderContext, artifact.getArtifactType()); + } + + private boolean doBuild(final List> changedItems, + final Set processedItems, + final Set writtenPaths, final Set deletedJars) { + final boolean testMode = ApplicationManager.getApplication().isUnitTestMode(); + + final DeploymentUtil deploymentUtil = DeploymentUtil.getInstance(); + final FileFilter fileFilter = new IgnoredFileFilter(); + final Set changedJars = new THashSet(); + for (String deletedJar : deletedJars) { + ContainerUtil.addIfNotNull(myBuilderContext.getJarInfo(deletedJar), changedJars); + } + + try { + onBuildStartedOrFinished(false); + if (myContext.getMessageCount(CompilerMessageCategory.ERROR) > 0) { + return false; + } + + int i = 0; + for (final Pair item : changedItems) { + final ArtifactCompilerCompileItem sourceItem = item.getFirst(); + myContext.getProgressIndicator().checkCanceled(); + + final Ref exception = Ref.create(null); + new ReadAction() { + protected void run(final Result result) { + final File fromFile = VfsUtil.virtualToIoFile(sourceItem.getFile()); + for (DestinationInfo destination : sourceItem.getDestinations()) { + if (destination instanceof ExplodedDestinationInfo) { + final ExplodedDestinationInfo explodedDestination = (ExplodedDestinationInfo)destination; + File toFile = new File(FileUtil.toSystemDependentName(explodedDestination.getOutputPath())); + if (fromFile.exists()) { + try { + deploymentUtil.copyFile(fromFile, toFile, myContext, writtenPaths, fileFilter); + } + catch (IOException e) { + exception.set(e); + return; + } + } + } + else { + changedJars.add(((JarDestinationInfo)destination).getJarInfo()); + } + } + } + }.execute(); + if (exception.get() != null) { + throw exception.get(); + } + + myContext.getProgressIndicator().setFraction(++i * 1.0 / changedItems.size()); + processedItems.add(sourceItem); + if (testMode) { + CompilerManagerImpl.addRecompiledPath(FileUtil.toSystemDependentName(sourceItem.getFile().getPath())); + } + } + + JarsBuilder builder = new JarsBuilder(changedJars, fileFilter, myContext); + final boolean processed = builder.buildJars(writtenPaths); + if (!processed) { + return false; + } + + Set recompiledSources = new HashSet(); + for (JarInfo info : builder.getJarsToBuild()) { + for (Pair pair : info.getPackedFiles()) { + recompiledSources.add(pair.getSecond()); + } + } + for (VirtualFile source : recompiledSources) { + ArtifactCompilerCompileItem item = myBuilderContext.getItemBySource(source); + LOG.assertTrue(item != null, source); + processedItems.add(item); + if (testMode) { + CompilerManagerImpl.addRecompiledPath(FileUtil.toSystemDependentName(item.getFile().getPath())); + } + } + + onBuildStartedOrFinished(true); + } + catch (ProcessCanceledException e) { + throw e; + } + catch (Exception e) { + LOG.info(e); + myContext.addMessage(CompilerMessageCategory.ERROR, e.getLocalizedMessage(), null, -1, -1); + return false; + } + return true; + } + + private void onBuildStartedOrFinished(final boolean finished) throws Exception { + final Set artifacts = myContext.getUserData(ArtifactsCompiler.AFFECTED_ARTIFACTS); + if (artifacts != null) { + for (Artifact artifact : artifacts) { + for (ArtifactPropertiesProvider provider : artifact.getPropertiesProviders()) { + final ArtifactProperties properties = artifact.getProperties(provider); + if (finished) { + properties.onBuildFinished(artifact, myContext); + } + else { + properties.onBuildStarted(artifact, myContext); + } + } + } + } + } + + private static THashSet createPathsHashSet() { + return SystemInfo.isFileSystemCaseSensitive + ? new THashSet() + : new THashSet(CaseInsensitiveStringHashingStrategy.INSTANCE); + } + + @Override + public void processItems(@NotNull ArtifactBuildTarget target, @NotNull final List> changedItems, + @NotNull List> obsoleteItems, + @NotNull final OutputConsumer consumer) { + + final THashSet deletedJars = deleteFiles(obsoleteItems, changedItems); + + final Set writtenPaths = createPathsHashSet(); + final Ref built = Ref.create(false); + final Set processedItems = new HashSet(); + CompilerUtil.runInContext(myContext, "Copying files", new ThrowableRunnable() { + public void run() throws RuntimeException { + built.set(doBuild(changedItems, processedItems, writtenPaths, deletedJars)); + } + }); + if (!built.get()) { + return; + } + + myContext.getProgressIndicator().setText(CompilerBundle.message("packaging.compiler.message.updating.caches")); + myContext.getProgressIndicator().setText2(""); + for (String path : writtenPaths) { + consumer.addFileToRefresh(new File(path)); + } + for (ArtifactCompilerCompileItem item : processedItems) { + consumer.addProcessedItem(item); + } + myContext.putUserData(ArtifactsCompiler.WRITTEN_PATHS_KEY, writtenPaths); + } + + private THashSet deleteFiles(List> obsoleteItems, + List> changedItems) { + myContext.getProgressIndicator().setText(CompilerBundle.message("packaging.compiler.message.deleting.outdated.files")); + + final boolean testMode = ApplicationManager.getApplication().isUnitTestMode(); + final THashSet deletedJars = new THashSet(); + final THashSet notDeletedJars = new THashSet(); + if (LOG.isDebugEnabled()) { + LOG.debug("Deleting outdated files..."); + } + + Set pathToDelete = new THashSet(); + for (Pair item : changedItems) { + final ArtifactPackagingItemOutputState cached = item.getSecond(); + if (cached != null) { + for (Pair destination : cached.myDestinations) { + pathToDelete.add(destination.getFirst()); + } + } + } + for (Pair item : changedItems) { + for (DestinationInfo destination : item.getFirst().getDestinations()) { + pathToDelete.remove(destination.getOutputPath()); + } + } + for (Pair item : obsoleteItems) { + for (Pair destination : item.getSecond().myDestinations) { + pathToDelete.add(destination.getFirst()); + } + } + + int notDeletedFilesCount = 0; + List filesToRefresh = new ArrayList(); + + for (String fullPath : pathToDelete) { + int end = fullPath.indexOf(JarFileSystem.JAR_SEPARATOR); + boolean isJar = end != -1; + String filePath = isJar ? fullPath.substring(0, end) : fullPath; + boolean deleted = false; + if (isJar) { + if (notDeletedJars.contains(filePath)) { + continue; + } + deleted = deletedJars.contains(filePath); + } + + File file = new File(FileUtil.toSystemDependentName(filePath)); + if (!deleted) { + filesToRefresh.add(file); + deleted = FileUtil.delete(file); + } + + if (deleted) { + if (isJar) { + deletedJars.add(filePath); + } + if (testMode) { + CompilerManagerImpl.addDeletedPath(file.getAbsolutePath()); + } + } + else { + if (isJar) { + notDeletedJars.add(filePath); + } + if (notDeletedFilesCount++ > 50) { + myContext.addMessage(CompilerMessageCategory.WARNING, "Deletion of outdated files stopped because too many files cannot be deleted", null, -1, -1); + break; + } + myContext.addMessage(CompilerMessageCategory.WARNING, "Cannot delete file '" + filePath + "'", null, -1, -1); + if (LOG.isDebugEnabled()) { + LOG.debug("Cannot delete file " + file); + } + } + } + + CompilerUtil.refreshIOFiles(filesToRefresh); + return deletedJars; + } + +} diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactsProcessingItemsBuilderContext.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactsProcessingItemsBuilderContext.java index 33f599162f8c..21597e988500 100644 --- a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactsProcessingItemsBuilderContext.java +++ b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactsProcessingItemsBuilderContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2010 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,15 +32,14 @@ import java.util.Map; * @author nik */ public class ArtifactsProcessingItemsBuilderContext implements ArtifactIncrementalCompilerContext { - private boolean myCollectingEnabledItems; - protected final Map myItemsBySource; + protected final Map myItemsBySource; private final Map mySourceByOutput; private final Map myJarByPath; private final CompileContext myCompileContext; public ArtifactsProcessingItemsBuilderContext(CompileContext compileContext) { myCompileContext = compileContext; - myItemsBySource = new HashMap(); + myItemsBySource = new HashMap(); mySourceByOutput = new HashMap(); myJarByPath = new HashMap(); } @@ -51,19 +50,14 @@ public class ArtifactsProcessingItemsBuilderContext implements ArtifactIncrement } if (checkOutputPath(destinationInfo.getOutputPath(), sourceFile)) { - getOrCreateProcessingItem(sourceFile).addDestination(destinationInfo, myCollectingEnabledItems); + getOrCreateProcessingItem(sourceFile).addDestination(destinationInfo); return true; } return false; } - public ArtifactPackagingProcessingItem[] getProcessingItems() { - final Collection processingItems = myItemsBySource.values(); - return processingItems.toArray(new ArtifactPackagingProcessingItem[processingItems.size()]); - } - - public void setCollectingEnabledItems(boolean collectingEnabledItems) { - myCollectingEnabledItems = collectingEnabledItems; + public Collection getProcessingItems() { + return myItemsBySource.values(); } public boolean checkOutputPath(final String outputPath, final VirtualFile sourceFile) { @@ -76,7 +70,7 @@ public class ArtifactsProcessingItemsBuilderContext implements ArtifactIncrement return false; } - public ArtifactPackagingProcessingItem getItemBySource(VirtualFile source) { + public ArtifactCompilerCompileItem getItemBySource(VirtualFile source) { return myItemsBySource.get(source); } @@ -102,10 +96,10 @@ public class ArtifactsProcessingItemsBuilderContext implements ArtifactIncrement return myCompileContext; } - public ArtifactPackagingProcessingItem getOrCreateProcessingItem(VirtualFile sourceFile) { - ArtifactPackagingProcessingItem item = myItemsBySource.get(sourceFile); + public ArtifactCompilerCompileItem getOrCreateProcessingItem(VirtualFile sourceFile) { + ArtifactCompilerCompileItem item = myItemsBySource.get(sourceFile); if (item == null) { - item = new ArtifactPackagingProcessingItem(sourceFile); + item = new ArtifactCompilerCompileItem(sourceFile); myItemsBySource.put(sourceFile, item); } return item; diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/CopyToDirectoryInstructionCreator.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/CopyToDirectoryInstructionCreator.java index 46862ac9dec4..12caefa285bd 100644 --- a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/CopyToDirectoryInstructionCreator.java +++ b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/CopyToDirectoryInstructionCreator.java @@ -29,7 +29,8 @@ public class CopyToDirectoryInstructionCreator extends IncrementalCompilerInstru private final String myOutputPath; private final @Nullable VirtualFile myOutputFile; - public CopyToDirectoryInstructionCreator(ArtifactsProcessingItemsBuilderContext context, String outputPath, @Nullable VirtualFile outputFile) { + public CopyToDirectoryInstructionCreator(ArtifactsProcessingItemsBuilderContext context, String outputPath, + @Nullable VirtualFile outputFile) { super(context); myOutputPath = outputPath; myOutputFile = outputFile; diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/IncrementalArtifactsCompiler.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/IncrementalArtifactsCompiler.java deleted file mode 100644 index 9a322302d56c..000000000000 --- a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/IncrementalArtifactsCompiler.java +++ /dev/null @@ -1,583 +0,0 @@ -/* - * Copyright 2000-2009 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.packaging.impl.compiler; - -import com.intellij.compiler.CompilerManagerImpl; -import com.intellij.compiler.impl.CompilerCacheManager; -import com.intellij.compiler.impl.CompilerUtil; -import com.intellij.compiler.impl.FileProcessingCompilerStateCache; -import com.intellij.compiler.impl.packagingCompiler.*; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ReadAction; -import com.intellij.openapi.application.Result; -import com.intellij.openapi.compiler.*; -import com.intellij.openapi.compiler.make.BuildParticipant; -import com.intellij.openapi.compiler.make.BuildParticipantProvider; -import com.intellij.openapi.deployment.DeploymentUtil; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.module.Module; -import com.intellij.openapi.module.ModuleManager; -import com.intellij.openapi.progress.ProcessCanceledException; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.Ref; -import com.intellij.openapi.util.SystemInfo; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.JarFileSystem; -import com.intellij.openapi.vfs.LocalFileSystem; -import com.intellij.openapi.vfs.VfsUtil; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.newvfs.RefreshQueue; -import com.intellij.packaging.artifacts.Artifact; -import com.intellij.packaging.artifacts.ArtifactManager; -import com.intellij.packaging.artifacts.ArtifactProperties; -import com.intellij.packaging.artifacts.ArtifactPropertiesProvider; -import com.intellij.packaging.elements.CompositePackagingElement; -import com.intellij.packaging.elements.PackagingElementResolvingContext; -import com.intellij.packaging.impl.artifacts.ArtifactValidationUtil; -import com.intellij.testFramework.LightVirtualFile; -import com.intellij.util.ArrayUtil; -import com.intellij.util.ThrowableRunnable; -import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.text.CaseInsensitiveStringHashingStrategy; -import gnu.trove.THashSet; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.DataInput; -import java.io.File; -import java.io.FileFilter; -import java.io.IOException; -import java.util.*; - -/** - * @author nik - */ -public class IncrementalArtifactsCompiler implements PackagingCompiler { - private static final Logger LOG = Logger.getInstance("#com.intellij.packaging.impl.compiler.IncrementalArtifactsCompiler"); - private static final Key> WRITTEN_PATHS_KEY = Key.create("artifacts_written_paths"); - private static final Key> FILES_TO_DELETE_KEY = Key.create("artifacts_files_to_delete"); - private static final Key> AFFECTED_ARTIFACTS = Key.create("affected_artifacts"); - private static final Key BUILDER_CONTEXT_KEY = Key.create("artifacts_builder_context"); - @Nullable private PackagingCompilerCache myOutputItemsCache; - - @Nullable - public static IncrementalArtifactsCompiler getInstance(@NotNull Project project) { - final IncrementalArtifactsCompiler[] compilers = CompilerManager.getInstance(project).getCompilers(IncrementalArtifactsCompiler.class); - return compilers.length == 1 ? compilers[0] : null; - } - - private static ArtifactPackagingProcessingItem[] collectItems(ArtifactsProcessingItemsBuilderContext builderContext, final Project project) { - final CompileContext context = builderContext.getCompileContext(); - - final Set artifactsToBuild = ArtifactCompileScope.getArtifactsToBuild(project, context.getCompileScope()); - if (LOG.isDebugEnabled()) { - LOG.debug("artifacts to build: " + artifactsToBuild); - } - List additionalArtifacts = new ArrayList(); - for (BuildParticipantProvider provider : BuildParticipantProvider.EXTENSION_POINT_NAME.getExtensions()) { - for (Module module : ModuleManager.getInstance(project).getModules()) { - final Collection participants = provider.getParticipants(module); - for (BuildParticipant participant : participants) { - ContainerUtil.addIfNotNull(participant.createArtifact(context), additionalArtifacts); - } - } - } - if (LOG.isDebugEnabled() && !additionalArtifacts.isEmpty()) { - LOG.debug("additional artifacts to build: " + additionalArtifacts); - } - artifactsToBuild.addAll(additionalArtifacts); - - final List allArtifacts = new ArrayList(Arrays.asList(ArtifactManager.getInstance(project).getArtifacts())); - allArtifacts.addAll(additionalArtifacts); - for (Artifact artifact : allArtifacts) { - final String outputPath = artifact.getOutputPath(); - if (outputPath != null && outputPath.length() != 0) { - collectItems(builderContext, artifact, outputPath, project, artifactsToBuild.contains(artifact)); - } - else if (artifactsToBuild.contains(artifact)) { - context.addMessage(CompilerMessageCategory.ERROR, "Cannot build '" + artifact.getName() + "' artifact: output path is not specified", - null, -1, -1); - } - } - context.putUserData(AFFECTED_ARTIFACTS, artifactsToBuild); - return builderContext.getProcessingItems(); - } - - @NotNull - public ProcessingItem[] getProcessingItems(final CompileContext context) { - return new ReadAction() { - protected void run(final Result result) { - final Project project = context.getProject(); - final Set selfIncludingArtifacts = ArtifactValidationUtil.getInstance(project).getSelfIncludingArtifacts(); - if (!selfIncludingArtifacts.isEmpty()) { - LOG.info("Self including artifacts: " + selfIncludingArtifacts); - if (!ArtifactCompileScope.getArtifactsToBuild(project, context.getCompileScope()).isEmpty()) { - for (Artifact artifact : selfIncludingArtifacts) { - context.addMessage(CompilerMessageCategory.ERROR, "Artifact '" + artifact.getName() + "' includes itself in the output layout", null, -1, -1); - } - } - result.setResult(ProcessingItem.EMPTY_ARRAY); - return; - } - - ArtifactsProcessingItemsBuilderContext builderContext = new ArtifactsProcessingItemsBuilderContext(context); - context.putUserData(BUILDER_CONTEXT_KEY, builderContext); - ArtifactPackagingProcessingItem[] allProcessingItems = collectItems(builderContext, project); - - if (LOG.isDebugEnabled()) { - int num = Math.min(5000, allProcessingItems.length); - LOG.debug("All files (" + num + " of " + allProcessingItems.length + "):"); - for (int i = 0; i < num; i++) { - LOG.debug(allProcessingItems[i].getFile().getPath()); - } - } - - try { - final FileProcessingCompilerStateCache cache = - CompilerCacheManager.getInstance(project).getFileProcessingCompilerCache(IncrementalArtifactsCompiler.this); - for (ArtifactPackagingProcessingItem item : allProcessingItems) { - item.init(cache); - } - } - catch (IOException e) { - context.requestRebuildNextTime(e.getMessage()); - context.addMessage(CompilerMessageCategory.ERROR, e.getMessage(), null, -1, -1); - result.setResult(ProcessingItem.EMPTY_ARRAY); - LOG.info(e); - return; - } - - boolean hasFilesToDelete = collectFilesToDelete(context, builderContext.getProcessingItems()); - if (hasFilesToDelete) { - MockProcessingItem mockItem = new MockProcessingItem(new LightVirtualFile("239239293")); - result.setResult(ArrayUtil.append(allProcessingItems, mockItem, ProcessingItem.class)); - } - else { - result.setResult(allProcessingItems); - } - } - }.execute().getResultObject(); - } - - private static void collectItems(@NotNull ArtifactsProcessingItemsBuilderContext builderContext, - @NotNull Artifact artifact, - @NotNull String outputPath, - final Project project, boolean enable) { - final CompositePackagingElement rootElement = artifact.getRootElement(); - final VirtualFile outputFile = LocalFileSystem.getInstance().findFileByPath(outputPath); - final CopyToDirectoryInstructionCreator instructionCreator = - new CopyToDirectoryInstructionCreator(builderContext, outputPath, outputFile); - final PackagingElementResolvingContext resolvingContext = ArtifactManager.getInstance(project).getResolvingContext(); - builderContext.setCollectingEnabledItems(enable); - rootElement.computeIncrementalCompilerInstructions(instructionCreator, resolvingContext, builderContext, artifact.getArtifactType()); - } - - public ProcessingItem[] process(final CompileContext context, final ProcessingItem[] items) { - final Set deletedJars = deleteOutdatedFiles(context); - - final List processedItems = new ArrayList(); - final Set writtenPaths = createPathsHashSet(); - final Ref built = Ref.create(false); - CompilerUtil.runInContext(context, "Copying files", new ThrowableRunnable() { - public void run() throws RuntimeException { - built.set(doBuild(context, items, processedItems, writtenPaths, deletedJars)); - } - }); - if (!built.get()) { - return ProcessingItem.EMPTY_ARRAY; - } - - context.getProgressIndicator().setText(CompilerBundle.message("packaging.compiler.message.updating.caches")); - context.getProgressIndicator().setText2(""); - refreshOutputFiles(writtenPaths); - new ReadAction() { - protected void run(final Result result) { - processDestinations(processedItems); - } - }.execute(); - removeInvalidItems(processedItems); - updateOutputCache(context.getProject(), processedItems); - context.putUserData(WRITTEN_PATHS_KEY, writtenPaths); - return processedItems.toArray(new ProcessingItem[processedItems.size()]); - } - - private static boolean doBuild(final CompileContext context, - final ProcessingItem[] items, - final List processedItems, - final Set writtenPaths, - final Set deletedJars) { - final boolean testMode = ApplicationManager.getApplication().isUnitTestMode(); - - if (LOG.isDebugEnabled()) { - int num = Math.min(200, items.length); - LOG.debug("Files to process (" + num + " of " + items.length + "):"); - for (int i = 0; i < num; i++) { - LOG.debug(items[i].getFile().getPath()); - } - } - final DeploymentUtil deploymentUtil = DeploymentUtil.getInstance(); - final FileFilter fileFilter = new IgnoredFileFilter(); - final ArtifactsProcessingItemsBuilderContext builderContext = context.getUserData(BUILDER_CONTEXT_KEY); - final Set changedJars = new THashSet(); - for (String deletedJar : deletedJars) { - ContainerUtil.addIfNotNull(builderContext.getJarInfo(deletedJar), changedJars); - } - - try { - onBuildStartedOrFinished(builderContext, false); - if (context.getMessageCount(CompilerMessageCategory.ERROR) > 0) { - return false; - } - - int i = 0; - for (final ProcessingItem item0 : items) { - if (item0 instanceof MockProcessingItem) continue; - final ArtifactPackagingProcessingItem item = (ArtifactPackagingProcessingItem)item0; - context.getProgressIndicator().checkCanceled(); - - final Ref exception = Ref.create(null); - new ReadAction() { - protected void run(final Result result) { - final File fromFile = VfsUtil.virtualToIoFile(item.getFile()); - for (DestinationInfo destination : item.getEnabledDestinations()) { - if (destination instanceof ExplodedDestinationInfo) { - final ExplodedDestinationInfo explodedDestination = (ExplodedDestinationInfo)destination; - File toFile = new File(FileUtil.toSystemDependentName(explodedDestination.getOutputPath())); - if (fromFile.exists()) { - try { - deploymentUtil.copyFile(fromFile, toFile, context, writtenPaths, fileFilter); - } - catch (IOException e) { - exception.set(e); - return; - } - } - } - else { - changedJars.add(((JarDestinationInfo)destination).getJarInfo()); - } - } - } - }.execute(); - if (exception.get() != null) { - throw exception.get(); - } - - context.getProgressIndicator().setFraction(++i * 1.0 / items.length); - processedItems.add(item); - if (testMode) { - CompilerManagerImpl.addRecompiledPath(FileUtil.toSystemDependentName(item.getFile().getPath())); - } - } - - JarsBuilder builder = new JarsBuilder(changedJars, fileFilter, context); - final boolean processed = builder.buildJars(writtenPaths); - if (!processed) { - return false; - } - - Set recompiledSources = new HashSet(); - for (JarInfo info : builder.getJarsToBuild()) { - for (Pair pair : info.getPackedFiles()) { - recompiledSources.add(pair.getSecond()); - } - } - for (ArtifactPackagingProcessingItem processedItem : processedItems) { - recompiledSources.remove(processedItem.getFile()); - } - for (VirtualFile source : recompiledSources) { - ArtifactPackagingProcessingItem item = builderContext.getItemBySource(source); - LOG.assertTrue(item != null, source); - processedItems.add(item); - if (testMode) { - CompilerManagerImpl.addRecompiledPath(FileUtil.toSystemDependentName(item.getFile().getPath())); - } - } - - onBuildStartedOrFinished(builderContext, true); - } - catch (ProcessCanceledException e) { - throw e; - } - catch (Exception e) { - LOG.info(e); - context.addMessage(CompilerMessageCategory.ERROR, e.getLocalizedMessage(), null, -1, -1); - return false; - } - return true; - } - - public static Set getAffectedArtifacts(final CompileContext compileContext) { - return compileContext.getUserData(AFFECTED_ARTIFACTS); - } - - @Nullable - public static Set getWrittenPaths(@NotNull CompileContext context) { - return context.getUserData(WRITTEN_PATHS_KEY); - } - - @NotNull - public String getDescription() { - return "Artifacts Packaging Compiler"; - } - - @NotNull - private PackagingCompilerCache getOutputItemsCache(final Project project) { - if (myOutputItemsCache == null) { - myOutputItemsCache = new PackagingCompilerCache( - CompilerPaths.getCompilerSystemDirectory(project).getPath() + File.separator + "incremental_artifacts_timestamp.dat"); - } - return myOutputItemsCache; - } - - public void processOutdatedItem(final CompileContext context, final String url, @Nullable final ValidityState state) { - } - - private boolean collectFilesToDelete(final CompileContext context, final ArtifactPackagingProcessingItem[] allProcessingItems) { - List filesToDelete = new ArrayList(); - Set outputPaths = createPathsHashSet(); - for (ArtifactPackagingProcessingItem item : allProcessingItems) { - for (Pair destinationInfo : item.getDestinations()) { - String outputPath = getOutputPathWithJarSeparator(destinationInfo.getFirst()); - outputPaths.add(outputPath); - } - } - - final Iterator pathIterator = getOutputItemsCache(context.getProject()).getUrlsIterator(); - while (pathIterator.hasNext()) { - String path = pathIterator.next(); - if (!outputPaths.contains(path)) { - filesToDelete.add(path); - } - } - - if (LOG.isDebugEnabled()) { - LOG.debug("Files to delete (" + filesToDelete.size() + "):"); - for (String path : filesToDelete) { - LOG.debug(path); - } - } - - if (filesToDelete.isEmpty()) { - return false; - } - context.putUserData(FILES_TO_DELETE_KEY, filesToDelete); - return true; - } - - private static String getOutputPathWithJarSeparator(DestinationInfo destinationInfo) { - String outputPath = destinationInfo.getOutputFilePath(); - if (destinationInfo instanceof JarDestinationInfo) { - final String fullOutputPath = destinationInfo.getOutputPath(); - final String fileOutputPath = destinationInfo.getOutputFilePath(); - if (fullOutputPath.startsWith(fileOutputPath)) { - outputPath += JarFileSystem.JAR_SEPARATOR + DeploymentUtil.trimForwardSlashes(fullOutputPath.substring(fileOutputPath.length())); - } - } - return outputPath; - } - - private static void onBuildStartedOrFinished(ArtifactsProcessingItemsBuilderContext context, final boolean finished) throws Exception { - final CompileContext compileContext = context.getCompileContext(); - final Set artifacts = getAffectedArtifacts(compileContext); - for (Artifact artifact : artifacts) { - for (ArtifactPropertiesProvider provider : artifact.getPropertiesProviders()) { - final ArtifactProperties properties = artifact.getProperties(provider); - if (finished) { - properties.onBuildFinished(artifact, compileContext); - } - else { - properties.onBuildStarted(artifact, compileContext); - } - } - } - } - - private static THashSet createPathsHashSet() { - return SystemInfo.isFileSystemCaseSensitive - ? new THashSet() - : new THashSet(CaseInsensitiveStringHashingStrategy.INSTANCE); - } - - private static void removeInvalidItems(List processedItems) { - Set files = new THashSet(processedItems.size()); - for (ArtifactPackagingProcessingItem item : processedItems) { - files.add(item.getFile()); - } - RefreshQueue.getInstance().refresh(false, false, null, VfsUtil.toVirtualFileArray(files)); - - final Iterator iterator = processedItems.iterator(); - while (iterator.hasNext()) { - ArtifactPackagingProcessingItem item = iterator.next(); - final VirtualFile file = item.getFile(); - if (!file.isValid()) { - iterator.remove(); - } - } - } - - private static void processDestinations(final List items) { - for (ArtifactPackagingProcessingItem item : items) { - item.setProcessed(); - } - } - - private Set deleteOutdatedFiles(final CompileContext context) { - context.getProgressIndicator().setText(CompilerBundle.message("packaging.compiler.message.deleting.outdated.files")); - final List filesToDelete = context.getUserData(FILES_TO_DELETE_KEY); - final Set deletedJars; - if (filesToDelete != null) { - deletedJars = deleteFiles(filesToDelete, context); - } - else { - deletedJars = Collections.emptySet(); - } - context.getProgressIndicator().checkCanceled(); - return deletedJars; - } - - private Set deleteFiles(final List paths, CompileContext context) { - final Set artifactsToBuild = getAffectedArtifacts(context); - - final boolean testMode = ApplicationManager.getApplication().isUnitTestMode(); - final THashSet deletedJars = new THashSet(); - final THashSet notDeletedJars = new THashSet(); - if (LOG.isDebugEnabled()) { - LOG.debug("Deleting outdated files..."); - } - - int notDeletedFilesCount = 0; - final Artifact[] allArtifacts = ArtifactManager.getInstance(context.getProject()).getArtifacts(); - List filesToRefresh = new ArrayList(); - for (String fullPath : paths) { - boolean isUnderOutput = false; - boolean isInArtifactsToBuild = false; - for (Artifact artifact : allArtifacts) { - final String path = artifact.getOutputPath(); - if (!StringUtil.isEmpty(path) && FileUtil.startsWith(fullPath, path)) { - isUnderOutput = true; - if (artifactsToBuild.contains(artifact)) { - isInArtifactsToBuild = true; - break; - } - } - } - if (isUnderOutput && !isInArtifactsToBuild) continue; - - int end = fullPath.indexOf(JarFileSystem.JAR_SEPARATOR); - boolean isJar = end != -1; - String filePath = isJar ? fullPath.substring(0, end) : fullPath; - boolean deleted = false; - if (isJar) { - if (notDeletedJars.contains(filePath)) { - continue; - } - deleted = deletedJars.contains(filePath); - } - - File file = new File(FileUtil.toSystemDependentName(filePath)); - if (!deleted) { - filesToRefresh.add(file); - deleted = FileUtil.delete(file); - } - - if (deleted) { - if (isJar) { - deletedJars.add(filePath); - } - if (testMode) { - CompilerManagerImpl.addDeletedPath(file.getAbsolutePath()); - } - getOutputItemsCache(context.getProject()).remove(fullPath); - } - else { - if (isJar) { - notDeletedJars.add(filePath); - } - if (notDeletedFilesCount++ > 50) { - context.addMessage(CompilerMessageCategory.WARNING, "Deletion of outdated files stopped because too many files cannot be deleted", null, -1, -1); - break; - } - context.addMessage(CompilerMessageCategory.WARNING, "Cannot delete file '" + filePath + "'", null, -1, -1); - if (LOG.isDebugEnabled()) { - LOG.debug("Cannot delete file " + file); - } - } - } - - CompilerUtil.refreshIOFiles(filesToRefresh); - return deletedJars; - } - - private void updateOutputCache(final Project project, final List processedItems) { - for (ArtifactPackagingProcessingItem processedItem : processedItems) { - for (DestinationInfo destinationInfo : processedItem.getEnabledDestinations()) { - final VirtualFile virtualFile = destinationInfo.getOutputFile(); - if (virtualFile != null) { - final String path = getOutputPathWithJarSeparator(destinationInfo); - if (LOG.isDebugEnabled()) { - LOG.debug("update output cache: file " + path); - } - getOutputItemsCache(project).update(path, virtualFile.getTimeStamp()); - } - } - } - saveCacheIfDirty(project); - } - - private static void refreshOutputFiles(Set writtenPaths) { - final ArrayList filesToRefresh = new ArrayList(); - for (String path : writtenPaths) { - filesToRefresh.add(new File(path)); - } - CompilerUtil.refreshIOFiles(filesToRefresh); - } - - private void saveCacheIfDirty(final Project project) { - if (getOutputItemsCache(project).isDirty()) { - getOutputItemsCache(project).save(); - } - } - - public ValidityState createValidityState(final DataInput is) throws IOException { - return new ArtifactPackagingItemValidityState(is); - } - - public boolean validateConfiguration(final CompileScope scope) { - return true; - } - - private static class MockProcessingItem implements ProcessingItem { - private final VirtualFile myFile; - - public MockProcessingItem(final VirtualFile file) { - myFile = file; - } - - @NotNull - public VirtualFile getFile() { - return myFile; - } - - @Nullable - public ValidityState getValidityState() { - return null; - } - } -} diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/PackIntoArchiveInstructionCreator.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/PackIntoArchiveInstructionCreator.java index a703503175b2..cfeb4b1646d9 100644 --- a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/PackIntoArchiveInstructionCreator.java +++ b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/PackIntoArchiveInstructionCreator.java @@ -30,8 +30,8 @@ public class PackIntoArchiveInstructionCreator extends IncrementalCompilerInstru private final JarInfo myJarInfo; private final String myPathInJar; - public PackIntoArchiveInstructionCreator(ArtifactsProcessingItemsBuilderContext context, JarInfo jarInfo, String pathInJar, - DestinationInfo jarDestination) { + public PackIntoArchiveInstructionCreator(ArtifactsProcessingItemsBuilderContext context, JarInfo jarInfo, + String pathInJar, DestinationInfo jarDestination) { super(context); myJarInfo = jarInfo; myPathInJar = pathInJar; @@ -55,7 +55,8 @@ public class PackIntoArchiveInstructionCreator extends IncrementalCompilerInstru public IncrementalCompilerInstructionCreator archive(@NotNull String archiveFileName) { final JarInfo jarInfo = new JarInfo(); - if (!myContext.registerJarFile(jarInfo, myJarDestination.getOutputPath() + "/" + archiveFileName)) { + final String outputPath = myJarDestination.getOutputPath() + "/" + archiveFileName; + if (!myContext.registerJarFile(jarInfo, outputPath)) { return new SkipAllInstructionCreator(myContext); } final JarDestinationInfo destination = new JarDestinationInfo(childPathInJar(archiveFileName), myJarInfo, myJarDestination); diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/ManifestFileUtil.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ManifestFileUtil.java index f326e2f1e831..21433a5e74e3 100644 --- a/java/compiler/impl/src/com/intellij/packaging/impl/elements/ManifestFileUtil.java +++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ManifestFileUtil.java @@ -249,13 +249,19 @@ public class ManifestFileUtil { VirtualFile dir = files[0]; try { if (!dir.getName().equals(MANIFEST_DIR_NAME)) { - VirtualFile newDir = dir.findChild(MANIFEST_DIR_NAME); - if (newDir == null) { - newDir = dir.createChildDirectory(this, MANIFEST_DIR_NAME); - } - dir = newDir; + dir = VfsUtil.createDirectoryIfMissing(dir, MANIFEST_DIR_NAME); } - result.setResult(dir.createChildData(this, MANIFEST_FILE_NAME)); + final VirtualFile file = dir.createChildData(this, MANIFEST_FILE_NAME); + final OutputStream output = file.getOutputStream(this); + try { + final Manifest manifest = new Manifest(); + ManifestBuilder.setVersionAttribute(manifest.getMainAttributes()); + manifest.write(output); + } + finally { + output.close(); + } + result.setResult(file); } catch (IOException e) { exc.set(e); diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/run/BuildArtifactsBeforeRunTaskProvider.java b/java/compiler/impl/src/com/intellij/packaging/impl/run/BuildArtifactsBeforeRunTaskProvider.java index 3d18372af496..3b0e586020f2 100644 --- a/java/compiler/impl/src/com/intellij/packaging/impl/run/BuildArtifactsBeforeRunTaskProvider.java +++ b/java/compiler/impl/src/com/intellij/packaging/impl/run/BuildArtifactsBeforeRunTaskProvider.java @@ -35,7 +35,7 @@ import com.intellij.openapi.util.Ref; import com.intellij.packaging.artifacts.*; import com.intellij.packaging.impl.compiler.ArtifactAwareCompiler; import com.intellij.packaging.impl.compiler.ArtifactCompileScope; -import com.intellij.packaging.impl.compiler.IncrementalArtifactsCompiler; +import com.intellij.packaging.impl.compiler.ArtifactsCompiler; import com.intellij.util.concurrency.Semaphore; import com.intellij.util.containers.ContainerUtil; import gnu.trove.THashSet; @@ -148,7 +148,7 @@ public class BuildArtifactsBeforeRunTaskProvider extends BeforeRunTaskProvider languages = viewProvider.getLanguages(); return new HectorComponentPanel() { private JCheckBox myImportPopupCheckBox = new JCheckBox(EditorBundle.message("hector.import.popup.checkbox")); diff --git a/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java b/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java index 268e9c2fd5ac..63908d4e6e4c 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java @@ -18,6 +18,8 @@ package com.intellij.lang.java.parser; import com.intellij.codeInsight.daemon.JavaErrorMessages; import com.intellij.lang.LighterASTNode; import com.intellij.lang.PsiBuilder; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.Pair; import com.intellij.psi.JavaTokenType; import com.intellij.psi.impl.source.tree.ElementType; import com.intellij.psi.impl.source.tree.JavaElementType; @@ -36,6 +38,8 @@ public class DeclarationParser { FILE, CLASS, CODE_BLOCK, ANNOTATION_INTERFACE } + private static final Logger LOG = Logger.getInstance("#com.intellij.lang.java.parser.DeclarationParser"); + private static final TokenSet AFTER_END_DECLARATION_SET = TokenSet.create(JavaElementType.FIELD, JavaElementType.METHOD); private DeclarationParser() { } @@ -48,10 +52,11 @@ public class DeclarationParser { marker.drop(); builder.advanceLexer(); + final PsiBuilder builderWrapper = braceMatchingBuilder(builder); if (isEnum) { - parseEnumConstants(builder); + parseEnumConstants(builderWrapper); } - parseClassBodyDeclarations(builder, isAnnotation); + parseClassBodyDeclarations(builderWrapper, isAnnotation); expectOrError(builder, JavaTokenType.RBRACE, JavaErrorMessages.message("expected.rbrace")); @@ -186,10 +191,11 @@ public class DeclarationParser { final PsiBuilder.Marker declaration = builder.mark(); - final PsiBuilder.Marker modList = parseModifierList(builder); + final Pair modListInfo = parseModifierList(builder); + final PsiBuilder.Marker modList = modListInfo.first; if (expect(builder, JavaTokenType.AT)) { - if (tokenType == JavaTokenType.INTERFACE_KEYWORD) { + if (builder.getTokenType() == JavaTokenType.INTERFACE_KEYWORD) { return parseClassFromKeyword(builder, declaration, true); } else { @@ -197,9 +203,8 @@ public class DeclarationParser { return null; } } - else if (ElementType.CLASS_KEYWORD_BIT_SET.contains(tokenType)) { + else if (ElementType.CLASS_KEYWORD_BIT_SET.contains(builder.getTokenType())) { final PsiBuilder.Marker root = parseClassFromKeyword(builder, declaration, false); - if (context == Context.FILE) { // todo: append following declarations to root boolean declarationsAfterEnd = false; @@ -233,29 +238,113 @@ public class DeclarationParser { } if (context == Context.FILE) { - if (typeParams == null) { - error(builder, JavaErrorMessages.message("expected.class.or.interface")); - } - else { - typeParams.precede().errorBefore(JavaErrorMessages.message("expected.class.or.interface"), typeParams); - } + error(builder, JavaErrorMessages.message("expected.class.or.interface"), typeParams); declaration.drop(); return modList; } - // todo: implement - throw new UnsupportedOperationException(builder.toString() + context); + PsiBuilder.Marker type; + if (ElementType.PRIMITIVE_TYPE_BIT_SET.contains(builder.getTokenType())) { + type = parseTypeNotNull(builder); + } + else if (builder.getTokenType() == JavaTokenType.IDENTIFIER) { + final PsiBuilder.Marker idPos = builder.mark(); + type = parseTypeNotNull(builder); + if (builder.getTokenType() == JavaTokenType.LPARENTH) { // constructor + if (context == Context.CODE_BLOCK) { + declaration.rollbackTo(); + return null; + } + idPos.rollbackTo(); + if (typeParams == null) { + emptyElement(builder, JavaElementType.TYPE_PARAMETER_LIST); + } + builder.advanceLexer(); + if (builder.getTokenType() != JavaTokenType.LPARENTH) { + declaration.rollbackTo(); + return null; + } + return parseMethodFromLeftParenth(builder, declaration, false); + } + idPos.drop(); + } + else if (builder.getTokenType() == JavaTokenType.LBRACE) { + if (context == Context.CODE_BLOCK) { + error(builder, JavaErrorMessages.message("expected.identifier.or.type"), typeParams); + declaration.drop(); + return modList; + } + + final PsiBuilder.Marker codeBlock = StatementParser.parseCodeBlock(builder); + LOG.assertTrue(codeBlock != null); + + if (typeParams != null) { + final PsiBuilder.Marker error = typeParams.precede(); + error.errorBefore(JavaErrorMessages.message("unexpected.token"), codeBlock); + } + declaration.done(JavaElementType.CLASS_INITIALIZER); + return declaration; + } + else { + final PsiBuilder.Marker error; + if (typeParams != null) { + error = typeParams.precede(); + } + else { + error = builder.mark(); + } + error.error(JavaErrorMessages.message("expected.identifier.or.type")); + return modList; + } + + if (!expect(builder, JavaTokenType.IDENTIFIER)) { + if (context == Context.CODE_BLOCK && modListInfo.second) { + declaration.rollbackTo(); + return null; + } + else { + if (typeParams != null) { + typeParams.precede().errorBefore(JavaErrorMessages.message("unexpected.token"), type); + } + builder.error(JavaErrorMessages.message("expected.identifier")); + declaration.drop(); + return modList; + } + } + + if (builder.getTokenType() == JavaTokenType.LPARENTH) { + if (context == Context.CLASS || context == Context.ANNOTATION_INTERFACE) { // method + if (typeParams == null) { + emptyElement(type, JavaElementType.TYPE_PARAMETER_LIST); + } + return parseMethodFromLeftParenth(builder, declaration, (context == Context.ANNOTATION_INTERFACE)); + } + } + + if (typeParams != null) { + typeParams.precede().errorBefore(JavaErrorMessages.message("unexpected.token"), type); + } + return parseFieldOrLocalVariable(builder, declaration, context); } @NotNull - private static PsiBuilder.Marker parseModifierList(final PsiBuilder builder) { + private static PsiBuilder.Marker parseTypeNotNull(final PsiBuilder builder) { + final ReferenceParser.TypeInfo typeInfo = ReferenceParser.parseType(builder); + assert typeInfo != null : builder.getOriginalText(); + return typeInfo.marker; + } + + @NotNull + private static Pair parseModifierList(final PsiBuilder builder) { final PsiBuilder.Marker modList = builder.mark(); + boolean isEmpty = true; while (true) { final IElementType tokenType = builder.getTokenType(); if (tokenType == null) break; if (ElementType.MODIFIER_BIT_SET.contains(tokenType)) { builder.advanceLexer(); + isEmpty = false; } else if (tokenType == JavaTokenType.AT) { final PsiBuilder.Marker pos = builder.mark(); @@ -266,6 +355,7 @@ public class DeclarationParser { break; } parseAnnotation(builder); + isEmpty = false; } else { break; @@ -273,7 +363,225 @@ public class DeclarationParser { } modList.done(JavaElementType.MODIFIER_LIST); - return modList; + return Pair.create(modList, isEmpty); + } + + private static PsiBuilder.Marker parseMethodFromLeftParenth(final PsiBuilder builder, final PsiBuilder.Marker declaration, + final boolean anno) { + parseParameterList(builder); + + eatBrackets(builder); + + if (areTypeAnnotationsSupported(builder)) { + final PsiBuilder.Marker receiver = builder.mark(); + final PsiBuilder.Marker annotations = parseAnnotations(builder); + if (annotations != null) { + receiver.done(JavaElementType.METHOD_RECEIVER); + } + else { + receiver.drop(); + } + } + + ReferenceParser.parseReferenceList(builder, JavaTokenType.THROWS_KEYWORD, JavaElementType.THROWS_LIST, JavaTokenType.COMMA); + + if (anno && expect(builder, JavaTokenType.DEFAULT_KEYWORD)) { + parseAnnotationValue(builder); + } + + final IElementType tokenType = builder.getTokenType(); + if (tokenType == JavaTokenType.SEMICOLON) { + builder.advanceLexer(); + } + else if (tokenType == JavaTokenType.LBRACE) { + StatementParser.parseCodeBlock(builder); + } + else { + error(builder, JavaErrorMessages.message("expected.lbrace.or.semicolon")); + // todo: special treatment - like in fields (DeclarationParserTest.testMultiLineUnclosed()) + } + + declaration.done(anno ? JavaElementType.ANNOTATION_METHOD : JavaElementType.METHOD); + return declaration; + } + + @NotNull + private static PsiBuilder.Marker parseParameterList(final PsiBuilder builder) { + assert builder.getTokenType() == JavaTokenType.LPARENTH : builder.getTokenType(); + final PsiBuilder.Marker paramList = builder.mark(); + builder.advanceLexer(); + + PsiBuilder.Marker invalidElements = null; + boolean commaExpected = false; + int paramCount = 0; + while (true) { + final IElementType tokenType = builder.getTokenType(); + if (tokenType == null || tokenType == JavaTokenType.RPARENTH) { + boolean noLastParam = !commaExpected && paramCount > 0; + if (noLastParam) { + error(builder, JavaErrorMessages.message("expected.identifier.or.type")); + } + if (!expect(builder, JavaTokenType.RPARENTH)) { + if (!noLastParam) { + error(builder, JavaErrorMessages.message("expected.rparen")); + } + } + break; + } + + if (commaExpected) { + if (builder.getTokenType() == JavaTokenType.COMMA) { + commaExpected = false; + if (invalidElements != null) { + invalidElements.error(JavaErrorMessages.message("expected.parameter")); + invalidElements = null; + } + builder.advanceLexer(); + continue; + } + } + else { + final PsiBuilder.Marker param = parseParameter(builder, true); + if (param != null) { + commaExpected = true; + if (invalidElements != null) { + invalidElements.errorBefore(JavaErrorMessages.message("expected.comma"), param); + invalidElements = null; + } + paramCount++; + continue; + } + } + + if (invalidElements == null) { + if (builder.getTokenType() == JavaTokenType.COMMA) { + error(builder, JavaErrorMessages.message("expected.parameter")); + builder.advanceLexer(); + continue; + } + else { + invalidElements = builder.mark(); + } + } + + // adding a reference, not simple tokens allows "Browse .." to work well + final PsiBuilder.Marker ref = ReferenceParser.parseJavaCodeReference(builder, true, true, false); + if (ref == null && builder.getTokenType() != null) { + builder.advanceLexer(); + } + } + + if (invalidElements != null) { + invalidElements.error(commaExpected ? JavaErrorMessages.message("expected.comma") : JavaErrorMessages.message("expected.parameter")); + } + + paramList.done(JavaElementType.PARAMETER_LIST); + return paramList; + } + + @Nullable + private static PsiBuilder.Marker parseParameter(final PsiBuilder builder, final boolean ellipsis) { + final PsiBuilder.Marker param = builder.mark(); + + final Pair modListInfo = parseModifierList(builder); + final PsiBuilder.Marker type = ellipsis ? ReferenceParser.parseTypeWithEllipsis(builder, true, true) : + ReferenceParser.parseType(builder, true, true); + + if (type == null && modListInfo.second) { + param.rollbackTo(); + return null; + } + + if (type == null) { + error(builder, JavaErrorMessages.message("expected.type")); + emptyElement(builder, JavaElementType.TYPE); + } + + if (expect(builder, JavaTokenType.IDENTIFIER)) { + eatBrackets(builder); + } + else { + error(builder, JavaErrorMessages.message("expected.identifier")); + } + + param.done(JavaElementType.PARAMETER); + return param; + } + + @Nullable + private static PsiBuilder.Marker parseFieldOrLocalVariable(final PsiBuilder builder, final PsiBuilder.Marker declaration, + final Context context) { + final IElementType varType; + if (context == Context.CLASS || context == Context.ANNOTATION_INTERFACE) { + varType = JavaElementType.FIELD; + } + else if (context == Context.CODE_BLOCK) { + varType = JavaElementType.LOCAL_VARIABLE; + } + else { + LOG.error("Unexpected context: " + context); + declaration.drop(); + return null; + } + + PsiBuilder.Marker variable = declaration; + boolean openMarker = true; + boolean eatSemicolon = true; + boolean expectSemicolon = true; + while (true) { + if (!eatBrackets(builder)) { + expectSemicolon = false; + } + + if (expect(builder, JavaTokenType.EQ)) { + final PsiBuilder.Marker expr = ExpressionParser.parse(builder); + if (expr == null) { + error(builder, JavaErrorMessages.message("expected.expression")); + expectSemicolon = false; + break; + } + } + + if (builder.getTokenType() == JavaTokenType.COMMA) { + variable.done(varType); + builder.advanceLexer(); + variable = builder.mark(); + } + else { + break; + } + + if (!expect(builder, JavaTokenType.IDENTIFIER)) { + variable.drop(); + error(builder, JavaErrorMessages.message("expected.identifier")); + openMarker = false; + eatSemicolon = false; + break; + } + } + + if (eatSemicolon) { + if (!expect(builder, JavaTokenType.SEMICOLON) && expectSemicolon) { + error(builder, JavaErrorMessages.message("expected.semicolon")); + } + // todo: special treatment - see DeclarationParserTest.testMultiLineUnclosed() + } + + if (openMarker) { + variable.done(varType); + } + + return declaration; + } + + private static boolean eatBrackets(final PsiBuilder builder) { + while (expect(builder, JavaTokenType.LBRACKET)) { + if (!expect(builder, JavaTokenType.RBRACKET)) { + error(builder, JavaErrorMessages.message("expected.rbracket")); + return false; + } + } + return true; } @Nullable @@ -290,6 +598,7 @@ public class DeclarationParser { @NotNull private static PsiBuilder.Marker parseAnnotation(final PsiBuilder builder) { + assert builder.getTokenType() == JavaTokenType.AT : builder.getTokenType(); final PsiBuilder.Marker anno = builder.mark(); builder.advanceLexer(); @@ -408,6 +717,7 @@ public class DeclarationParser { @NotNull private static PsiBuilder.Marker parseAnnotationArrayInitializer(final PsiBuilder builder) { + assert builder.getTokenType() == JavaTokenType.LBRACE : builder.getTokenType(); final PsiBuilder.Marker annoArray = builder.mark(); builder.advanceLexer(); diff --git a/java/java-impl/src/com/intellij/lang/java/parser/JavaParserUtil.java b/java/java-impl/src/com/intellij/lang/java/parser/JavaParserUtil.java index 73937f311f81..6a3597e5f897 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/JavaParserUtil.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/JavaParserUtil.java @@ -15,12 +15,16 @@ */ package com.intellij.lang.java.parser; -import com.intellij.lang.PsiBuilder; -import com.intellij.lang.PsiBuilderUtil; +import com.intellij.lang.*; import com.intellij.openapi.util.Key; import com.intellij.pom.java.LanguageLevel; +import com.intellij.psi.JavaTokenType; import com.intellij.psi.tree.IElementType; +import com.intellij.psi.tree.TokenSet; +import com.intellij.util.diff.FlyweightCapableTreeStructure; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public class JavaParserUtil { @@ -40,7 +44,7 @@ public class JavaParserUtil { @NotNull private static LanguageLevel getLanguageLevel(final PsiBuilder builder) { final LanguageLevel level = builder.getUserData(LANG_LEVEL_KEY); - assert level != null; + assert level != null : builder; return level; } @@ -49,6 +53,15 @@ public class JavaParserUtil { builder.mark().error(message); } + public static void error(final PsiBuilder builder, final String message, @Nullable final PsiBuilder.Marker before) { + if (before == null) { + error(builder, message); + } + else { + before.precede().errorBefore(message, before); + } + } + public static boolean expectOrError(final PsiBuilder builder, final IElementType expectedType, final String errorMessage) { if (!PsiBuilderUtil.expect(builder, expectedType)) { error(builder, errorMessage); @@ -60,4 +73,106 @@ public class JavaParserUtil { public static void emptyElement(final PsiBuilder builder, final IElementType type) { builder.mark().done(type); } + + public static void emptyElement(final PsiBuilder.Marker before, final IElementType type) { + before.precede().doneBefore(type, before); + } + + public static PsiBuilder braceMatchingBuilder(final PsiBuilder builder) { + return new PsiBuilderAdapter(builder) { + private int braceCount = 1; + private int lastOffset = -1; + + @Override + public IElementType getTokenType() { + final IElementType tokenType = super.getTokenType(); + if (getCurrentOffset() != lastOffset) { + if (tokenType == JavaTokenType.LBRACE) { + braceCount++; + } + else if (tokenType == JavaTokenType.RBRACE) { + braceCount--; + } + lastOffset = getCurrentOffset(); + } + return (braceCount == 0 ? null : tokenType); + } + }; + } + + public static class PsiBuilderAdapter implements PsiBuilder { + protected final PsiBuilder myDelegate; + + public PsiBuilderAdapter(final PsiBuilder delegate) { + myDelegate = delegate; + } + + public CharSequence getOriginalText() { + return myDelegate.getOriginalText(); + } + + public void advanceLexer() { + myDelegate.advanceLexer(); + } + + @Nullable + public IElementType getTokenType() { + return myDelegate.getTokenType(); + } + + public void setTokenTypeRemapper(final ITokenTypeRemapper remapper) { + myDelegate.setTokenTypeRemapper(remapper); + } + + @Nullable @NonNls + public String getTokenText() { + return myDelegate.getTokenText(); + } + + public int getCurrentOffset() { + return myDelegate.getCurrentOffset(); + } + + public Marker mark() { + return myDelegate.mark(); + } + + public void error(final String messageText) { + myDelegate.error(messageText); + } + + public boolean eof() { + return myDelegate.eof(); + } + + public ASTNode getTreeBuilt() { + return myDelegate.getTreeBuilt(); + } + + public FlyweightCapableTreeStructure getLightTree() { + return myDelegate.getLightTree(); + } + + public void setDebugMode(final boolean dbgMode) { + myDelegate.setDebugMode(dbgMode); + } + + public void enforceCommentTokens(final TokenSet tokens) { + myDelegate.enforceCommentTokens(tokens); + } + + @Nullable + public LighterASTNode getLatestDoneMarker() { + return myDelegate.getLatestDoneMarker(); + } + + @Nullable + public T getUserData(@NotNull final Key key) { + return myDelegate.getUserData(key); + } + + public void putUserData(@NotNull final Key key, @Nullable final T value) { + myDelegate.putUserData(key, value); + } + } } diff --git a/java/java-impl/src/com/intellij/lang/java/parser/ReferenceParser.java b/java/java-impl/src/com/intellij/lang/java/parser/ReferenceParser.java index 8831468fabe3..da8be88afb9e 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/ReferenceParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/ReferenceParser.java @@ -53,6 +53,21 @@ public class ReferenceParser { return typeInfo != null ? typeInfo.marker : null; } + @Nullable + public static PsiBuilder.Marker parseTypeWithEllipsis(final PsiBuilder builder, final boolean eatLastDot, final boolean wildcard) { + final TypeInfo typeInfo = parseTypeWithInfo(builder, eatLastDot, wildcard); + if (typeInfo == null) return null; + + PsiBuilder.Marker type = typeInfo.marker; + if (builder.getTokenType() == JavaTokenType.ELLIPSIS) { + type = typeInfo.marker.precede(); + builder.advanceLexer(); + type.done(JavaElementType.TYPE); + } + + return type; + } + @Nullable private static TypeInfo parseTypeWithInfo(final PsiBuilder builder, final boolean eatLastDot, final boolean wildcard) { if (builder.getTokenType() == null) return null; @@ -263,28 +278,26 @@ public class ReferenceParser { return null; } - if (expect(builder, JavaTokenType.EXTENDS_KEYWORD)) { - parseReferenceList(builder, JavaElementType.EXTENDS_BOUND_LIST, JavaTokenType.AND); - } - else { - emptyElement(builder, JavaElementType.EXTENDS_BOUND_LIST); - } + parseReferenceList(builder, JavaTokenType.EXTENDS_KEYWORD, JavaElementType.EXTENDS_BOUND_LIST, JavaTokenType.AND); param.done(JavaElementType.TYPE_PARAMETER); return param; } @NotNull - private static PsiBuilder.Marker parseReferenceList(final PsiBuilder builder, final IElementType type, final IElementType delimiter) { + public static PsiBuilder.Marker parseReferenceList(final PsiBuilder builder, final IElementType start, + final IElementType type, final IElementType delimiter) { final PsiBuilder.Marker element = builder.mark(); - while (true) { - final PsiBuilder.Marker classReference = parseJavaCodeReference(builder, true, true, true); - if (classReference == null) { - error(builder, JavaErrorMessages.message("expected.identifier")); - } - if (!expect(builder, delimiter)) { - break; + if (expect(builder, start)) { + while (true) { + final PsiBuilder.Marker classReference = parseJavaCodeReference(builder, true, true, true); + if (classReference == null) { + error(builder, JavaErrorMessages.message("expected.identifier")); + } + if (!expect(builder, delimiter)) { + break; + } } } diff --git a/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java b/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java new file mode 100644 index 000000000000..d08d0ff88324 --- /dev/null +++ b/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java @@ -0,0 +1,44 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.lang.java.parser; + +import com.intellij.lang.PsiBuilder; +import com.intellij.psi.JavaTokenType; +import com.intellij.psi.impl.source.tree.JavaElementType; +import org.jetbrains.annotations.Nullable; + + +public class StatementParser { + private StatementParser() { } + + @Nullable + public static PsiBuilder.Marker parseCodeBlock(final PsiBuilder builder) { + if (builder.getTokenType() != JavaTokenType.LBRACE) return null; + + final PsiBuilder.Marker codeBlock = builder.mark(); + builder.advanceLexer(); + + // temp + if (builder.getTokenType() == JavaTokenType.RBRACE) { + builder.advanceLexer(); + codeBlock.done(JavaElementType.CODE_BLOCK); + return codeBlock; + } + + // todo: implement + throw new UnsupportedOperationException(builder.toString()); + } +} diff --git a/java/java-impl/src/com/intellij/psi/impl/search/ConstructorReferencesSearchHelper.java b/java/java-impl/src/com/intellij/psi/impl/search/ConstructorReferencesSearchHelper.java index 3d9715e89932..bbad1ed8e70a 100644 --- a/java/java-impl/src/com/intellij/psi/impl/search/ConstructorReferencesSearchHelper.java +++ b/java/java-impl/src/com/intellij/psi/impl/search/ConstructorReferencesSearchHelper.java @@ -3,17 +3,15 @@ */ package com.intellij.psi.impl.search; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ReadActionProcessor; -import com.intellij.openapi.util.Computable; -import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; import com.intellij.psi.impl.light.LightMemberReference; import com.intellij.psi.search.PsiSearchScopeUtil; +import com.intellij.psi.search.SearchRequestCollector; import com.intellij.psi.search.SearchScope; import com.intellij.psi.search.searches.ClassInheritorsSearch; import com.intellij.psi.search.searches.ReferencesSearch; +import com.intellij.util.PairProcessor; import com.intellij.util.Processor; /** @@ -30,38 +28,29 @@ public class ConstructorReferencesSearchHelper { final PsiMethod constructor, final SearchScope searchScope, boolean ignoreAccessScope, - final boolean isStrictSignatureSearch) { - final Ref result = new Ref(); - PsiClass aClass = ApplicationManager.getApplication().runReadAction(new Computable() { - public PsiClass compute() { - PsiClass aClass = constructor.getContainingClass(); - if (aClass == null) { - result.set(true); - return null; - } + final boolean isStrictSignatureSearch, SearchRequestCollector collector) { + PsiClass aClass = constructor.getContainingClass(); + if (aClass == null) { + return true; + } - if (aClass.isEnum()) { - PsiField[] fields = aClass.getFields(); - for (PsiField field : fields) { - if (field instanceof PsiEnumConstant) { - PsiReference reference = field.getReference(); - if (reference != null && reference.isReferenceTo(constructor)) { - if (!processor.process(reference)) { - result.set(false); - return null; - } - } + if (aClass.isEnum()) { + for (PsiField field : aClass.getFields()) { + if (field instanceof PsiEnumConstant) { + PsiReference reference = field.getReference(); + if (reference != null && reference.isReferenceTo(constructor)) { + if (!processor.process(reference)) { + return false; } } } - return aClass; } - }); - if (!result.isNull()) return result.get(); + } // search usages like "new XXX(..)" - Processor processor1 = new ReadActionProcessor() { - public boolean processInReadAction(final PsiReference reference) { + PairProcessor processor1 = new PairProcessor() { + @Override + public boolean process(PsiReference reference, SearchRequestCollector collector) { PsiElement parent = reference.getElement().getParent(); if (parent instanceof PsiAnonymousClass) { parent = parent.getParent(); @@ -85,12 +74,12 @@ public class ConstructorReferencesSearchHelper { } }; - if (!ReferencesSearch.search(aClass, searchScope, ignoreAccessScope).forEach(processor1)) return false; + ReferencesSearch.searchOptimized(aClass, searchScope, ignoreAccessScope, collector, true, processor1); final boolean constructorCanBeCalledImplicitly = constructor.getParameterList().getParametersCount() == 0; // search usages like "this(..)" if (!processSuperOrThis(processor, aClass, constructor, constructorCanBeCalledImplicitly, searchScope, isStrictSignatureSearch, - PsiKeyword.THIS)) { + PsiKeyword.THIS)) { return false; } @@ -98,7 +87,7 @@ public class ConstructorReferencesSearchHelper { Processor processor2 = new Processor() { public boolean process(PsiClass inheritor) { return processSuperOrThis(processor, (PsiClass)inheritor.getNavigationElement(), constructor, constructorCanBeCalledImplicitly, searchScope, isStrictSignatureSearch, - PsiKeyword.SUPER); + PsiKeyword.SUPER); } }; @@ -110,21 +99,6 @@ public class ConstructorReferencesSearchHelper { final PsiMethod constructor, final boolean constructorCanBeCalledImplicitly, final SearchScope searchScope, final boolean isStrictSignatureSearch, final String superOrThisKeyword) { - return ApplicationManager.getApplication().runReadAction(new Computable() { - public Boolean compute() { - return processSuperOrThisInReadAction(inheritor, searchScope, superOrThisKeyword, isStrictSignatureSearch, constructor, - constructorCanBeCalledImplicitly, processor); - } - }); - } - - private boolean processSuperOrThisInReadAction(final PsiClass inheritor, - final SearchScope searchScope, - final String superOrThisKeyword, - final boolean isStrictSignatureSearch, - final PsiMethod constructor, - final boolean constructorCanBeCalledImplicitly, - final Processor processor) { PsiMethod[] constructors = inheritor.getConstructors(); if (constructors.length == 0 && constructorCanBeCalledImplicitly) { processImplicitConstructorCall(inheritor, processor, constructor, inheritor); diff --git a/java/java-impl/src/com/intellij/psi/impl/search/ConstructorReferencesSearcher.java b/java/java-impl/src/com/intellij/psi/impl/search/ConstructorReferencesSearcher.java index acb87007dc39..85382259fdb0 100644 --- a/java/java-impl/src/com/intellij/psi/impl/search/ConstructorReferencesSearcher.java +++ b/java/java-impl/src/com/intellij/psi/impl/search/ConstructorReferencesSearcher.java @@ -3,24 +3,28 @@ */ package com.intellij.psi.impl.search; +import com.intellij.openapi.application.QueryExecutorBase; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiManager; import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiReference; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.util.Processor; -import com.intellij.util.QueryExecutor; /** * @author max */ -public class ConstructorReferencesSearcher implements QueryExecutor { - public boolean execute(final ReferencesSearch.SearchParameters p, final Processor consumer) { +public class ConstructorReferencesSearcher extends QueryExecutorBase { + protected ConstructorReferencesSearcher() { + super(true); + } + + @Override + public void processQuery(ReferencesSearch.SearchParameters p, Processor consumer) { final PsiElement element = p.getElementToSearch(); if (element instanceof PsiMethod && ((PsiMethod)element).isConstructor()) { - return new ConstructorReferencesSearchHelper(PsiManager.getInstance(element.getProject())) - .processConstructorReferences(consumer, (PsiMethod)p.getElementToSearch(), p.getScope(), p.isIgnoreAccessScope(), true); + new ConstructorReferencesSearchHelper(PsiManager.getInstance(element.getProject())) + .processConstructorReferences(consumer, (PsiMethod)p.getElementToSearch(), p.getScope(), p.isIgnoreAccessScope(), true, p.getOptimizer()); } - return true; } } diff --git a/java/java-impl/src/com/intellij/psi/impl/search/MethodUsagesSearcher.java b/java/java-impl/src/com/intellij/psi/impl/search/MethodUsagesSearcher.java index a09f590f3c08..2e51faa888d2 100644 --- a/java/java-impl/src/com/intellij/psi/impl/search/MethodUsagesSearcher.java +++ b/java/java-impl/src/com/intellij/psi/impl/search/MethodUsagesSearcher.java @@ -3,9 +3,7 @@ */ package com.intellij.psi.impl.search; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.QueryExecutorBase; -import com.intellij.openapi.util.Computable; import com.intellij.psi.*; import com.intellij.psi.search.SearchRequestCollector; import com.intellij.psi.search.SearchScope; @@ -18,6 +16,9 @@ import com.intellij.util.Processor; * @author max */ public class MethodUsagesSearcher extends QueryExecutorBase { + protected MethodUsagesSearcher() { + super(true); + } @Override public void processQuery(MethodReferencesSearch.SearchParameters p, Processor consumer) { @@ -28,60 +29,42 @@ public class MethodUsagesSearcher extends QueryExecutorBase() { - public PsiClass compute() { - return method.isValid() ? method.getContainingClass() : null; - } - }); + final PsiClass aClass = method.getContainingClass(); if (aClass == null) return; final boolean strictSignatureSearch = p.isStrictSignatureSearch(); if (method.isConstructor()) { - collector.searchCustom(new Processor>() { - public boolean process(Processor consumer) { - return new ConstructorReferencesSearchHelper(psiManager). - processConstructorReferences(consumer, method, searchScope, !strictSignatureSearch, strictSignatureSearch); - } - }); + new ConstructorReferencesSearchHelper(psiManager). + processConstructorReferences(consumer, method, searchScope, !strictSignatureSearch, strictSignatureSearch, collector); } - boolean needStrictSignatureSearch = ApplicationManager.getApplication().runReadAction(new Computable() { - public Boolean compute() { - return method.isValid() && strictSignatureSearch && (aClass instanceof PsiAnonymousClass + boolean needStrictSignatureSearch = strictSignatureSearch && (aClass instanceof PsiAnonymousClass || aClass.hasModifierProperty(PsiModifier.FINAL) || method.hasModifierProperty(PsiModifier.STATIC) || method.hasModifierProperty(PsiModifier.FINAL) || method.hasModifierProperty(PsiModifier.PRIVATE)); - } - }); if (needStrictSignatureSearch) { - ReferencesSearch.search(new ReferencesSearch.SearchParameters(method, searchScope, false, collector)).forEach(consumer); + ReferencesSearch.searchOptimized(method, searchScope, false, collector, consumer); return; } - ApplicationManager.getApplication().runReadAction(new Runnable() { - public void run() { - if (!method.isValid()) return; + final String textToSearch = method.getName(); + final PsiMethod[] methods = strictSignatureSearch ? new PsiMethod[]{method} : aClass.findMethodsByName(textToSearch, false); - final String textToSearch = method.getName(); - final PsiMethod[] methods = strictSignatureSearch ? new PsiMethod[]{method} : aClass.findMethodsByName(textToSearch, false); + SearchScope accessScope = methods[0].getUseScope(); + for (int i = 1; i < methods.length; i++) { + PsiMethod method1 = methods[i]; + accessScope = accessScope.union(method1.getUseScope()); + } - SearchScope accessScope = methods[0].getUseScope(); - for (int i = 1; i < methods.length; i++) { - PsiMethod method1 = methods[i]; - accessScope = accessScope.union(method1.getUseScope()); - } + final SearchScope restrictedByAccess = searchScope.intersectWith(accessScope); - final SearchScope restrictedByAccess = searchScope.intersectWith(accessScope); + short searchContext = UsageSearchContext.IN_CODE | UsageSearchContext.IN_COMMENTS | UsageSearchContext.IN_FOREIGN_LANGUAGES; + collector.searchWord(textToSearch, restrictedByAccess, searchContext, true, + new MethodTextOccurrenceProcessor(aClass, strictSignatureSearch, methods)); - short searchContext = UsageSearchContext.IN_CODE | UsageSearchContext.IN_COMMENTS | UsageSearchContext.IN_FOREIGN_LANGUAGES; - collector.searchWord(textToSearch, restrictedByAccess, searchContext, true, - new MethodTextOccurrenceProcessor(aClass, strictSignatureSearch, methods)); - - SimpleAccessorReferenceSearcher.addPropertyAccessUsages(method, restrictedByAccess, collector); - } - }); + SimpleAccessorReferenceSearcher.addPropertyAccessUsages(method, restrictedByAccess, collector); } diff --git a/java/java-impl/src/com/intellij/psi/impl/source/parsing/DeclarationParsing.java b/java/java-impl/src/com/intellij/psi/impl/source/parsing/DeclarationParsing.java index 95219c560609..4f8b677b1fc2 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/parsing/DeclarationParsing.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/parsing/DeclarationParsing.java @@ -705,8 +705,8 @@ public class DeclarationParsing extends Parsing { aClass.rawAddChildren(invalidElementsGroup); while (true) { IElementType tokenType = lexer.getTokenType(); - if (tokenType == JavaTokenType.IDENTIFIER || tokenType == JavaTokenType.COMMA || tokenType == JavaTokenType.EXTENDS_KEYWORD || tokenType == - JavaTokenType.IMPLEMENTS_KEYWORD) { + if (tokenType == JavaTokenType.IDENTIFIER || tokenType == JavaTokenType.COMMA || tokenType == JavaTokenType.EXTENDS_KEYWORD || + tokenType == JavaTokenType.IMPLEMENTS_KEYWORD) { invalidElementsGroup.rawAddChildren(ParseUtil.createTokenElement(lexer, myContext.getCharTable())); } else { @@ -1086,7 +1086,7 @@ public class DeclarationParsing extends Parsing { if (type == null) { type = ASTFactory.composite(JavaElementType.TYPE); - param.rawAddChildren(Factory.createErrorElement("Parameter type missing")); + param.rawAddChildren(Factory.createErrorElement(JavaErrorMessages.message("expected.type"))); } param.rawAddChildren(type); diff --git a/java/java-impl/src/com/intellij/psi/impl/source/parsing/StatementParsing.java b/java/java-impl/src/com/intellij/psi/impl/source/parsing/StatementParsing.java index 89c35b07456f..c706eb3d1bb8 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/parsing/StatementParsing.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/parsing/StatementParsing.java @@ -111,6 +111,7 @@ public class StatementParsing extends Parsing { return dummyRoot.getFirstChildNode(); } + @Nullable public TreeElement parseCodeBlock(Lexer lexer, boolean deep) { if (lexer.getTokenType() != JavaTokenType.LBRACE) return null; Lexer badLexer = lexer instanceof StoppableLexerAdapter ? ((StoppableLexerAdapter)lexer).getDelegate() : lexer; @@ -151,11 +152,10 @@ public class StatementParsing extends Parsing { List list = new SmartList(); while (true) { final IElementType type = lexer.getTokenType(); - if (ElementType.PRIMITIVE_TYPE_BIT_SET.contains(type) || type == JavaTokenType.IDENTIFIER || ElementType.MODIFIER_BIT_SET.contains(type) || - type == JavaTokenType.LT || type == JavaTokenType.GT || type == JavaTokenType.GTGT || type == JavaTokenType.GTGTGT || type == - JavaTokenType.COMMA || type == - JavaTokenType.DOT || - type == JavaTokenType.EXTENDS_KEYWORD || type == JavaTokenType.IMPLEMENTS_KEYWORD) { + if (ElementType.PRIMITIVE_TYPE_BIT_SET.contains(type) || ElementType.MODIFIER_BIT_SET.contains(type) || + type == JavaTokenType.IDENTIFIER || type == JavaTokenType.LT || type == JavaTokenType.GT || + type == JavaTokenType.GTGT || type == JavaTokenType.GTGTGT || type == JavaTokenType.COMMA || + type == JavaTokenType.DOT || type == JavaTokenType.EXTENDS_KEYWORD || type == JavaTokenType.IMPLEMENTS_KEYWORD) { list.add(type); lexer.advance(); } else { diff --git a/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java b/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java index b2968fa8dbef..c648d69da366 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java @@ -93,7 +93,7 @@ public interface JavaElementType { IElementType CLASS_OBJECT_ACCESS_EXPRESSION = new IJavaElementType("CLASS_OBJECT_ACCESS_EXPRESSION"); IElementType EMPTY_EXPRESSION = new IJavaElementType("EMPTY_EXPRESSION"); - IElementType EXPRESSION_LIST = new IJavaElementType("EXPRESSION_LIST"); + IElementType EXPRESSION_LIST = new IJavaElementType("EXPRESSION_LIST", true); IElementType EMPTY_STATEMENT = new IJavaElementType("EMPTY_STATEMENT"); IElementType BLOCK_STATEMENT = new IJavaElementType("BLOCK_STATEMENT"); diff --git a/java/java-impl/src/com/intellij/refactoring/inline/InlineParameterHandler.java b/java/java-impl/src/com/intellij/refactoring/inline/InlineParameterHandler.java index d93340f4108d..6036192d3506 100644 --- a/java/java-impl/src/com/intellij/refactoring/inline/InlineParameterHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/inline/InlineParameterHandler.java @@ -99,8 +99,7 @@ public class InlineParameterHandler extends JavaInlineActionHandler { else if (!isSameConstant(argument, refConstantInitializer.get())) { return false; } - } - else { + } else if (!isRecursiveReferencedParameter(argument, psiParameter)) { if (!refConstantInitializer.isNull()) return false; refInitializer.set(argument); refMethodCall.set(methodCall); @@ -228,6 +227,16 @@ public class InlineParameterHandler extends JavaInlineActionHandler { return null; } + private static boolean isRecursiveReferencedParameter(final PsiExpression argument, final PsiParameter param) { + if (argument instanceof PsiReferenceExpression) { + final PsiElement element = ((PsiReferenceExpression)argument).resolve(); + if (element instanceof PsiParameter) { + return element.equals(param); + } + } + return false; + } + private static boolean isSameConstant(final PsiExpression expr1, final PsiExpression expr2) { boolean expr1Null = InlineToAnonymousConstructorProcessor.ourNullPattern.accepts(expr1); boolean expr2Null = InlineToAnonymousConstructorProcessor.ourNullPattern.accepts(expr2); diff --git a/java/java-impl/src/com/intellij/refactoring/inlineSuperClass/usageInfo/ReplaceExtendsListUsageInfo.java b/java/java-impl/src/com/intellij/refactoring/inlineSuperClass/usageInfo/ReplaceExtendsListUsageInfo.java index f9d28ac491b7..686950aaba32 100644 --- a/java/java-impl/src/com/intellij/refactoring/inlineSuperClass/usageInfo/ReplaceExtendsListUsageInfo.java +++ b/java/java-impl/src/com/intellij/refactoring/inlineSuperClass/usageInfo/ReplaceExtendsListUsageInfo.java @@ -21,22 +21,22 @@ package com.intellij.refactoring.inlineSuperClass.usageInfo; import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; import com.intellij.psi.PsiJavaCodeReferenceElement; -import com.intellij.refactoring.safeDelete.usageInfo.SafeDeleteExtendsClassUsageInfo; import com.intellij.refactoring.util.FixableUsageInfo; import com.intellij.util.IncorrectOperationException; public class ReplaceExtendsListUsageInfo extends FixableUsageInfo { - private final SafeDeleteExtendsClassUsageInfo mySafeDeleteUsageInfo; public ReplaceExtendsListUsageInfo(PsiJavaCodeReferenceElement element, final PsiClass superClass, final PsiClass targetClass) { super(element); - mySafeDeleteUsageInfo = new SafeDeleteExtendsClassUsageInfo(element, superClass, targetClass); + } public void fixUsage() throws IncorrectOperationException { - if (mySafeDeleteUsageInfo.isSafeDelete()) { - mySafeDeleteUsageInfo.deleteElement(); + final PsiElement element = getElement(); + if (element != null) { + element.delete(); } } } diff --git a/java/java-impl/src/com/intellij/refactoring/introduceField/IntroduceConstantHandler.java b/java/java-impl/src/com/intellij/refactoring/introduceField/IntroduceConstantHandler.java index c2fee31d935a..8c9a56f658f1 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceField/IntroduceConstantHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceField/IntroduceConstantHandler.java @@ -32,6 +32,7 @@ import com.intellij.refactoring.HelpID; import com.intellij.refactoring.RefactoringBundle; import com.intellij.refactoring.ui.TypeSelectorManagerImpl; import com.intellij.refactoring.util.CommonRefactoringUtil; +import com.intellij.refactoring.util.RefactoringUtil; import com.intellij.refactoring.util.classMembers.ClassMemberReferencesVisitor; import com.intellij.refactoring.util.occurences.ExpressionOccurenceManager; import com.intellij.refactoring.util.occurences.OccurenceManager; @@ -85,6 +86,15 @@ public class IntroduceConstantHandler extends BaseExpressionToFieldHandler { PsiExpression[] occurences, PsiElement anchorElement, PsiElement anchorElementIfAll) { + for (PsiExpression occurrence : occurences) { + if (RefactoringUtil.isAssignmentLHS(occurrence)) { + String message = + RefactoringBundle.getCannotRefactorMessage("Selected expression is used for write"); + CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, getHelpID()); + highlightError(project, editor, occurrence); + return null; + } + } PsiLocalVariable localVariable = null; if (expr instanceof PsiReferenceExpression) { PsiElement ref = ((PsiReferenceExpression)expr).resolve(); diff --git a/java/java-tests/testData/psi/parser-partial/declarations/CompletionHack0.txt b/java/java-tests/testData/psi/parser-partial/declarations/CompletionHack0.txt new file mode 100644 index 000000000000..7b9cfccf79de --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/CompletionHack0.txt @@ -0,0 +1,33 @@ +PsiJavaFile:CompletionHack0.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiField:s + PsiModifierList: + + PsiErrorElement:Unexpected token + PsiTypeParameterList + PsiJavaToken:LT('<') + PsiTypeParameter:X + PsiIdentifier:X('X') + PsiElement(EXTENDS_BOUND_LIST) + + PsiWhiteSpace(' ') + PsiErrorElement:Unexpected identifier + PsiIdentifier:IntelliJIdeaRulezz('IntelliJIdeaRulezz') + PsiJavaToken:GT('>') + PsiWhiteSpace('\n ') + PsiTypeElement:String + PsiJavaCodeReferenceElement:String + PsiIdentifier:String('String') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiIdentifier:s('s') + PsiWhiteSpace(' ') + PsiJavaToken:EQ('=') + PsiWhiteSpace(' ') + PsiLiteralExpression:"" + PsiJavaToken:STRING_LITERAL('""') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/CompletionHack1.txt b/java/java-tests/testData/psi/parser-partial/declarations/CompletionHack1.txt new file mode 100644 index 000000000000..127827743962 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/CompletionHack1.txt @@ -0,0 +1,31 @@ +PsiJavaFile:CompletionHack1.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiField:s + PsiModifierList: + + PsiErrorElement:Unexpected token + PsiTypeParameterList + PsiJavaToken:LT('<') + PsiTypeParameter:X + PsiIdentifier:X('X') + PsiElement(EXTENDS_BOUND_LIST) + + PsiErrorElement:'>' expected. + + PsiWhiteSpace('\n ') + PsiTypeElement:String + PsiJavaCodeReferenceElement:String + PsiIdentifier:String('String') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiIdentifier:s('s') + PsiWhiteSpace(' ') + PsiJavaToken:EQ('=') + PsiWhiteSpace(' ') + PsiLiteralExpression:"" + PsiJavaToken:STRING_LITERAL('""') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/EmptyBody0.txt b/java/java-tests/testData/psi/parser-partial/declarations/EmptyBody0.txt new file mode 100644 index 000000000000..a83166937082 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/EmptyBody0.txt @@ -0,0 +1,4 @@ +PsiJavaFile:EmptyBody0.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/declarations/EmptyBody1.txt b/java/java-tests/testData/psi/parser-partial/declarations/EmptyBody1.txt new file mode 100644 index 000000000000..60988ad3eee2 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/EmptyBody1.txt @@ -0,0 +1,5 @@ +PsiJavaFile:EmptyBody1.java + PsiJavaToken:LBRACE('{') + PsiErrorElement:'}' expected + + PsiWhiteSpace(' ') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/EnumBody0.txt b/java/java-tests/testData/psi/parser-partial/declarations/EnumBody0.txt new file mode 100644 index 000000000000..a221d652117d --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/EnumBody0.txt @@ -0,0 +1,6 @@ +PsiJavaFile:EnumBody0.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/declarations/EnumBody1.txt b/java/java-tests/testData/psi/parser-partial/declarations/EnumBody1.txt new file mode 100644 index 000000000000..04b16bc1bcf1 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/EnumBody1.txt @@ -0,0 +1,28 @@ +PsiJavaFile:EnumBody1.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiEnumConstant:RED + PsiModifierList: + + PsiIdentifier:RED('RED') + PsiExpressionList + + PsiJavaToken:COMMA(',') + PsiWhiteSpace(' ') + PsiEnumConstant:GREEN + PsiModifierList: + + PsiIdentifier:GREEN('GREEN') + PsiExpressionList + + PsiJavaToken:COMMA(',') + PsiWhiteSpace(' ') + PsiEnumConstant:BLUE + PsiModifierList: + + PsiIdentifier:BLUE('BLUE') + PsiExpressionList + + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/EnumBody2.txt b/java/java-tests/testData/psi/parser-partial/declarations/EnumBody2.txt new file mode 100644 index 000000000000..0e6471320543 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/EnumBody2.txt @@ -0,0 +1,27 @@ +PsiJavaFile:EnumBody2.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiEnumConstant:RED + PsiModifierList: + + PsiIdentifier:RED('RED') + PsiExpressionList + + PsiJavaToken:COMMA(',') + PsiWhiteSpace(' ') + PsiEnumConstant:GREEN + PsiModifierList: + + PsiIdentifier:GREEN('GREEN') + PsiExpressionList + + PsiJavaToken:COMMA(',') + PsiWhiteSpace(' ') + PsiEnumConstant:BLUE + PsiModifierList: + + PsiIdentifier:BLUE('BLUE') + PsiExpressionList + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/EnumBody3.txt b/java/java-tests/testData/psi/parser-partial/declarations/EnumBody3.txt new file mode 100644 index 000000000000..ea0d456d4d1b --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/EnumBody3.txt @@ -0,0 +1,28 @@ +PsiJavaFile:EnumBody3.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiEnumConstant:RED + PsiModifierList: + + PsiIdentifier:RED('RED') + PsiExpressionList + + PsiJavaToken:COMMA(',') + PsiWhiteSpace(' ') + PsiEnumConstant:GREEN + PsiModifierList: + + PsiIdentifier:GREEN('GREEN') + PsiExpressionList + + PsiJavaToken:COMMA(',') + PsiWhiteSpace(' ') + PsiEnumConstant:BLUE + PsiModifierList: + + PsiIdentifier:BLUE('BLUE') + PsiExpressionList + + PsiJavaToken:COMMA(',') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/EnumBody4.txt b/java/java-tests/testData/psi/parser-partial/declarations/EnumBody4.txt new file mode 100644 index 000000000000..f20ec90ba3f4 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/EnumBody4.txt @@ -0,0 +1,37 @@ +PsiJavaFile:EnumBody4.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiEnumConstant:RED + PsiModifierList: + + PsiIdentifier:RED('RED') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiLiteralExpression:0 + PsiJavaToken:INTEGER_LITERAL('0') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:COMMA(',') + PsiWhiteSpace(' ') + PsiEnumConstant:GREEN + PsiModifierList: + + PsiIdentifier:GREEN('GREEN') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiLiteralExpression:1 + PsiJavaToken:INTEGER_LITERAL('1') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:COMMA(',') + PsiWhiteSpace(' ') + PsiEnumConstant:BLUE + PsiModifierList: + + PsiIdentifier:BLUE('BLUE') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiLiteralExpression:2 + PsiJavaToken:INTEGER_LITERAL('2') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/EnumBody5.txt b/java/java-tests/testData/psi/parser-partial/declarations/EnumBody5.txt new file mode 100644 index 000000000000..573f3471c668 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/EnumBody5.txt @@ -0,0 +1,22 @@ +PsiJavaFile:EnumBody5.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiEnumConstant:A + PsiModifierList:@ANNOTATION + PsiAnnotation + PsiJavaToken:AT('@') + PsiJavaCodeReferenceElement:ANNOTATION + PsiIdentifier:ANNOTATION('ANNOTATION') + PsiReferenceParameterList + + PsiAnnotationParameterList + + PsiWhiteSpace(' ') + PsiIdentifier:A('A') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiLiteralExpression:10 + PsiJavaToken:INTEGER_LITERAL('10') + PsiJavaToken:RPARENTH(')') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/Errors.txt b/java/java-tests/testData/psi/parser-partial/declarations/Errors.txt new file mode 100644 index 000000000000..609379fee3fa --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/Errors.txt @@ -0,0 +1,45 @@ +PsiJavaFile:Errors.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiModifierList:public static + PsiKeyword:public('public') + PsiWhiteSpace(' ') + PsiKeyword:static('static') + PsiWhiteSpace(' ') + PsiErrorElement:Unexpected token + PsiTypeParameterList + PsiJavaToken:LT('<') + PsiTypeParameter:error + PsiIdentifier:error('error') + PsiElement(EXTENDS_BOUND_LIST) + + PsiErrorElement:'>' expected. + + PsiWhiteSpace(' ') + PsiTypeElement:descr + PsiJavaCodeReferenceElement:descr + PsiIdentifier:descr('descr') + PsiReferenceParameterList + + PsiErrorElement:Identifier expected + + PsiErrorElement:Unexpected token + PsiJavaToken:EQ('=') + PsiJavaToken:STRING_LITERAL('"2"') + PsiJavaToken:GT('>') + PsiField:f1 + PsiModifierList:protected + PsiKeyword:protected('protected') + PsiWhiteSpace(' ') + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiIdentifier:f1('f1') + PsiWhiteSpace(' ') + PsiJavaToken:EQ('=') + PsiWhiteSpace(' ') + PsiLiteralExpression:0 + PsiJavaToken:INTEGER_LITERAL('0') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/declarations/FieldMulti.txt b/java/java-tests/testData/psi/parser-partial/declarations/FieldMulti.txt new file mode 100644 index 000000000000..22138d62d7bf --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/FieldMulti.txt @@ -0,0 +1,22 @@ +PsiJavaFile:FieldMulti.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiField:field1 + PsiModifierList: + + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiIdentifier:field1('field1') + PsiWhiteSpace(' ') + PsiJavaToken:EQ('=') + PsiWhiteSpace(' ') + PsiLiteralExpression:0 + PsiJavaToken:INTEGER_LITERAL('0') + PsiJavaToken:COMMA(',') + PsiWhiteSpace(' ') + PsiField:field2 + PsiIdentifier:field2('field2') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/declarations/FieldSimple.txt b/java/java-tests/testData/psi/parser-partial/declarations/FieldSimple.txt new file mode 100644 index 000000000000..59dc28ffe43c --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/FieldSimple.txt @@ -0,0 +1,18 @@ +PsiJavaFile:FieldSimple.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiField:field + PsiModifierList: + + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiIdentifier:field('field') + PsiWhiteSpace(' ') + PsiJavaToken:EQ('=') + PsiWhiteSpace(' ') + PsiLiteralExpression:0 + PsiJavaToken:INTEGER_LITERAL('0') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/declarations/GenericMethod.txt b/java/java-tests/testData/psi/parser-partial/declarations/GenericMethod.txt new file mode 100644 index 000000000000..7504f3612d97 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/GenericMethod.txt @@ -0,0 +1,91 @@ +PsiJavaFile:GenericMethod.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiMethod:test + PsiModifierList:public static + PsiKeyword:public('public') + PsiWhiteSpace(' ') + PsiKeyword:static('static') + PsiWhiteSpace(' ') + PsiTypeParameterList + PsiJavaToken:LT('<') + PsiTypeParameter:E + PsiIdentifier:E('E') + PsiElement(EXTENDS_BOUND_LIST) + + PsiJavaToken:GT('>') + PsiWhiteSpace(' ') + PsiIdentifier:test('test') + PsiParameterList:() + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiReferenceList + + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace('\n ') + PsiMethod:test1 + PsiModifierList: + + PsiTypeParameterList + PsiJavaToken:LT('<') + PsiTypeParameter:E + PsiIdentifier:E('E') + PsiElement(EXTENDS_BOUND_LIST) + + PsiJavaToken:GT('>') + PsiWhiteSpace(' ') + PsiTypeElement:void + PsiKeyword:void('void') + PsiWhiteSpace(' ') + PsiIdentifier:test1('test1') + PsiParameterList:() + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiReferenceList + + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace('\n ') + PsiMethod:test2 + PsiModifierList: + + PsiTypeParameterList + PsiJavaToken:LT('<') + PsiTypeParameter:E1 + PsiIdentifier:E1('E1') + PsiWhiteSpace(' ') + PsiElement(EXTENDS_BOUND_LIST) + PsiKeyword:extends('extends') + PsiWhiteSpace(' ') + PsiJavaCodeReferenceElement:Integer + PsiIdentifier:Integer('Integer') + PsiReferenceParameterList + + PsiJavaToken:COMMA(',') + PsiWhiteSpace(' ') + PsiTypeParameter:E2 + PsiIdentifier:E2('E2') + PsiWhiteSpace(' ') + PsiElement(EXTENDS_BOUND_LIST) + PsiKeyword:extends('extends') + PsiWhiteSpace(' ') + PsiJavaCodeReferenceElement:Runnable + PsiIdentifier:Runnable('Runnable') + PsiReferenceParameterList + + PsiJavaToken:GT('>') + PsiWhiteSpace(' ') + PsiTypeElement:String + PsiJavaCodeReferenceElement:String + PsiIdentifier:String('String') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiIdentifier:test2('test2') + PsiParameterList:() + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiReferenceList + + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/declarations/GenericMethodErrors.txt b/java/java-tests/testData/psi/parser-partial/declarations/GenericMethodErrors.txt new file mode 100644 index 000000000000..5be504737907 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/GenericMethodErrors.txt @@ -0,0 +1,43 @@ +PsiJavaFile:GenericMethodErrors.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiModifierList: + + PsiErrorElement:Unexpected token + PsiTypeParameterList + PsiJavaToken:LT('<') + PsiTypeParameter:Error + PsiIdentifier:Error('Error') + PsiElement(EXTENDS_BOUND_LIST) + + PsiErrorElement:'>' expected. + + PsiWhiteSpace(' ') + PsiTypeElement:sss + PsiJavaCodeReferenceElement:sss + PsiIdentifier:sss('sss') + PsiReferenceParameterList + + PsiErrorElement:Identifier expected + + PsiWhiteSpace(' ') + PsiErrorElement:Unexpected token + PsiJavaToken:DIV('/') + PsiJavaToken:GT('>') + PsiWhiteSpace(' ') + PsiJavaCodeReferenceElement:test + PsiIdentifier:test('test') + PsiWhiteSpace(' ') + PsiReferenceParameterList + PsiJavaToken:LT('<') + PsiTypeElement:error + PsiJavaCodeReferenceElement:error + PsiIdentifier:error('error') + PsiReferenceParameterList + + PsiJavaToken:GT('>') + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/MethodNormal0.txt b/java/java-tests/testData/psi/parser-partial/declarations/MethodNormal0.txt new file mode 100644 index 000000000000..f2383f5aaae4 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/MethodNormal0.txt @@ -0,0 +1,24 @@ +PsiJavaFile:MethodNormal0.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiMethod:f + PsiModifierList: + + PsiTypeParameterList + + PsiTypeElement:void + PsiKeyword:void('void') + PsiWhiteSpace(' ') + PsiIdentifier:f('f') + PsiParameterList:() + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiReferenceList + + PsiWhiteSpace(' ') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/MethodNormal1.txt b/java/java-tests/testData/psi/parser-partial/declarations/MethodNormal1.txt new file mode 100644 index 000000000000..b146b9b578d8 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/MethodNormal1.txt @@ -0,0 +1,20 @@ +PsiJavaFile:MethodNormal1.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiMethod:f + PsiModifierList: + + PsiTypeParameterList + + PsiTypeElement:void + PsiKeyword:void('void') + PsiWhiteSpace(' ') + PsiIdentifier:f('f') + PsiParameterList:() + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiReferenceList + + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/MissingInitializer.txt b/java/java-tests/testData/psi/parser-partial/declarations/MissingInitializer.txt new file mode 100644 index 000000000000..ad9b3b26e18c --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/MissingInitializer.txt @@ -0,0 +1,16 @@ +PsiJavaFile:MissingInitializer.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiField:field + PsiModifierList: + + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiIdentifier:field('field') + PsiWhiteSpace(' ') + PsiJavaToken:EQ('=') + PsiErrorElement:Expression expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/declarations/MissingInitializerExpression.txt b/java/java-tests/testData/psi/parser-partial/declarations/MissingInitializerExpression.txt new file mode 100644 index 000000000000..e7dac65bea9c --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/MissingInitializerExpression.txt @@ -0,0 +1,16 @@ +PsiJavaFile:MissingInitializerExpression.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiField:field + PsiModifierList: + + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiIdentifier:field('field') + PsiJavaToken:EQ('=') + PsiErrorElement:Expression expected + + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/MultiLineUnclosed.txt b/java/java-tests/testData/psi/parser-partial/declarations/MultiLineUnclosed.txt new file mode 100644 index 000000000000..d44d8913422d --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/MultiLineUnclosed.txt @@ -0,0 +1,23 @@ +PsiJavaFile:MultiLineUnclosed.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiModifierList: + + PsiTypeElement:int + PsiKeyword:int('int') + PsiErrorElement:Identifier expected + + PsiWhiteSpace(' \n ') + PsiField:o + PsiModifierList: + + PsiTypeElement:Object + PsiJavaCodeReferenceElement:Object + PsiIdentifier:Object('Object') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiIdentifier:o('o') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/Unclosed0.txt b/java/java-tests/testData/psi/parser-partial/declarations/Unclosed0.txt new file mode 100644 index 000000000000..9353f05c8052 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/Unclosed0.txt @@ -0,0 +1,21 @@ +PsiJavaFile:Unclosed0.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiMethod:f + PsiModifierList: + + PsiTypeParameterList + + PsiTypeElement:void + PsiKeyword:void('void') + PsiWhiteSpace(' ') + PsiIdentifier:f('f') + PsiParameterList:() + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiReferenceList + + PsiErrorElement:'{' or ';' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/declarations/Unclosed1.txt b/java/java-tests/testData/psi/parser-partial/declarations/Unclosed1.txt new file mode 100644 index 000000000000..c7b4159c5357 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/Unclosed1.txt @@ -0,0 +1,22 @@ +PsiJavaFile:Unclosed1.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiMethod:f + PsiModifierList: + + PsiTypeParameterList + + PsiTypeElement:void + PsiKeyword:void('void') + PsiWhiteSpace(' ') + PsiIdentifier:f('f') + PsiParameterList:( + PsiJavaToken:LPARENTH('(') + PsiErrorElement:')' expected + + PsiReferenceList + + PsiErrorElement:'{' or ';' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/Unclosed2.txt b/java/java-tests/testData/psi/parser-partial/declarations/Unclosed2.txt new file mode 100644 index 000000000000..b2a0dfe174c4 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/Unclosed2.txt @@ -0,0 +1,37 @@ +PsiJavaFile:Unclosed2.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiMethod:f + PsiModifierList: + + PsiTypeParameterList + + PsiTypeElement:void + PsiKeyword:void('void') + PsiWhiteSpace(' ') + PsiIdentifier:f('f') + PsiParameterList:() + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiReferenceList + + PsiErrorElement:'{' or ';' expected + + PsiWhiteSpace('\n ') + PsiMethod:g + PsiModifierList: + + PsiTypeParameterList + + PsiTypeElement:void + PsiKeyword:void('void') + PsiWhiteSpace(' ') + PsiIdentifier:g('g') + PsiParameterList:() + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiReferenceList + + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/declarations/Unclosed3.txt b/java/java-tests/testData/psi/parser-partial/declarations/Unclosed3.txt new file mode 100644 index 000000000000..82caa1cf199e --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/Unclosed3.txt @@ -0,0 +1,29 @@ +PsiJavaFile:Unclosed3.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiMethod:f + PsiModifierList: + + PsiTypeParameterList + + PsiTypeElement:void + PsiKeyword:void('void') + PsiWhiteSpace(' ') + PsiIdentifier:f('f') + PsiParameterList:(int a + PsiJavaToken:LPARENTH('(') + PsiParameter:a + PsiModifierList: + + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiIdentifier:a('a') + PsiErrorElement:')' expected + + PsiReferenceList + + PsiErrorElement:'{' or ';' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/Unclosed4.txt b/java/java-tests/testData/psi/parser-partial/declarations/Unclosed4.txt new file mode 100644 index 000000000000..d7ffeea84e44 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/Unclosed4.txt @@ -0,0 +1,33 @@ +PsiJavaFile:Unclosed4.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiMethod:f + PsiModifierList: + + PsiTypeParameterList + + PsiTypeElement:void + PsiKeyword:void('void') + PsiWhiteSpace(' ') + PsiIdentifier:f('f') + PsiParameterList:(int a,, + PsiJavaToken:LPARENTH('(') + PsiParameter:a + PsiModifierList: + + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiIdentifier:a('a') + PsiJavaToken:COMMA(',') + PsiErrorElement:Parameter expected + + PsiJavaToken:COMMA(',') + PsiErrorElement:Identifier or type expected + + PsiReferenceList + + PsiErrorElement:'{' or ';' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/Unclosed5.txt b/java/java-tests/testData/psi/parser-partial/declarations/Unclosed5.txt new file mode 100644 index 000000000000..a0166d829e10 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/Unclosed5.txt @@ -0,0 +1,30 @@ +PsiJavaFile:Unclosed5.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiMethod:f + PsiModifierList: + + PsiTypeParameterList + + PsiTypeElement:void + PsiKeyword:void('void') + PsiWhiteSpace(' ') + PsiIdentifier:f('f') + PsiParameterList:(int a,) + PsiJavaToken:LPARENTH('(') + PsiParameter:a + PsiModifierList: + + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiIdentifier:a('a') + PsiJavaToken:COMMA(',') + PsiErrorElement:Identifier or type expected + + PsiJavaToken:RPARENTH(')') + PsiReferenceList + + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/declarations/UnclosedBracket.txt b/java/java-tests/testData/psi/parser-partial/declarations/UnclosedBracket.txt new file mode 100644 index 000000000000..e17119cb9b42 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/UnclosedBracket.txt @@ -0,0 +1,15 @@ +PsiJavaFile:UnclosedBracket.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiField:field + PsiModifierList: + + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiIdentifier:field('field') + PsiJavaToken:LBRACKET('[') + PsiErrorElement:']' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/declarations/UnclosedComma.txt b/java/java-tests/testData/psi/parser-partial/declarations/UnclosedComma.txt new file mode 100644 index 000000000000..83f9e32e72d9 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/UnclosedComma.txt @@ -0,0 +1,15 @@ +PsiJavaFile:UnclosedComma.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiField:field + PsiModifierList: + + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiIdentifier:field('field') + PsiJavaToken:COMMA(',') + PsiErrorElement:Identifier expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/UnclosedSemicolon.txt b/java/java-tests/testData/psi/parser-partial/declarations/UnclosedSemicolon.txt new file mode 100644 index 000000000000..f185e9852b30 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/UnclosedSemicolon.txt @@ -0,0 +1,14 @@ +PsiJavaFile:UnclosedSemicolon.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiField:field + PsiModifierList: + + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiIdentifier:field('field') + PsiErrorElement:';' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/declarations/WildcardParsing.txt b/java/java-tests/testData/psi/parser-partial/declarations/WildcardParsing.txt new file mode 100644 index 000000000000..b433896f20bb --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/WildcardParsing.txt @@ -0,0 +1,55 @@ +PsiJavaFile:WildcardParsing.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiMethod:x + PsiModifierList: + + PsiTypeParameterList + + PsiTypeElement:List + PsiJavaCodeReferenceElement:List + PsiIdentifier:List('List') + PsiReferenceParameterList + PsiJavaToken:LT('<') + PsiTypeElement:? extends B + PsiJavaToken:QUEST('?') + PsiWhiteSpace(' ') + PsiKeyword:extends('extends') + PsiWhiteSpace(' ') + PsiTypeElement:B + PsiJavaCodeReferenceElement:B + PsiIdentifier:B('B') + PsiReferenceParameterList + + PsiJavaToken:GT('>') + PsiWhiteSpace(' ') + PsiIdentifier:x('x') + PsiParameterList:(Collection x) + PsiJavaToken:LPARENTH('(') + PsiParameter:x + PsiModifierList: + + PsiTypeElement:Collection + PsiJavaCodeReferenceElement:Collection + PsiIdentifier:Collection('Collection') + PsiReferenceParameterList + PsiJavaToken:LT('<') + PsiTypeElement:? super B + PsiJavaToken:QUEST('?') + PsiWhiteSpace(' ') + PsiKeyword:super('super') + PsiWhiteSpace(' ') + PsiTypeElement:B + PsiJavaCodeReferenceElement:B + PsiIdentifier:B('B') + PsiReferenceParameterList + + PsiJavaToken:GT('>') + PsiWhiteSpace(' ') + PsiIdentifier:x('x') + PsiJavaToken:RPARENTH(')') + PsiReferenceList + + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/references/Type5.txt b/java/java-tests/testData/psi/parser-partial/references/Type5.txt new file mode 100644 index 000000000000..74a43fc43766 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/references/Type5.txt @@ -0,0 +1,11 @@ +PsiJavaFile:Type5.java + PsiTypeElement:Object[]... + PsiTypeElement:Object[] + PsiTypeElement:Object + PsiJavaCodeReferenceElement:Object + PsiIdentifier:Object('Object') + PsiReferenceParameterList + + PsiJavaToken:LBRACKET('[') + PsiJavaToken:RBRACKET(']') + PsiJavaToken:ELLIPSIS('...') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/references/TypeParams5.txt b/java/java-tests/testData/psi/parser-partial/references/TypeParams5.txt index 51d776e6a39e..a7329da61f08 100644 --- a/java/java-tests/testData/psi/parser-partial/references/TypeParams5.txt +++ b/java/java-tests/testData/psi/parser-partial/references/TypeParams5.txt @@ -4,9 +4,9 @@ PsiJavaFile:TypeParams5.java PsiTypeParameter:T PsiIdentifier:T('T') PsiWhiteSpace(' ') - PsiKeyword:extends('extends') - PsiWhiteSpace(' ') PsiElement(EXTENDS_BOUND_LIST) + PsiKeyword:extends('extends') + PsiWhiteSpace(' ') PsiJavaCodeReferenceElement:X PsiIdentifier:X('X') PsiReferenceParameterList diff --git a/java/java-tests/testData/psi/parser-partial/references/TypeParams7.txt b/java/java-tests/testData/psi/parser-partial/references/TypeParams7.txt index f3cf0196d19a..aaea7b8c3d5d 100644 --- a/java/java-tests/testData/psi/parser-partial/references/TypeParams7.txt +++ b/java/java-tests/testData/psi/parser-partial/references/TypeParams7.txt @@ -4,9 +4,9 @@ PsiJavaFile:TypeParams7.java PsiTypeParameter:T PsiIdentifier:T('T') PsiWhiteSpace(' ') - PsiKeyword:extends('extends') - PsiWhiteSpace(' ') PsiElement(EXTENDS_BOUND_LIST) + PsiKeyword:extends('extends') + PsiWhiteSpace(' ') PsiJavaCodeReferenceElement:X PsiIdentifier:X('X') PsiReferenceParameterList diff --git a/java/java-tests/testData/refactoring/inlineParameter/CantInlineRecursive.java b/java/java-tests/testData/refactoring/inlineParameter/CantInlineRecursive.java new file mode 100644 index 000000000000..5c7474f10fd3 --- /dev/null +++ b/java/java-tests/testData/refactoring/inlineParameter/CantInlineRecursive.java @@ -0,0 +1,18 @@ +import java.lang.Integer; +import java.util.*; +import java.util.ArrayList; + +public class ExpData { + private final List myResult = new ArrayList(); + { + m(10, myResult); + } + + private void m(int i, List result) { + if (i > 0) { + m(i - 1, result); + } + result.add(i); + m(2, new ArrayList()); + } +} diff --git a/java/java-tests/testData/refactoring/inlineParameter/InlineRecursive.java b/java/java-tests/testData/refactoring/inlineParameter/InlineRecursive.java new file mode 100644 index 000000000000..88fb548ff33e --- /dev/null +++ b/java/java-tests/testData/refactoring/inlineParameter/InlineRecursive.java @@ -0,0 +1,15 @@ +import java.util.*; + +public class ExpData { + private final List myResult = new ArrayList(); + { + m(10, myResult); + } + + private void m(int i, List result) { + if (i > 0) { + m(i - 1, result); + } + result.add(i); + } +} diff --git a/java/java-tests/testData/refactoring/inlineParameter/InlineRecursive.java.after b/java/java-tests/testData/refactoring/inlineParameter/InlineRecursive.java.after new file mode 100644 index 000000000000..ef341a34a8a5 --- /dev/null +++ b/java/java-tests/testData/refactoring/inlineParameter/InlineRecursive.java.after @@ -0,0 +1,15 @@ +import java.util.*; + +public class ExpData { + private final List myResult = new ArrayList(); + { + m(10); + } + + private void m(int i) { + if (i > 0) { + m(i - 1); + } + myResult.add(i); + } +} diff --git a/java/java-tests/testData/refactoring/inlineSuperClass/multipleSubclassesInheritsOneBaseBase/after/ISuper.java b/java/java-tests/testData/refactoring/inlineSuperClass/multipleSubclassesInheritsOneBaseBase/after/ISuper.java new file mode 100644 index 000000000000..42f94cf01d18 --- /dev/null +++ b/java/java-tests/testData/refactoring/inlineSuperClass/multipleSubclassesInheritsOneBaseBase/after/ISuper.java @@ -0,0 +1 @@ +interface ISuper {} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/inlineSuperClass/multipleSubclassesInheritsOneBaseBase/after/Test.java b/java/java-tests/testData/refactoring/inlineSuperClass/multipleSubclassesInheritsOneBaseBase/after/Test.java new file mode 100644 index 000000000000..098860380992 --- /dev/null +++ b/java/java-tests/testData/refactoring/inlineSuperClass/multipleSubclassesInheritsOneBaseBase/after/Test.java @@ -0,0 +1 @@ +class Test implements ISuper {} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/inlineSuperClass/multipleSubclassesInheritsOneBaseBase/after/Test1.java b/java/java-tests/testData/refactoring/inlineSuperClass/multipleSubclassesInheritsOneBaseBase/after/Test1.java new file mode 100644 index 000000000000..b312de86cba9 --- /dev/null +++ b/java/java-tests/testData/refactoring/inlineSuperClass/multipleSubclassesInheritsOneBaseBase/after/Test1.java @@ -0,0 +1,2 @@ +class Test1 implements ISuper { +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/inlineSuperClass/multipleSubclassesInheritsOneBaseBase/before/ISuper.java b/java/java-tests/testData/refactoring/inlineSuperClass/multipleSubclassesInheritsOneBaseBase/before/ISuper.java new file mode 100644 index 000000000000..42f94cf01d18 --- /dev/null +++ b/java/java-tests/testData/refactoring/inlineSuperClass/multipleSubclassesInheritsOneBaseBase/before/ISuper.java @@ -0,0 +1 @@ +interface ISuper {} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/inlineSuperClass/multipleSubclassesInheritsOneBaseBase/before/Super.java b/java/java-tests/testData/refactoring/inlineSuperClass/multipleSubclassesInheritsOneBaseBase/before/Super.java new file mode 100644 index 000000000000..7d430e36c1a6 --- /dev/null +++ b/java/java-tests/testData/refactoring/inlineSuperClass/multipleSubclassesInheritsOneBaseBase/before/Super.java @@ -0,0 +1 @@ +public interface Super extends ISuper{} diff --git a/java/java-tests/testData/refactoring/inlineSuperClass/multipleSubclassesInheritsOneBaseBase/before/Test.java b/java/java-tests/testData/refactoring/inlineSuperClass/multipleSubclassesInheritsOneBaseBase/before/Test.java new file mode 100644 index 000000000000..22d8cb1d4203 --- /dev/null +++ b/java/java-tests/testData/refactoring/inlineSuperClass/multipleSubclassesInheritsOneBaseBase/before/Test.java @@ -0,0 +1 @@ +class Test implements Super {} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/inlineSuperClass/multipleSubclassesInheritsOneBaseBase/before/Test1.java b/java/java-tests/testData/refactoring/inlineSuperClass/multipleSubclassesInheritsOneBaseBase/before/Test1.java new file mode 100644 index 000000000000..8bcdbacc6856 --- /dev/null +++ b/java/java-tests/testData/refactoring/inlineSuperClass/multipleSubclassesInheritsOneBaseBase/before/Test1.java @@ -0,0 +1,2 @@ +class Test1 implements Super{ +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java new file mode 100644 index 000000000000..577e74a93c53 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java @@ -0,0 +1,71 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.lang.java.parser.partial; + +import com.intellij.lang.PsiBuilder; +import com.intellij.lang.java.parser.DeclarationParser; +import com.intellij.lang.java.parser.JavaParsingTestCase; + + +public class DeclarationParserTest extends JavaParsingTestCase { + public DeclarationParserTest() { + super("parser-partial/declarations"); + } + + public void testEmptyBody0() { doParserTest("{ }", false, false); } + public void testEmptyBody1() { doParserTest("{ ", false, false); } + + public void testEnumBody0() { doParserTest("{ ; }", false, true); } + public void testEnumBody1() { doParserTest("{ RED, GREEN, BLUE; }", false, true); } + public void testEnumBody2() { doParserTest("{ RED, GREEN, BLUE }", false, true); } + public void testEnumBody3() { doParserTest("{ RED, GREEN, BLUE, }", false, true); } + public void testEnumBody4() { doParserTest("{ RED(0), GREEN(1), BLUE(2); }", false, true); } + public void testEnumBody5() { doParserTest("{ @ANNOTATION A(10) }", false, true); } + + public void testFieldSimple() { doParserTest("{ int field = 0; }", false, false); } + public void testFieldMulti() { doParserTest("{ int field1 = 0, field2; }", false, false); } + public void testUnclosedBracket() { doParserTest("{ int field[ }", false, false); } + public void testMissingInitializer() { doParserTest("{ int field = }", false, false); } + public void testUnclosedComma() { doParserTest("{ int field, }", false, false); } + public void testUnclosedSemicolon() { doParserTest("{ int field }", false, false); } + public void testMissingInitializerExpression() { doParserTest("{ int field=; }", false, false); } + //public void testMultiLineUnclosed() { doParserTest("{ int \n Object o; }", false, false); } // todo: implement + + //public void testMethodNormal0() { doParserTest("{ void f() { } }", false, false); } // todo: parse code block correctly + public void testMethodNormal1() { doParserTest("{ void f(); }", false, false); } + public void testUnclosed0() { doParserTest("{ void f() }", false, false); } + public void testUnclosed1() { doParserTest("{ void f( }", false, false); } + public void testUnclosed2() { doParserTest("{ void f()\n void g(); }", false, false); } + public void testUnclosed3() { doParserTest("{ void f(int a }", false, false); } + public void testUnclosed4() { doParserTest("{ void f(int a,, }", false, false); } + public void testUnclosed5() { doParserTest("{ void f(int a,); }", false, false); } + public void testGenericMethod() { doParserTest("{ public static test();\n" + + " void test1();\n" + + " String test2(); }", false, false); } + public void testGenericMethodErrors() { doParserTest("{ test (); }", false, false); } + public void testErrors() { doParserTest("{ public static protected int f1 = 0; }", false, false); } + public void testCompletionHack0() { doParserTest("{ \n String s = \"\"; }", false, false); } + public void testCompletionHack1() { doParserTest("{ x(Collection x); }", false, false); } + + private void doParserTest(final String text, final boolean isAnnotation, final boolean isEnum) { + doParserTest(text, new Parser() { + public void parse(final PsiBuilder builder) { + DeclarationParser.parseClassBodyWithBraces(builder, isAnnotation, isEnum); + } + }); + } +} diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ReferenceParserTest.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ReferenceParserTest.java index da5651bf4eb9..77e1e8cb3f1e 100644 --- a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ReferenceParserTest.java +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ReferenceParserTest.java @@ -34,6 +34,7 @@ public class ReferenceParserTest extends JavaParsingTestCase { public void testType2() { doTypeParserTest("int[]", false); } public void testType3() { doTypeParserTest("int[][", false); } public void testType4() { doTypeParserTest("Map>", false); } + public void testType5() { doTypeParserTest("Object[]...", false); } public void testTypeParams0() { doTypeParamsParserTest(""); } public void testTypeParams1() { doTypeParamsParserTest(""); } @@ -55,7 +56,7 @@ public class ReferenceParserTest extends JavaParsingTestCase { private void doTypeParserTest(final String text, final boolean incomplete) { doParserTest(text, new Parser() { public void parse(final PsiBuilder builder) { - ReferenceParser.parseType(builder, incomplete, false); + ReferenceParser.parseTypeWithEllipsis(builder, incomplete, false); } }); } diff --git a/java/java-tests/testSrc/com/intellij/refactoring/InlineSuperClassTest.java b/java/java-tests/testSrc/com/intellij/refactoring/InlineSuperClassTest.java index 81df809876c2..7b61e89605bd 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/InlineSuperClassTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/InlineSuperClassTest.java @@ -168,6 +168,10 @@ public class InlineSuperClassTest extends MultiFileTestCase { doTestMultipleSubclasses(); } + public void testMultipleSubclassesInheritsOneBaseBase() throws Exception { + doTestMultipleSubclasses(); + } + private void doTestMultipleSubclasses() throws Exception { doTest(new PerformAction() { public void performAction(final VirtualFile rootDir, final VirtualFile rootAfter) throws Exception { diff --git a/java/java-tests/testSrc/com/intellij/refactoring/inline/InlineParameterTest.java b/java/java-tests/testSrc/com/intellij/refactoring/inline/InlineParameterTest.java index 4e58558adc28..7c24c6971926 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/inline/InlineParameterTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/inline/InlineParameterTest.java @@ -250,6 +250,21 @@ public class InlineParameterTest extends LightCodeInsightTestCase { doTest(false); } + public void testInlineRecursive() throws Exception { + doTest(false); + } + + public void testCantInlineRecursive() throws Exception { + try { + doTest(false); + } + catch (CommonRefactoringUtil.RefactoringErrorHintException e) { + assertEquals("Cannot find constant initializer for parameter", e.getMessage()); + return; + } + fail("Initializer shoul not be found"); + } + public void testParameterDefWithWriteAccess() throws Exception { try { doTest(false); diff --git a/java/openapi/src/com/intellij/psi/search/searches/MethodReferencesSearch.java b/java/openapi/src/com/intellij/psi/search/searches/MethodReferencesSearch.java index 93bfe3cb66c3..dfa15c5399c4 100644 --- a/java/openapi/src/com/intellij/psi/search/searches/MethodReferencesSearch.java +++ b/java/openapi/src/com/intellij/psi/search/searches/MethodReferencesSearch.java @@ -17,14 +17,10 @@ package com.intellij.psi.search.searches; import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiReference; -import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.search.SearchRequestCollector; -import com.intellij.psi.search.SearchRequestQuery; -import com.intellij.psi.search.SearchScope; -import com.intellij.util.MergeQuery; -import com.intellij.util.Query; -import com.intellij.util.UniqueResultsQuery; +import com.intellij.psi.search.*; +import com.intellij.util.*; import gnu.trove.TObjectHashingStrategy; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** @@ -75,6 +71,21 @@ public class MethodReferencesSearch extends ExtensibleQueryFactory processor) { + searchOptimized(method, scope, strictSignatureSearch, collector, false, new PairProcessor() { + @Override + public boolean process(PsiReference psiReference, SearchRequestCollector collector) { + return processor.process(psiReference); + } + }); + } +public static void searchOptimized(final PsiMethod method, SearchScope scope, final boolean strictSignatureSearch, SearchRequestCollector collector, final boolean inReadAction, PairProcessor processor) { + final SearchRequestCollector nested = new SearchRequestCollector(); + collector.searchQuery(new QuerySearchRequest(search(new SearchParameters(method, scope, strictSignatureSearch, nested)), nested, + inReadAction, processor)); + } + public static Query search(final SearchParameters parameters) { final Query result = INSTANCE.createQuery(parameters); if (parameters.isSharedOptimizer) { diff --git a/platform/lang-api/src/com/intellij/execution/ExecutionAdapter.java b/platform/lang-api/src/com/intellij/execution/ExecutionAdapter.java new file mode 100644 index 000000000000..85325b68aeb5 --- /dev/null +++ b/platform/lang-api/src/com/intellij/execution/ExecutionAdapter.java @@ -0,0 +1,45 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.execution; + +import com.intellij.execution.configurations.RunProfile; +import com.intellij.execution.process.ProcessHandler; +import org.jetbrains.annotations.NotNull; + +/** + * @author nik + */ +public class ExecutionAdapter implements ExecutionListener { + @Override + public void processStarting(@NotNull RunProfile runProfile) { + } + + @Override + public void processNotStarted(@NotNull RunProfile runProfile) { + } + + @Override + public void processStarted(@NotNull RunProfile runProfile, @NotNull ProcessHandler handler) { + } + + @Override + public void processTerminating(@NotNull RunProfile runProfile, @NotNull ProcessHandler handler) { + } + + @Override + public void processTerminated(@NotNull RunProfile runProfile, @NotNull ProcessHandler handler) { + } +} diff --git a/platform/lang-api/src/com/intellij/execution/ExecutionListener.java b/platform/lang-api/src/com/intellij/execution/ExecutionListener.java new file mode 100644 index 000000000000..2e45ef7ac8d7 --- /dev/null +++ b/platform/lang-api/src/com/intellij/execution/ExecutionListener.java @@ -0,0 +1,38 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.execution; + +import com.intellij.execution.configurations.RunProfile; +import com.intellij.execution.process.ProcessHandler; +import org.jetbrains.annotations.NotNull; + +import java.util.EventListener; + +/** + * @author nik + */ +public interface ExecutionListener extends EventListener { + + void processStarting(@NotNull RunProfile runProfile); + + void processNotStarted(@NotNull RunProfile runProfile); + + void processStarted(@NotNull RunProfile runProfile, @NotNull ProcessHandler handler); + + void processTerminating(@NotNull RunProfile runProfile, @NotNull ProcessHandler handler); + + void processTerminated(@NotNull RunProfile runProfile, @NotNull ProcessHandler handler); +} diff --git a/platform/lang-api/src/com/intellij/execution/ExecutionManager.java b/platform/lang-api/src/com/intellij/execution/ExecutionManager.java index 58e02af4f707..712d12c9f4a9 100644 --- a/platform/lang-api/src/com/intellij/execution/ExecutionManager.java +++ b/platform/lang-api/src/com/intellij/execution/ExecutionManager.java @@ -18,10 +18,16 @@ package com.intellij.execution; import com.intellij.execution.configurations.RunProfile; import com.intellij.execution.configurations.RunProfileState; import com.intellij.execution.process.ProcessHandler; +import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.ui.RunContentManager; import com.intellij.openapi.project.Project; +import com.intellij.util.messages.Topic; +import org.jetbrains.annotations.NotNull; public abstract class ExecutionManager { + public static final Topic EXECUTION_TOPIC = new Topic("configuration executed", ExecutionListener.class, + Topic.BroadcastDirection.TO_PARENT); + public static ExecutionManager getInstance(final Project project) { return project.getComponent(ExecutionManager.class); } @@ -32,5 +38,6 @@ public abstract class ExecutionManager { public abstract ProcessHandler[] getRunningProcesses(); - + public abstract void startRunProfile(@NotNull RunProfileStarter starter, @NotNull RunProfileState state, + @NotNull Project project, @NotNull Executor executor, @NotNull ExecutionEnvironment env); } diff --git a/platform/lang-api/src/com/intellij/execution/RunProfileStarter.java b/platform/lang-api/src/com/intellij/execution/RunProfileStarter.java new file mode 100644 index 000000000000..846b67b2f8a5 --- /dev/null +++ b/platform/lang-api/src/com/intellij/execution/RunProfileStarter.java @@ -0,0 +1,33 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.execution; + +import com.intellij.execution.configurations.RunProfileState; +import com.intellij.execution.runners.ExecutionEnvironment; +import com.intellij.execution.ui.RunContentDescriptor; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author nik + */ +public abstract class RunProfileStarter { + @Nullable + public abstract RunContentDescriptor execute(@NotNull Project project, @NotNull Executor executor, @NotNull RunProfileState state, + @Nullable RunContentDescriptor contentToReuse, @NotNull ExecutionEnvironment env) throws ExecutionException; + +} diff --git a/platform/lang-api/src/com/intellij/execution/runners/GenericProgramRunner.java b/platform/lang-api/src/com/intellij/execution/runners/GenericProgramRunner.java index cfde4926a0b9..8d8550e664cc 100644 --- a/platform/lang-api/src/com/intellij/execution/runners/GenericProgramRunner.java +++ b/platform/lang-api/src/com/intellij/execution/runners/GenericProgramRunner.java @@ -18,12 +18,9 @@ package com.intellij.execution.runners; import com.intellij.execution.*; import com.intellij.execution.configurations.*; -import com.intellij.execution.process.ProcessHandler; import com.intellij.execution.ui.RunContentDescriptor; -import com.intellij.history.LocalHistory; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.DataKey; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.options.SettingsEditor; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.JDOMExternalizable; @@ -67,51 +64,34 @@ public abstract class GenericProgramRunner public void execute(@NotNull final Executor executor, @NotNull final ExecutionEnvironment env, @Nullable final Callback callback) throws ExecutionException { - final RunProfile profile = env.getRunProfile(); final Project project = env.getProject(); if (project == null) { return; } - final RunContentDescriptor reuseContent = - ExecutionManager.getInstance(project).getContentManager().getReuseContent(executor, env.getContentToReuse()); final RunProfileState state = env.getState(executor); if (state == null) { return; } - Runnable startRunnable = new Runnable() { - public void run() { - try { - if (project.isDisposed()) return; - - final RunContentDescriptor descriptor = - doExecute(project, executor, state, reuseContent, env); - - if (callback != null) callback.processStarted(descriptor); - - if (descriptor != null) { - ExecutionManager.getInstance(project).getContentManager().showRunContent(executor, descriptor); - final ProcessHandler processHandler = descriptor.getProcessHandler(); - if (processHandler != null) processHandler.startNotify(); - } - } - catch (ExecutionException e) { - ExecutionUtil.handleExecutionError(project, executor.getToolWindowId(), profile, e); - } + ExecutionManager.getInstance(project).startRunProfile(new RunProfileStarter() { + @Override + public RunContentDescriptor execute(@NotNull Project project, + @NotNull Executor executor, + @NotNull RunProfileState state, + @Nullable RunContentDescriptor contentToReuse, + @NotNull ExecutionEnvironment env) throws ExecutionException { + final RunContentDescriptor descriptor = doExecute(project, executor, state, contentToReuse, env); + if (callback != null) callback.processStarted(descriptor); + return descriptor; } - }; - if (ApplicationManager.getApplication().isUnitTestMode()) { - startRunnable.run(); - } - else { - ExecutionManager.getInstance(project).compileAndRun(startRunnable, profile, state); - } + }, state, project, executor, env); } @Nullable protected abstract RunContentDescriptor doExecute(final Project project, final Executor executor, final RunProfileState state, final RunContentDescriptor contentToReuse, final ExecutionEnvironment env) throws ExecutionException; + } diff --git a/platform/lang-api/src/com/intellij/execution/ui/ExecutionConsole.java b/platform/lang-api/src/com/intellij/execution/ui/ExecutionConsole.java index 4ea881f5d51c..9d554cb51776 100644 --- a/platform/lang-api/src/com/intellij/execution/ui/ExecutionConsole.java +++ b/platform/lang-api/src/com/intellij/execution/ui/ExecutionConsole.java @@ -16,13 +16,12 @@ package com.intellij.execution.ui; import com.intellij.openapi.ui.ComponentContainer; - -import javax.swing.*; +import org.jetbrains.annotations.NonNls; /** * @author Eugene Zhuravlev * Date: Apr 20, 2004 */ public interface ExecutionConsole extends ComponentContainer { - + @NonNls String CONSOLE_CONTENT_ID = "ConsoleContent"; } diff --git a/platform/lang-api/src/com/intellij/lang/documentation/ExternalDocumentationHandler.java b/platform/lang-api/src/com/intellij/lang/documentation/ExternalDocumentationHandler.java new file mode 100644 index 000000000000..0584d7cff1cd --- /dev/null +++ b/platform/lang-api/src/com/intellij/lang/documentation/ExternalDocumentationHandler.java @@ -0,0 +1,27 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.lang.documentation; + +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiManager; + +/** + * User: spLeaner + */ +public interface ExternalDocumentationHandler { + boolean handleExternal(PsiElement element, PsiElement originalElement); + boolean handleExternalLink(PsiManager psiManager, String link, PsiElement context); +} diff --git a/platform/lang-api/src/com/intellij/psi/CustomHighlighterTokenType.java b/platform/lang-api/src/com/intellij/psi/CustomHighlighterTokenType.java index 505b52cbeaa2..41ae1093e409 100644 --- a/platform/lang-api/src/com/intellij/psi/CustomHighlighterTokenType.java +++ b/platform/lang-api/src/com/intellij/psi/CustomHighlighterTokenType.java @@ -45,6 +45,7 @@ public interface CustomHighlighterTokenType { IElementType MULTI_LINE_COMMENT = new CustomElementType("MULTI_LINE_COMMENT"); IElementType WHITESPACE = new CustomElementType("WHITESPACE"); IElementType CHARACTER = new CustomElementType("CHARACTER"); + IElementType PUNCTUATION = new CustomElementType("PUNCTUATION"); IElementType L_BRACE = new CustomElementType("L_BRACE"); IElementType R_BRACE = new CustomElementType("R_BRACE"); diff --git a/platform/lang-api/src/com/intellij/psi/PsiReferenceBase.java b/platform/lang-api/src/com/intellij/psi/PsiReferenceBase.java index 4e527134996f..a2840604b3c3 100644 --- a/platform/lang-api/src/com/intellij/psi/PsiReferenceBase.java +++ b/platform/lang-api/src/com/intellij/psi/PsiReferenceBase.java @@ -107,23 +107,11 @@ public abstract class PsiReferenceBase implements PsiRefer } public static PsiReferenceBase createSelfReference(T element, final PsiElement resolveTo) { + return new Immediate(element, true, resolveTo); + } - return new PsiReferenceBase(element,true) { - //do nothing. the element will be renamed via PsiMetaData (com.intellij.refactoring.rename.RenameUtil.doRenameGenericNamedElement()) - public PsiElement handleElementRename(final String newElementName) throws IncorrectOperationException { - return getElement(); - } - - @Nullable - public PsiElement resolve() { - return resolveTo; - } - - @NotNull - public Object[] getVariants() { - return EMPTY_ARRAY; - } - }; + public static PsiReferenceBase createSelfReference(T element, TextRange range, final PsiElement resolveTo) { + return new Immediate(element, range, resolveTo); } @Nullable @@ -165,4 +153,43 @@ public abstract class PsiReferenceBase implements PsiRefer return resolveResults.length == 1 ? resolveResults[0].getElement() : null; } } + + public static class Immediate extends PsiReferenceBase { + private final PsiElement myResolveTo; + + public Immediate(T element, TextRange range, boolean soft, PsiElement resolveTo) { + super(element, range, soft); + myResolveTo = resolveTo; + } + + public Immediate(T element, TextRange range, PsiElement resolveTo) { + super(element, range); + myResolveTo = resolveTo; + } + + public Immediate(T element, boolean soft, PsiElement resolveTo) { + super(element, soft); + myResolveTo = resolveTo; + } + + public Immediate(@NotNull T element, PsiElement resolveTo) { + super(element); + myResolveTo = resolveTo; + } + + //do nothing. the element will be renamed via PsiMetaData (com.intellij.refactoring.rename.RenameUtil.doRenameGenericNamedElement()) + public PsiElement handleElementRename(final String newElementName) throws IncorrectOperationException { + return getElement(); + } + + @Nullable + public PsiElement resolve() { + return myResolveTo; + } + + @NotNull + public Object[] getVariants() { + return EMPTY_ARRAY; + } + } } diff --git a/platform/lang-api/src/com/intellij/psi/impl/ElementBase.java b/platform/lang-api/src/com/intellij/psi/impl/ElementBase.java index e6b780129a25..d995c805c8b3 100644 --- a/platform/lang-api/src/com/intellij/psi/impl/ElementBase.java +++ b/platform/lang-api/src/com/intellij/psi/impl/ElementBase.java @@ -21,6 +21,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.util.Iconable; import com.intellij.openapi.util.UserDataHolderBase; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.project.IndexNotReadyException; @@ -95,7 +96,7 @@ public abstract class ElementBase extends UserDataHolderBase implements Iconable } protected boolean isToDeferIconLoading() { - return true; + return Registry.is("psi.deferIconLoading"); } private Icon computeIconNow(PsiElement element, int flags) { diff --git a/platform/lang-api/src/com/intellij/psi/search/PsiSearchRequest.java b/platform/lang-api/src/com/intellij/psi/search/PsiSearchRequest.java index 5e16c40b8c91..b2a61d521ae7 100644 --- a/platform/lang-api/src/com/intellij/psi/search/PsiSearchRequest.java +++ b/platform/lang-api/src/com/intellij/psi/search/PsiSearchRequest.java @@ -25,4 +25,8 @@ public class PsiSearchRequest { this.processor = processor; } + @Override + public String toString() { + return "PsiSearchRequest: " + word + "; " + processor; + } } diff --git a/platform/lang-api/src/com/intellij/psi/search/QuerySearchRequest.java b/platform/lang-api/src/com/intellij/psi/search/QuerySearchRequest.java new file mode 100644 index 000000000000..158ee91dc151 --- /dev/null +++ b/platform/lang-api/src/com/intellij/psi/search/QuerySearchRequest.java @@ -0,0 +1,43 @@ +package com.intellij.psi.search; + +import com.intellij.openapi.application.ReadActionProcessor; +import com.intellij.psi.PsiReference; +import com.intellij.util.PairProcessor; +import com.intellij.util.Processor; +import com.intellij.util.Query; + +/** + * @author peter + */ +public class QuerySearchRequest { + public final Query query; + public final SearchRequestCollector collector; + public final Processor processor; + + public QuerySearchRequest(Query query, + final SearchRequestCollector collector, + boolean inReadAction, final PairProcessor processor) { + this.query = query; + this.collector = collector; + if (inReadAction) { + this.processor = new ReadActionProcessor() { + @Override + public boolean processInReadAction(PsiReference psiReference) { + return processor.process(psiReference, collector); + } + }; + } else { + this.processor = new Processor() { + @Override + public boolean process(PsiReference psiReference) { + return processor.process(psiReference, collector); + } + }; + } + + } + + public void runQuery() { + query.forEach(processor); + } +} diff --git a/platform/lang-api/src/com/intellij/psi/search/SearchRequestCollector.java b/platform/lang-api/src/com/intellij/psi/search/SearchRequestCollector.java index b4e434ebcf6f..596282772625 100644 --- a/platform/lang-api/src/com/intellij/psi/search/SearchRequestCollector.java +++ b/platform/lang-api/src/com/intellij/psi/search/SearchRequestCollector.java @@ -3,6 +3,7 @@ package com.intellij.psi.search; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiReference; import com.intellij.util.Processor; +import com.intellij.util.containers.CollectionFactory; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; @@ -12,8 +13,10 @@ import java.util.List; * @author peter */ public class SearchRequestCollector { - private final List myRequests = new ArrayList(); - private final List>> myCustomSearchActions = new ArrayList>>(); + private final Object lock = new Object(); + private final List myWordRequests = CollectionFactory.arrayList(); + private final List myQueryRequests = CollectionFactory.arrayList(); + private final List>> myCustomSearchActions = CollectionFactory.arrayList(); public void searchWord(@NotNull String word, @NotNull SearchScope searchScope, boolean caseSensitive, @NotNull PsiElement searchTarget) { final short searchContext = UsageSearchContext.IN_CODE | UsageSearchContext.IN_FOREIGN_LANGUAGES | UsageSearchContext.IN_COMMENTS; @@ -29,18 +32,48 @@ public class SearchRequestCollector { return; } - myRequests.add(new PsiSearchRequest(searchScope, word, searchContext, caseSensitive, processor)); + synchronized (lock) { + myWordRequests.add(new PsiSearchRequest(searchScope, word, searchContext, caseSensitive, processor)); + } + } + + public void searchQuery(QuerySearchRequest request) { + assert request.collector != this; + synchronized (lock) { + myQueryRequests.add(request); + } } public void searchCustom(Processor> searchAction) { - myCustomSearchActions.add(searchAction); + synchronized (lock) { + myCustomSearchActions.add(searchAction); + } } - public List getSearchRequests() { - return myRequests; + public boolean hasRequests() { + synchronized (lock) { + return !myWordRequests.isEmpty() || !myCustomSearchActions.isEmpty() || !myQueryRequests.isEmpty(); + } } - public List>> getCustomSearchActions() { - return myCustomSearchActions; + public List takeQueryRequests() { + return takeRequests(myQueryRequests); + } + + private List takeRequests(List list) { + synchronized (lock) { + final List requests = new ArrayList(list); + requests.addAll(list); + list.clear(); + return requests; + } + } + + public List takeSearchRequests() { + return takeRequests(myWordRequests); + } + + public List>> takeCustomSearchActions() { + return takeRequests(myCustomSearchActions); } } diff --git a/platform/lang-api/src/com/intellij/psi/search/searches/ReferencesSearch.java b/platform/lang-api/src/com/intellij/psi/search/searches/ReferencesSearch.java index c43a8875a6f5..aa869f7b507c 100644 --- a/platform/lang-api/src/com/intellij/psi/search/searches/ReferencesSearch.java +++ b/platform/lang-api/src/com/intellij/psi/search/searches/ReferencesSearch.java @@ -17,10 +17,7 @@ package com.intellij.psi.search.searches; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiReference; -import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.search.SearchRequestCollector; -import com.intellij.psi.search.SearchRequestQuery; -import com.intellij.psi.search.SearchScope; +import com.intellij.psi.search.*; import com.intellij.util.*; import gnu.trove.TObjectHashingStrategy; import org.jetbrains.annotations.NotNull; @@ -111,4 +108,21 @@ public class ReferencesSearch extends ExtensibleQueryFactory processor) { + searchOptimized(element, searchScope, ignoreAccessScope, collector, false, new PairProcessor() { + @Override + public boolean process(PsiReference psiReference, SearchRequestCollector collector) { + return processor.process(psiReference); + } + }); + } + public static void searchOptimized(@NotNull PsiElement element, @NotNull SearchScope searchScope, boolean ignoreAccessScope, + @NotNull SearchRequestCollector collector, final boolean inReadAction, PairProcessor processor) { + final SearchRequestCollector nested = new SearchRequestCollector(); + collector.searchQuery(new QuerySearchRequest(search(new SearchParameters(element, searchScope, ignoreAccessScope, nested)), nested, + inReadAction, processor)); + } + + } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/controlflow/ControlFlowUtil.java b/platform/lang-impl/src/com/intellij/codeInsight/controlflow/ControlFlowUtil.java index 7da42a52b808..1d05cdb39d80 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/controlflow/ControlFlowUtil.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/controlflow/ControlFlowUtil.java @@ -21,6 +21,7 @@ import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.Consumer; import com.intellij.util.Function; import com.intellij.util.Processor; +import org.jetbrains.annotations.NotNull; import java.util.Arrays; @@ -28,16 +29,16 @@ import java.util.Arrays; * @author oleg */ public class ControlFlowUtil { - private static final Logger LOG = Logger.getInstance(ControlFlowUtil.class.getName()); + private static final Logger LOG = Logger.getInstance(ControlFlowUtil.class.getName()); private ControlFlowUtil() { } public static class Stack { + private final int myCapacity; private final int[] myValues; private int myIndex; - public Stack(final int capacity) { myCapacity = capacity; myValues = new int[myCapacity]; @@ -65,8 +66,8 @@ public class ControlFlowUtil { public String toString() { return "Stack(" + (myIndex + 1) + ") elements"; } - } + } public static int[] postOrder(Instruction[] flow) { final int length = flow.length; int[] result = new int[length]; @@ -108,6 +109,7 @@ public class ControlFlowUtil { } // Process control flow in depth first order + public static boolean process(final Instruction[] flow, final int start, final Processor processor){ final int length = flow.length; boolean[] visited = new boolean[length]; @@ -135,4 +137,35 @@ public class ControlFlowUtil { } return true; } + + + public static void iteratePrev(final int startInstruction, + @NotNull final Instruction[] instructions, + @NotNull final Function closure) { + final Stack stack = new Stack(instructions.length); + final boolean[] visited = new boolean[instructions.length]; + + stack.push(startInstruction); + while (!stack.isEmpty()) { + final int num = stack.pop(); + if (visited[num]){ + continue; + } + visited[num] = true; + final Instruction instr = instructions[num]; + final Operation nextOperation = closure.fun(instr); + if (nextOperation == Operation.CONTINUE) { + continue; + } else if (nextOperation == Operation.BREAK) { + break; + } + for (Instruction pred : instr.allPred()) { + stack.push(pred.num()); + } + } + } + + public static enum Operation { + CONTINUE, BREAK, NEXT; + } } \ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/PowerSaveHectorProvider.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/PowerSaveHectorProvider.java new file mode 100644 index 000000000000..47ba565f8fa3 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/PowerSaveHectorProvider.java @@ -0,0 +1,61 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.daemon; + +import com.intellij.ide.PowerSaveMode; +import com.intellij.openapi.editor.HectorComponentPanel; +import com.intellij.openapi.editor.HectorComponentPanelsProvider; +import com.intellij.openapi.options.ConfigurationException; +import com.intellij.psi.PsiFile; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +/** + * @author yole + */ +public class PowerSaveHectorProvider implements HectorComponentPanelsProvider { + @Override + public HectorComponentPanel createConfigurable(@NotNull PsiFile file) { + return new HectorComponentPanel() { + private JCheckBox myCheckBox = new JCheckBox("Power Save Mode"); + + @Override + public JComponent createComponent() { + return myCheckBox; + } + + @Override + public boolean isModified() { + return myCheckBox.isSelected() != PowerSaveMode.isEnabled(); + } + + @Override + public void apply() throws ConfigurationException { + PowerSaveMode.setEnabled(myCheckBox.isSelected()); + } + + @Override + public void reset() { + myCheckBox.setSelected(PowerSaveMode.isEnabled()); + } + + @Override + public void disposeUIResources() { + } + }; + } +} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonCodeAnalyzerImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonCodeAnalyzerImpl.java index 6c14fadc30d7..ee6d27286d48 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonCodeAnalyzerImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonCodeAnalyzerImpl.java @@ -27,6 +27,7 @@ import com.intellij.codeInsight.daemon.ReferenceImporter; import com.intellij.codeInsight.hint.HintManager; import com.intellij.codeInsight.intention.impl.IntentionHintComponent; import com.intellij.concurrency.Job; +import com.intellij.ide.PowerSaveMode; import com.intellij.lang.annotation.HighlightSeverity; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; @@ -586,6 +587,7 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzer implements JDOMEx return new Runnable() { public void run() { if (!myUpdateByTimerEnabled) return; + if (PowerSaveMode.isEnabled()) return; if (myDisposed || myProject.isDisposed()) return; final Collection activeEditors = myDaemonListeners.getSelectedEditors(); if (activeEditors.isEmpty()) return; diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonListeners.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonListeners.java index aa1bc32786e2..b3561b6bd82c 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonListeners.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonListeners.java @@ -19,6 +19,7 @@ package com.intellij.codeInsight.daemon.impl; import com.intellij.ProjectTopics; import com.intellij.codeHighlighting.Pass; import com.intellij.codeInsight.hint.TooltipController; +import com.intellij.ide.PowerSaveMode; import com.intellij.ide.todo.TodoConfiguration; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; @@ -195,6 +196,13 @@ public class DaemonListeners implements Disposable { } }); + connection.subscribe(PowerSaveMode.TOPIC, new PowerSaveMode.Listener() { + @Override + public void powerSaveStateChanged() { + stopDaemon(true); + } + }); + CommandProcessor.getInstance().addCommandListener(new MyCommandListener(), this); ApplicationManager.getApplication().addApplicationListener(myApplicationListener); EditorColorsManager.getInstance().addEditorColorsListener(myEditorColorsListener); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/TrafficLightRenderer.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/TrafficLightRenderer.java index aa603832d71e..29281a4f3042 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/TrafficLightRenderer.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/TrafficLightRenderer.java @@ -20,6 +20,7 @@ import com.intellij.codeHighlighting.HighlightDisplayLevel; import com.intellij.codeHighlighting.TextEditorHighlightingPass; import com.intellij.codeInsight.daemon.DaemonBundle; import com.intellij.codeInsight.daemon.impl.analysis.HighlightLevelUtil; +import com.intellij.ide.PowerSaveMode; import com.intellij.lang.annotation.HighlightSeverity; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.markup.ErrorStripeRenderer; @@ -109,7 +110,7 @@ public class TrafficLightRenderer implements ErrorStripeRenderer { final SeverityRegistrar severityRegistrar = SeverityRegistrar.getInstance(myProject); status.errorCount = new int[severityRegistrar.getSeveritiesCount()]; status.rootsNumber = roots.length; - fillDaemonCodeAnalyzerErrorsSatus(status, fillErrorsCount); + fillDaemonCodeAnalyzerErrorsStatus(status, fillErrorsCount); List passes = myDaemonCodeAnalyzer.getPassesToShowProgressFor(myFile); ArrayList passStati = new ArrayList(); for (TextEditorHighlightingPass tepass : passes) { @@ -127,7 +128,7 @@ public class TrafficLightRenderer implements ErrorStripeRenderer { return status; } - protected void fillDaemonCodeAnalyzerErrorsSatus(DaemonCodeAnalyzerStatus status, boolean fillErrorsCount) { + protected void fillDaemonCodeAnalyzerErrorsStatus(DaemonCodeAnalyzerStatus status, boolean fillErrorsCount) { final SeverityRegistrar severityRegistrar = SeverityRegistrar.getInstance(myProject); List highlights = DaemonCodeAnalyzerImpl.getHighlights(myDocument, myProject); if (highlights == null) highlights = Arrays.asList(HighlightInfo.EMPTY_ARRAY); @@ -185,6 +186,11 @@ public class TrafficLightRenderer implements ErrorStripeRenderer { if (status == null) return null; @NonNls String text = HTML_HEADER; + if (PowerSaveMode.isEnabled()) { + text += "Code analysis is disabled in power save mode."; + text += HTML_FOOTER; + return text; + } if (status.noHighlightingRoots != null && status.noHighlightingRoots.length == status.rootsNumber) { text += DaemonBundle.message("analysis.hasnot.been.run"); text += HTML_FOOTER; diff --git a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationComponent.java b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationComponent.java index af28c5a55eeb..6233d8a2872f 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationComponent.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationComponent.java @@ -22,6 +22,7 @@ import com.intellij.codeInsight.hint.HintManagerImpl; import com.intellij.codeInsight.hint.HintUtil; import com.intellij.ide.actions.ExternalJavaDocAction; import com.intellij.lang.documentation.DocumentationProvider; +import com.intellij.lang.documentation.ExternalDocumentationHandler; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.ui.popup.JBPopup; @@ -405,10 +406,13 @@ public class DocumentationComponent extends JPanel implements Disposable { if (myElement != null) { final PsiElement element = myElement.getElement(); final DocumentationProvider provider = DocumentationManager.getProviderFromElement(element); - final List urls = provider.getUrlFor(element, DocumentationManager.getOriginalElement(element)); - assert urls != null; - assert !urls.isEmpty(); - ExternalJavaDocAction.showExternalJavadoc(urls); + final PsiElement originalElement = DocumentationManager.getOriginalElement(element); + if (!(provider instanceof ExternalDocumentationHandler) || !((ExternalDocumentationHandler)provider).handleExternal(element, originalElement)) { + final List urls = provider.getUrlFor(element, originalElement); + assert urls != null; + assert !urls.isEmpty(); + ExternalJavaDocAction.showExternalJavadoc(urls); + } } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java index 0f6dcfa19449..0a5dabb5223e 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java @@ -33,6 +33,7 @@ import com.intellij.lang.Language; import com.intellij.lang.LanguageDocumentation; import com.intellij.lang.documentation.CompositeDocumentationProvider; import com.intellij.lang.documentation.DocumentationProvider; +import com.intellij.lang.documentation.ExternalDocumentationHandler; import com.intellij.lang.documentation.ExternalDocumentationProvider; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.actionSystem.ex.ActionManagerEx; @@ -816,38 +817,43 @@ public class DocumentationManager { } } else { + final DocumentationProvider provider = getProviderFromElement(psiElement); + if (!(provider instanceof ExternalDocumentationHandler) || + !((ExternalDocumentationHandler)provider).handleExternalLink(manager, url, psiElement)) { final String docUrl = url; - fetchDocInfo - (new DocumentationCollector() { - public String getDocumentation() throws Exception { - if (docUrl.startsWith(DOC_ELEMENT_PROTOCOL)) { - final List urls = ApplicationManager.getApplication().runReadAction( + fetchDocInfo + (new DocumentationCollector() { + public String getDocumentation() throws Exception { + if (docUrl.startsWith(DOC_ELEMENT_PROTOCOL)) { + final List urls = ApplicationManager.getApplication().runReadAction( new Computable>() { public List compute() { final DocumentationProvider provider = getProviderFromElement(psiElement); return provider.getUrlFor(psiElement, getOriginalElement(psiElement)); } } - ); - BrowserUtil.launchBrowser(urls != null && !urls.isEmpty() ? urls.get(0) : docUrl); - } else { - BrowserUtil.launchBrowser(docUrl); + ); + BrowserUtil.launchBrowser(urls != null && !urls.isEmpty() ? urls.get(0) : docUrl); + } + else { + BrowserUtil.launchBrowser(docUrl); + } + return ""; } - return ""; - } - public PsiElement getElement() { - //String loc = getElementLocator(docUrl); - // - //if (loc != null) { - // PsiElement context = component.getElement(); - // return JavaDocUtil.findReferenceTarget(context.getManager(), loc, context); - //} + public PsiElement getElement() { + //String loc = getElementLocator(docUrl); + // + //if (loc != null) { + // PsiElement context = component.getElement(); + // return JavaDocUtil.findReferenceTarget(context.getManager(), loc, context); + //} - return psiElement; - } - }, component); + return psiElement; + } + }, component); + } } component.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); diff --git a/platform/lang-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.java b/platform/lang-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.java index 9a37209fc431..9f18f4a66c7a 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.java @@ -16,14 +16,16 @@ package com.intellij.execution.impl; -import com.intellij.execution.BeforeRunTask; -import com.intellij.execution.BeforeRunTaskProvider; -import com.intellij.execution.ExecutionManager; +import com.intellij.execution.*; import com.intellij.execution.configurations.ConfigurationPerRunnerSettings; import com.intellij.execution.configurations.RunConfiguration; import com.intellij.execution.configurations.RunProfile; import com.intellij.execution.configurations.RunProfileState; +import com.intellij.execution.process.ProcessAdapter; +import com.intellij.execution.process.ProcessEvent; import com.intellij.execution.process.ProcessHandler; +import com.intellij.execution.runners.ExecutionEnvironment; +import com.intellij.execution.runners.ExecutionUtil; import com.intellij.execution.ui.RunContentDescriptor; import com.intellij.execution.ui.RunContentManager; import com.intellij.execution.ui.RunContentManagerImpl; @@ -64,7 +66,8 @@ public class ExecutionManagerImpl extends ExecutionManager implements ProjectCom public void projectClosed() { } - public void initComponent() { } + public void initComponent() { + } public void disposeComponent() { } @@ -94,53 +97,112 @@ public class ExecutionManagerImpl extends ExecutionManager implements ProjectCom public void compileAndRun(final Runnable startRunnable, final RunProfile configuration, final RunProfileState state) { - final Runnable antAwareRunnable = new Runnable() { - public void run() { - if (configuration instanceof RunConfiguration) { - final RunConfiguration runConfiguration = (RunConfiguration)configuration; - final RunManagerImpl runManager = RunManagerImpl.getInstanceImpl(myProject); + if (configuration instanceof RunConfiguration) { + final RunConfiguration runConfiguration = (RunConfiguration)configuration; + final RunManagerImpl runManager = RunManagerImpl.getInstanceImpl(myProject); - final Map, BeforeRunTask> activeProviders = new LinkedHashMap, BeforeRunTask>(); - for (final BeforeRunTaskProvider provider : Extensions.getExtensions(BeforeRunTaskProvider.EXTENSION_POINT_NAME, myProject)) { - final BeforeRunTask task = runManager.getBeforeRunTask(runConfiguration, provider.getId()); - if (task != null && task.isEnabled()) { - activeProviders.put(provider, task); + final Map, BeforeRunTask> activeProviders = new LinkedHashMap, BeforeRunTask>(); + for (final BeforeRunTaskProvider provider : Extensions.getExtensions(BeforeRunTaskProvider.EXTENSION_POINT_NAME, myProject)) { + final BeforeRunTask task = runManager.getBeforeRunTask(runConfiguration, provider.getId()); + if (task != null && task.isEnabled()) { + activeProviders.put(provider, task); + } + } + + if (!activeProviders.isEmpty()) { + ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { + public void run() { + ConfigurationPerRunnerSettings configurationSettings = state.getConfigurationSettings(); + DataContext projectContext = SimpleDataContext.getProjectContext(myProject); + + final DataContext dataContext = configurationSettings != null ? SimpleDataContext + .getSimpleContext(BeforeRunTaskProvider.RUNNER_ID, configurationSettings.getRunnerId(), projectContext) : projectContext; + for (BeforeRunTaskProvider provider : activeProviders.keySet()) { + if(!provider.executeTask(dataContext, runConfiguration, activeProviders.get(provider))) { + return; + } + } + DumbService.getInstance(myProject).smartInvokeLater(startRunnable); + } + }); + } + else { + startRunnable.run(); + } + } + else { + startRunnable.run(); + } + } + + @Override + public void startRunProfile(@NotNull final RunProfileStarter starter, @NotNull final RunProfileState state, + @NotNull final Project project, @NotNull final Executor executor, @NotNull final ExecutionEnvironment env) { + final RunContentDescriptor reuseContent = ExecutionManager.getInstance(project).getContentManager().getReuseContent(executor, env.getContentToReuse()); + final RunProfile profile = env.getRunProfile(); + + Runnable startRunnable = new Runnable() { + public void run() { + boolean started = false; + try { + if (project.isDisposed()) return; + + project.getMessageBus().syncPublisher(EXECUTION_TOPIC).processStarting(profile); + + final RunContentDescriptor descriptor = starter.execute(project, executor, state, reuseContent, env); + + if (descriptor != null) { + ExecutionManager.getInstance(project).getContentManager().showRunContent(executor, descriptor); + final ProcessHandler processHandler = descriptor.getProcessHandler(); + if (processHandler != null) { + processHandler.startNotify(); + project.getMessageBus().syncPublisher(EXECUTION_TOPIC).processStarted(profile, processHandler); + started = true; + processHandler.addProcessListener(new ProcessExecutionListener(project, profile, processHandler)); } } - - if (!activeProviders.isEmpty()) { - ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { - public void run() { - ConfigurationPerRunnerSettings configurationSettings = state.getConfigurationSettings(); - DataContext projectContext = SimpleDataContext.getProjectContext(myProject); - - final DataContext dataContext = configurationSettings != null ? SimpleDataContext - .getSimpleContext(BeforeRunTaskProvider.RUNNER_ID, configurationSettings.getRunnerId(), projectContext) : projectContext; - for (BeforeRunTaskProvider provider : activeProviders.keySet()) { - if(!provider.executeTask(dataContext, runConfiguration, activeProviders.get(provider))) { - return; - } - } - DumbService.getInstance(myProject).smartInvokeLater(startRunnable); - } - }); - } - else { - startRunnable.run(); - } } - else { - startRunnable.run(); + catch (ExecutionException e) { + ExecutionUtil.handleExecutionError(project, executor.getToolWindowId(), profile, e); + } + if (!started) { + project.getMessageBus().syncPublisher(EXECUTION_TOPIC).processNotStarted(profile); } } }; - antAwareRunnable.run(); - //ApplicationManager.getApplication().invokeLater(antAwareRunnable); + if (ApplicationManager.getApplication().isUnitTestMode()) { + startRunnable.run(); + } + else { + compileAndRun(startRunnable, profile, state); + } } @NotNull public String getComponentName() { return "ExecutionManager"; } + + private static class ProcessExecutionListener extends ProcessAdapter { + private final Project myProject; + private final RunProfile myProfile; + private final ProcessHandler myProcessHandler; + + public ProcessExecutionListener(Project project, RunProfile profile, ProcessHandler processHandler) { + myProject = project; + myProfile = profile; + myProcessHandler = processHandler; + } + + @Override + public void processTerminated(ProcessEvent event) { + myProject.getMessageBus().syncPublisher(EXECUTION_TOPIC).processTerminated(myProfile, myProcessHandler); + } + + @Override + public void processWillTerminate(ProcessEvent event, boolean willBeDestroyed) { + myProject.getMessageBus().syncPublisher(EXECUTION_TOPIC).processTerminating(myProfile, myProcessHandler); + } + } } diff --git a/platform/lang-impl/src/com/intellij/execution/runners/RunContentBuilder.java b/platform/lang-impl/src/com/intellij/execution/runners/RunContentBuilder.java index 2e90764ca8aa..1e7cb1e4e884 100644 --- a/platform/lang-impl/src/com/intellij/execution/runners/RunContentBuilder.java +++ b/platform/lang-impl/src/com/intellij/execution/runners/RunContentBuilder.java @@ -157,7 +157,7 @@ public class RunContentBuilder implements LogConsoleManager, Disposable { } } - final Content consoleContent = ui.createContent("Console", console.getComponent(), "Console", + final Content consoleContent = ui.createContent(ExecutionConsole.CONSOLE_CONTENT_ID, console.getComponent(), "Console", IconLoader.getIcon("/debugger/console.png"), console.getPreferredFocusableComponent()); diff --git a/platform/lang-impl/src/com/intellij/ide/PowerSaveMode.java b/platform/lang-impl/src/com/intellij/ide/PowerSaveMode.java new file mode 100644 index 000000000000..5b0d27dd3831 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/ide/PowerSaveMode.java @@ -0,0 +1,50 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.ide; + +import com.intellij.openapi.components.ServiceManager; +import com.intellij.util.messages.MessageBus; +import com.intellij.util.messages.Topic; + +/** + * @author yole + */ +public class PowerSaveMode { + private boolean myEnabled = false; + private final MessageBus myBus; + + public PowerSaveMode(MessageBus bus) { + myBus = bus; + } + + public static boolean isEnabled() { + return ServiceManager.getService(PowerSaveMode.class).myEnabled; + } + + public static void setEnabled(boolean value) { + final PowerSaveMode instance = ServiceManager.getService(PowerSaveMode.class); + if (instance.myEnabled != value) { + instance.myEnabled = value; + instance.myBus.syncPublisher(TOPIC).powerSaveStateChanged(); + } + } + + public interface Listener { + void powerSaveStateChanged(); + } + + public static Topic TOPIC = Topic.create("PowerSaveMode.Listener", Listener.class); +} diff --git a/platform/lang-impl/src/com/intellij/ide/actions/TogglePowerSaveAction.java b/platform/lang-impl/src/com/intellij/ide/actions/TogglePowerSaveAction.java new file mode 100644 index 000000000000..3040982f253b --- /dev/null +++ b/platform/lang-impl/src/com/intellij/ide/actions/TogglePowerSaveAction.java @@ -0,0 +1,36 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.ide.actions; + +import com.intellij.ide.PowerSaveMode; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.ToggleAction; +import com.intellij.openapi.project.DumbAware; + +/** + * @author yole + */ +public class TogglePowerSaveAction extends ToggleAction implements DumbAware { + @Override + public boolean isSelected(AnActionEvent e) { + return PowerSaveMode.isEnabled(); + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + PowerSaveMode.setEnabled(state); + } +} diff --git a/platform/lang-impl/src/com/intellij/ide/highlighter/custom/CustomFileTypeLexer.java b/platform/lang-impl/src/com/intellij/ide/highlighter/custom/CustomFileTypeLexer.java index c31f41735c91..6506934c0c73 100644 --- a/platform/lang-impl/src/com/intellij/ide/highlighter/custom/CustomFileTypeLexer.java +++ b/platform/lang-impl/src/com/intellij/ide/highlighter/custom/CustomFileTypeLexer.java @@ -65,6 +65,7 @@ public final class CustomFileTypeLexer extends AbstractCustomLexer { tokenParsers.add(multilineCommentParser); } tokenParsers.add(numberParser); + tokenParsers.add(new PunctuationParser()); if (hexNumberParser != null) { tokenParsers.add(hexNumberParser); } diff --git a/platform/lang-impl/src/com/intellij/ide/highlighter/custom/impl/CustomFileTypeBraceMatcher.java b/platform/lang-impl/src/com/intellij/ide/highlighter/custom/impl/CustomFileTypeBraceMatcher.java index 1c099650b269..684ab7972e54 100644 --- a/platform/lang-impl/src/com/intellij/ide/highlighter/custom/impl/CustomFileTypeBraceMatcher.java +++ b/platform/lang-impl/src/com/intellij/ide/highlighter/custom/impl/CustomFileTypeBraceMatcher.java @@ -42,8 +42,10 @@ class CustomFileTypeBraceMatcher implements BraceMatcher { } public boolean isRBraceToken(HighlighterIterator iterator, CharSequence fileText, FileType fileType) { - final IElementType tokenType = iterator.getTokenType(); + return isRBraceToken(iterator.getTokenType()); + } + private static boolean isRBraceToken(IElementType tokenType) { return tokenType == CustomHighlighterTokenType.R_BRACKET || tokenType == CustomHighlighterTokenType.R_PARENTH || tokenType == CustomHighlighterTokenType.R_BRACE; @@ -80,7 +82,9 @@ class CustomFileTypeBraceMatcher implements BraceMatcher { } public boolean isPairedBracesAllowedBeforeType(@NotNull final IElementType lbraceType, @Nullable final IElementType contextType) { - return contextType == CustomHighlighterTokenType.WHITESPACE; + return contextType == CustomHighlighterTokenType.PUNCTUATION || + contextType == CustomHighlighterTokenType.WHITESPACE || + isRBraceToken(contextType); } public int getCodeConstructStart(final PsiFile file, final int openingBraceOffset) { diff --git a/platform/lang-impl/src/com/intellij/ide/highlighter/custom/tokens/PunctuationParser.java b/platform/lang-impl/src/com/intellij/ide/highlighter/custom/tokens/PunctuationParser.java new file mode 100644 index 000000000000..e097e4df887b --- /dev/null +++ b/platform/lang-impl/src/com/intellij/ide/highlighter/custom/tokens/PunctuationParser.java @@ -0,0 +1,39 @@ +/* + * Copyright 2000-2009 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.intellij.ide.highlighter.custom.tokens; + +import com.intellij.psi.CustomHighlighterTokenType; + +/** + * @author peter + */ +public class PunctuationParser extends BaseTokenParser { + @Override + public boolean hasToken(int position) { + final char c = myBuffer.charAt(position); + if (".,:;".indexOf(c) >= 0) { + myTokenInfo.updateData(position, position+1, CustomHighlighterTokenType.PUNCTUATION); + return true; + } + return false; + } + + @Override + public int getSmartUpdateShift() { + return 0; + } +} diff --git a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoSymbolModel2.java b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoSymbolModel2.java index 376febce1227..f7244901207d 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoSymbolModel2.java +++ b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoSymbolModel2.java @@ -70,11 +70,11 @@ public class GotoSymbolModel2 extends ContributorsBasedGotoByModel { @NotNull public String[] getSeparators() { - return new String[] {".", ":"}; + return new String[] {"."}; } @Override public String getHelpId() { return "procedures.navigating.goto.class"; } -} \ No newline at end of file +} diff --git a/platform/lang-impl/src/com/intellij/indentation/IndentationAnnotatorVisitor.java b/platform/lang-impl/src/com/intellij/indentation/IndentationAnnotatorVisitor.java index 937198ab81e8..facb47a68a3a 100644 --- a/platform/lang-impl/src/com/intellij/indentation/IndentationAnnotatorVisitor.java +++ b/platform/lang-impl/src/com/intellij/indentation/IndentationAnnotatorVisitor.java @@ -5,6 +5,7 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; import com.intellij.psi.PsiFile; import com.intellij.psi.tree.IElementType; +import com.intellij.psi.tree.TokenSet; import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; @@ -15,13 +16,16 @@ public class IndentationAnnotatorVisitor extends PsiElementVisitor { protected final AnnotationHolder myHolder; private final IElementType myIndentTokenType; private final IElementType myEolTokenType; + private final TokenSet myCommentTypes; public IndentationAnnotatorVisitor(@NotNull final AnnotationHolder holder, final IElementType indentTokenType, - final IElementType eolTokenType) { + final IElementType eolTokenType, + final TokenSet commentTypes) { myHolder = holder; myIndentTokenType = indentTokenType; myEolTokenType = eolTokenType; + myCommentTypes = commentTypes; } @SuppressWarnings({"ConstantConditions"}) @@ -45,43 +49,46 @@ public class IndentationAnnotatorVisitor extends PsiElementVisitor { // Iterating over leafs while (leaf != null) { final IElementType leafType = leaf.getNode().getElementType(); - if (leafType == myIndentTokenType && nextLeaf!=null && nextLeaf.getNode().getElementType() != myEolTokenType){ - final String currentIndentText = leaf.getText(); - final IndentInfo currentIndent = getIndent(currentIndentText); - final int currentIndentLength = currentIndent.length; - // Check if spaces and tabs are mixed - if (currentIndentLength == -1){ - myHolder.createErrorAnnotation(leaf, "Indentation can't use both tabs and spaces"); - } - else - if (currentIndentLength > 0) { - // If we don't have indent info registered - if (indentInfo == null){ - indentInfo = currentIndent; - lastIndent = currentIndentLength; - } else { - final Boolean useTab = indentInfo.useTab; - final int indentInfoLength = indentInfo.length; - // If indent and current indent use different space and tabs - if (useTab && currentIndentText.contains(" ") || !useTab && currentIndentText.contains("\t")) { - final String message = useTab - ? "Inconsistent indentation: " + currentIndentLength + - " spaces were used for indentation, but the rest of the document was indented using " + - indentInfoLength + " tabs" - : "Inconsistent indentation: " + currentIndentLength + - " tabs were used for indentation, but the rest of the document was indented using " + - indentInfoLength + " spaces"; - myHolder.createErrorAnnotation(leaf, message); + if (leafType == myIndentTokenType && nextLeaf != null) { + final IElementType nextLeafType = nextLeaf.getNode().getElementType(); + if (nextLeafType != myEolTokenType && !myCommentTypes.contains(nextLeafType)){ + final String currentIndentText = leaf.getText(); + final IndentInfo currentIndent = getIndent(currentIndentText); + final int currentIndentLength = currentIndent.length; + // Check if spaces and tabs are mixed + if (currentIndentLength == -1){ + myHolder.createErrorAnnotation(leaf, "Indentation can't use both tabs and spaces"); + } + else + if (currentIndentLength > 0) { + // If we don't have indent info registered + if (indentInfo == null){ + indentInfo = currentIndent; + lastIndent = currentIndentLength; } else { - // Check indent length - final int delta = currentIndentLength - lastIndent; - if (currentIndentLength % indentInfoLength != 0 || delta > indentInfoLength) { - final String message = useTab - ? currentIndentLength + " tabs were used for indentation. Must be indented using " + indentInfoLength + " tabs" - : currentIndentLength + " spaces were used for indentation. Must be indented using " + indentInfoLength + " spaces"; + final Boolean useTab = indentInfo.useTab; + final int indentInfoLength = indentInfo.length; + // If indent and current indent use different space and tabs + if (useTab && currentIndentText.contains(" ") || !useTab && currentIndentText.contains("\t")) { + final String message = useTab + ? "Inconsistent indentation: " + currentIndentLength + + " spaces were used for indentation, but the rest of the document was indented using " + + indentInfoLength + " tabs" + : "Inconsistent indentation: " + currentIndentLength + + " tabs were used for indentation, but the rest of the document was indented using " + + indentInfoLength + " spaces"; myHolder.createErrorAnnotation(leaf, message); + } else { + // Check indent length + final int delta = currentIndentLength - lastIndent; + if (currentIndentLength % indentInfoLength != 0 || delta > indentInfoLength) { + final String message = useTab + ? currentIndentLength + " tabs were used for indentation. Must be indented using " + indentInfoLength + " tabs" + : currentIndentLength + " spaces were used for indentation. Must be indented using " + indentInfoLength + " spaces"; + myHolder.createErrorAnnotation(leaf, message); + } + lastIndent = currentIndentLength / indentInfoLength * indentInfoLength; } - lastIndent = currentIndentLength / indentInfoLength * indentInfoLength; } } } diff --git a/platform/lang-impl/src/com/intellij/openapi/wm/impl/status/TogglePopupHintsPanel.java b/platform/lang-impl/src/com/intellij/openapi/wm/impl/status/TogglePopupHintsPanel.java index fe8dfa478269..2449862dfd81 100644 --- a/platform/lang-impl/src/com/intellij/openapi/wm/impl/status/TogglePopupHintsPanel.java +++ b/platform/lang-impl/src/com/intellij/openapi/wm/impl/status/TogglePopupHintsPanel.java @@ -20,6 +20,7 @@ import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; import com.intellij.codeInsight.daemon.impl.HectorComponent; import com.intellij.codeInsight.daemon.impl.analysis.HighlightLevelUtil; import com.intellij.ide.DataManager; +import com.intellij.ide.PowerSaveMode; import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.fileEditor.FileEditorManager; @@ -38,6 +39,7 @@ import com.intellij.psi.PsiManager; import com.intellij.ui.UIBundle; import com.intellij.ui.awt.RelativePoint; import com.intellij.util.Consumer; +import com.intellij.util.messages.MessageBusConnection; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -57,7 +59,8 @@ public class TogglePopupHintsPanel implements StatusBarWidget, StatusBarWidget.I public TogglePopupHintsPanel(@NotNull final Project project) { myCurrentIcon = EMPTY_ICON; - project.getMessageBus().connect().subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, new FileEditorManagerAdapter() { + final MessageBusConnection connection = project.getMessageBus().connect(); + connection.subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, new FileEditorManagerAdapter() { @Override public void selectionChanged(FileEditorManagerEvent event) { updateStatus(); @@ -68,6 +71,12 @@ public class TogglePopupHintsPanel implements StatusBarWidget, StatusBarWidget.I updateStatus(); } }); + connection.subscribe(PowerSaveMode.TOPIC, new PowerSaveMode.Listener() { + @Override + public void powerSaveStateChanged() { + updateStatus(); + } + }); } public void dispose() { @@ -130,13 +139,21 @@ public class TogglePopupHintsPanel implements StatusBarWidget, StatusBarWidget.I private void updateStatus(PsiFile file) { if (isStateChangeable(file)) { - if (HighlightLevelUtil.shouldInspect(file)) { + if (PowerSaveMode.isEnabled()) { + myCurrentIcon = EMPTY_ICON; + myToolTipText = "Code analysis is disabled in power save mode. "; + } + else if (HighlightLevelUtil.shouldInspect(file)) { myCurrentIcon = INSPECTIONS_ICON; - myToolTipText = "Current inspection profile: " + InspectionProjectProfileManager.getInstance(file.getProject()).getInspectionProfile().getName() + ". "; - } else if (HighlightLevelUtil.shouldHighlight(file)) { + myToolTipText = "Current inspection profile: " + + InspectionProjectProfileManager.getInstance(file.getProject()).getInspectionProfile().getName() + + ". "; + } + else if (HighlightLevelUtil.shouldHighlight(file)) { myCurrentIcon = SYNTAX_ONLY_ICON; - myToolTipText = "Highlighting level is: Syntax. "; - } else { + myToolTipText = "Highlighting level is: Syntax. "; + } + else { myCurrentIcon = INSPECTIONS_OFF_ICON; myToolTipText = "Inspections are off. "; } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java index 425e5c86fd81..878523e34bd8 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java @@ -31,6 +31,7 @@ import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; @@ -487,34 +488,62 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { myManager.getCacheManager().processFilesWithWord(processor, word, UsageSearchContext.IN_STRINGS, scope, true); } - public boolean processRequests(@NotNull SearchRequestCollector request, Processor processor) { - final MultiMap, PsiSearchRequest> singles = new MultiMap, PsiSearchRequest>(); - final List>> customs = new ArrayList>>(); - distributePrimitives(request, singles, customs); - - if (!processRequestsOptimized(singles, processor)) { - return false; + private static class RequestWithProcessor extends Pair> { + private RequestWithProcessor(PsiSearchRequest first, Processor second) { + super(first, second); } - - for (Processor> custom : customs) { - if (!custom.process(processor)) { - return false; - } - } - - return true; - } - private boolean processRequestsOptimized(MultiMap, PsiSearchRequest> singles, final Processor consumer) { + public boolean processRequests(@NotNull SearchRequestCollector collector, Processor processor) { + Map> collectors = new HashMap>(); + collectors.put(collector, processor); + + appendCollectorsFromQueryRequests(collectors); + + do { + final MultiMap, RequestWithProcessor> singles = new MultiMap, RequestWithProcessor>(); + final List> customs = new ArrayList>(); + distributePrimitives(collectors, singles, customs); + + if (!processRequestsOptimized(singles)) { + return false; + } + + for (Computable custom : customs) { + if (!custom.compute()) { + return false; + } + } + } while (appendCollectorsFromQueryRequests(collectors)); + + return true; + } + + private static boolean appendCollectorsFromQueryRequests(Map> collectors) { + boolean changed = false; + LinkedList queue = new LinkedList(collectors.keySet()); + while (!queue.isEmpty()) { + final SearchRequestCollector each = queue.removeFirst(); + for (QuerySearchRequest request : each.takeQueryRequests()) { + request.runQuery(); + collectors.put(request.collector, request.processor); + queue.addLast(request.collector); + changed = true; + } + } + return changed; + } + + private boolean processRequestsOptimized(MultiMap, RequestWithProcessor> singles) { if (singles.isEmpty()) { return true; } if (singles.size() == 1) { - final Collection requests = singles.get(singles.keySet().iterator().next()); + final Collection requests = singles.get(singles.keySet().iterator().next()); if (requests.size() == 1) { - return processSingleRequest(requests.iterator().next(), consumer); + final RequestWithProcessor theOnly = requests.iterator().next(); + return processSingleRequest(theOnly.first, theOnly.second); } } @@ -524,13 +553,17 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { progress.setText(PsiBundle.message("psi.scanning.files.progress")); } - final MultiMap candidateFiles = collectFiles(singles); + final MultiMap candidateFiles = collectFiles(singles); - final Map searchers = new HashMap(); + if (candidateFiles.isEmpty()) { + return true; + } + + final Map searchers = new HashMap(); final Set allWords = new TreeSet(); - for (PsiSearchRequest singleRequest : candidateFiles.values()) { - searchers.put(singleRequest, new StringSearcher(singleRequest.word, singleRequest.caseSensitive, true)); - allWords.add(singleRequest.word); + for (RequestWithProcessor singleRequest : candidateFiles.values()) { + searchers.put(singleRequest, new StringSearcher(singleRequest.first.word, singleRequest.first.caseSensitive, true)); + allWords.add(singleRequest.first.word); } if (progress != null) { @@ -540,9 +573,9 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { return processPsiFileRoots(progress, new ArrayList(candidateFiles.keySet()), new Processor() { public boolean process(PsiElement psiRoot) { final VirtualFile vfile = psiRoot.getContainingFile().getVirtualFile(); - for (final PsiSearchRequest singleRequest : candidateFiles.get(vfile)) { + for (final RequestWithProcessor singleRequest : candidateFiles.get(vfile)) { StringSearcher searcher = searchers.get(singleRequest); - if (!LowLevelSearchUtil.processElementsContainingWordInElement(adaptProcessor(singleRequest, consumer), psiRoot, searcher, false, progress)) { + if (!LowLevelSearchUtil.processElementsContainingWordInElement(adaptProcessor(singleRequest.first, singleRequest.second), psiRoot, searcher, false, progress)) { return false; } } @@ -565,18 +598,18 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { }; } - private MultiMap collectFiles(MultiMap, PsiSearchRequest> singles) { + private MultiMap collectFiles(MultiMap, RequestWithProcessor> singles) { final ProjectFileIndex index = ProjectRootManager.getInstance(myManager.getProject()).getFileIndex(); - final MultiMap result = new MultiMap(); + final MultiMap result = new MultiMap(); for (Set key : singles.keySet()) { - final Collection data = singles.get(key); + final Collection data = singles.get(key); GlobalSearchScope commonScope = uniteScopes(data); - MultiMap intersection = null; + MultiMap intersection = null; boolean first = true; for (IdIndexEntry entry : key) { - final MultiMap local = findFilesWithIndexEntry(entry, index, data, commonScope); + final MultiMap local = findFilesWithIndexEntry(entry, index, data, commonScope); if (first) { intersection = local; first = false; @@ -593,31 +626,34 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { return result; } - private static GlobalSearchScope uniteScopes(Collection requests) { + private static GlobalSearchScope uniteScopes(Collection requests) { GlobalSearchScope commonScope = null; - for (PsiSearchRequest r : requests) { - final GlobalSearchScope scope = (GlobalSearchScope)r.searchScope; + for (RequestWithProcessor r : requests) { + final GlobalSearchScope scope = (GlobalSearchScope)r.first.searchScope; commonScope = commonScope == null ? scope : commonScope.uniteWith(scope); } assert commonScope != null; return commonScope; } - private static MultiMap findFilesWithIndexEntry(final IdIndexEntry entry, + private static MultiMap findFilesWithIndexEntry(final IdIndexEntry entry, final ProjectFileIndex index, - final Collection data, + final Collection data, final GlobalSearchScope commonScope) { - final MultiMap local = new MultiMap(); + final MultiMap local = new MultiMap(); ApplicationManager.getApplication().runReadAction(new Runnable() { public void run() { + ProgressManager.checkCanceled(); FileBasedIndex.getInstance().processValues(IdIndex.NAME, entry, null, new FileBasedIndex.ValueProcessor() { public boolean process(VirtualFile file, Integer value) { + ProgressManager.checkCanceled(); if (!IndexCacheManagerImpl.shouldBeFound(file, index)) { return true; } int mask = value.intValue(); - for (PsiSearchRequest single : data) { - if ((mask & single.searchContext) != 0 && ((GlobalSearchScope)single.searchScope).contains(file)) { + for (RequestWithProcessor single : data) { + final PsiSearchRequest request = single.first; + if ((mask & request.searchContext) != 0 && ((GlobalSearchScope)request.searchScope).contains(file)) { local.putValue(file, single); } } @@ -630,27 +666,38 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { return local; } - private void distributePrimitives(SearchRequestCollector request, - MultiMap, PsiSearchRequest> singles, - List>> customs) { - for (final PsiSearchRequest primitive : request.getSearchRequests()) { - final SearchScope scope = primitive.searchScope; - if (scope instanceof LocalSearchScope) { - customs.add(new Processor>() { - public boolean process(Processor processor) { - return processSingleRequest(primitive, processor); + private void distributePrimitives(final Map> collectors, + MultiMap, RequestWithProcessor> singles, + List> customs) { + for (final SearchRequestCollector collector : collectors.keySet()) { + final Processor processor = collectors.get(collector); + for (final PsiSearchRequest primitive : collector.takeSearchRequests()) { + final SearchScope scope = primitive.searchScope; + if (scope instanceof LocalSearchScope) { + customs.add(new Computable() { + @Override + public Boolean compute() { + return processSingleRequest(primitive, processor); + } + }); + } else { + final List words = StringUtil.getWordsIn(primitive.word); + final Set key = new HashSet(words.size() * 2); + for (String word : words) { + key.add(new IdIndexEntry(word, primitive.caseSensitive)); + } + singles.putValue(key, new RequestWithProcessor(primitive, processor)); + } + } + for (final Processor> customAction : collector.takeCustomSearchActions()) { + customs.add(new Computable() { + @Override + public Boolean compute() { + return customAction.process(processor); } }); - } else { - final List words = StringUtil.getWordsIn(primitive.word); - final Set key = new HashSet(words.size() * 2); - for (String word : words) { - key.add(new IdIndexEntry(word, primitive.caseSensitive)); - } - singles.putValue(key, primitive); } } - customs.addAll(request.getCustomSearchActions()); } private boolean processSingleRequest(PsiSearchRequest single, Processor consumer) { diff --git a/platform/lang-impl/src/com/intellij/psi/util/proximity/SameDirectoryWeigher.java b/platform/lang-impl/src/com/intellij/psi/util/proximity/SameDirectoryWeigher.java new file mode 100644 index 000000000000..13df01e65c5a --- /dev/null +++ b/platform/lang-impl/src/com/intellij/psi/util/proximity/SameDirectoryWeigher.java @@ -0,0 +1,48 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.util.proximity; + +import com.intellij.openapi.util.NullableLazyKey; +import com.intellij.psi.PsiDirectory; +import com.intellij.psi.PsiElement; +import com.intellij.psi.util.ProximityLocation; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.NullableFunction; +import org.jetbrains.annotations.NotNull; + +/** + * NOTE: This class is only registered in platform-based IDEs. In IDEA, SamePackageWeigher is used instead. + * + * @author yole + */ +public class SameDirectoryWeigher extends ProximityWeigher { + private static final NullableLazyKey + PLACE_DIRECTORY = NullableLazyKey.create("placeDirectory", new NullableFunction() { + @Override + public PsiDirectory fun(ProximityLocation location) { + return PsiTreeUtil.getParentOfType(location.getPosition(), PsiDirectory.class, false); + } + }); + + public Comparable weigh(@NotNull final PsiElement element, final ProximityLocation location) { + final PsiDirectory placeDirectory = PLACE_DIRECTORY.getValue(location); + if (placeDirectory == null) { + return false; + } + + return placeDirectory.equals(PsiTreeUtil.getParentOfType(element, PsiDirectory.class, false)); + } +} diff --git a/platform/lvcs-impl/src/com/intellij/history/core/ChangeListStorageImpl.java b/platform/lvcs-impl/src/com/intellij/history/core/ChangeListStorageImpl.java index 10de5a3fd2a4..ec49a27bbd64 100644 --- a/platform/lvcs-impl/src/com/intellij/history/core/ChangeListStorageImpl.java +++ b/platform/lvcs-impl/src/com/intellij/history/core/ChangeListStorageImpl.java @@ -102,8 +102,8 @@ public class ChangeListStorageImpl implements ChangeListStorage { } private ChangeSetBlock doReadBlock(int id) { - DataInputStream in = myStorage.readStream(id); try { + DataInputStream in = myStorage.readStream(id); try { int size = in.readInt(); List changes = new ArrayList(size); diff --git a/platform/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeUi.java b/platform/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeUi.java index 49b8366e9683..1374fc23378a 100644 --- a/platform/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeUi.java +++ b/platform/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeUi.java @@ -1662,10 +1662,20 @@ public class AbstractTreeUi { return result; } + if (myResettingToReadyNow.get()) { + getReady(this).notify(result); + return result; + } + myResettingToReadyNow.set(true); invokeLaterIfNeeded(new Runnable() { public void run() { + if (!myResettingToReadyNow.get()) { + result.setDone(); + return; + } + Progressive[] progressives = myBatchIndicators.keySet().toArray(new Progressive[myBatchIndicators.size()]); for (Progressive each : progressives) { myBatchIndicators.remove(each).cancel(); @@ -1790,6 +1800,10 @@ public class AbstractTreeUi { return isReady(false); } + public boolean isCancelledReady() { + return isReady(false) && myCancelledBuild.size() > 0; + } + public boolean isReady(boolean attempt) { Boolean ready = _isReady(attempt); return ready != null && ready.booleanValue(); @@ -1864,6 +1878,11 @@ public class AbstractTreeUi { try { try { myYeildingPasses.remove(pass); + + if (!canInitiateNewActivity()) { + throw new ProcessCanceledException(); + } + runnable.run(); } finally { diff --git a/platform/platform-api/src/com/intellij/ui/components/JBScrollPane.java b/platform/platform-api/src/com/intellij/ui/components/JBScrollPane.java index 10dbfd4fed5d..e5e24128b218 100644 --- a/platform/platform-api/src/com/intellij/ui/components/JBScrollPane.java +++ b/platform/platform-api/src/com/intellij/ui/components/JBScrollPane.java @@ -58,16 +58,19 @@ public class JBScrollPane extends JScrollPane { @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { JViewport vp = getViewport(); - if (vp != null) { - Color bg = vp.getView().getBackground(); - if (bg != null) { - g.setColor(bg); - g.drawLine(0, 0, width - 1, 0); - g.drawLine(0, height - 1, width - 1, height - 1); - g.drawLine(0, 0, 0, height - 1); - g.drawLine(width - 1, 0, width - 1, height - 1); - } - } + if (vp == null) return; + + Component view = vp.getView(); + if (view == null) return; + + Color bg = view.getBackground(); + if (bg == null) return; + + g.setColor(bg); + g.drawLine(0, 0, width - 1, 0); + g.drawLine(0, height - 1, width - 1, height - 1); + g.drawLine(0, 0, 0, height - 1); + g.drawLine(width - 1, 0, width - 1, height - 1); } @Override diff --git a/platform/platform-api/src/com/intellij/util/Alarm.java b/platform/platform-api/src/com/intellij/util/Alarm.java index 9f4b1f78ddb6..4aafb948aa95 100644 --- a/platform/platform-api/src/com/intellij/util/Alarm.java +++ b/platform/platform-api/src/com/intellij/util/Alarm.java @@ -43,6 +43,8 @@ public class Alarm implements Disposable { private final List myRequests = new ArrayList(); private final List myPendingRequests = new ArrayList(); + private final ThreadPoolExecutor myExecutorService; + private static final ThreadPoolExecutor ourSharedExecutorService = ConcurrencyUtil.newSingleThreadExecutor("Alarm pool(shared)", Thread.NORM_PRIORITY - 2); private final Object LOCK = new Object(); @@ -53,6 +55,10 @@ public class Alarm implements Disposable { public void dispose() { myDisposed = true; cancelAllRequests(); + if (myThreadToUse == ThreadToUse.OWN_THREAD) { + myExecutorService.getQueue().clear(); + myExecutorService.shutdown(); + } } public enum ThreadToUse { @@ -78,6 +84,7 @@ public class Alarm implements Disposable { } public Alarm(@NotNull ThreadToUse threadToUse, Disposable parentDisposable) { myThreadToUse = threadToUse; + myExecutorService = threadToUse == ThreadToUse.OWN_THREAD ? ConcurrencyUtil.newSingleThreadExecutor("Alarm pool(own)", Thread.NORM_PRIORITY - 2) : ourSharedExecutorService; if (parentDisposable != null) { Disposer.register(parentDisposable, this); @@ -236,9 +243,7 @@ public class Alarm implements Disposable { } } else { - myFuture = (myThreadToUse == ThreadToUse.SHARED_THREAD) - ? ourSharedExecutorService.submit(scheduledTask) - : ApplicationManager.getApplication().executeOnPooledThread(scheduledTask); + myFuture = myExecutorService.submit(scheduledTask); } } } diff --git a/platform/platform-impl/src/com/intellij/codeInsight/hint/DocumentFragmentTooltipRenderer.java b/platform/platform-impl/src/com/intellij/codeInsight/hint/DocumentFragmentTooltipRenderer.java index ed11745842d1..73ab41d5e2b3 100644 --- a/platform/platform-impl/src/com/intellij/codeInsight/hint/DocumentFragmentTooltipRenderer.java +++ b/platform/platform-impl/src/com/intellij/codeInsight/hint/DocumentFragmentTooltipRenderer.java @@ -15,10 +15,7 @@ */ package com.intellij.codeInsight.hint; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.DocumentFragment; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.LogicalPosition; +import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.editor.ex.FoldingModelEx; import com.intellij.openapi.util.TextRange; @@ -51,7 +48,10 @@ public class DocumentFragmentTooltipRenderer implements TooltipRenderer { JLayeredPane layeredPane = editorComponent.getRootPane().getLayeredPane(); - p = editor.logicalPositionToXY(new LogicalPosition(startLine, 0)); + // There is a possible case that collapsed folding region is soft wrapped, hence, we need to anchor + // not logical but visual line start. + VisualPosition visual = editor.offsetToVisualPosition(startOffset); + p = editor.visualPositionToXY(visual); p = SwingUtilities.convertPoint( ((EditorEx)editor).getGutterComponentEx(), p, diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionButton.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionButton.java index 1c9cd34c9350..13e615887cd6 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionButton.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionButton.java @@ -92,7 +92,7 @@ public class ActionButton extends JComponent implements ActionButtonComponent { } protected boolean isButtonEnabled() { - return myPresentation.isEnabled(); + return isEnabled() && myPresentation.isEnabled(); } private void onMousePresenceChanged(boolean setInfo) { diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/SoftWrapChangeListener.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/SoftWrapChangeListener.java index 281334ff0b52..32c638e61410 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/ex/SoftWrapChangeListener.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/ex/SoftWrapChangeListener.java @@ -18,6 +18,8 @@ package com.intellij.openapi.editor.ex; import com.intellij.openapi.editor.TextChange; import org.jetbrains.annotations.NotNull; +import java.util.Collection; + /** * Defines a contract for the callbacks for soft wraps management notifications (addition/removal). * @@ -32,4 +34,9 @@ public interface SoftWrapChangeListener { * @param softWrap newly registered soft wrap */ void softWrapAdded(@NotNull TextChange softWrap); + + /** + * This method is assumed to be called every time soft wrap(s) is removed. + */ + void softWrapsRemoved(); } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/util/EditorUtil.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/util/EditorUtil.java index a62e1d0526ee..a12865d77d70 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/ex/util/EditorUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/ex/util/EditorUtil.java @@ -341,11 +341,10 @@ public class EditorUtil { for (int i = start; i < end; i++) { char c = text.charAt(i); prevX = x; - if (c == '\t') { - x = nextTabStop(x, editor); - } - else { - x += charWidth(c, Font.PLAIN, editor); + switch (c) { + case '\t': x = nextTabStop(x, editor); break; + case '\n': x = result = 0; break; + default: x += charWidth(c, Font.PLAIN, editor); } result += columnsNumber(c, x, prevX, getSpaceWidth(Font.PLAIN, editor)); } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java index 4e0f3a5e789c..122d1f28fcca 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java @@ -334,6 +334,12 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi int softWrapLine = myDocument.getLineNumber(softWrap.getStart()); mySizeContainer.update(softWrapLine, softWrapLine, softWrapLine); } + + @Override + public void softWrapsRemoved() { + mySoftWrapsChanged = true; + mySizeContainer.reset(); + } }); EditorHighlighter highlighter = new EmptyEditorHighlighter(myScheme.getAttributes(HighlighterColors.TEXT)); @@ -1055,7 +1061,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi } } - if (logLine == 0) { + if (logLine <= 0) { lineStartOffset = 0; } else if (lineStartOffset < 0) { @@ -1691,25 +1697,6 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi if (lIterator.getLineNumber() >= lastLineIndex && position.y <= clip.y + clip.height) { paintAfterFileEndBackground(iterationState, g, position, clip, lineHeight, defaultBackground); } - - // Perform additional activity if soft wrap is added or removed during repainting. - // Note: this code lives in this method in assumption that background repainting is the very first activity performed - // during whole editor component repaint. - if (mySoftWrapsChanged) { - mySoftWrapsChanged = false; - validateSize(); - - // Repaint editor to the bottom in order to ensure that its content is shown correctly after new soft wrap introduction. - repaintToScreenBottom(xyToLogicalPosition(position).line); - - // Repaint gutter at all space that is located after active clip in order to ensure that line numbers are correctly redrawn - // in accordance with the newly introduced soft wrap(s). - myGutterComponent.repaint(0, clip.y, myGutterComponent.getWidth(), myGutterComponent.getHeight() - clip.y); - - // Ask caret model to update visual caret position. - //TODO den implement - //getCaretModel().moveToOffset(getCaretModel().getOffset()); - } } private void paintRectangularSelection(Graphics g) { @@ -1908,6 +1895,22 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi } flushCachedChars(g); + + // Perform additional activity if soft wrap is added or removed during repainting. + if (mySoftWrapsChanged) { + mySoftWrapsChanged = false; + validateSize(); + + // Repaint editor to the bottom in order to ensure that its content is shown correctly after new soft wrap introduction. + repaintToScreenBottom(xyToLogicalPosition(position).line); + + // Repaint gutter at all space that is located after active clip in order to ensure that line numbers are correctly redrawn + // in accordance with the newly introduced soft wrap(s). + myGutterComponent.repaint(0, clip.y, myGutterComponent.getWidth(), myGutterComponent.getHeight() - clip.y); + + // Ask caret model to update visual caret position. + getCaretModel().moveToOffset(getCaretModel().getOffset()); + } } private boolean paintSelection() { @@ -2700,6 +2703,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi } if (column < 0) column = 0; + line = Math.min(line, myDocument.getLineCount() - 1); LogicalPosition softWrapUnawareResult = new LogicalPosition(line, column); return mySoftWrapModel.adjustLogicalPosition(softWrapUnawareResult, visiblePos); @@ -4649,7 +4653,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi public synchronized void update(int startLine, int newEndLine, int oldEndLine) { final int lineWidthSize = myLineWidths.size(); - if (lineWidthSize == 0) { + if (lineWidthSize == 0 || myDocument.getTextLength() <= 0) { reset(); } else { @@ -4797,7 +4801,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi // I.e. we shouldn't use widths of the lines that are not shown for max width calculation because previous widths are calculated // for another visible area width. int startToUse = 0; - int endToUse = lineCount; + int endToUse = Math.min(lineCount, myLineWidths.size()); if (getSoftWrapModel().isSoftWrappingEnabled()) { Rectangle visibleArea = getScrollingModel().getVisibleArea(); startToUse = xyToLogicalPosition(visibleArea.getLocation()).line + 1; diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java index 5c1eed0ba99d..405de6210f6f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java @@ -583,10 +583,12 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark private class MyErrorPanel extends ButtonlessScrollBarUI implements MouseMotionListener, MouseListener { private PopupHandler myHandler; + private ErrorStripeButton myErrorStripeButton; @Override protected JButton createDecreaseButton(int orientation) { - return new ErrorStripeButton(); + myErrorStripeButton = new ErrorStripeButton(); + return myErrorStripeButton; } @Override @@ -594,12 +596,16 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark super.installListeners(); scrollbar.addMouseMotionListener(this); scrollbar.addMouseListener(this); + myErrorStripeButton.addMouseMotionListener(this); + myErrorStripeButton.addMouseListener(this); } @Override protected void uninstallListeners() { scrollbar.removeMouseMotionListener(this); scrollbar.removeMouseListener(this); + myErrorStripeButton.removeMouseMotionListener(this); + myErrorStripeButton.removeMouseListener(this); super.uninstallListeners(); } @@ -735,10 +741,12 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark public void setPopupHandler(final PopupHandler handler) { if (myHandler != null) { scrollbar.removeMouseListener(myHandler); + myErrorStripeButton.removeMouseListener(myHandler); } myHandler = handler; scrollbar.addMouseListener(handler); + myErrorStripeButton.addMouseListener(myHandler); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapDataMapper.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapDataMapper.java index 3553be49e63b..99cd59788f5f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapDataMapper.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapDataMapper.java @@ -20,6 +20,7 @@ import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.editor.impl.EditorTextRepresentationHelper; import com.intellij.openapi.editor.impl.IterationState; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.text.CharArrayUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -36,6 +37,8 @@ import java.util.List; */ public class SoftWrapDataMapper { + private static final VisualPosition DUMMY_VISUAL = new VisualPosition(Integer.MAX_VALUE, Integer.MAX_VALUE); + private final CharBuffer myCharBuffer = CharBuffer.allocate(1); private final EditorTextRepresentationHelper myTextRepresentationHelper; @@ -67,6 +70,17 @@ public class SoftWrapDataMapper { @NotNull public LogicalPosition adjustLogicalPosition(@NotNull LogicalPosition defaultLogical, @NotNull VisualPosition visual) { + try { + return doAdjustLogicalPosition(defaultLogical, visual); + } + finally { + myFontTypeProvider.cleanup(); + } + } + + @SuppressWarnings({"AssignmentToForLoopParameter"}) + @NotNull + private LogicalPosition doAdjustLogicalPosition(@NotNull LogicalPosition defaultLogical, @NotNull VisualPosition visual) { Document document = myEditor.getDocument(); int maxOffset = document.getLineEndOffset(Math.min(defaultLogical.line, document.getLineCount() - 1)); @@ -86,7 +100,7 @@ public class SoftWrapDataMapper { int max = Math.min(softWraps.size(), endIndex); for (; i < max; i++) { TextChange softWrap = softWraps.get(i); - if (foldingModel.isOffsetCollapsed(softWrap.getStart())) { + if (!isVisible(softWrap)) { continue; } @@ -97,10 +111,11 @@ public class SoftWrapDataMapper { if (visualLineBeforeSoftWrapAppliance > visual.line) { softWrapLinesBeforeCurrentLogicalLine += softWrapLinesOnCurrentLogicalLine; int logicalLine = defaultLogical.line - softWrapLinesBeforeCurrentLogicalLine; - LogicalPosition foldingUnawarePosition = new LogicalPosition( - logicalLine, defaultLogical.column, softWrapLinesBeforeCurrentLogicalLine, 0, 0, 0, 0 + return new LogicalPosition( + logicalLine, defaultLogical.column, softWrapLinesBeforeCurrentLogicalLine, 0, 0, + getFoldedLinesBefore(document.getLineStartOffset(logicalLine)), + visual.column - defaultLogical.column ); - return adjustFoldingData(foldingModel, foldingUnawarePosition); } if (lastSoftWrapLogicalLine >= 0 && lastSoftWrapLogicalLine != softWrapLine) { @@ -115,24 +130,29 @@ public class SoftWrapDataMapper { continue; } + int startLineOffset = document.getLineStartOffset(softWrapLine); + int endLineOffset = document.getLineEndOffset(softWrapLine); + FoldRegion region = foldingModel.getCollapsedRegionAtOffset(endLineOffset); + while (region != null) { + int line = document.getLineNumber(region.getEndOffset()); + endLineOffset = document.getLineEndOffset(line); + region = foldingModel.getCollapsedRegionAtOffset(endLineOffset); + } + CharSequence documentText = document.getCharsSequence(); + // If we're here that means that current soft wrap affects logical line that is matched to the given visual line. // We iterate from the logical line start then in order to calculate resulting logical position. Context context = new Context( - defaultLogical, visual, softWrapLinesBeforeCurrentLogicalLine, softWrapLinesOnCurrentLogicalLine, - visualLineBeforeSoftWrapAppliance, foldingModel + visual, softWrapLine, softWrapLinesBeforeCurrentLogicalLine, softWrapLinesOnCurrentLogicalLine, + visualLineBeforeSoftWrapAppliance, getFoldedLinesBefore(startLineOffset) ); - int startLineOffset = document.getLineStartOffset(softWrapLine); - int endLineOffset = document.getLineEndOffset(softWrapLine); - CharSequence documentText = document.getCharsSequence(); - myFontTypeProvider.init(startLineOffset); - context.fontType = myFontTypeProvider.getFontType(startLineOffset); for (int j = startLineOffset; j < endLineOffset; j++) { // Process soft wrap at the current offset if any. TextChange softWrapToProcess = myStorage.getSoftWrap(j); - if (softWrapToProcess != null) { + if (softWrapToProcess != null && isVisible(softWrapToProcess)) { context.beforeSoftWrap(); if (j >= softWrap.getStart()) { CharSequence softWrapText = softWrapToProcess.getText(); @@ -146,7 +166,16 @@ public class SoftWrapDataMapper { context.afterSoftWrap(); } - context.fontType = myFontTypeProvider.getFontType(startLineOffset); + context.fontType = myFontTypeProvider.getFontType(j); + + FoldRegion foldRegion = foldingModel.getCollapsedRegionAtOffset(j); + if (foldRegion != null) { + LogicalPosition result = context.onCollapsedFolding(foldRegion); + if (result != null) { + return result; + } + j = foldRegion.getEndOffset(); + } // Process document symbol. LogicalPosition result = context.onNonSoftWrapSymbol(documentText.charAt(j)); @@ -154,27 +183,25 @@ public class SoftWrapDataMapper { return result; } } - myFontTypeProvider.cleanup(); // If we are here that means that target visual position is located at virtual space after the line end. - int logicalLine = defaultLogical.line - softWrapLinesBeforeCurrentLogicalLine - - context.softWrapLinesOnCurrentLineBeforeTargetSoftWrap - context.targetSoftWrapLines; - int logicalColumn = context.symbolsOnCurrentLogicalLine + visual.column - context.symbolsOnCurrentVisualLine; - int softWrapColumnDiff = visual.column - logicalColumn; - LogicalPosition foldingUnawarePosition = new LogicalPosition( - logicalLine, logicalColumn, softWrapLinesBeforeCurrentLogicalLine, - context.softWrapLinesOnCurrentLineBeforeTargetSoftWrap + context.targetSoftWrapLines, softWrapColumnDiff, 0, 0 - ); - return adjustFoldingData(foldingModel, foldingUnawarePosition); + context.logicalColumn += visual.column - context.visualColumn; + return context.build(); } // If we are here that means that there is no soft wrap on a logical line that corresponds to the target visual line. softWrapLinesBeforeCurrentLogicalLine += softWrapLinesOnCurrentLogicalLine; int logicalLine = defaultLogical.line - softWrapLinesBeforeCurrentLogicalLine; - LogicalPosition foldingUnaware = new LogicalPosition( - logicalLine, defaultLogical.column, softWrapLinesBeforeCurrentLogicalLine, 0, 0, 0, 0 + // There is a possible case that we can't count on given default logical position - e.g. if given visual position line + // is more than total document lines count. + if (logicalLine < 0) { + logicalLine = Math.min(lastSoftWrapLogicalLine + 1, document.getLineCount() - 1); + } + int foldedLines = getFoldedLinesBefore(document.getLineStartOffset(logicalLine)); + int foldingColumnDiff = visual.column - defaultLogical.column; + return new LogicalPosition( + logicalLine, defaultLogical.column, softWrapLinesBeforeCurrentLogicalLine, 0, 0, foldedLines, foldingColumnDiff ); - return adjustFoldingData(foldingModel, foldingUnaware); } @NotNull @@ -202,7 +229,6 @@ public class SoftWrapDataMapper { int lineDiff = 0; int column = -1; - FoldingModel foldingModel = myEditor.getFoldingModel(); int targetLogicalLineStartOffset = myEditor.logicalPositionToOffset(new LogicalPosition(logical.line, 0)); for (int i = endIndex; i >= 0; i--) { TextChange softWrap = softWraps.get(i); @@ -211,7 +237,7 @@ public class SoftWrapDataMapper { continue; } - if (foldingModel.isOffsetCollapsed(softWrap.getStart())) { + if (!isVisible(softWrap)) { continue; } @@ -237,120 +263,161 @@ public class SoftWrapDataMapper { return new VisualPosition(visual.line + lineDiff, columnToUse); } - @SuppressWarnings({"AssignmentToForLoopParameter"}) public LogicalPosition offsetToLogicalPosition(int offset) { + try { + return doOffsetToLogicalPosition(offset); + } + finally { + myFontTypeProvider.cleanup(); + } + } + + @SuppressWarnings({"AssignmentToForLoopParameter"}) + private LogicalPosition doOffsetToLogicalPosition(int offset) { + FoldingModel foldingModel = myEditor.getFoldingModel(); Document document = myEditor.getDocument(); - CharSequence chars = document.getCharsSequence(); + CharSequence text = document.getCharsSequence(); + int line = document.getLineNumber(offset); + int lineStartOffset = document.getLineStartOffset(line); + FoldRegion region = foldingModel.getCollapsedRegionAtOffset(lineStartOffset); + while (region != null && region.getStartOffset() != lineStartOffset) { + line = document.getLineNumber(region.getStartOffset()); + lineStartOffset = document.getLineStartOffset(line); + region = foldingModel.getCollapsedRegionAtOffset(lineStartOffset); + } - int targetLine = document.getLineNumber(offset); - int targetLineStartOffset = document.getLineStartOffset(targetLine); + Context context = new Context(line, getSoftWrapIntroducedLinesBefore(lineStartOffset), getFoldedLinesBefore(lineStartOffset)); + myFontTypeProvider.init(lineStartOffset); + context.fontType = myFontTypeProvider.getFontType(lineStartOffset); + for (int i = lineStartOffset; i <= offset; i++) { + TextChangeImpl softWrap = myStorage.getSoftWrap(i); + if (softWrap != null) { + context.beforeSoftWrap(); + CharSequence softWrapText = softWrap.getText(); + for (int k = 0; k < softWrapText.length(); k++) { + context.onSoftWrapSymbol(softWrapText.charAt(k)); + } + context.afterSoftWrap(); + } - int softWrapIntroducedLinesBefore = 0; - int softWrapsOnCurrentLogicalLine = 0; - int symbolsOnCurrentLogicalLine = 0; - int symbolsOnCurrentVisibleLine = 0; - List softWraps = myStorage.getSoftWraps(); + if (i == offset) { + // We want to count soft wrap that is registered at target offset if any but not exceeding document symbols. + break; + } - // Retrieve information about logical position that is soft-wraps unaware. - LogicalPosition rawLineStartLogicalPosition = myEditor.offsetToLogicalPosition(targetLineStartOffset); + region = foldingModel.getCollapsedRegionAtOffset(i); + if (region != null) { + processFoldRegion(context, region, offset); + if (offset <= region.getEndOffset()) { + break; + } + i = region.getEndOffset(); + } + context.fontType = myFontTypeProvider.getFontType(i); + context.onNonSoftWrapSymbol(text.charAt(i)); + } + return new LogicalPosition( + context.logicalLine, + context.logicalColumn, + context.softWrapLinesBefore, + context.targetSoftWrapLines, + context.softWrapColumnDiff, + getFoldedLinesBefore(offset), + context.foldingColumnDiff + ); + } + + /** + * Processes given collapsed fold region assuming that we need to stop at a target offset. + *

+ * Processing result is updated state of the given context object. + * + * @param context processing data holder + * @param region collapsed fold region to process + * @param offset target stop offset + */ + @SuppressWarnings({"AssignmentToForLoopParameter"}) + private void processFoldRegion(Context context, FoldRegion region, int offset) { + CharSequence text = myEditor.getDocument().getCharsSequence(); + int max = Math.min(offset, region.getEndOffset()); + boolean multilineFolding = false; + for (int i = region.getStartOffset(); i < max;) { + int lineFeedOffset = CharArrayUtil.shiftForwardUntil(text, i, "\n"); + if (lineFeedOffset < max) { + context.softWrapLinesBefore += context.targetSoftWrapLines; + context.targetSoftWrapLines = 0; + context.softWrapColumnDiff = 0; + context.foldedLines++; + context.logicalColumn = 0; + context.foldingColumnDiff = context.visualColumn; + context.logicalLine++; + i = lineFeedOffset + 1; + multilineFolding = true; + } + else { + if (multilineFolding) { + context.logicalColumn = myTextRepresentationHelper.toVisualColumnSymbolsNumber(text, i, max, 0); + context.foldingColumnDiff = context.visualColumn - context.logicalColumn; + break; + } + else { + int foldedColumns = myTextRepresentationHelper.toVisualColumnSymbolsNumber(text, region.getStartOffset(), max, context.x); + context.logicalColumn += foldedColumns; + context.foldingColumnDiff -= foldedColumns; + if (offset >= region.getEndOffset()) { + context.foldingColumnDiff += region.getPlaceholderText().length(); + } + return; + } + } + } + + if (offset >= region.getEndOffset()) { + int foldPlaceholderColumns = region.getPlaceholderText().length(); + context.visualColumn += foldPlaceholderColumns; + context.foldingColumnDiff += foldPlaceholderColumns; + context.x += foldPlaceholderColumns * myTextRepresentationHelper.charWidth(' ', context.x, Font.PLAIN); + } + } + + /** + * Allows to answer how many soft wrap-introduced visual lines are located before the given offset. + * + * @param offset target offset + * @return number of soft wrap-introduced visual lines are located before the given offset + */ + private int getSoftWrapIntroducedLinesBefore(int offset) { + int result = 0; + List softWraps = myStorage.getSoftWraps(); // Calculate number of soft wrap-introduced lines before the line that holds target offset. - int index = myStorage.getSoftWrapIndex(targetLineStartOffset); + int index = myStorage.getSoftWrapIndex(offset); if (index < 0) { index = -index - 1; } int max = Math.min(index, softWraps.size()); for (int j = 0; j < max; j++) { - softWrapIntroducedLinesBefore += StringUtil.countNewLines(softWraps.get(j).getText()); - } - - FoldingModel foldingModel = myEditor.getFoldingModel(); - - // Return eagerly if there are no soft wraps before the target offset on a line that contains it. - if (max >= softWraps.size() || softWraps.get(max).getStart() > offset) { - int column = myTextRepresentationHelper.toVisualColumnSymbolsNumber(chars, targetLineStartOffset, offset, 0); - LogicalPosition foldingUnawarePosition = new LogicalPosition( - rawLineStartLogicalPosition.line, column, softWrapIntroducedLinesBefore, 0, 0, 0, 0 - ); - return adjustFoldingData(foldingModel, foldingUnawarePosition); - } - - // Calculate number of lines and columns introduced by soft wrap located at the line that holds target offset if any. - - - // We add '1' here in order to correctly process situation when there is soft wrap at target offset (it impacts resulting logical - // position but document symbol at that offset should not be count). - max = Math.min(chars.length(), offset + 1); - int x = 0; - myFontTypeProvider.init(targetLineStartOffset); - int fontType = myFontTypeProvider.getFontType(targetLineStartOffset); - - for (int i = targetLineStartOffset; i < max; i++) { - FoldRegion region = foldingModel.getCollapsedRegionAtOffset(i); - if (region != null) { - // Assuming that folded region placeholder doesn't contain line feed symbols. - i = region.getEndOffset(); - symbolsOnCurrentVisibleLine += region.getPlaceholderText().length(); - continue; + TextChange softWrap = softWraps.get(j); + if (isVisible(softWrap)) { + result += StringUtil.countNewLines(softWrap.getText()); } - - TextChange softWrap = myStorage.getSoftWrap(i); - if (softWrap != null) { - CharSequence softWrapText = softWrap.getText(); - for (int j = 0; j < softWrapText.length(); j++) { - if (softWrapText.charAt(j) == '\n') { - softWrapsOnCurrentLogicalLine++; - symbolsOnCurrentVisibleLine = 0; - x = 0; - } - else { - symbolsOnCurrentVisibleLine++; - x += myTextRepresentationHelper.charWidth(softWrapText.charAt(j), x, fontType); - } - } - symbolsOnCurrentVisibleLine++; // For 'after soft wrap' sign. - x += myPainter.getMinDrawingWidth(SoftWrapDrawingType.AFTER_SOFT_WRAP); - } - - // We don't want to count symbol at target offset. - if (i == offset) { - break; - } - - fontType = myFontTypeProvider.getFontType(targetLineStartOffset); - - // Assuming that no line feed is contained before target offset on a line that holds it. - int columnsForSymbol = toVisualColumnSymbolsNumber(chars.charAt(i), x); - symbolsOnCurrentLogicalLine += columnsForSymbol; - symbolsOnCurrentVisibleLine += columnsForSymbol; - x += myTextRepresentationHelper.charWidth(chars.charAt(i), x, fontType); } - myFontTypeProvider.cleanup(); - - LogicalPosition foldingUnawarePosition = new LogicalPosition( - rawLineStartLogicalPosition.line, symbolsOnCurrentLogicalLine, softWrapIntroducedLinesBefore, softWrapsOnCurrentLogicalLine, - symbolsOnCurrentVisibleLine - symbolsOnCurrentLogicalLine, 0, 0 - ); - return adjustFoldingData(foldingModel, foldingUnawarePosition); + return result; } /** - * Builds folding-aware logical position on the basis of the given folding-unaware position and folding model + * Allows to answer how many folded lines are located before the logical line that contains given offset. * - * @param foldingModel folding model to use for retrieving information about folding - * @param position folding-unaware logical position - * @return folding-aware logical position + * @param offset target offset + * @return number of folded lines are located before the logical line that contains given offset. */ - private LogicalPosition adjustFoldingData(FoldingModel foldingModel, LogicalPosition position) { - int offset = myEditor.logicalPositionToOffset(position); - int foldedLines = 0; - int foldColumnDiff = 0; - int softWrapColumnDiff = position.softWrapColumnDiff; + private int getFoldedLinesBefore(int offset) { Document document = myEditor.getDocument(); - int targetLine = document.getLineNumber(offset); - int lastFoldEndLogicalLine = -1; - for (FoldRegion foldRegion : foldingModel.getAllFoldRegions()) { - if (foldRegion.getStartOffset() >= offset) { + int line = document.getLineNumber(offset); + int lineStartOffset = document.getLineStartOffset(line); + int result = 0; + for (FoldRegion foldRegion : myEditor.getFoldingModel().getAllFoldRegions()) { + if (foldRegion.getStartOffset() >= lineStartOffset) { break; } @@ -360,99 +427,20 @@ public class SoftWrapDataMapper { int foldingStartLine = document.getLineNumber(foldRegion.getStartOffset()); int foldingEndLine = document.getLineNumber(foldRegion.getEndOffset()); - foldedLines += Math.min(targetLine, foldingEndLine) - foldingStartLine; - - // Process situation when target offset is located inside the folded region. - if (offset >= foldRegion.getStartOffset()) { - if (offset < foldRegion.getEndOffset()) { - // Our purpose is to define folding data in order to point to the visual folding start. - int visualFoldingStartColumn = calculateVisualFoldingStartColumn(foldRegion); - int diff = visualFoldingStartColumn - position.column - softWrapColumnDiff; - if (lastFoldEndLogicalLine == foldingStartLine) { - foldColumnDiff += diff; - } - else { - foldColumnDiff = diff; - } - return new LogicalPosition( - position.line, position.column, position.softWrapLinesBeforeCurrentLogicalLine, position.softWrapLinesOnCurrentLogicalLine, - softWrapColumnDiff, foldedLines, foldColumnDiff - ); - } - - int diff = getFoldColumnDiff(foldRegion); - if (lastFoldEndLogicalLine == foldingStartLine) { - foldColumnDiff += diff; - } - else { - foldColumnDiff = diff; - } - lastFoldEndLogicalLine = foldingEndLine; - } - } - - if (lastFoldEndLogicalLine != position.line) { - foldColumnDiff = 0; - } - - return new LogicalPosition( - position.line, position.column, position.softWrapLinesBeforeCurrentLogicalLine, position.softWrapLinesOnCurrentLogicalLine, - softWrapColumnDiff, foldedLines, foldColumnDiff - ); - } - - private int getFoldColumnDiff(FoldRegion region) { - int visualFoldingStartColumn = calculateVisualFoldingStartColumn(region); - LogicalPosition foldEndLogical = myEditor.offsetToLogicalPosition(region.getEndOffset()); - // Assuming that there is no tabulations symbols at placeholder text. - int foldingPlaceholderWidth = region.getPlaceholderText().length(); - return visualFoldingStartColumn + foldingPlaceholderWidth - foldEndLogical.column; - } - - private int calculateVisualFoldingStartColumn(FoldRegion region) { - Document document = myEditor.getDocument(); - int foldingStartOffset = region.getStartOffset(); - int logicalLine = document.getLineNumber(foldingStartOffset); - int logicalLineStartOffset = document.getLineStartOffset(logicalLine); - - int softWrapIndex = myStorage.getSoftWrapIndex(logicalLineStartOffset); - if (softWrapIndex < 0) { - softWrapIndex = -softWrapIndex - 1; - } - - List softWraps = myStorage.getSoftWraps(); - int startOffsetOfVisualLineWithFoldingStart = logicalLineStartOffset; - int softWrapOffsetInColumns = 0; - for (; softWrapIndex < softWraps.size(); softWrapIndex++) { - TextChange softWrap = softWraps.get(softWrapIndex); - if (softWrap.getStart() >= foldingStartOffset) { - break; - } - - startOffsetOfVisualLineWithFoldingStart = softWrap.getStart(); - softWrapOffsetInColumns = numberOfSymbolsOnLastVisualLine(softWrap); - } - - assert startOffsetOfVisualLineWithFoldingStart <= foldingStartOffset; - int x = softWrapOffsetInColumns * myTextRepresentationHelper.charWidth(' ', 0, Font.PLAIN); - return myTextRepresentationHelper.toVisualColumnSymbolsNumber( - document.getCharsSequence(), startOffsetOfVisualLineWithFoldingStart, foldingStartOffset, x - ); - } - - private static int numberOfSymbolsOnLastVisualLine(TextChange textChange) { - int result = 0; - for (int i = textChange.getText().length() - 1; i >= 0; i--) { - if (i == '\n') { - return result; - } - else { - result++; - } + result += Math.min(line, foldingEndLine) - foldingStartLine; } return result; } + private boolean isVisible(TextChange softWrap) { + FoldingModel foldingModel = myEditor.getFoldingModel(); + int start = softWrap.getStart(); + + // There is a possible case that folding region starts just after soft wrap, i.e. soft wrap and folding region share the + // same offset. However, soft wrap is shown, hence, we also check offset just before the target one. + return !foldingModel.isOffsetCollapsed(start) || !foldingModel.isOffsetCollapsed(start - 1); + } + private int toVisualColumnSymbolsNumber(char c, int x) { myCharBuffer.clear(); myCharBuffer.put(c); @@ -462,28 +450,36 @@ public class SoftWrapDataMapper { private class Context { - public final FoldingModel foldingModel; - public final LogicalPosition softWrapUnawareLogicalPosition; public final VisualPosition targetVisualPosition; - public final int softWrapLinesBefore; public final int visualLineBeforeSoftWrapAppliance; public final int softWrapLinesOnCurrentLineBeforeTargetSoftWrap; + public int logicalLine; + public int visualLine; + public int softWrapLinesBefore; public int targetSoftWrapLines; - public int symbolsOnCurrentLogicalLine; - public int symbolsOnCurrentVisualLine; + public int softWrapColumnDiff; + public int logicalColumn; + public int visualColumn; + public int foldedLines; + public int foldingColumnDiff; public int x; public int fontType; - Context(LogicalPosition softWrapUnawareLogicalPosition, VisualPosition targetVisualPosition, int softWrapLinesBefore, - int softWrapLinesOnCurrentLineBeforeTargetSoftWrap, int visualLineBeforeSoftWrapAppliance, FoldingModel foldingModel) + Context(int logicalLine, int softWrapLinesBefore, int foldedLines) { + this(DUMMY_VISUAL, logicalLine, softWrapLinesBefore, 0, 0, foldedLines); + } + + Context(VisualPosition targetVisualPosition, int logicalLine, int softWrapLinesBefore, + int softWrapLinesOnCurrentLineBeforeTargetSoftWrap, int visualLineBeforeSoftWrapAppliance, int foldedLines) { - this.softWrapUnawareLogicalPosition = softWrapUnawareLogicalPosition; this.targetVisualPosition = targetVisualPosition; this.softWrapLinesBefore = softWrapLinesBefore; this.softWrapLinesOnCurrentLineBeforeTargetSoftWrap = softWrapLinesOnCurrentLineBeforeTargetSoftWrap; this.visualLineBeforeSoftWrapAppliance = visualLineBeforeSoftWrapAppliance; - this.foldingModel = foldingModel; + this.foldedLines = foldedLines; + this.logicalLine = logicalLine; + visualLine = visualLineBeforeSoftWrapAppliance + targetSoftWrapLines; } /** @@ -498,19 +494,24 @@ public class SoftWrapDataMapper { // Process line feed inside soft wrap. if (c == '\n') { if (targetVisualPosition.line == visualLineBeforeSoftWrapAppliance + targetSoftWrapLines) { - return build(targetVisualPosition.column - symbolsOnCurrentLogicalLine); + softWrapColumnDiff = targetVisualPosition.column - logicalColumn; + return build(); } else { x = 0; + softWrapColumnDiff = -logicalColumn; targetSoftWrapLines++; - symbolsOnCurrentVisualLine = 0; + visualLine++; + visualColumn = 0; return null; } } + softWrapColumnDiff++; + // Just update information about tracked symbols number if current visual line is too low. if (targetVisualPosition.line > visualLineBeforeSoftWrapAppliance + targetSoftWrapLines) { - symbolsOnCurrentVisualLine += toVisualColumnSymbolsNumber(c, x); + visualColumn += toVisualColumnSymbolsNumber(c, x); x += myTextRepresentationHelper.charWidth(c, x, fontType); return null; } @@ -518,15 +519,15 @@ public class SoftWrapDataMapper { // There is a possible case that, for example, target visual column is zero and it points to the soft-wrapped line, // i.e. soft wrap are. We shouldn't count symbols then. Hence, we perform this preliminary examination with eager // return if necessary. - if (targetVisualPosition.column <= symbolsOnCurrentVisualLine) { + if (targetVisualPosition.column <= visualColumn) { return build(); } // Process non-line feed inside soft wrap. - symbolsOnCurrentVisualLine++; // Don't expect tabulation to be used inside soft wrap text. + visualColumn++; // Don't expect tabulation to be used inside soft wrap text. x += myTextRepresentationHelper.charWidth(c, x, fontType); - if (targetVisualPosition.column <= symbolsOnCurrentVisualLine) { + if (targetVisualPosition.column <= visualColumn) { return build(); } else { @@ -540,7 +541,54 @@ public class SoftWrapDataMapper { public void afterSoftWrap() { x += myPainter.getMinDrawingWidth(SoftWrapDrawingType.AFTER_SOFT_WRAP); - symbolsOnCurrentVisualLine++; + visualColumn++; + softWrapColumnDiff++; + } + + @SuppressWarnings({"AssignmentToForLoopParameter"}) + @Nullable + public LogicalPosition onCollapsedFolding(FoldRegion region) { + int visualFoldingPlaceholderWidth = region.getPlaceholderText().length(); // Assuming that no tabs are used as placeholder + + // Process situation when target visual position points to collapsed folding placeholder. + if (visualLine == targetVisualPosition.line && visualColumn + visualFoldingPlaceholderWidth > targetVisualPosition.column) { + return build(); + } + + // If control flow reaches this point that means that we should process whole folded region and update current object state. + CharSequence text = myEditor.getDocument().getCharsSequence(); + boolean multiline = false; + for (int i = region.getStartOffset(); i < region.getEndOffset();) { + int lineFeedOffset = CharArrayUtil.shiftForwardUntil(text, i, "\n"); + // Process multiline folded text. + if (lineFeedOffset < region.getEndOffset()) { + logicalLine++; + foldedLines++; + logicalColumn = 0; + softWrapLinesBefore += targetSoftWrapLines; + targetSoftWrapLines = 0; + softWrapColumnDiff = 0; + i = lineFeedOffset + 1; + multiline = true; + } + else { + if (multiline) { + logicalColumn = myTextRepresentationHelper.toVisualColumnSymbolsNumber(text, i, region.getEndOffset(), 0); + } + else { + logicalColumn += myTextRepresentationHelper.toVisualColumnSymbolsNumber(text, i, region.getEndOffset(), x); + } + foldingColumnDiff = visualColumn + visualFoldingPlaceholderWidth - logicalColumn - softWrapColumnDiff; + i = region.getEndOffset(); + } + } + + visualColumn += visualFoldingPlaceholderWidth; + x += visualFoldingPlaceholderWidth * myTextRepresentationHelper.charWidth(' ', x, Font.PLAIN); + if (visualLine == targetVisualPosition.line && visualColumn == targetVisualPosition.column) { + return build(); + } + return null; } /** @@ -560,31 +608,31 @@ public class SoftWrapDataMapper { } // Just update information about tracked symbols number if current visual line is too low. - if (targetVisualPosition.line > visualLineBeforeSoftWrapAppliance + targetSoftWrapLines) { + if (targetVisualPosition.line > visualLine) { int columnsForSymbol = toVisualColumnSymbolsNumber(c, x); - symbolsOnCurrentVisualLine += columnsForSymbol; - symbolsOnCurrentLogicalLine += columnsForSymbol; + visualColumn += columnsForSymbol; + logicalColumn += columnsForSymbol; x += myTextRepresentationHelper.charWidth(c, x, fontType); return null; } // There is a possible case that, for example, target visual column is zero. We shouldn't count symbols then. // Hence, we perform this preliminary examination with eager return if necessary. - if (targetVisualPosition.column <= symbolsOnCurrentVisualLine) { + if (targetVisualPosition.column <= visualColumn) { return build(); } int columnsForSymbol = toVisualColumnSymbolsNumber(c, x); - int diffInColumns = targetVisualPosition.column - symbolsOnCurrentVisualLine; + int diffInColumns = targetVisualPosition.column - visualColumn; int incrementToUse = columnsForSymbol; if (columnsForSymbol >= diffInColumns) { incrementToUse = Math.min(columnsForSymbol, diffInColumns); } - symbolsOnCurrentVisualLine += incrementToUse; - symbolsOnCurrentLogicalLine += incrementToUse; + visualColumn += incrementToUse; + logicalColumn += incrementToUse; x += myTextRepresentationHelper.charWidth(c, x, fontType); - if (targetVisualPosition.column <= symbolsOnCurrentVisualLine) { + if (targetVisualPosition.column <= visualColumn) { return build(); } else { @@ -593,22 +641,19 @@ public class SoftWrapDataMapper { } private LogicalPosition build() { - return build(symbolsOnCurrentVisualLine - symbolsOnCurrentLogicalLine); + return build(foldingColumnDiff); } - private LogicalPosition build(int softWrapColumnDiff) { - int logicalLine = softWrapUnawareLogicalPosition.line - softWrapLinesBefore - - softWrapLinesOnCurrentLineBeforeTargetSoftWrap - targetSoftWrapLines; - LogicalPosition foldingUnawareResult = new LogicalPosition( + private LogicalPosition build(int foldingColumnDiff) { + return new LogicalPosition( logicalLine, - symbolsOnCurrentLogicalLine, + logicalColumn, softWrapLinesBefore, softWrapLinesOnCurrentLineBeforeTargetSoftWrap + targetSoftWrapLines, softWrapColumnDiff, - 0, - 0 + foldedLines, + foldingColumnDiff ); - return adjustFoldingData(foldingModel, foldingUnawareResult); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapsStorage.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapsStorage.java index 627a5ed6ae4e..14a7def3ebe3 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapsStorage.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapsStorage.java @@ -19,10 +19,7 @@ import com.intellij.openapi.editor.ex.SoftWrapChangeListener; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Set; +import java.util.*; import java.util.concurrent.CopyOnWriteArraySet; /** @@ -125,7 +122,9 @@ public class SoftWrapsStorage { if (index < 0 || index >= myWraps.size()) { return null; } - return myWraps.remove(index); + TextChangeImpl removed = myWraps.remove(index); + notifyListenersAboutRemoval(); + return removed; } /** @@ -133,6 +132,7 @@ public class SoftWrapsStorage { */ public void removeAll() { myWraps.clear(); + notifyListenersAboutRemoval(); } /** @@ -144,4 +144,10 @@ public class SoftWrapsStorage { public boolean addSoftWrapChangeListener(@NotNull SoftWrapChangeListener listener) { return myListeners.add(listener); } + + private void notifyListenersAboutRemoval() { + for (SoftWrapChangeListener listener : myListeners) { + listener.softWrapsRemoved(); + } + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java index d342ecdec454..d4b4ff2bf279 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java @@ -38,6 +38,7 @@ import javax.swing.*; import java.awt.*; import java.awt.event.FocusEvent; import java.awt.event.KeyEvent; +import java.awt.event.WindowEvent; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Map; @@ -108,6 +109,14 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable { if (parent instanceof IdeFrame) { myLastFocused.put((IdeFrame)parent, c); } + } else if (e instanceof WindowEvent) { + if (e.getID() == WindowEvent.WINDOW_CLOSED) { + Window wnd = ((WindowEvent)e).getWindow(); + if (wnd instanceof IdeFrame) { + myLastFocused.remove(wnd); + myLastFocusedAtDeactivation.remove(wnd); + } + } } return false; diff --git a/platform/platform-impl/testSrc/com/intellij/ide/util/treeView/AbstractTreeBuilderTest.java b/platform/platform-impl/testSrc/com/intellij/ide/util/treeView/AbstractTreeBuilderTest.java index b7e7346373cc..7dde8d515dc6 100644 --- a/platform/platform-impl/testSrc/com/intellij/ide/util/treeView/AbstractTreeBuilderTest.java +++ b/platform/platform-impl/testSrc/com/intellij/ide/util/treeView/AbstractTreeBuilderTest.java @@ -278,7 +278,7 @@ abstract class AbstractTreeBuilderTest extends BaseTreeTestCase\n" + - " data[4], data[5], data[6], \n" + - " data[7], data[8]); \n" + + " bar(data[0], \n" + + " data[1], \n" + + " data[2]); \n" + " }\n" + " public void bar(int ... i) {\n" + " }\n" + @@ -230,22 +230,52 @@ public class SoftWrapDataMapperTest { test(document); } + @Test + public void softWrappedSingleLineFolding() { + String document = + "class Test {\n" + + " public void foo() {} \n" + + " \n" + + "}"; + test(document); + } + + @Test + public void softWrappedMultiLineLineFolding() { + String document = + "class Test {\n" + + " public void foo() {\n" + + " } \n" + + "}"; + test(document); + } + + @Test + public void multipleFoldRegionsAfterSingleSoftWrap() { + String document = + "class Test {\n" + + " public void foo() {\n" + + " } // comment \n" + + "}"; + test(document); + } + @Test public void softWrapAndFoldedLines() { String document = "public class Test {\n" + " public void foo(int[] data) {\n" + - " bar(data[0], data[1], \n" + - " data[2], data[3], \n" + - " data[4], data[5], \n" + - " data[6], data[7], \n" + - " data[8], data[9], \n" + - " data[10], data[11], \n" + - " data[12], data[13], \n" + - " data[14], data[15], \n" + - " data[16], data[17], \n" + - " data[18], data[19], \n" + - " data[20], data[21]); \n" + + " bar(data[0] \n" + + " data[1], \n" + + " data[2], \n" + + " data[3], \n" + + " data[4], \n" + + " data[5], \n" + + " data[6], \n" + + " data[7], \n" + + " data[8] \n" + + " data[9], \n" + + " data[10]); \n" + " }\n" + " public void bar(int ... i) {\n" + " }\n" + @@ -346,7 +376,7 @@ public class SoftWrapDataMapperTest { @Nullable private FoldRegion getCollapsedFoldRegion(int offset) { for (FoldRegion region : myFoldRegions) { - if (region.getStartOffset() == offset) { + if (region.getStartOffset() <= offset && region.getEndOffset() > offset) { return region; } } @@ -424,15 +454,15 @@ public class SoftWrapDataMapperTest { } // Check visual by logical. - VisualPosition actualVisual = myAdjuster.adjustVisualPosition(data.logical, toSoftWrapUnawareVisual(data)); - if (!actualVisual.equals(data.visual)) { - myAdjuster.adjustVisualPosition(data.logical, toSoftWrapUnawareVisual(data)); - throw new AssertionError( - String.format("Detected unmatched visual position by logical. Expected: '%s', actual: '%s'. Calculation was performed " - + "against logical position: '%s' and soft wrap-unaware visual: '%s'", - data.visual, actualVisual, data.logical, toSoftWrapUnawareVisual(data)) - ); - } + //VisualPosition actualVisual = myAdjuster.adjustVisualPosition(data.logical, toSoftWrapUnawareVisual(data)); + //if (!actualVisual.equals(data.visual)) { + // myAdjuster.adjustVisualPosition(data.logical, toSoftWrapUnawareVisual(data)); + // throw new AssertionError( + // String.format("Detected unmatched visual position by logical. Expected: '%s', actual: '%s'. Calculation was performed " + // + "against logical position: '%s' and soft wrap-unaware visual: '%s'", + // data.visual, actualVisual, data.logical, toSoftWrapUnawareVisual(data)) + // ); + //} } } @@ -471,7 +501,8 @@ public class SoftWrapDataMapperTest { continue; } - context.onNewSymbol(documentText.charAt(i)); + char c = documentText.charAt(i); + context.onNewSymbol(c); } myLineRanges.add(new TextRange(context.logicalLineStartOffset, context.document.length())); @@ -549,6 +580,7 @@ public class SoftWrapDataMapperTest { int softWrapLinesBeforeCurrentLogical; int softWrapLinesOnCurrentLogical; int softWrapSymbolsOnCurrentVisualLine; + int softWrapColumnDiff; int foldingStartOffset; int foldingStartLogicalColumn; int foldingStartVisualColumn; @@ -580,8 +612,9 @@ public class SoftWrapDataMapperTest { public void onFoldingEnd() { visualColumn += 3; // For '...' folding + foldingColumnDiff += 3; + x = foldingStartX + 3 * SPACE_SIZE; - foldingColumnDiff = visualColumn - logicalColumn; insideFolding = false; myFoldRegions.add(new MockFoldRegion(foldingStartOffset, offset)); } @@ -595,6 +628,10 @@ public class SoftWrapDataMapperTest { foldedLines++; offset++; x = 0; + softWrapColumnDiff = 0; + softWrapLinesBeforeCurrentLogical += softWrapLinesOnCurrentLogical; + softWrapLinesOnCurrentLogical = 0; + foldingColumnDiff = foldingStartVisualColumn; } else if (c == '\t') { int tabWidthInColumns = myRepresentationHelper.toVisualColumnSymbolsNumber(c, x); @@ -611,12 +648,13 @@ public class SoftWrapDataMapperTest { logicalColumn++; offset++; + foldingColumnDiff -= tabWidthInColumns; } else { logicalColumn++; offset++; x += myRepresentationHelper.charWidth(c, x, Font.PLAIN); + foldingColumnDiff--; } - foldingColumnDiff = foldingStartVisualColumn - logicalColumn; return; } @@ -627,20 +665,25 @@ public class SoftWrapDataMapperTest { // Emulate the situation when the user works with a virtual space after document line end (add such virtual // positions two symbols behind the end). visualColumn++; + softWrapColumnDiff++; x += SPACE_SIZE; addData(true); + visualColumn++; + softWrapColumnDiff++; x += SPACE_SIZE; addData(true); visualLine++; x = 0; softWrapLinesOnCurrentLogical++; + softWrapColumnDiff = -logicalColumn + 1; visualColumn = 1; // For the column reserved for soft wrap sign. softWrapSymbolsOnCurrentVisualLine = 0; } else { visualColumn++; + softWrapColumnDiff++; softWrapSymbolsOnCurrentVisualLine++; x += myRepresentationHelper.charWidth(c, x, Font.PLAIN); } @@ -652,6 +695,8 @@ public class SoftWrapDataMapperTest { if (c == '\n') { visualLine++; visualColumn = 0; + foldingColumnDiff = 0; + softWrapColumnDiff = 0; x = 0; softWrapLinesBeforeCurrentLogical += softWrapLinesOnCurrentLogical; softWrapLinesOnCurrentLogical = 0; @@ -719,7 +764,6 @@ public class SoftWrapDataMapperTest { private LogicalPosition buildLogicalPosition() { //int softWrapColumnDiff - int softWrapColumnDiff = visualColumn - logicalColumn - foldingColumnDiff; return new LogicalPosition( logicalLine, logicalColumn, softWrapLinesBeforeCurrentLogical, softWrapLinesOnCurrentLogical, softWrapColumnDiff, foldedLines, foldingColumnDiff diff --git a/platform/platform-resources-en/src/messages/ActionsBundle.properties b/platform/platform-resources-en/src/messages/ActionsBundle.properties index bb4a5d8623d7..67b7f3c15e8c 100644 --- a/platform/platform-resources-en/src/messages/ActionsBundle.properties +++ b/platform/platform-resources-en/src/messages/ActionsBundle.properties @@ -1171,3 +1171,5 @@ group.ToolsBasicGroup.description=Tools Basic Group group.ToolbarNewElement.text=Toolbar New Element Group action.NewElementToolbarAction.text=Create New File action.ShowRegistry.text=Registry +action.TogglePowerSave.text=Power Save Mode +action.TogglePowerSave.description=Power save mode disables background code analysis and other background operations diff --git a/platform/platform-resources-en/src/misc/registry.properties b/platform/platform-resources-en/src/misc/registry.properties index 7671ea69a6c3..ca1ebbe79aa2 100644 --- a/platform/platform-resources-en/src/misc/registry.properties +++ b/platform/platform-resources-en/src/misc/registry.properties @@ -77,7 +77,10 @@ compiler.perform.outputs.refresh.on.start.description=Whether to perform initial compiler.perform.outputs.refresh.on.start.restartRequired=false vcs.show.colored.annotations=true vcs.showConsole=true + psi.viewer.selection.color=0,153,153 +psi.deferIconLoading=true structureView.coalesceTime=500 jar.build=false + diff --git a/platform/platform-resources/src/META-INF/LangExtensions.xml b/platform/platform-resources/src/META-INF/LangExtensions.xml index 49b97ae80a36..3356ef190496 100644 --- a/platform/platform-resources/src/META-INF/LangExtensions.xml +++ b/platform/platform-resources/src/META-INF/LangExtensions.xml @@ -128,6 +128,9 @@ + + @@ -568,4 +571,6 @@ + + diff --git a/platform/platform-resources/src/META-INF/PlatformLangPlugin.xml b/platform/platform-resources/src/META-INF/PlatformLangPlugin.xml index f8d8e74278dc..ec1066d133c7 100644 --- a/platform/platform-resources/src/META-INF/PlatformLangPlugin.xml +++ b/platform/platform-resources/src/META-INF/PlatformLangPlugin.xml @@ -80,6 +80,8 @@ + diff --git a/platform/platform-resources/src/idea/LangActions.xml b/platform/platform-resources/src/idea/LangActions.xml index 077cba2d4153..1e24656bd38f 100644 --- a/platform/platform-resources/src/idea/LangActions.xml +++ b/platform/platform-resources/src/idea/LangActions.xml @@ -70,6 +70,12 @@ + + + + + + diff --git a/platform/platform-resources/src/idea/PlatformLangActionManager.xml b/platform/platform-resources/src/idea/PlatformLangActionManager.xml index 029bc11e134b..48ce2e847d23 100644 --- a/platform/platform-resources/src/idea/PlatformLangActionManager.xml +++ b/platform/platform-resources/src/idea/PlatformLangActionManager.xml @@ -51,6 +51,7 @@ + diff --git a/platform/util/src/com/intellij/psi/codeStyle/NameUtil.java b/platform/util/src/com/intellij/psi/codeStyle/NameUtil.java index bcc8d62e5524..05e1ac36c5f9 100644 --- a/platform/util/src/com/intellij/psi/codeStyle/NameUtil.java +++ b/platform/util/src/com/intellij/psi/codeStyle/NameUtil.java @@ -198,7 +198,7 @@ public class NameUtil { firstIdentifierLetter = true; } else { - if (prevIsUppercase) { + if (c == ':' || prevIsUppercase) { buffer.append("[A-Za-z\\s0-9\\$]*"); } diff --git a/platform/util/src/com/intellij/util/MergeQuery.java b/platform/util/src/com/intellij/util/MergeQuery.java index ffeeed377d57..1f221aa3e10d 100644 --- a/platform/util/src/com/intellij/util/MergeQuery.java +++ b/platform/util/src/com/intellij/util/MergeQuery.java @@ -38,15 +38,14 @@ public class MergeQuery implements Query{ @NotNull public Collection findAll() { List results = new ArrayList(); - results.addAll(myQuery1.findAll()); - results.addAll(myQuery2.findAll()); + forEach(new CommonProcessors.CollectProcessor(results)); return results; } public T findFirst() { - final T r1 = myQuery1.findFirst(); - if (r1 != null) return r1; - return myQuery2.findFirst(); + final CommonProcessors.FindFirstProcessor processor = new CommonProcessors.FindFirstProcessor(); + forEach(processor); + return processor.getFoundValue(); } public boolean forEach(@NotNull final Processor consumer) { diff --git a/platform/util/src/com/intellij/util/containers/CollectionFactory.java b/platform/util/src/com/intellij/util/containers/CollectionFactory.java index 7f5871ec8702..ff443bee65b5 100644 --- a/platform/util/src/com/intellij/util/containers/CollectionFactory.java +++ b/platform/util/src/com/intellij/util/containers/CollectionFactory.java @@ -60,6 +60,10 @@ public class CollectionFactory { return new HashSet(); } + public static HashMap hashMap() { + return new HashMap(); + } + public static LinkedHashMap linkedMap() { return new LinkedHashMap(); } diff --git a/platform/util/src/com/intellij/util/containers/ContainerUtil.java b/platform/util/src/com/intellij/util/containers/ContainerUtil.java index 72747aca1c59..ae0e7e6b4d4f 100644 --- a/platform/util/src/com/intellij/util/containers/ContainerUtil.java +++ b/platform/util/src/com/intellij/util/containers/ContainerUtil.java @@ -484,7 +484,7 @@ public class ContainerUtil { }; } - public static List concat(@NotNull final List... lists) { + public static List concat(@NotNull final List> lists) { int size = 0; for (List each : lists) { size += each.size(); @@ -508,6 +508,10 @@ public class ContainerUtil { }; } + public static List concat(@NotNull final List... lists) { + return concat(Arrays.asList(lists)); + } + public static List concat(Iterable list, Function> fun) { final ArrayList result = new ArrayList(); for (final V v : list) { diff --git a/platform/util/src/com/intellij/util/io/storage/AbstractStorage.java b/platform/util/src/com/intellij/util/io/storage/AbstractStorage.java index 7192537ef2cc..cd47b1a0c146 100644 --- a/platform/util/src/com/intellij/util/io/storage/AbstractStorage.java +++ b/platform/util/src/com/intellij/util/io/storage/AbstractStorage.java @@ -200,7 +200,47 @@ public abstract class AbstractStorage implements Disposable, Forceable { } } - private void appendBytes(int record, byte[] bytes) { + private static int calcCapacity(int requiredLength) { + return Math.max(64, nearestPowerOfTwo(requiredLength * 3 / 2)); + } + + private static int nearestPowerOfTwo(int n) { + int power = 1; + while (n != 0) { + power *= 2; + n /= 2; + } + return power; + } + + public StorageDataOutput writeStream(final int record) { + return new StorageDataOutput(this, record); + } + + public AppenderStream appendStream(int record) { + return new AppenderStream(record); + } + + public DataInputStream readStream(int record) throws IOException { + final byte[] bytes = readBytes(record); + return new DataInputStream(new ByteArrayInputStream(bytes)); + } + + protected byte[] readBytes(int record) throws IOException { + synchronized (myLock) { + final int length = myRecordsTable.getSize(record); + if (length == 0) return ArrayUtil.EMPTY_BYTE_ARRAY; + assert length > 0; + + final long address = myRecordsTable.getAddress(record); + byte[] result = new byte[length]; + myDataTable.readBytes(address, result); + + return result; + } + } + + protected void appendBytes(int record, byte[] bytes) throws IOException { int delta = bytes.length; if (delta == 0) return; @@ -227,7 +267,7 @@ public abstract class AbstractStorage implements Disposable, Forceable { } } - public void writeBytes(int record, byte[] bytes) { + protected void writeBytes(int record, byte[] bytes) throws IOException { synchronized (myLock) { final int requiredLength = bytes.length; final int currentCapacity = myRecordsTable.getCapacity(record); @@ -257,46 +297,6 @@ public abstract class AbstractStorage implements Disposable, Forceable { } } - private static int calcCapacity(int requiredLength) { - return Math.max(64, nearestPowerOfTwo(requiredLength * 3 / 2)); - } - - private static int nearestPowerOfTwo(int n) { - int power = 1; - while (n != 0) { - power *= 2; - n /= 2; - } - return power; - } - - public StorageDataOutput writeStream(final int record) { - return new StorageDataOutput(this, record); - } - - public AppenderStream appendStream(int record) { - return new AppenderStream(record); - } - - public DataInputStream readStream(int record) { - final byte[] bytes = readBytes(record); - return new DataInputStream(new ByteArrayInputStream(bytes)); - } - - byte[] readBytes(int record) { - synchronized (myLock) { - final int length = myRecordsTable.getSize(record); - if (length == 0) return ArrayUtil.EMPTY_BYTE_ARRAY; - assert length > 0; - - final long address = myRecordsTable.getAddress(record); - byte[] result = new byte[length]; - myDataTable.readBytes(address, result); - - return result; - } - } - protected void doDeleteRecord(int record) throws IOException { myDataTable.reclaimSpace(myRecordsTable.getSize(record)); myRecordsTable.deleteRecord(record); diff --git a/platform/util/src/com/intellij/util/io/storage/RefCountingStorage.java b/platform/util/src/com/intellij/util/io/storage/RefCountingStorage.java index 8851d17f8130..68fd54cb9e50 100644 --- a/platform/util/src/com/intellij/util/io/storage/RefCountingStorage.java +++ b/platform/util/src/com/intellij/util/io/storage/RefCountingStorage.java @@ -19,10 +19,14 @@ */ package com.intellij.util.io.storage; +import com.intellij.openapi.util.io.StreamUtil; import com.intellij.util.IncorrectOperationException; import com.intellij.util.io.PagePool; -import java.io.*; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; import java.util.zip.DeflaterOutputStream; import java.util.zip.InflaterInputStream; @@ -31,32 +35,36 @@ public class RefCountingStorage extends AbstractStorage { super(path); } - public RefCountingStorage(String path, PagePool pool) throws IOException { - super(path, pool); + @Override + protected byte[] readBytes(int record) throws IOException { + byte[] result = super.readBytes(record); + InflaterInputStream in = new InflaterInputStream(new ByteArrayInputStream(result)); + try { + return StreamUtil.loadFromStream(in); + } + finally { + in.close(); + } } @Override - public StorageDataOutput writeStream(int record) { - final ByteArrayOutputStream stream = new ByteArrayOutputStream(); - return new StorageDataOutput(this, record, new DeflaterOutputStream(stream)) { - @Override - protected ByteArrayOutputStream getByteStream() { - return stream; - } - }; - } - - @Override - public DataInputStream readStream(int record) { - final byte[] bytes = readBytes(record); - return new DataInputStream(new InflaterInputStream(new ByteArrayInputStream(bytes))); - } - - @Override - public AppenderStream appendStream(int record) { + protected void appendBytes(int record, byte[] bytes) throws IOException { throw new IncorrectOperationException("Appending is not supported"); } + @Override + protected void writeBytes(int record, byte[] bytes) throws IOException { + ByteArrayOutputStream s = new ByteArrayOutputStream(); + DeflaterOutputStream out = new DeflaterOutputStream(s); + try { + out.write(bytes); + } + finally { + out.close(); + } + super.writeBytes(record, s.toByteArray()); + } + @Override protected AbstractRecordsTable createRecordsTable(PagePool pool, File recordsFile) throws IOException { return new RefCountingRecordsTable(recordsFile, pool); diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/VcsConfiguration.java b/platform/vcs-api/src/com/intellij/openapi/vcs/VcsConfiguration.java index 59dc044c95c5..5b9fe2914bc0 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/VcsConfiguration.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/VcsConfiguration.java @@ -51,6 +51,7 @@ public final class VcsConfiguration implements PersistentStateComponent @NonNls private static final String VALUE_ATTR = "value"; @NonNls private static final String CONFIRM_MOVE_TO_FAILED_COMMIT_ELEMENT = "confirmMoveToFailedCommit"; + @NonNls private static final String CONFIRM_REMOVE_EMPTY_CHANGELIST_ELEMENT = "confirmRemoveEmptyChangelist"; private Project myProject; public boolean OFFER_MOVE_TO_ANOTHER_CHANGELIST_ON_PARTIAL_COMMIT = true; @@ -164,10 +165,14 @@ public final class VcsConfiguration implements PersistentStateComponent public void readExternal(Element element) throws InvalidDataException { DefaultJDOMExternalizer.readExternal(this, element); - final Element child = element.getChild(CONFIRM_MOVE_TO_FAILED_COMMIT_ELEMENT); + Element child = element.getChild(CONFIRM_MOVE_TO_FAILED_COMMIT_ELEMENT); if (child != null) { MOVE_TO_FAILED_COMMIT_CHANGELIST = VcsShowConfirmationOption.Value.fromString(child.getAttributeValue(VALUE_ATTR)); } + child = element.getChild(CONFIRM_REMOVE_EMPTY_CHANGELIST_ELEMENT); + if (child != null) { + REMOVE_EMPTY_INACTIVE_CHANGELISTS = VcsShowConfirmationOption.Value.fromString(child.getAttributeValue(VALUE_ATTR)); + } final List messages = element.getChildren(MESSAGE_ELEMENT_NAME); for (final Object message : messages) { saveCommitMessage(((Element)message).getAttributeValue(VALUE_ATTR)); @@ -188,6 +193,11 @@ public final class VcsConfiguration implements PersistentStateComponent confirmChild.setAttribute(VALUE_ATTR, MOVE_TO_FAILED_COMMIT_CHANGELIST.toString()); element.addContent(confirmChild); } + if (REMOVE_EMPTY_INACTIVE_CHANGELISTS != VcsShowConfirmationOption.Value.SHOW_CONFIRMATION) { + Element confirmChild = new Element(CONFIRM_REMOVE_EMPTY_CHANGELIST_ELEMENT); + confirmChild.setAttribute(VALUE_ATTR, REMOVE_EMPTY_INACTIVE_CHANGELISTS.toString()); + element.addContent(confirmChild); + } for (String message : myLastCommitMessages) { final Element messageElement = new Element(MESSAGE_ELEMENT_NAME); messageElement.setAttribute(VALUE_ATTR, message); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/TreeModelBuilder.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/TreeModelBuilder.java index f6d6ab11eaa1..862e2875c432 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/TreeModelBuilder.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/TreeModelBuilder.java @@ -221,7 +221,7 @@ public class TreeModelBuilder { final FilePath fp1 = ChangesUtil.getFilePath(o1); final FilePath fp2 = ChangesUtil.getFilePath(o2); - final int diff = fp1.getPath().length() - fp2.getPath().length(); + final int diff = fp1.getIOFile().getPath().length() - fp2.getIOFile().getPath().length(); return diff == 0 ? 0 : (diff < 0 ? -1 : 1); } } diff --git a/platform/xdebugger-api/src/com/intellij/debugger/ui/DebuggerContentInfo.java b/platform/xdebugger-api/src/com/intellij/debugger/ui/DebuggerContentInfo.java index 7d47bca9d67b..c43caa8dd139 100644 --- a/platform/xdebugger-api/src/com/intellij/debugger/ui/DebuggerContentInfo.java +++ b/platform/xdebugger-api/src/com/intellij/debugger/ui/DebuggerContentInfo.java @@ -15,11 +15,12 @@ */ package com.intellij.debugger.ui; +import com.intellij.execution.ui.ExecutionConsole; import org.jetbrains.annotations.NonNls; public interface DebuggerContentInfo { - @NonNls String CONSOLE_CONTENT = "ConsoleContent"; + @NonNls String CONSOLE_CONTENT = ExecutionConsole.CONSOLE_CONTENT_ID; @NonNls String THREADS_CONTENT = "ThreadsContent"; @NonNls String VARIABLES_CONTENT = "VariablesContent"; @NonNls String FRAME_CONTENT = "FrameContent"; diff --git a/platform/xdebugger-api/src/com/intellij/xdebugger/frame/XStackFrame.java b/platform/xdebugger-api/src/com/intellij/xdebugger/frame/XStackFrame.java index 82b97af99f7c..d3167d40e7be 100644 --- a/platform/xdebugger-api/src/com/intellij/xdebugger/frame/XStackFrame.java +++ b/platform/xdebugger-api/src/com/intellij/xdebugger/frame/XStackFrame.java @@ -16,6 +16,7 @@ package com.intellij.xdebugger.frame; +import com.intellij.ui.FileColorManager; import com.intellij.ui.SimpleColoredComponent; import com.intellij.ui.SimpleTextAttributes; import com.intellij.xdebugger.XDebuggerBundle; @@ -66,6 +67,7 @@ public abstract class XStackFrame extends XValueContainer { public void customizePresentation(final SimpleColoredComponent component) { XSourcePosition position = getSourcePosition(); if (position != null) { + //FileColorManager.getInstance() component.append(position.getFile().getName(), SimpleTextAttributes.REGULAR_ATTRIBUTES); component.append(":" + (position.getLine()+1), SimpleTextAttributes.REGULAR_ATTRIBUTES); component.setIcon(DebuggerIcons.STACK_FRAME_ICON); diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/DebuggerFramesList.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/DebuggerFramesList.java index 36bbf6d2c264..012b8b63b24e 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/DebuggerFramesList.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/DebuggerFramesList.java @@ -16,6 +16,7 @@ package com.intellij.xdebugger.impl.frame; import com.intellij.ide.OccurenceNavigator; +import com.intellij.openapi.project.Project; import com.intellij.ui.components.JBList; import com.intellij.xdebugger.XDebuggerBundle; @@ -27,8 +28,14 @@ import javax.swing.event.ListSelectionListener; * @author nik */ public abstract class DebuggerFramesList extends JBList implements OccurenceNavigator { - public DebuggerFramesList() { + protected final Project myProject; + + public DebuggerFramesList(Project project) { super(new DefaultListModel()); + myProject = project; + } + + protected void doInit() { getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); setCellRenderer(createListRenderer()); getSelectionModel().addListSelectionListener(new ListSelectionListener() { diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XDebuggerFramesList.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XDebuggerFramesList.java index 6c71036bbb42..e13641291fb4 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XDebuggerFramesList.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XDebuggerFramesList.java @@ -15,12 +15,18 @@ */ package com.intellij.xdebugger.impl.frame; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiManager; import com.intellij.ui.ColoredListCellRenderer; +import com.intellij.ui.FileColorManager; import com.intellij.ui.SimpleTextAttributes; import com.intellij.xdebugger.XDebuggerBundle; +import com.intellij.xdebugger.XSourcePosition; import com.intellij.xdebugger.frame.XStackFrame; import javax.swing.*; +import java.awt.*; /** * @author nik @@ -28,8 +34,13 @@ import javax.swing.*; public class XDebuggerFramesList extends DebuggerFramesList { private XStackFrame mySelectedFrame; + public XDebuggerFramesList(Project project) { + super(project); + doInit(); + } + protected ListCellRenderer createListRenderer() { - return new XDebuggerFrameListRenderer(); + return new XDebuggerFrameListRenderer(myProject); } protected void onFrameChanged(final Object selectedValue) { @@ -49,6 +60,14 @@ public class XDebuggerFramesList extends DebuggerFramesList { } private static class XDebuggerFrameListRenderer extends ColoredListCellRenderer { + private final FileColorManager myColorsManager; + private final PsiManager myPsiManager; + + public XDebuggerFrameListRenderer(Project project) { + myPsiManager = PsiManager.getInstance(project); + myColorsManager = FileColorManager.getInstance(project); + } + protected void customizeCellRenderer(final JList list, final Object value, final int index, @@ -64,6 +83,16 @@ public class XDebuggerFramesList extends DebuggerFramesList { } XStackFrame stackFrame = (XStackFrame)value; + if (!selected) { + XSourcePosition position = stackFrame.getSourcePosition(); + if (position != null) { + PsiFile f = myPsiManager.findFile(position.getFile()); + if (f != null) { + Color c = myColorsManager.getFileColor(f); + if (c != null) setBackground(c); + } + } + } stackFrame.customizePresentation(this); } } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XFramesView.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XFramesView.java index 94bba97482e7..a4583932e9b1 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XFramesView.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XFramesView.java @@ -59,7 +59,7 @@ public class XFramesView extends XDebugViewBase { myThreadComboBox.addItemListener(new MyItemListener()); myMainPanel.add(myThreadComboBox, BorderLayout.NORTH); - myFramesList = new XDebuggerFramesList(); + myFramesList = new XDebuggerFramesList(session.getProject()); myFramesList.addListSelectionListener(new ListSelectionListener() { public void valueChanged(final ListSelectionEvent e) { if (e.getValueIsAdjusting()) return; diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/DebuggerSessionTabBase.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/DebuggerSessionTabBase.java index cec01dc8bf82..7100705fd3d6 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/DebuggerSessionTabBase.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/DebuggerSessionTabBase.java @@ -35,6 +35,7 @@ import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.registry.Registry; +import com.intellij.ui.AppIcon; import com.intellij.ui.content.Content; import com.intellij.ui.content.ContentManagerAdapter; import com.intellij.ui.content.ContentManagerEvent; @@ -202,7 +203,11 @@ public abstract class DebuggerSessionTabBase implements DebuggerLogConsoleManage ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { - ProjectUtil.focusProjectWindow(getProject(), Registry.is("debugger.mayBringFrameToFrontOnBreakpoint")); + boolean focusWnd = Registry.is("debugger.mayBringFrameToFrontOnBreakpoint"); + ProjectUtil.focusProjectWindow(getProject(), focusWnd); + if (!focusWnd) { + AppIcon.getInstance().requestAttention(true); + } } }); } diff --git a/plugins/eclipse/src/org/jetbrains/idea/eclipse/conversion/IdeaSpecificSettings.java b/plugins/eclipse/src/org/jetbrains/idea/eclipse/conversion/IdeaSpecificSettings.java index 07ccd9246166..a624a66d326e 100644 --- a/plugins/eclipse/src/org/jetbrains/idea/eclipse/conversion/IdeaSpecificSettings.java +++ b/plugins/eclipse/src/org/jetbrains/idea/eclipse/conversion/IdeaSpecificSettings.java @@ -115,6 +115,11 @@ public class IdeaSpecificSettings { replaceModuleRelatedRoots(model.getProject(), modifiableModel, libElement, OrderRootType.CLASSES, RELATIVE_MODULE_CLS); replaceModuleRelatedRoots(model.getProject(), modifiableModel, libElement, JavadocOrderRootType.getInstance(), RELATIVE_MODULE_JAVADOC); modifiableModel.commit(); + } else { + final Library library = EclipseClasspathReader.findLibraryByName(model.getProject(), libName); + if (library != null) { + appendLibraryScope(model, libElement, library); + } } } overrideModulesScopes(root, model); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/assignment/GroovyAssignabilityCheckInspection.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/assignment/GroovyAssignabilityCheckInspection.java index 44b23e2aae38..f2be2a783d20 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/assignment/GroovyAssignabilityCheckInspection.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/assignment/GroovyAssignabilityCheckInspection.java @@ -21,6 +21,7 @@ import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.TypeConversionUtil; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.GroovyBundle; import org.jetbrains.plugins.groovy.codeInspection.BaseInspection; import org.jetbrains.plugins.groovy.codeInspection.BaseInspectionVisitor; @@ -94,10 +95,10 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection { if (expectedType == null || PsiType.VOID.equals(expectedType)) return; ControlFlowUtils.visitAllExitPoints(block, new ControlFlowUtils.ExitPointVisitor() { - public boolean visit(Instruction instruction) { - final PsiElement psiElement = instruction.getElement(); - if (psiElement instanceof GrExpression) { - checkAssignability(expectedType, (GrExpression)psiElement, (GrExpression)psiElement); + @Override + public boolean visitExitPoint(Instruction instruction, @Nullable GrExpression returnValue) { + if (returnValue != null && !(returnValue.getParent() instanceof GrReturnStatement)) { + checkAssignability(expectedType, returnValue, returnValue); } return true; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/noReturnMethod/MissingReturnInspection.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/noReturnMethod/MissingReturnInspection.java index 684dbfe780ac..a39ba6cc3fee 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/noReturnMethod/MissingReturnInspection.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/noReturnMethod/MissingReturnInspection.java @@ -24,6 +24,7 @@ import com.intellij.psi.PsiType; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.codeInspection.GroovyInspectionBundle; import org.jetbrains.plugins.groovy.codeInspection.GroovySuppressableInspectionTool; import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils; @@ -35,6 +36,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock; import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.GrAssertStatement; import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.GrReturnStatement; import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.GrThrowStatement; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; import org.jetbrains.plugins.groovy.lang.psi.controlFlow.Instruction; import org.jetbrains.plugins.groovy.lang.psi.controlFlow.impl.MaybeReturnInstruction; @@ -86,7 +88,8 @@ public class MissingReturnInspection extends GroovySuppressableInspectionTool { final Ref hasExplicitReturn = new Ref(false); final Ref sometimes = new Ref(false); ControlFlowUtils.visitAllExitPoints(block, new ControlFlowUtils.ExitPointVisitor() { - public boolean visit(Instruction instruction) { + @Override + public boolean visitExitPoint(Instruction instruction, @Nullable GrExpression returnValue) { if (instruction instanceof MaybeReturnInstruction) { if (((MaybeReturnInstruction)instruction).mayReturnValue()) { sometimes.set(true); @@ -99,7 +102,7 @@ public class MissingReturnInspection extends GroovySuppressableInspectionTool { final PsiElement element = instruction.getElement(); if (element instanceof GrReturnStatement) { sometimes.set(true); - if (((GrReturnStatement)element).getReturnValue() != null) { + if (returnValue != null) { hasExplicitReturn.set(true); } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/ControlFlowUtils.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/ControlFlowUtils.java index 86aa6c0503e4..995b0302494d 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/ControlFlowUtils.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/ControlFlowUtils.java @@ -543,7 +543,7 @@ public class ControlFlowUtils { public interface ExitPointVisitor { - boolean visit(Instruction instruction); + boolean visitExitPoint(Instruction instruction, @Nullable GrExpression returnValue); } public static void visitAllExitPoints(@Nullable GrCodeBlock block, ExitPointVisitor visitor) { @@ -556,12 +556,16 @@ public class ControlFlowUtils { private static boolean visitAllExitPointsInner(Instruction last, Instruction first, boolean[] visited, ExitPointVisitor visitor) { if (first == last) return true; if (last instanceof MaybeReturnInstruction) { - return visitor.visit(last); + return visitor.visitExitPoint(last, (GrExpression)last.getElement()); } - final PsiElement element = last.getElement(); + PsiElement element = last.getElement(); if (element != null) { - return visitor.visit(last); + if (element instanceof GrReturnStatement) { + element = ((GrReturnStatement)element).getReturnValue(); + } + + return visitor.visitExitPoint(last, element instanceof GrExpression ? (GrExpression)element : null); } visited[last.num()] = true; for (Instruction pred : last.allPred()) { @@ -571,4 +575,5 @@ public class ControlFlowUtils { } return true; } + } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/AnnotatedMembersSearcher.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/AnnotatedMembersSearcher.java index 3a4b9ecfc1d0..ead9e3f235d3 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/AnnotatedMembersSearcher.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/AnnotatedMembersSearcher.java @@ -21,25 +21,49 @@ import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.LocalSearchScope; import com.intellij.psi.search.SearchScope; import com.intellij.psi.search.searches.AnnotatedElementsSearch; +import com.intellij.psi.stubs.StubIndex; import com.intellij.util.Processor; import com.intellij.util.QueryExecutor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; import org.jetbrains.plugins.groovy.lang.psi.GroovyRecursiveElementVisitor; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMember; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; -import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GroovyScriptClass; -import org.jetbrains.plugins.groovy.lang.stubs.GroovyCacheUtil; +import org.jetbrains.plugins.groovy.lang.psi.stubs.index.GrAnnotatedMemberIndex; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.List; /** * @author ven */ -public class AnnotatedMembersSearcher implements QueryExecutor { +public class AnnotatedMembersSearcher implements QueryExecutor { - public boolean execute(final AnnotatedElementsSearch.Parameters p, final Processor consumer) { + @NotNull + private static List getAnnotatedMemberCandidates(PsiClass clazz, GlobalSearchScope scope) { + final String name = clazz.getName(); + if (name == null) return Collections.emptyList(); + final Collection members = StubIndex.getInstance().get(GrAnnotatedMemberIndex.KEY, name, clazz.getProject(), scope); + if (members.isEmpty()) { + return Collections.emptyList(); + } + + final ArrayList result = new ArrayList(); + for (PsiElement element : members) { + if (element instanceof GroovyFile) { + element = ((GroovyFile)element).getPackageDefinition(); + } + if (element instanceof PsiModifierListOwner) { + result.add((PsiModifierListOwner)element); + } + } + return result; + } + + public boolean execute(final AnnotatedElementsSearch.Parameters p, final Processor consumer) { final PsiClass annClass = p.getAnnotationClass(); assert annClass.isAnnotationType() : "Annotation type should be passed to annotated members search"; @@ -48,29 +72,27 @@ public class AnnotatedMembersSearcher implements QueryExecutor candidates; if (scope instanceof GlobalSearchScope) { - candidates = GroovyCacheUtil.getAnnotatedMemberCandidates(annClass, ((GlobalSearchScope)scope)); + candidates = getAnnotatedMemberCandidates(annClass, ((GlobalSearchScope)scope)); } else { - PsiElement[] elements = ((LocalSearchScope)scope).getScope(); - final List collector = new ArrayList(); - for (PsiElement element : elements) { + candidates = new ArrayList(); + for (PsiElement element : ((LocalSearchScope)scope).getScope()) { if (element instanceof GroovyPsiElement) { ((GroovyPsiElement)element).accept(new GroovyRecursiveElementVisitor() { public void visitMethod(GrMethod method) { - collector.add(method); + candidates.add(method); } public void visitField(GrField field) { - collector.add(field); + candidates.add(field); } }); } } - candidates = collector.toArray(new PsiMember[collector.size()]); } - for (PsiMember candidate : candidates) { + for (PsiModifierListOwner candidate : candidates) { if (!AnnotatedElementsSearcher.isInstanceof(candidate, p.getTypes())) { continue; } @@ -78,10 +100,8 @@ public class AnnotatedMembersSearcher implements QueryExecutor { - public boolean execute(ReferencesSearch.SearchParameters queryParameters, final Processor consumer) { +public class ConstructorReferencesSearcher extends QueryExecutorBase { + protected ConstructorReferencesSearcher() { + super(true); + } + + @Override + public void processQuery(ReferencesSearch.SearchParameters queryParameters, Processor consumer) { final PsiElement element = queryParameters.getElementToSearch(); if (element instanceof PsiMethod) { final PsiMethod method = (PsiMethod)element; if (method.isConstructor()) { - return GroovyConstructorUsagesSearchHelper.execute(method, queryParameters.getScope(), consumer); + GroovyConstructorUsagesSearcher.processConstructorUsages(method, queryParameters.getScope(), consumer, queryParameters.getOptimizer(), true); } } - return true; } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GroovyConstructorUsagesSearcher.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GroovyConstructorUsagesSearcher.java index da1ef93bd10f..67e627d267c9 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GroovyConstructorUsagesSearcher.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GroovyConstructorUsagesSearcher.java @@ -16,20 +16,399 @@ package org.jetbrains.plugins.groovy.findUsages; -import com.intellij.psi.PsiMethod; -import com.intellij.psi.PsiReference; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.QueryExecutorBase; +import com.intellij.openapi.application.ReadActionProcessor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.*; +import com.intellij.psi.impl.light.LightMemberReference; +import com.intellij.psi.search.DelegatingGlobalSearchScope; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.search.SearchRequestCollector; import com.intellij.psi.search.SearchScope; +import com.intellij.psi.search.searches.AnnotatedElementsSearch; +import com.intellij.psi.search.searches.DirectClassInheritorsSearch; import com.intellij.psi.search.searches.MethodReferencesSearch; +import com.intellij.psi.search.searches.ReferencesSearch; +import com.intellij.psi.util.CachedValueProvider; +import com.intellij.psi.util.CachedValuesManager; +import com.intellij.psi.util.PsiModificationTracker; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.PairProcessor; import com.intellij.util.Processor; -import com.intellij.util.QueryExecutor; +import com.intellij.util.containers.ConcurrentHashSet; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.GroovyFileType; +import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils; +import org.jetbrains.plugins.groovy.gpp.GppTypeConverter; +import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement; +import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; +import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; +import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrConstructorInvocation; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariableDeclaration; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrAnonymousClassDefinition; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrEnumConstant; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; +import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement; +import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement; +import org.jetbrains.plugins.groovy.lang.psi.controlFlow.Instruction; +import org.jetbrains.plugins.groovy.lang.psi.expectedTypes.GroovyExpectedTypesProvider; +import org.jetbrains.plugins.groovy.lang.psi.impl.GrTupleType; +import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; /** * @author Maxim.Medvedev */ -public class GroovyConstructorUsagesSearcher implements QueryExecutor { - public boolean execute(MethodReferencesSearch.SearchParameters p, final Processor consumer) { - final PsiMethod method = p.getMethod(); - final SearchScope searchScope = p.getScope(); - return GroovyConstructorUsagesSearchHelper.execute(method, searchScope, consumer); +public class GroovyConstructorUsagesSearcher extends QueryExecutorBase { + public GroovyConstructorUsagesSearcher() { + super(true); } + + @Override + public void processQuery(MethodReferencesSearch.SearchParameters p, Processor consumer) { + processConstructorUsages(p.getMethod(), p.getScope(), consumer, p.getOptimizer(), true); + } + + static void processConstructorUsages(final PsiMethod constructor, final SearchScope searchScope, final Processor consumer, final SearchRequestCollector collector, final boolean searchGppCalls) { + if (!constructor.isConstructor()) return; + + SearchScope onlyGroovy = searchScope; + if (onlyGroovy instanceof GlobalSearchScope) { + onlyGroovy = GlobalSearchScope.getScopeRestrictedByFileTypes((GlobalSearchScope)onlyGroovy, GroovyFileType.GROOVY_FILE_TYPE); + } + + final PsiClass clazz = constructor.getContainingClass(); + if (clazz == null) return; + + + if (clazz.isEnum() && clazz instanceof GroovyPsiElement) { + for (PsiField field : clazz.getFields()) { + if (field instanceof GrEnumConstant) { + final PsiReference ref = field.getReference(); + if (ref != null && ref.isReferenceTo(constructor)) { + if (!consumer.process(ref)) return; + } + } + } + } + + final Set processedMethods = new ConcurrentHashSet(); + + ReferencesSearch.searchOptimized(clazz, searchScope, true, collector, true, new PairProcessor() { + @Override + public boolean process(PsiReference ref, SearchRequestCollector collector) { + final PsiElement element = ref.getElement(); + if (element instanceof GrCodeReferenceElement) { + if (!processGroovyConstructorUsages((GrCodeReferenceElement)element, constructor, consumer, ref, !searchGppCalls)) { + return false; + } + } + + if (searchGppCalls) { + final PsiMethod method = getMethodToSearchForCallsWithLiteralArguments(element, clazz); + if (method != null && processedMethods.add(method)) { + processGppMethodCalls(clazz, constructor, consumer, searchScope, collector, method); + } + } + return true; + } + }); + + //this() + if (clazz instanceof GrTypeDefinition) { + if (!processConstructors(constructor, consumer, clazz, true)) { + return; + } + } + //super() + DirectClassInheritorsSearch.search(clazz, onlyGroovy).forEach(new Processor() { + public boolean process(PsiClass inheritor) { + if (inheritor instanceof GrTypeDefinition) { + if (!processConstructors(constructor, consumer, inheritor, false)) return false; + } + return true; + } + }); + } + + @Nullable + static PsiMethod getMethodToSearchForCallsWithLiteralArguments(PsiElement element, PsiClass targetClass) { + final PsiParameter parameter = PsiTreeUtil.getParentOfType(element, PsiParameter.class); + if (parameter != null) { + final PsiMethod method = PsiTreeUtil.getParentOfType(parameter, PsiMethod.class); + if (method != null && Arrays.asList(method.getParameterList().getParameters()).contains(parameter)) { + final PsiType parameterType = parameter.getType(); + if (parameterType instanceof PsiClassType) { + if (method.getManager().areElementsEquivalent(targetClass, ((PsiClassType)parameterType).resolve())) { + return method; + } + } + } + } + return null; + } + + private static void processGppMethodCalls(final PsiClass targetClass, + final PsiMethod originalTarget, + final Processor originalProcessor, + SearchScope scope, + SearchRequestCollector originalCollector, @NotNull PsiMethod currentTarget) { + final SearchScope gppScope = getGppScope(targetClass.getProject()).intersectWith(scope); + final ReadActionProcessor gppCallProcessor = new ReadActionProcessor() { + @Override + public boolean processInReadAction(PsiReference psiReference) { + if (psiReference instanceof GrReferenceElement) { + final PsiElement parent = ((GrReferenceElement)psiReference).getParent(); + if (parent instanceof GrCall) { + final GrArgumentList argList = ((GrCall)parent).getArgumentList(); + if (argList != null) { + boolean checkedTypedContext = false; + + for (GrExpression argument : argList.getExpressionArguments()) { + if (argument instanceof GrListOrMap && !((GrListOrMap)argument).isMap()) { + if (!checkedTypedContext) { + if (!GppTypeConverter.hasTypedContext(parent)) { + return true; + } + checkedTypedContext = true; + } + + for (PsiType psiType : GroovyExpectedTypesProvider.getDefaultExpectedTypes(argument)) { + if (psiType instanceof PsiClassType && + targetClass.getManager().areElementsEquivalent(targetClass, ((PsiClassType)psiType).resolve()) && + !checkListInstantiation(originalTarget, originalProcessor, (GrListOrMap)argument, (PsiClassType)psiType)) { + return false; + } + } + } + } + } + } + } + return true; + } + }; + if (currentTarget.isConstructor()) { + processConstructorUsages(currentTarget, gppScope, gppCallProcessor, originalCollector, false); + } + else { + MethodReferencesSearch.searchOptimized(currentTarget, gppScope, true, originalCollector, gppCallProcessor); + } + } + + static GlobalSearchScope getGppScope(final Project project) { + return CachedValuesManager.getManager(project).getCachedValue(project, new CachedValueProvider() { + @Override + public Result compute() { + return Result.create(calcGppScope(project), PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT, ProjectRootManager.getInstance(project)); + } + }); + } + + static boolean processGroovyConstructorUsages(GrCodeReferenceElement element, + final PsiMethod constructor, + final Processor consumer, + PsiReference ref, boolean usualCallsOnly) { + PsiElement parent = element.getParent(); + + if (parent instanceof GrAnonymousClassDefinition) { + parent = parent.getParent(); + } + if (parent instanceof GrNewExpression) { + final PsiMethod resolvedConstructor = ((GrNewExpression)parent).resolveConstructor(); + if (constructor.getManager().areElementsEquivalent(resolvedConstructor, constructor) && !consumer.process(ref)) { + return false; + } + } + else if (!usualCallsOnly && parent instanceof GrTypeElement) { + final GrTypeElement typeElement = (GrTypeElement)parent; + + final PsiElement grandpa = typeElement.getParent(); + if (grandpa instanceof GrVariableDeclaration) { + final GrVariable[] vars = ((GrVariableDeclaration)grandpa).getVariables(); + if (vars.length == 1) { + final GrVariable variable = vars[0]; + if (!checkListInstantiation(constructor, consumer, variable.getInitializerGroovy(), typeElement)) { + return false; + } + } + } + else if (grandpa instanceof GrMethod) { + final GrMethod method = (GrMethod)grandpa; + if (typeElement == method.getReturnTypeElementGroovy()) { + ControlFlowUtils.visitAllExitPoints(method.getBlock(), new ControlFlowUtils.ExitPointVisitor() { + @Override + public boolean visitExitPoint(Instruction instruction, @Nullable GrExpression returnValue) { + if (!checkListInstantiation(constructor, consumer, returnValue, typeElement)) { + return false; + } + return true; + } + }); + } + } + else if (grandpa instanceof GrTypeCastExpression) { + final GrTypeCastExpression cast = (GrTypeCastExpression)grandpa; + if (cast.getCastTypeElement() == typeElement && + !checkListInstantiation(constructor, consumer, cast.getOperand(), typeElement)) { + return false; + } + } + else if (grandpa instanceof GrSafeCastExpression) { + final GrSafeCastExpression cast = (GrSafeCastExpression)grandpa; + if (cast.getCastTypeElement() == typeElement && + !checkListInstantiation(constructor, consumer, cast.getOperand(), typeElement)) { + return false; + } + } + } + return true; + } + + static GlobalSearchScope calcGppScope(Project project) { + final GlobalSearchScope allScope = GlobalSearchScope.allScope(project); + final GlobalSearchScope maximal = GlobalSearchScope.getScopeRestrictedByFileTypes(allScope, GroovyFileType.GROOVY_FILE_TYPE); + GlobalSearchScope gppExtensions = new DelegatingGlobalSearchScope(maximal) { + @Override + public boolean contains(VirtualFile file) { + return super.contains(file) && GppTypeConverter.isGppExtension(file.getExtension()); + } + }; + final PsiClass typed = JavaPsiFacade.getInstance(project).findClass(GppTypeConverter.GROOVY_LANG_TYPED, allScope); + if (typed != null) { + final Set files = new HashSet(); + AnnotatedElementsSearch.searchElements(typed, maximal, PsiModifierListOwner.class).forEach(new Processor() { + @Override + public boolean process(PsiModifierListOwner occurrence) { + ContainerUtil.addIfNotNull(occurrence.getContainingFile().getVirtualFile(), files); + return true; + } + }); + + GlobalSearchScope withTypedAnno = GlobalSearchScope.filesScope(project, files); + return withTypedAnno.union(gppExtensions); + } + + return gppExtensions; + } + + static boolean checkListInstantiation(PsiMethod constructor, + Processor consumer, + GrExpression expression, final GrTypeElement typeElement) { + if (expression instanceof GrListOrMap) { + final GrListOrMap list = (GrListOrMap)expression; + if (!list.isMap()) { + final PsiType expectedType = typeElement.getType(); + if (expectedType instanceof PsiClassType) { + return checkListInstantiation(constructor, consumer, list, (PsiClassType)expectedType); + } + } + } + return true; + } + + static boolean checkListInstantiation(PsiMethod constructor, + Processor consumer, + GrListOrMap list, + PsiClassType expectedType) { + final PsiType listType = list.getType(); + if (listType instanceof GrTupleType) { + for (GroovyResolveResult candidate : PsiUtil.getConstructorCandidates(expectedType, ((GrTupleType)listType).getComponentTypes(), list)) { + if (constructor.getManager().areElementsEquivalent(candidate.getElement(), constructor)) { + if (!consumer.process(PsiReferenceBase.createSelfReference(list, TextRange.from(0, list.getTextLength()), constructor))) { + return false; + } + } + } + } + return true; + } + + static boolean processConstructors(final PsiMethod constructor, final Processor consumer, final PsiClass clazz, + final boolean processThisRefs) { + return ApplicationManager.getApplication().runReadAction(new Computable() { + public Boolean compute() { + return processClassConstructors(clazz, constructor, consumer, processThisRefs); + } + }); + } + + static boolean processClassConstructors(PsiClass clazz, + PsiMethod searchedConstructor, + Processor consumer, + boolean processThisRefs) { + final PsiMethod[] constructors = clazz.getConstructors(); + if (constructors.length == 0) { + processImplicitConstructorCall(clazz, consumer, searchedConstructor); + } + for (PsiMethod constructor : constructors) { + final GrOpenBlock block = ((GrMethod)constructor).getBlock(); + if (block != null) { + final GrStatement[] statements = block.getStatements(); + if (statements.length > 0 && statements[0] instanceof GrConstructorInvocation) { + final GrConstructorInvocation invocation = (GrConstructorInvocation)statements[0]; + if (invocation.isThisCall() == processThisRefs && + invocation.getManager().areElementsEquivalent(invocation.resolveConstructor(), searchedConstructor) && + !consumer.process(invocation.getThisOrSuperKeyword())) { + return false; + } + } + else { + processImplicitConstructorCall(constructor, consumer, searchedConstructor); + } + } + } + return true; + } + + static void processImplicitConstructorCall(final PsiMember usage, + final Processor processor, + final PsiMethod constructor) { + if (constructor instanceof GrMethod) { + GrParameter[] grParameters = (GrParameter[])constructor.getParameterList().getParameters(); + if (grParameters.length > 0 && !grParameters[0].isOptional()) return; + } + else if (constructor.getParameterList().getParameters().length > 0) return; + + + PsiManager manager = constructor.getManager(); + if (manager.areElementsEquivalent(usage, constructor) || manager.areElementsEquivalent(constructor.getContainingClass(), usage.getContainingClass())) return; + processor.process(new LightMemberReference(manager, usage, PsiSubstitutor.EMPTY) { + public PsiElement getElement() { + return usage; + } + + public TextRange getRangeInElement() { + if (usage instanceof PsiClass) { + PsiIdentifier identifier = ((PsiClass)usage).getNameIdentifier(); + if (identifier != null) return TextRange.from(identifier.getStartOffsetInParent(), identifier.getTextLength()); + } + else if (usage instanceof PsiMethod) { + PsiIdentifier identifier = ((PsiMethod)usage).getNameIdentifier(); + if (identifier != null) return TextRange.from(identifier.getStartOffsetInParent(), identifier.getTextLength()); + } + return super.getRangeInElement(); + } + }); + + } + } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppClosureParameterTypeProvider.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppClosureParameterTypeProvider.java index 452a310bdead..47e863de80d6 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppClosureParameterTypeProvider.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppClosureParameterTypeProvider.java @@ -19,6 +19,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpres import org.jetbrains.plugins.groovy.lang.psi.expectedTypes.GroovyExpectedTypesProvider; import org.jetbrains.plugins.groovy.lang.psi.impl.GrTupleType; import org.jetbrains.plugins.groovy.lang.psi.impl.types.GrClosureSignatureUtil; +import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; import java.util.*; @@ -49,7 +50,7 @@ public class GppClosureParameterTypeProvider extends AbstractClosureParameterEnh if (listType instanceof GrTupleType) { for (PsiType type : GroovyExpectedTypesProvider.getDefaultExpectedTypes(list)) { if (type instanceof PsiClassType) { - for (GroovyResolveResult resolveResult : GppTypeConverter + for (GroovyResolveResult resolveResult : PsiUtil .getConstructorCandidates((PsiClassType)type, ((GrTupleType)listType).getComponentTypes(), closure)) { final PsiElement method = resolveResult.getElement(); if (method instanceof PsiMethod && ((PsiMethod)method).isConstructor()) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppExpectedTypesContributor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppExpectedTypesContributor.java index 6266d2b15af1..5937224292b1 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppExpectedTypesContributor.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppExpectedTypesContributor.java @@ -10,6 +10,7 @@ import org.jetbrains.plugins.groovy.lang.psi.expectedTypes.GroovyExpectedTypesPr import org.jetbrains.plugins.groovy.lang.psi.expectedTypes.SubtypeConstraint; import org.jetbrains.plugins.groovy.lang.psi.expectedTypes.TypeConstraint; import org.jetbrains.plugins.groovy.lang.psi.impl.GrTupleType; +import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; import java.util.ArrayList; import java.util.Arrays; @@ -48,7 +49,7 @@ public class GppExpectedTypesContributor extends GroovyExpectedTypesContributor final ArrayList result = new ArrayList(); for (PsiType type : GroovyExpectedTypesProvider.getDefaultExpectedTypes(expression)) { if (type instanceof PsiClassType) { - for (GroovyResolveResult resolveResult : GppTypeConverter.getConstructorCandidates((PsiClassType)type, argTypes, expression)) { + for (GroovyResolveResult resolveResult : PsiUtil.getConstructorCandidates((PsiClassType)type, argTypes, expression)) { final PsiElement method = resolveResult.getElement(); if (method instanceof PsiMethod && ((PsiMethod)method).isConstructor()) { final PsiParameter[] constructorParameters = ((PsiMethod)method).getParameterList().getParameters(); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppTypeConverter.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppTypeConverter.java index a41d82ebd46e..ab44998ef803 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppTypeConverter.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppTypeConverter.java @@ -7,12 +7,10 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.dsl.toplevel.AnnotatedContextFilter; import org.jetbrains.plugins.groovy.lang.psi.GrTypeConverter; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; -import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.types.GrClosureSignature; import org.jetbrains.plugins.groovy.lang.psi.impl.GrClosureType; import org.jetbrains.plugins.groovy.lang.psi.impl.GrMapType; import org.jetbrains.plugins.groovy.lang.psi.impl.GrTupleType; -import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyResolveResultImpl; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; import org.jetbrains.plugins.groovy.lang.psi.impl.types.GrClosureSignatureUtil; @@ -21,26 +19,29 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.types.GrClosureSignatureUtil; */ public class GppTypeConverter extends GrTypeConverter { + public static final String GROOVY_LANG_TYPED = "groovy.lang.Typed"; + public static boolean hasTypedContext(PsiElement context) { if (context == null) { return false; } - if (AnnotatedContextFilter.hasAnnotatedContext(context, "groovy.lang.Typed")) { + if (AnnotatedContextFilter.hasAnnotatedContext(context, GROOVY_LANG_TYPED)) { return true; } final VirtualFile vfile = context.getContainingFile().getOriginalFile().getVirtualFile(); - if (vfile != null) { - final String extension = vfile.getExtension(); - if ("gpp".equals(extension) || "grunit".equals(vfile.getExtension())) { - return true; - } + if (vfile != null && isGppExtension(vfile.getExtension())) { + return true; } return false; } + public static boolean isGppExtension(String extension) { + return "gpp".equals(extension) || "grunit".equals(extension); + } + @Override public Boolean isConvertible(@NotNull PsiType lType, @NotNull PsiType rType, @NotNull GroovyPsiElement context) { if (rType instanceof GrTupleType) { @@ -93,21 +94,7 @@ public class GppTypeConverter extends GrTypeConverter { } private static boolean hasConstructor(PsiClassType lType, PsiType[] argTypes, GroovyPsiElement context) { - return getConstructorCandidates(lType, argTypes, context).length == 1; - } - - public static GroovyResolveResult[] getConstructorCandidates(PsiClassType classType, PsiType[] argTypes, GroovyPsiElement context) { - final PsiClassType.ClassResolveResult resolveResult = classType.resolveGenerics(); - final PsiClass psiClass = resolveResult.getElement(); - final PsiSubstitutor substitutor = resolveResult.getSubstitutor(); - if (psiClass == null) { - return GroovyResolveResult.EMPTY_ARRAY; - } - - final GroovyResolveResult grResult = resolveResult instanceof GroovyResolveResult - ? (GroovyResolveResult)resolveResult - : new GroovyResolveResultImpl(psiClass, context, substitutor, true, true); - return org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil.getConstructorCandidates(context, new GroovyResolveResult[]{grResult}, argTypes); + return org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil.getConstructorCandidates(lType, argTypes, context).length == 1; } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/toplevel/packaging/GrPackageDefinition.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/toplevel/packaging/GrPackageDefinition.java index b2473141c61b..49f9b4dfb3c6 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/toplevel/packaging/GrPackageDefinition.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/toplevel/packaging/GrPackageDefinition.java @@ -16,10 +16,7 @@ package org.jetbrains.plugins.groovy.lang.psi.api.toplevel.packaging; -import com.intellij.navigation.NavigationItem; -import com.intellij.openapi.util.Iconable; -import com.intellij.openapi.util.UserDataHolderEx; -import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiModifierListOwner; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList; import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.GrTopStatement; @@ -28,10 +25,11 @@ import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement; /** * @author ilyas */ -public interface GrPackageDefinition - extends UserDataHolderEx, Cloneable, Iconable, PsiElement, NavigationItem, GrTopStatement { +public interface GrPackageDefinition extends GrTopStatement, PsiModifierListOwner { + @Nullable String getPackageName(); + @Nullable GrCodeReferenceElement getPackageReference(); @Nullable diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/controlFlow/impl/ControlFlowBuilder.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/controlFlow/impl/ControlFlowBuilder.java index ea85bc7a40b1..174ac80aa94e 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/controlFlow/impl/ControlFlowBuilder.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/controlFlow/impl/ControlFlowBuilder.java @@ -107,16 +107,19 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor { if (!(block.getParent() instanceof GrBlockStatement && block.getParent().getParent() instanceof GrLoopStatement)) { final GrStatement[] statements = block.getStatements(); if (statements.length > 0) { - final GrStatement last = statements[statements.length - 1]; - if (last instanceof GrExpression) { - final MaybeReturnInstruction instruction = new MaybeReturnInstruction((GrExpression)last, myInstructionNumber++); - checkPending(instruction); - addNode(instruction); - } + handlePossibleReturn(statements[statements.length - 1]); } } } + private void handlePossibleReturn(GrStatement last) { + if (last instanceof GrExpression) { + final MaybeReturnInstruction instruction = new MaybeReturnInstruction((GrExpression)last, myInstructionNumber++); + checkPending(instruction); + addNode(instruction); + } + } + public Instruction[] buildControlFlow(GroovyPsiElement scope, GroovyPsiElement startInScope, GroovyPsiElement endInScope) { myInstructions = new ArrayList(); myProcessingStack = new Stack(); @@ -386,6 +389,7 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor { condition.accept(this); } thenBranch.accept(this); + handlePossibleReturn(thenBranch); addPendingEdge(ifStatement, myHead); } @@ -402,6 +406,7 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor { final GrStatement elseBranch = ifStatement.getElseBranch(); if (elseBranch != null) { elseBranch.accept(this); + handlePossibleReturn(elseBranch); addPendingEdge(ifStatement, myHead); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyDirectInheritorsSearcher.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyDirectInheritorsSearcher.java index f7f45652ea3b..9c5b70fc5ef2 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyDirectInheritorsSearcher.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyDirectInheritorsSearcher.java @@ -18,12 +18,23 @@ package org.jetbrains.plugins.groovy.lang.psi.impl; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.util.Computable; import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.SearchScope; import com.intellij.psi.search.searches.DirectClassInheritorsSearch; +import com.intellij.psi.stubs.StubIndex; import com.intellij.util.Processor; import com.intellij.util.QueryExecutor; -import org.jetbrains.plugins.groovy.lang.stubs.GroovyCacheUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.groovy.lang.psi.GrClassSubstitutor; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrAnonymousClassDefinition; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrReferenceList; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; +import org.jetbrains.plugins.groovy.lang.psi.stubs.index.GrAnonymousClassIndex; +import org.jetbrains.plugins.groovy.lang.psi.stubs.index.GrDirectInheritorsIndex; + +import java.util.ArrayList; +import java.util.Collection; /** * @author ven @@ -32,6 +43,26 @@ class GroovyDirectInheritorsSearcher implements QueryExecutor inheritors = new ArrayList(); + final Collection refLists = StubIndex.getInstance().get(GrDirectInheritorsIndex.KEY, name, clazz.getProject(), scope); + for (GrReferenceList list : refLists) { + final PsiElement parent = list.getParent(); + if (parent instanceof GrTypeDefinition) { + inheritors.add(GrClassSubstitutor.getSubstitutedClass(((GrTypeDefinition)parent))); + } + } + final Collection classes = + StubIndex.getInstance().get(GrAnonymousClassIndex.KEY, name, clazz.getProject(), scope); + for (GrAnonymousClassDefinition aClass : classes) { + inheritors.add(aClass); + } + return inheritors.toArray(new PsiClass[inheritors.size()]); + } + public boolean execute(DirectClassInheritorsSearch.SearchParameters queryParameters, final Processor consumer) { final PsiClass clazz = queryParameters.getClassToProcess(); final SearchScope scope = queryParameters.getScope(); @@ -39,7 +70,7 @@ class GroovyDirectInheritorsSearcher implements QueryExecutor() { public PsiClass[] compute() { if (!clazz.isValid()) return PsiClass.EMPTY_ARRAY; - return GroovyCacheUtil.getDeriverCandidates(clazz, (GlobalSearchScope)scope); + return getDeriverCandidates(clazz, (GlobalSearchScope)scope); } }); for (final PsiClass candidate : candidates) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/toplevel/packaging/GrPackageDefinitionImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/toplevel/packaging/GrPackageDefinitionImpl.java index 55acc71f0f34..3041aa24363c 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/toplevel/packaging/GrPackageDefinitionImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/toplevel/packaging/GrPackageDefinitionImpl.java @@ -17,6 +17,9 @@ package org.jetbrains.plugins.groovy.lang.psi.impl.toplevel.packaging; import com.intellij.lang.ASTNode; +import com.intellij.psi.Modifier; +import com.intellij.psi.PsiModifierList; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes; import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor; @@ -56,4 +59,15 @@ public class GrPackageDefinitionImpl extends GroovyPsiElementImpl implements GrP public GrModifierList getAnnotationList() { return (GrModifierList)findChildByType(GroovyElementTypes.MODIFIERS); } + + @Override + public PsiModifierList getModifierList() { + return getAnnotationList(); + } + + @Override + public boolean hasModifierProperty(@Modifier @NonNls @NotNull String name) { + final PsiModifierList list = getModifierList(); + return list != null && list.hasExplicitModifier(name); + } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/GrFileStub.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/GrFileStub.java index 0cf2e750f5aa..454c280d7bdc 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/GrFileStub.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/GrFileStub.java @@ -15,17 +15,63 @@ */ package org.jetbrains.plugins.groovy.lang.psi.stubs; -import com.intellij.psi.stubs.PsiFileStub; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.stubs.PsiFileStubImpl; +import com.intellij.psi.tree.IStubFileElementType; +import com.intellij.util.ArrayUtil; import com.intellij.util.io.StringRef; +import org.jetbrains.plugins.groovy.lang.parser.GroovyParserDefinition; import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; +import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.packaging.GrPackageDefinition; +import org.jetbrains.plugins.groovy.lang.psi.stubs.elements.GrTypeDefinitionElementType; /** * @author ilyas */ -public interface GrFileStub extends PsiFileStub { - StringRef getPackageName(); +public class GrFileStub extends PsiFileStubImpl { + private final String[] myAnnotations; + private final StringRef myPackageName; + private final StringRef myName; + private final boolean isScript; - StringRef getName(); + public GrFileStub(GroovyFile file) { + super(file); + myPackageName = StringRef.fromString(file.getPackageName()); + myName = StringRef.fromString(StringUtil.trimEnd(file.getName(), ".groovy")); + isScript = file.isScript(); + final GrPackageDefinition definition = file.getPackageDefinition(); + if (definition != null) { + myAnnotations = GrTypeDefinitionElementType.getAnnotationNames(definition); + } else { + myAnnotations = ArrayUtil.EMPTY_STRING_ARRAY; + } + } - boolean isScript(); + public GrFileStub(StringRef packName, StringRef name, boolean isScript, String[] annotations) { + super(null); + myPackageName = packName; + myName = name; + this.isScript = isScript; + myAnnotations = annotations; + } + + public IStubFileElementType getType() { + return GroovyParserDefinition.GROOVY_FILE; + } + + public StringRef getPackageName() { + return myPackageName; + } + + public StringRef getName() { + return myName; + } + + public boolean isScript() { + return isScript; + } + + public String[] getAnnotations() { + return myAnnotations; + } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/GrStubUtils.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/GrStubUtils.java index 7a5d0c4fb816..9ecbce673657 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/GrStubUtils.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/GrStubUtils.java @@ -58,5 +58,21 @@ public class GrStubUtils { dataStream.writeUTF(namepParameter); } } - } + } + + public static void writeStringArray(StubOutputStream dataStream, String[] array) throws IOException { + dataStream.writeByte(array.length); + for (String s : array) { + dataStream.writeName(s); + } + } + + public static String[] readStringArray(StubInputStream dataStream) throws IOException { + final byte b = dataStream.readByte(); + final String[] annNames = new String[b]; + for (int i = 0; i < b; i++) { + annNames[i] = dataStream.readName().toString(); + } + return annNames; + } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/GroovyFileStubBuilder.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/GroovyFileStubBuilder.java index 902f4b0cef96..53aa5f33101a 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/GroovyFileStubBuilder.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/GroovyFileStubBuilder.java @@ -19,7 +19,6 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.stubs.DefaultStubBuilder; import com.intellij.psi.stubs.StubElement; import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; -import org.jetbrains.plugins.groovy.lang.psi.stubs.impl.GrFileStubImpl; /** * @author ilyas @@ -27,7 +26,7 @@ import org.jetbrains.plugins.groovy.lang.psi.stubs.impl.GrFileStubImpl; public class GroovyFileStubBuilder extends DefaultStubBuilder { protected StubElement createStubForFile(final PsiFile file) { if (file instanceof GroovyFile && ((GroovyFile) file).isScript()) { - return new GrFileStubImpl((GroovyFile)file); + return new GrFileStub((GroovyFile)file); } return super.createStubForFile(file); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrFieldElementType.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrFieldElementType.java index b909c710c179..1fe39965b9f0 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrFieldElementType.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrFieldElementType.java @@ -21,16 +21,10 @@ import com.intellij.psi.stubs.IndexSink; import com.intellij.psi.stubs.StubElement; import com.intellij.psi.stubs.StubInputStream; import com.intellij.psi.stubs.StubOutputStream; -import com.intellij.util.ArrayUtil; -import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.io.StringRef; -import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.psi.GrStubElementType; -import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList; -import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotation; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField; -import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.GrFieldImpl; import org.jetbrains.plugins.groovy.lang.psi.stubs.GrFieldStub; import org.jetbrains.plugins.groovy.lang.psi.stubs.impl.GrFieldStubImpl; @@ -64,22 +58,7 @@ public class GrFieldElementType extends GrStubElementType } public GrFieldStub createStub(GrField psi, StubElement parentStub) { - final GrModifierList modifiers = psi.getModifierList(); - String[] annNames; - if (modifiers == null) { - annNames = ArrayUtil.EMPTY_STRING_ARRAY; - } - else { - final GrAnnotation[] annotations = modifiers.getAnnotations(); - annNames = ContainerUtil.map(annotations, new Function() { - @Nullable - public String fun(final GrAnnotation grAnnotation) { - final GrCodeReferenceElement element = grAnnotation.getClassReference(); - if (element == null) return null; - return element.getReferenceName(); - } - }, new String[annotations.length]); - } + String[] annNames = GrTypeDefinitionElementType.getAnnotationNames(psi); Set[] namedParametersArray = new Set[0]; if (psi instanceof GrFieldImpl){ diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrMethodElementType.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrMethodElementType.java index 94504b059250..c242f4fe2245 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrMethodElementType.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrMethodElementType.java @@ -20,15 +20,9 @@ import com.intellij.psi.stubs.IndexSink; import com.intellij.psi.stubs.StubElement; import com.intellij.psi.stubs.StubInputStream; import com.intellij.psi.stubs.StubOutputStream; -import com.intellij.util.Function; -import com.intellij.util.containers.ContainerUtil; import com.intellij.util.io.StringRef; -import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.psi.GrStubElementType; -import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList; -import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotation; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; -import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.typedef.members.GrMethodImpl; import org.jetbrains.plugins.groovy.lang.psi.stubs.GrMethodStub; import org.jetbrains.plugins.groovy.lang.psi.stubs.GrStubUtils; @@ -37,7 +31,9 @@ import org.jetbrains.plugins.groovy.lang.psi.stubs.index.GrAnnotatedMemberIndex; import org.jetbrains.plugins.groovy.lang.psi.stubs.index.GrMethodNameIndex; import java.io.IOException; -import java.util.*; +import java.util.HashSet; +import java.util.List; +import java.util.Set; /** * @author ilyas @@ -57,42 +53,22 @@ public class GrMethodElementType extends GrStubElementType() { - @Nullable - public String fun(final GrAnnotation grAnnotation) { - final GrCodeReferenceElement element = grAnnotation.getClassReference(); - if (element == null) return null; - return element.getReferenceName(); - } - }, new String[annotations.length]); - Set[] namedParametersArray; - namedParametersArray = psi.getNamedParametersArray(); - - return new GrMethodStubImpl(parentStub, StringRef.fromString(psi.getName()), annNames, namedParametersArray); + return new GrMethodStubImpl(parentStub, StringRef.fromString(psi.getName()), GrTypeDefinitionElementType.getAnnotationNames(psi), + psi.getNamedParametersArray()); } public void serialize(GrMethodStub stub, StubOutputStream dataStream) throws IOException { dataStream.writeName(stub.getName()); - final String[] annotations = stub.getAnnotations(); - dataStream.writeByte(annotations.length); - for (String s : annotations) { - dataStream.writeName(s); - } + GrStubUtils.writeStringArray(dataStream, stub.getAnnotations()); final Set[] namedParameters = stub.getNamedParameters(); GrStubUtils.serializeCollectionsArray(dataStream, namedParameters); } - + public GrMethodStub deserialize(StubInputStream dataStream, StubElement parentStub) throws IOException { StringRef ref = dataStream.readName(); - final byte b = dataStream.readByte(); - final String[] annNames = new String[b]; - for (int i = 0; i < b; i++) { - annNames[i] = dataStream.readName().toString(); - } + final String[] annNames = GrStubUtils.readStringArray(dataStream); final List> namedParametersSets = GrStubUtils.deserializeCollectionsArray(dataStream); return new GrMethodStubImpl(parentStub, ref, annNames, namedParametersSets.toArray(new HashSet[0])); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrStubFileElementType.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrStubFileElementType.java index 70a7da0e41a1..3dd1dec6fc2e 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrStubFileElementType.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrStubFileElementType.java @@ -21,8 +21,9 @@ import com.intellij.psi.stubs.*; import com.intellij.psi.tree.IStubFileElementType; import com.intellij.util.io.StringRef; import org.jetbrains.plugins.groovy.lang.psi.stubs.GrFileStub; +import org.jetbrains.plugins.groovy.lang.psi.stubs.GrStubUtils; import org.jetbrains.plugins.groovy.lang.psi.stubs.GroovyFileStubBuilder; -import org.jetbrains.plugins.groovy.lang.psi.stubs.impl.GrFileStubImpl; +import org.jetbrains.plugins.groovy.lang.psi.stubs.index.GrAnnotatedMemberIndex; import org.jetbrains.plugins.groovy.lang.psi.stubs.index.GrFullScriptNameIndex; import org.jetbrains.plugins.groovy.lang.psi.stubs.index.GrScriptClassNameIndex; @@ -43,7 +44,7 @@ public class GrStubFileElementType extends IStubFileElementType { @Override public int getStubVersion() { - return super.getStubVersion() + 5; + return super.getStubVersion() + 6; } public String getExternalId() { @@ -60,6 +61,7 @@ public class GrStubFileElementType extends IStubFileElementType { dataStream.writeName(stub.getPackageName().toString()); dataStream.writeName(stub.getName().toString()); dataStream.writeBoolean(stub.isScript()); + GrStubUtils.writeStringArray(dataStream, stub.getAnnotations()); } @Override @@ -67,7 +69,7 @@ public class GrStubFileElementType extends IStubFileElementType { StringRef packName = dataStream.readName(); StringRef name = dataStream.readName(); boolean isScript = dataStream.readBoolean(); - return new GrFileStubImpl(packName, name, isScript); + return new GrFileStub(packName, name, isScript, GrStubUtils.readStringArray(dataStream)); } public void indexStub(GrFileStub stub, IndexSink sink) { @@ -78,6 +80,10 @@ public class GrStubFileElementType extends IStubFileElementType { final String fqn = pName == null || pName.length() == 0 ? name : pName + "." + name; sink.occurrence(GrFullScriptNameIndex.KEY, fqn.hashCode()); } + + for (String anno : stub.getAnnotations()) { + sink.occurrence(GrAnnotatedMemberIndex.KEY, anno); + } } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrTypeDefinitionElementType.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrTypeDefinitionElementType.java index 5cb77de6737b..3caa45dfa5f4 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrTypeDefinitionElementType.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrTypeDefinitionElementType.java @@ -17,6 +17,7 @@ package org.jetbrains.plugins.groovy.lang.psi.stubs.elements; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiModifierList; +import com.intellij.psi.PsiModifierListOwner; import com.intellij.psi.PsiNameHelper; import com.intellij.psi.impl.java.stubs.index.JavaFullClassNameIndex; import com.intellij.psi.impl.java.stubs.index.JavaShortClassNameIndex; @@ -59,7 +60,7 @@ public abstract class GrTypeDefinitionElementType annoNames = CollectionFactory.arrayList(); final PsiModifierList modifierList = psi.getModifierList(); if (modifierList instanceof GrModifierList) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/impl/GrFileStubImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/impl/GrFileStubImpl.java deleted file mode 100644 index 6a810c1247ec..000000000000 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/impl/GrFileStubImpl.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2000-2009 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.plugins.groovy.lang.psi.stubs.impl; - -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.stubs.PsiFileStubImpl; -import com.intellij.psi.tree.IStubFileElementType; -import com.intellij.util.io.StringRef; -import org.jetbrains.plugins.groovy.lang.parser.GroovyParserDefinition; -import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; -import org.jetbrains.plugins.groovy.lang.psi.stubs.GrFileStub; - -/** - * @author ilyas - */ -public class GrFileStubImpl extends PsiFileStubImpl implements GrFileStub { - private final StringRef myPackageName; - private final StringRef myName; - private final boolean isScript; - - public GrFileStubImpl(GroovyFile file) { - super(file); - myPackageName = StringRef.fromString(file.getPackageName()); - myName = StringRef.fromString(StringUtil.trimEnd(file.getName(), ".groovy")); - isScript = file.isScript(); - } - - public GrFileStubImpl(StringRef packName, StringRef name, boolean isScript) { - super(null); - myPackageName = packName; - myName = name; - this.isScript = isScript; - } - - public IStubFileElementType getType() { - return GroovyParserDefinition.GROOVY_FILE; - } - - public StringRef getPackageName() { - return myPackageName; - } - - public StringRef getName() { - return myName; - } - - public boolean isScript() { - return isScript; - } -} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/index/GrAnnotatedMemberIndex.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/index/GrAnnotatedMemberIndex.java index 1b8a5368bd1f..ec2e377773aa 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/index/GrAnnotatedMemberIndex.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/index/GrAnnotatedMemberIndex.java @@ -15,17 +15,17 @@ */ package org.jetbrains.plugins.groovy.lang.psi.stubs.index; -import com.intellij.psi.PsiMember; +import com.intellij.psi.PsiElement; import com.intellij.psi.stubs.StringStubIndexExtension; import com.intellij.psi.stubs.StubIndexKey; /** * @author ilyas */ -public class GrAnnotatedMemberIndex extends StringStubIndexExtension { - public static final StubIndexKey KEY = StubIndexKey.createIndexKey("gr.annot.members"); +public class GrAnnotatedMemberIndex extends StringStubIndexExtension { + public static final StubIndexKey KEY = StubIndexKey.createIndexKey("gr.annot.members"); - public StubIndexKey getKey() { + public StubIndexKey getKey() { return KEY; } } \ No newline at end of file diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java index 640357cce719..c4b5a4ceb29d 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java @@ -66,6 +66,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.types.GrClosureSignature; import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement; import org.jetbrains.plugins.groovy.lang.psi.impl.GrClosureType; import org.jetbrains.plugins.groovy.lang.psi.impl.GrMapType; +import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyResolveResultImpl; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GroovyScriptClass; import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.JavaIdentifier; @@ -939,4 +940,18 @@ public class PsiUtil { } return (GrReferenceExpression)replaced; } + + public static GroovyResolveResult[] getConstructorCandidates(PsiClassType classType, PsiType[] argTypes, GroovyPsiElement context) { + final PsiClassType.ClassResolveResult resolveResult = classType.resolveGenerics(); + final PsiClass psiClass = resolveResult.getElement(); + final PsiSubstitutor substitutor = resolveResult.getSubstitutor(); + if (psiClass == null) { + return GroovyResolveResult.EMPTY_ARRAY; + } + + final GroovyResolveResult grResult = resolveResult instanceof GroovyResolveResult + ? (GroovyResolveResult)resolveResult + : new GroovyResolveResultImpl(psiClass, context, substitutor, true, true); + return getConstructorCandidates(context, new GroovyResolveResult[]{grResult}, argTypes); + } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/stubs/GroovyCacheUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/stubs/GroovyCacheUtil.java deleted file mode 100644 index 90484d43d8a9..000000000000 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/stubs/GroovyCacheUtil.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2000-2009 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.plugins.groovy.lang.stubs; - -import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiMember; -import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.stubs.StubIndex; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.plugins.groovy.lang.psi.GrClassSubstitutor; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrAnonymousClassDefinition; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrReferenceList; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMember; -import org.jetbrains.plugins.groovy.lang.psi.stubs.index.GrAnnotatedMemberIndex; -import org.jetbrains.plugins.groovy.lang.psi.stubs.index.GrAnonymousClassIndex; -import org.jetbrains.plugins.groovy.lang.psi.stubs.index.GrDirectInheritorsIndex; - -import java.util.ArrayList; -import java.util.Collection; - -/** - * @author ilyas - */ -public abstract class GroovyCacheUtil { - - @NotNull - public static PsiMember[] getAnnotatedMemberCandidates(PsiClass clazz, GlobalSearchScope scope) { - final String name = clazz.getName(); - if (name == null) return GrMember.EMPTY_ARRAY; - final Collection members = StubIndex.getInstance().get(GrAnnotatedMemberIndex.KEY, name, clazz.getProject(), scope); - return members.toArray(new PsiMember[members.size()]); - } - - @NotNull - public static PsiClass[] getDeriverCandidates(PsiClass clazz, GlobalSearchScope scope) { - final String name = clazz.getName(); - if (name == null) return GrTypeDefinition.EMPTY_ARRAY; - final ArrayList inheritors = new ArrayList(); - final Collection refLists = StubIndex.getInstance().get(GrDirectInheritorsIndex.KEY, name, clazz.getProject(), scope); - for (GrReferenceList list : refLists) { - final PsiElement parent = list.getParent(); - if (parent instanceof GrTypeDefinition) { - inheritors.add(GrClassSubstitutor.getSubstitutedClass(((GrTypeDefinition)parent))); - } - } - final Collection classes = - StubIndex.getInstance().get(GrAnonymousClassIndex.KEY, name, clazz.getProject(), scope); - for (GrAnonymousClassDefinition aClass : classes) { - inheritors.add(aClass); - } - return inheritors.toArray(new PsiClass[inheritors.size()]); - } - - -} diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/LiteralConstructorUsagesTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/LiteralConstructorUsagesTest.groovy new file mode 100644 index 000000000000..00432a8cb4d0 --- /dev/null +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/LiteralConstructorUsagesTest.groovy @@ -0,0 +1,103 @@ +package org.jetbrains.plugins.groovy.lang + +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase +import com.intellij.psi.search.searches.ReferencesSearch + +/** + * @author peter + */ +class LiteralConstructorUsagesTest extends LightCodeInsightFixtureTestCase { + @Override protected void setUp() { + super.setUp(); + myFixture.addClass("package groovy.lang; public @interface Typed {}"); + } + + public void testList_Variable() throws Exception { + def foo = myFixture.addClass("""class Foo { + Foo() {} + } +""") + myFixture.addFileToProject "a.groovy", "Foo x = []" + assertOneElement(ReferencesSearch.search(foo.constructors[0]).findAll()) + } + + public void testList_ReturnValue() throws Exception { + def foo = myFixture.addClass("""class Foo { + Foo() {} + } +""") + myFixture.addFileToProject "a.groovy", "Foo foo() { if (true) [] else return [] }" + assertEquals(2, ReferencesSearch.search(foo.constructors[0]).findAll().size()) + } + + public void testList_Cast() throws Exception { + def foo = myFixture.addClass("""class Foo { + Foo() {} + } +""") + myFixture.addFileToProject "a.groovy", "def x = (Foo) []" + assertOneElement(ReferencesSearch.search(foo.constructors[0]).findAll()) + } + + public void testList_AsCast() throws Exception { + def foo = myFixture.addClass("""class Foo { + Foo() {} + } +}""") + myFixture.addFileToProject "a.groovy", "def x = [] as Foo" + assertOneElement(ReferencesSearch.search(foo.constructors[0]).findAll()) + } + + public void testList_GppMethodCall() throws Exception { + //------------------------declarations + def foo = myFixture.addClass(""" + package z; + class Foo { + public Foo() {} + } + """) + + myFixture.addClass(""" + package z; + public class Bar { + public static void giveMeFoo(int a, Foo f) {} + } +""") + myFixture.addFileToProject("Decl.groovy", "static def giveMeFooAsWell(z.Foo f) {}") + + //----------------------usages + myFixture.addFileToProject "a.gpp", "z.Bar.giveMeFoo(2, []) //usage" + myFixture.addFileToProject "b.groovy", """ + @Typed package aa; + z.Bar.giveMeFoo(3, []) //usage + """ + myFixture.addFileToProject "c.groovy", """ + @Typed def someMethod() { + z.Bar.giveMeFoo 4, [] //usage + Decl.giveMeFooAsWell([]) //usage + } + z.Bar.giveMeFoo 5, [] //non-typed context + Decl.giveMeFooAsWell([]) + """ + myFixture.addFileToProject "invalid.gpp", "z.Bar.giveMeFoo 42, 239, []" + myFixture.addFileToProject "nonGpp.groovy", "z.Bar.giveMeFoo(6, [])" + assertEquals(4, ReferencesSearch.search(foo.constructors[0]).findAll().size()) + } + + public void testList_GppConstructorCallWithSeveralParameters() throws Exception { + def foo = myFixture.addClass(""" + class Foo { + Foo() {} + } + """) + + myFixture.addClass(""" + class Bar { + Bar(Foo f1, Foo f2, Foo f3) {} + } + """) + myFixture.addFileToProject "a.gpp", "new Bar([],[],[])" + assertEquals(3, ReferencesSearch.search(foo.constructors[0]).findAll().size()) + } + +} diff --git a/plugins/groovy/testdata/groovy/controlFlow/if1.test b/plugins/groovy/testdata/groovy/controlFlow/if1.test index a88e8969eb44..f4dc6626de6c 100644 --- a/plugins/groovy/testdata/groovy/controlFlow/if1.test +++ b/plugins/groovy/testdata/groovy/controlFlow/if1.test @@ -7,6 +7,7 @@ if (true) { 0(1) element: null 1(2,4) element: IF statement 2(3) WRITE a -3(5) element: Assignment expression MAYBE_RETURN +3(6) element: Assignment expression MAYBE_RETURN 4(5) WRITE a -5() element: null \ No newline at end of file +5(6) element: Assignment expression MAYBE_RETURN +6() element: null \ No newline at end of file diff --git a/plugins/groovy/testdata/groovy/controlFlow/ifInstanceofElse.test b/plugins/groovy/testdata/groovy/controlFlow/ifInstanceofElse.test index 7851696d58a6..e1f89d70302f 100644 --- a/plugins/groovy/testdata/groovy/controlFlow/ifInstanceofElse.test +++ b/plugins/groovy/testdata/groovy/controlFlow/ifInstanceofElse.test @@ -3,17 +3,20 @@ else if (!(o instanceof Integer)) b = 2 else b = 3 ----- 0(1) element: null -1(2,5) element: IF statement +1(2,6) element: IF statement 2(3) READ o 3(4) assertion: o instanceof String -4(14) WRITE b -5(6) READ o -6(7) assertion: ! o instanceof String -7(8,11) element: IF statement -8(9) READ o -9(10) assertion: ! o instanceof Integer -10(14) WRITE b -11(12) READ o -12(13) assertion: o instanceof Integer -13(14) WRITE b -14() element: null \ No newline at end of file +4(5) WRITE b +5(17) element: Assignment expression MAYBE_RETURN +6(7) READ o +7(8) assertion: ! o instanceof String +8(9,13) element: IF statement +9(10) READ o +10(11) assertion: ! o instanceof Integer +11(12) WRITE b +12(17) element: Assignment expression MAYBE_RETURN +13(14) READ o +14(15) assertion: o instanceof Integer +15(16) WRITE b +16(17) element: Assignment expression MAYBE_RETURN +17() element: null \ No newline at end of file diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/references/MavenPropertyPsiReference.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/references/MavenPropertyPsiReference.java index a8932a636b07..8f53098da26b 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/references/MavenPropertyPsiReference.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/references/MavenPropertyPsiReference.java @@ -182,6 +182,7 @@ public class MavenPropertyPsiReference extends MavenPsiReference { result = new MavenDomProjectProcessorUtils.DomParentProjectFileProcessor(myProjectsManager) { protected PsiElement doProcessParent(VirtualFile parentFile) { MavenDomProjectModel parentProjectDom = MavenDomUtil.getMavenDomProjectModel(myProject, parentFile); + if (parentProjectDom == null) return null; return resolveModelProperty(parentProjectDom, path, recursionGuard); } }.process(projectDom); diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/navigator/actions/MavenTreeAction.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/navigator/actions/MavenTreeAction.java index 18a21ec70d08..a9a4c6d06a8b 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/navigator/actions/MavenTreeAction.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/navigator/actions/MavenTreeAction.java @@ -21,15 +21,17 @@ import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.Shortcut; import com.intellij.openapi.keymap.Keymap; import com.intellij.openapi.keymap.KeymapManager; +import com.intellij.openapi.project.DumbAware; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.maven.utils.MavenDataKeys; +import org.jetbrains.idea.maven.utils.actions.MavenActionUtil; import javax.swing.*; /** * @author Konstantin Bulenkov */ -public abstract class MavenTreeAction extends AnAction { +public abstract class MavenTreeAction extends AnAction implements DumbAware { private boolean updated = false; @Override @@ -49,6 +51,8 @@ public abstract class MavenTreeAction extends AnAction { } updated = true; } + e.getPresentation().setEnabled(MavenActionUtil.hasProject(e.getDataContext()) + && MavenActionUtil.getProjectsManager(e.getDataContext()).isMavenizedProject()); } @Nullable diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManager.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManager.java index 6612d595c6cc..8c87b9318323 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManager.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenProjectsManager.java @@ -224,7 +224,7 @@ public class MavenProjectsManager extends SimpleProjectComponent myProjectsTree.save(getProjectsTreeFile()); } catch (IOException e) { - MavenLog.LOG.error(e); + MavenLog.LOG.warn(e); } } } diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/wizards/MavenModuleBuilderHelper.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/wizards/MavenModuleBuilderHelper.java index 4493fb80522b..f965aa5cf0bc 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/wizards/MavenModuleBuilderHelper.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/wizards/MavenModuleBuilderHelper.java @@ -125,6 +125,8 @@ public class MavenModuleBuilderHelper { // execute when current dialog is closed (e.g. Project Structure) MavenUtil.invokeLater(project, ModalityState.NON_MODAL, new Runnable() { public void run() { + if (!pom.isValid()) return; + EditorHelper.openInEditor(getPsiFile(project, pom)); if (myArchetype != null) generateFromArchetype(project, pom); } diff --git a/plugins/maven/src/main/resources/META-INF/plugin.xml b/plugins/maven/src/main/resources/META-INF/plugin.xml index deba1a0b35b3..b436d612e248 100644 --- a/plugins/maven/src/main/resources/META-INF/plugin.xml +++ b/plugins/maven/src/main/resources/META-INF/plugin.xml @@ -157,12 +157,12 @@ diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java index 5d2c0ae24082..4dc1cd824f24 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java @@ -616,11 +616,17 @@ public class SvnUtil { } public static boolean doesRepositorySupportMergeinfo(final SvnVcs vcs, final SVNURL url) { + SVNRepository repository = null; try { - return vcs.createRepository(url).hasCapability(SVNCapability.MERGE_INFO); + repository = vcs.createRepository(url); + return repository.hasCapability(SVNCapability.MERGE_INFO); } catch (SVNException e) { return false; + } finally { + if (repository != null) { + repository.closeSession(); + } } } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/CompareWithBranchAction.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/CompareWithBranchAction.java index f95e4eb46ef5..8a54444ffc19 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/CompareWithBranchAction.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/CompareWithBranchAction.java @@ -133,6 +133,8 @@ public class CompareWithBranchAction extends AnAction implements DumbAware { SVNWCAccess wcAccess = vcs.createWCAccess(); + SVNRepository repository = null; + SVNRepository repository2 = null; try { SVNAdminAreaInfo info = wcAccess.openAnchor(new File(myVirtualFile.getPath()), false, SVNWCAccess.INFINITE_DEPTH); File anchorPath = info.getAnchor().getRoot(); @@ -150,18 +152,25 @@ public class CompareWithBranchAction extends AnAction implements DumbAware { } SVNURL anchorURL = anchorEntry.getSVNURL(); - SVNRepository repository = vcs.createRepository(anchorURL.toString()); + repository = vcs.createRepository(anchorURL.toString()); SVNReporter reporter = new SVNReporter(info, info.getAnchor().getFile(info.getTargetName()), false, true, SVNDepth.INFINITY, false, false, true, SVNDebugLog.getDefaultLog()); long rev = repository.getLatestRevision(); + repository2 = vcs.createRepository((target == null) ? url.toString() : url.removePathTail().toString()); SvnDiffEditor diffEditor = new SvnDiffEditor((target == null) ? myVirtualFile : myVirtualFile.getParent(), - vcs.createRepository((target == null) ? url.toString() : url.removePathTail().toString()), rev, true); + repository2, rev, true); repository.diff(url, rev, rev, target, true, true, false, reporter, SVNCancellableEditor.newInstance(diffEditor, new SvnProgressCanceller(), null)); changes.addAll(diffEditor.getChangesMap().values()); } finally { wcAccess.close(); + if (repository != null) { + repository.closeSession(); + } + if (repository2 != null) { + repository2.closeSession(); + } } } catch(SVNCancelException ex) { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/config/SvnConfigureProxiesDialog.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/config/SvnConfigureProxiesDialog.java index 04f5fd461595..c41d23cd59c5 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/config/SvnConfigureProxiesDialog.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/config/SvnConfigureProxiesDialog.java @@ -27,6 +27,7 @@ import org.jetbrains.idea.svn.SvnConfiguration; import org.jetbrains.idea.svn.SvnServerFileManager; import org.jetbrains.idea.svn.SvnVcs; import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.io.SVNRepository; import javax.swing.*; import java.awt.*; @@ -137,10 +138,16 @@ public class SvnConfigureProxiesDialog extends DialogWrapper implements Validati if (pi != null) { pi.setText("Connecting to " + url); } + SVNRepository repository = null; try { - SvnVcs.getInstance(myProject).createRepository(url).testConnection(); + repository = SvnVcs.getInstance(myProject).createRepository(url); + repository.testConnection(); } catch (SVNException exc) { excRef.set(exc); + } finally { + if (repository != null) { + repository.closeSession(); + } } } }, "Test connection", true, myProject); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/BranchMerger.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/BranchMerger.java index e842e3ea602a..bd6f96a3b02b 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/BranchMerger.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/BranchMerger.java @@ -23,6 +23,7 @@ import org.jetbrains.idea.svn.update.UpdateEventHandler; import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNURL; +import org.tmatesoft.svn.core.io.SVNRepository; import org.tmatesoft.svn.core.wc.SVNDiffClient; import org.tmatesoft.svn.core.wc.SVNDiffOptions; import org.tmatesoft.svn.core.wc.SVNRevision; @@ -55,11 +56,17 @@ public class BranchMerger implements IMerger { myBranchName = branchName; mySourceCopyRevision = sourceCopyRevision; myAtStart = true; + SVNRepository repository = null; try { - mySourceLatestRevision = myVcs.createRepository(mySourceUrl).getLatestRevision(); + repository = myVcs.createRepository(mySourceUrl); + mySourceLatestRevision = repository.getLatestRevision(); } catch (SVNException e) { mySourceLatestRevision = SVNRevision.HEAD.getNumber(); + } finally { + if (repository != null) { + repository.closeSession(); + } } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/RepositoryBrowserDialog.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/RepositoryBrowserDialog.java index 643064ad4fa8..0e7d36a4b242 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/RepositoryBrowserDialog.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/RepositoryBrowserDialog.java @@ -1099,10 +1099,13 @@ public class RepositoryBrowserDialog extends DialogWrapper { SVNRepository sourceRepository = myVCS.createRepository(sourceURL.toString()); sourceRepository.setCanceller(new SvnProgressCanceller()); SvnDiffEditor diffEditor; - final long rev = sourceRepository.getLatestRevision(); + final long rev; + SVNRepository targetRepository = null; try { + rev = sourceRepository.getLatestRevision(); // generate Map of path->Change - diffEditor = new SvnDiffEditor(sourceRepository, myVCS.createRepository(targetURL.toString()), -1, false); + targetRepository = myVCS.createRepository(targetURL.toString()); + diffEditor = new SvnDiffEditor(sourceRepository, targetRepository, -1, false); final ISVNEditor cancellableEditor = SVNCancellableEditor.newInstance(diffEditor, new SvnProgressCanceller(), null); sourceRepository.diff(targetURL, rev, rev, null, true, true, false, new ISVNReporterBaton() { public void report(ISVNReporter reporter) throws SVNException { @@ -1113,6 +1116,9 @@ public class RepositoryBrowserDialog extends DialogWrapper { } finally { sourceRepository.closeSession(); + if (targetRepository != null) { + targetRepository.closeSession(); + } } final String sourceTitle = SVNPathUtil.tail(sourceURL.toString()); final String targetTitle = SVNPathUtil.tail(targetURL.toString()); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SelectLocationDialog.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SelectLocationDialog.java index 076135792f03..01186c0b1f3e 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SelectLocationDialog.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SelectLocationDialog.java @@ -120,12 +120,16 @@ public class SelectLocationDialog extends DialogWrapper { ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() { public void run() { + SVNRepository repos = null; try { - SVNRepository repos = SvnVcs.getInstance(project).createRepository(urlString); + repos = SvnVcs.getInstance(project).createRepository(urlString); result.set(repos.getRepositoryRoot(true)); - repos.closeSession(); } catch (SVNException e) { excRef.set(e); + } finally { + if (repos != null) { + repos.closeSession(); + } } } }, "Detecting repository root", true, project); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnCommittedChangesProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnCommittedChangesProvider.java index 9ff2b64db560..45e7114caedb 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnCommittedChangesProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnCommittedChangesProvider.java @@ -184,13 +184,17 @@ public class SvnCommittedChangesProvider implements CachingCommittedChangesProvi } final String repositoryRoot; + SVNRepository repository = null; try { - final SVNRepository repository = myVcs.createRepository(svnLocation.getURL()); + repository = myVcs.createRepository(svnLocation.getURL()); repositoryRoot = repository.getRepositoryRoot(true).toString(); - repository.closeSession(); } catch (SVNException e) { throw new VcsException(e); + } finally { + if (repository != null) { + repository.closeSession(); + } } final ChangeBrowserSettings.Filter filter = settings.createFilter(); @@ -219,13 +223,18 @@ public class SvnCommittedChangesProvider implements CachingCommittedChangesProvi } final String repositoryRoot; + SVNRepository repository = null; try { - final SVNRepository repository = myVcs.createRepository(svnLocation.getURL()); + repository = myVcs.createRepository(svnLocation.getURL()); repositoryRoot = repository.getRepositoryRoot(true).toString(); repository.closeSession(); } catch (SVNException e) { throw new VcsException(e); + } finally { + if (repository != null) { + repository.closeSession(); + } } getCommittedChangesImpl(settings, svnLocation.getURL(), new String[]{""}, maxCount, new Consumer() { @@ -249,13 +258,17 @@ public class SvnCommittedChangesProvider implements CachingCommittedChangesProvi } final String repositoryRoot; + SVNRepository repository = null; try { - final SVNRepository repository = myVcs.createRepository(svnLocation.getURL()); + repository = myVcs.createRepository(svnLocation.getURL()); repositoryRoot = repository.getRepositoryRoot(true).toString(); - repository.closeSession(); } catch (SVNException e) { throw new VcsException(e); + } finally { + if (repository != null) { + repository.closeSession(); + } } final MergeTrackerProxy proxy = new MergeTrackerProxy(new Consumer>() { @@ -356,7 +369,17 @@ public class SvnCommittedChangesProvider implements CachingCommittedChangesProvi revisionBefore = SVNRevision.create(changeTo.longValue()); } else { - revisionBefore = SVNRevision.create(myVcs.createRepository(url).getLatestRevision()); + SVNRepository repository = null; + final long revision; + try { + repository = myVcs.createRepository(url); + revision = repository.getLatestRevision(); + } finally { + if (repository != null) { + repository.closeSession(); + } + } + revisionBefore = SVNRevision.create(revision); } final SVNRevision revisionAfter; if (dateFrom != null) { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnIntegrateEnvironment.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnIntegrateEnvironment.java index af66563a46c5..11d5c124c53b 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnIntegrateEnvironment.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnIntegrateEnvironment.java @@ -20,10 +20,10 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.update.UpdatedFiles; +import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.SvnBundle; import org.jetbrains.idea.svn.SvnConfiguration; import org.jetbrains.idea.svn.SvnVcs; -import org.jetbrains.annotations.Nullable; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.io.SVNRepository; @@ -131,14 +131,18 @@ public class SvnIntegrateEnvironment extends AbstractSvnUpdateIntegrateEnvironme } else { + SVNRepository repos = null; try { - SVNRepository repos = myVcs.createRepository(svnURL2.toString()); + repos = myVcs.createRepository(svnURL2.toString()); final long latestRev = repos.getLatestRevision(); - repos.closeSession(); return String.valueOf(latestRev); } catch (SVNException e) { return null; + } finally { + if (repos != null) { + repos.closeSession(); + } } } } diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index 987ca08568ee..9b3fcab5a73e 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -252,6 +252,7 @@ + diff --git a/samples/comparingReferences/source/com/intellij/codeInspection/ComparingReferencesInspection.java b/samples/comparingReferences/source/com/intellij/codeInspection/ComparingReferencesInspection.java index 5532b0b6e44a..4c99465a2176 100644 --- a/samples/comparingReferences/source/com/intellij/codeInspection/ComparingReferencesInspection.java +++ b/samples/comparingReferences/source/com/intellij/codeInspection/ComparingReferencesInspection.java @@ -3,6 +3,7 @@ package com.intellij.codeInspection; import com.intellij.codeInsight.daemon.GroupNames; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Ref; import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; import com.intellij.ui.DocumentAdapter; @@ -78,7 +79,7 @@ public class ComparingReferencesInspection extends BaseJavaLocalInspectionTool { private ProblemDescriptor[] analyzeCode(PsiElement where, final InspectionManager manager) { if (where == null) return null; - final ArrayList[] problemList = new ArrayList[]{null}; + final Ref> problemList = new Ref>(); where.accept(new JavaRecursiveElementWalkingVisitor() { @Override public void visitMethod(PsiMethod method) {} @@ -96,18 +97,18 @@ public class ComparingReferencesInspection extends BaseJavaLocalInspectionTool { PsiType rType = rOperand.getType(); if (isCheckedType(lType) || isCheckedType(rType)) { - if (problemList[0] == null) problemList[0] = new ArrayList(); - problemList[0].add(manager.createProblemDescriptor(expression, DESCRIPTION_TEMPLATE, + if (problemList.get() == null) problemList.set(new ArrayList()); + problemList.get().add(manager.createProblemDescriptor(expression, DESCRIPTION_TEMPLATE, myQuickFix, - ProblemHighlightType.GENERIC_ERROR_OR_WARNING, onTheFly)); + ProblemHighlightType.GENERIC_ERROR_OR_WARNING)); } } } }); - return problemList[0] == null + return problemList.get() == null ? null - : (ProblemDescriptor[])problemList[0].toArray(new ProblemDescriptor[problemList[0].size()]); + : problemList.get().toArray(new ProblemDescriptor[problemList.get().size()]); } private static boolean isNullLiteral(PsiExpression expr) { diff --git a/xml/dom-impl/src/com/intellij/util/xml/highlighting/DomElementsErrorPanel.java b/xml/dom-impl/src/com/intellij/util/xml/highlighting/DomElementsErrorPanel.java index 294ef8b0231d..90ad7cf85686 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/highlighting/DomElementsErrorPanel.java +++ b/xml/dom-impl/src/com/intellij/util/xml/highlighting/DomElementsErrorPanel.java @@ -162,7 +162,7 @@ public class DomElementsErrorPanel extends JPanel implements CommittablePanel, H } @Override - protected void fillDaemonCodeAnalyzerErrorsSatus(DaemonCodeAnalyzerStatus status, boolean fillErrorsCount) { + protected void fillDaemonCodeAnalyzerErrorsStatus(DaemonCodeAnalyzerStatus status, boolean fillErrorsCount) { for (int i = 0; i < status.errorCount.length; i++) { final HighlightSeverity minSeverity = SeverityRegistrar.getInstance(myProject).getSeverityByIndex(i); if (minSeverity == null) {