diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/LayoutTreeComponent.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/LayoutTreeComponent.java index 3660a3ed79fc..acc0d18b0ce4 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/LayoutTreeComponent.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/LayoutTreeComponent.java @@ -538,6 +538,20 @@ public class LayoutTreeComponent implements DnDTarget, Disposable { updateAndSelect(myTree.getRootPackagingNode(), toSelect); } + public void putElements(@NotNull final String path, @NotNull final List> elements) { + final List> toSelect = new ArrayList>(); + editLayout(new Runnable() { + @Override + public void run() { + final CompositePackagingElement directory = + PackagingElementFactory.getInstance().getOrCreateDirectory(getArtifact().getRootElement(), path); + toSelect.addAll(directory.addOrFindChildren(elements)); + } + }); + myArtifactsEditor.getSourceItemsTree().rebuildTree(); + updateAndSelect(myTree.getRootPackagingNode(), toSelect); + } + public void packInto(@NotNull final List items, final String pathToJar) { final List> toSelect = new ArrayList>(); final CompositePackagingElement rootElement = getArtifact().getRootElement(); diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/SourceItemsTree.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/SourceItemsTree.java index 9035cca02fc9..10285fb6a09e 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/SourceItemsTree.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/SourceItemsTree.java @@ -73,6 +73,7 @@ public class SourceItemsTree extends SimpleDnDAwareTree implements AdvancedDnDSo group.add(new PutSourceItemIntoDefaultLocationAction(this, myArtifactsEditor)); group.add(new PackAndPutIntoDefaultLocationAction(this, myArtifactsEditor)); group.add(new PutSourceItemIntoParentAndLinkViaManifestAction(this, myArtifactsEditor)); + group.add(new ExtractIntoDefaultLocationAction(this, myArtifactsEditor)); group.add(Separator.getInstance()); group.add(new SourceItemNavigateAction(this)); diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/actions/ExtractIntoDefaultLocationAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/actions/ExtractIntoDefaultLocationAction.java new file mode 100644 index 000000000000..f373216efdb9 --- /dev/null +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/actions/ExtractIntoDefaultLocationAction.java @@ -0,0 +1,96 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.roots.ui.configuration.artifacts.sourceItems.actions; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.Presentation; +import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactEditorEx; +import com.intellij.openapi.roots.ui.configuration.artifacts.sourceItems.SourceItemsTree; +import com.intellij.openapi.vfs.JarFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.packaging.elements.PackagingElement; +import com.intellij.packaging.elements.PackagingElementFactory; +import com.intellij.packaging.elements.PackagingElementOutputKind; +import com.intellij.packaging.impl.artifacts.ArtifactUtil; +import com.intellij.packaging.impl.artifacts.PackagingElementPath; +import com.intellij.packaging.impl.artifacts.PackagingElementProcessor; +import com.intellij.packaging.impl.elements.FileCopyPackagingElement; +import com.intellij.packaging.ui.ArtifactEditorContext; +import com.intellij.packaging.ui.PackagingSourceItem; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * @author nik + */ +public class ExtractIntoDefaultLocationAction extends PutIntoDefaultLocationActionBase { + public ExtractIntoDefaultLocationAction(SourceItemsTree sourceItemsTree, ArtifactEditorEx artifactEditor) { + super(sourceItemsTree, artifactEditor); + } + + @Override + public void update(AnActionEvent e) { + final String pathForClasses = myArtifactEditor.getArtifact().getArtifactType().getDefaultPathFor(PackagingElementOutputKind.DIRECTORIES_WITH_CLASSES); + final Presentation presentation = e.getPresentation(); + if (onlyJarsSelected() && pathForClasses != null) { + presentation.setText("Extract Into " + getTargetLocationText(Collections.singleton(pathForClasses))); + presentation.setVisible(true); + } + else { + presentation.setVisible(false); + } + } + + private boolean onlyJarsSelected() { + for (PackagingSourceItem item : mySourceItemsTree.getSelectedItems()) { + if (item.isProvideElements() && (!item.getKindOfProducedElements().containsJarFiles() || item.getKindOfProducedElements().containsDirectoriesWithClasses())) { + return false; + } + } + return true; + } + + @Override + public void actionPerformed(AnActionEvent e) { + final String pathForClasses = myArtifactEditor.getArtifact().getArtifactType().getDefaultPathFor(PackagingElementOutputKind.DIRECTORIES_WITH_CLASSES); + if (pathForClasses != null) { + final List> extracted = new ArrayList>(); + for (PackagingSourceItem item : mySourceItemsTree.getSelectedItems()) { + final ArtifactEditorContext context = myArtifactEditor.getContext(); + final List> elements = item.createElements(context); + ArtifactUtil.processElementsWithSubstitutions(elements, context, context.getArtifactType(), PackagingElementPath.EMPTY, new PackagingElementProcessor>() { + @Override + public boolean process(@NotNull PackagingElement element, @NotNull PackagingElementPath path) { + if (element instanceof FileCopyPackagingElement) { + final VirtualFile file = ((FileCopyPackagingElement)element).findFile(); + if (file != null) { + final VirtualFile jarRoot = JarFileSystem.getInstance().getJarRootForLocalFile(file); + if (jarRoot != null) { + extracted.add(PackagingElementFactory.getInstance().createExtractedDirectory(jarRoot)); + } + } + } + return true; + } + }); + } + myArtifactEditor.getLayoutTreeComponent().putElements(pathForClasses, extracted); + } + } +} diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/actions/PutIntoDefaultLocationActionBase.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/actions/PutIntoDefaultLocationActionBase.java index 32e9faaeb24b..a0f1c9f4e388 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/actions/PutIntoDefaultLocationActionBase.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/actions/PutIntoDefaultLocationActionBase.java @@ -18,9 +18,12 @@ package com.intellij.openapi.roots.ui.configuration.artifacts.sourceItems.action import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactEditorEx; import com.intellij.openapi.roots.ui.configuration.artifacts.sourceItems.SourceItemsTree; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.packaging.ui.PackagingSourceItem; import org.jetbrains.annotations.Nullable; +import java.util.Set; + /** * @author nik */ @@ -37,4 +40,21 @@ public abstract class PutIntoDefaultLocationActionBase extends AnAction { protected String getDefaultPath(PackagingSourceItem item) { return myArtifactEditor.getArtifact().getArtifactType().getDefaultPathFor(item); } + + protected static String getTargetLocationText(Set paths) { + String target; + if (paths.size() == 1) { + final String path = StringUtil.trimStart(StringUtil.trimEnd(paths.iterator().next(), "/"), "/"); + if (path.length() > 0) { + target = "/" + path; + } + else { + target = "Output Root"; + } + } + else { + target = "Default Locations"; + } + return target; + } } diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/actions/PutSourceItemIntoDefaultLocationAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/actions/PutSourceItemIntoDefaultLocationAction.java index d2efd0086a95..a207e8df32e7 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/actions/PutSourceItemIntoDefaultLocationAction.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/artifacts/sourceItems/actions/PutSourceItemIntoDefaultLocationAction.java @@ -50,20 +50,7 @@ public class PutSourceItemIntoDefaultLocationAction extends PutIntoDefaultLocati } paths.add(StringUtil.trimStart(StringUtil.trimEnd(path, "/"), "/")); } - String target; - if (paths.size() == 1) { - final String path = paths.iterator().next(); - if (path.length() > 0) { - target = "/" + path; - } - else { - target = "output root"; - } - } - else { - target = "default locations"; - } - presentation.setText("Put into " + target); + presentation.setText("Put into " + getTargetLocationText(paths)); } presentation.setVisible(enabled); presentation.setEnabled(enabled); diff --git a/java/java-impl/src/com/intellij/analysis/PackagesScopesProvider.java b/java/java-impl/src/com/intellij/analysis/PackagesScopesProvider.java index 5325ef78d750..a3103855813c 100644 --- a/java/java-impl/src/com/intellij/analysis/PackagesScopesProvider.java +++ b/java/java-impl/src/com/intellij/analysis/PackagesScopesProvider.java @@ -23,7 +23,7 @@ package com.intellij.analysis; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.project.Project; import com.intellij.psi.search.scope.ProjectProductionScope; -import com.intellij.psi.search.scope.packageSet.CustomScopesProvider; +import com.intellij.psi.search.scope.packageSet.CustomScopesProviderEx; import com.intellij.psi.search.scope.packageSet.NamedScope; import org.jetbrains.annotations.NotNull; @@ -33,7 +33,7 @@ import java.util.List; /** * @author Konstantin Bulenkov */ -public class PackagesScopesProvider implements CustomScopesProvider { +public class PackagesScopesProvider extends CustomScopesProviderEx { private final NamedScope myProjectProductionScope; private final List myScopes; diff --git a/java/java-impl/src/com/intellij/codeInspection/deprecation/DeprecationInspection.java b/java/java-impl/src/com/intellij/codeInspection/deprecation/DeprecationInspection.java index c22621cfccba..1bfadfd2420b 100644 --- a/java/java-impl/src/com/intellij/codeInspection/deprecation/DeprecationInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/deprecation/DeprecationInspection.java @@ -129,8 +129,9 @@ public class DeprecationInspection extends BaseJavaLocalInspectionTool { if (results.length == 1) result = (MethodCandidateInfo)results[0]; PsiMethod constructor = result == null ? null : result.getElement(); - if (constructor != null && expression.getClassReference() != null) { - checkDeprecated(constructor, expression.getClassReference(), null, myIgnoreInsideDeprecated, myHolder); + if (constructor != null && expression.getClassOrAnonymousClassReference() != null) { + if (expression.getClassReference() == null && constructor.getParameterList().getParametersCount() == 0) return; + checkDeprecated(constructor, expression.getClassOrAnonymousClassReference(), null, myIgnoreInsideDeprecated, myHolder); } } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/Deprecated.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/Deprecated.java index fdc5e36d6aae..68482c38c8fc 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/Deprecated.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/Deprecated.java @@ -75,3 +75,15 @@ class Aaa { new Aaa("asdasdad") {}; } } + +class Anonym { + /** + * @deprecated + */ + public Anonym(String sss) { + System.out.println(sss); + } + public void foo() { + new Anonym("asdasd") {}; + } +} diff --git a/java/java-tests/testSrc/com/intellij/psi/PsiDocumentManagerImplTest.java b/java/java-tests/testSrc/com/intellij/psi/PsiDocumentManagerImplTest.java index b5ff32ecc0ab..ea3b243b8132 100644 --- a/java/java-tests/testSrc/com/intellij/psi/PsiDocumentManagerImplTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/PsiDocumentManagerImplTest.java @@ -52,6 +52,11 @@ public class PsiDocumentManagerImplTest extends PlatformTestCase { assertSame(document, FileDocumentManager.getInstance().getDocument(vFile)); } + @Override + protected boolean isRunInWriteAction() { + return false; + } + private static LightVirtualFile createFile() { return new LightVirtualFile("foo.java"); } @@ -99,6 +104,7 @@ public class PsiDocumentManagerImplTest extends PlatformTestCase { final TextBlock block = TextBlock.get(file); ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override public void run() { block.performAtomically(new Runnable() { @Override @@ -117,6 +123,7 @@ public class PsiDocumentManagerImplTest extends PlatformTestCase { final Document document = new MockDocument(); ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override public void run() { getPsiDocumentManager().documentChanged(new DocumentEventImpl(document, 0, "", "", document.getModificationStamp(), false)); } @@ -132,6 +139,7 @@ public class PsiDocumentManagerImplTest extends PlatformTestCase { final Document document = getPsiDocumentManager().getDocument(file); ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override public void run() { getPsiDocumentManager().documentChanged(new DocumentEventImpl(document, 0, "", "", document.getModificationStamp(), false)); } @@ -148,6 +156,7 @@ public class PsiDocumentManagerImplTest extends PlatformTestCase { final Document document = getPsiDocumentManager().getDocument(file); ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override public void run() { getPsiDocumentManager().documentChanged(new DocumentEventImpl(document, 0, "", "", document.getModificationStamp(), false)); } @@ -182,6 +191,7 @@ public class PsiDocumentManagerImplTest extends PlatformTestCase { assertEquals(0, getPsiDocumentManager().getUncommittedDocuments().length); ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override public void run() { getPsiDocumentManager() .documentChanged(new DocumentEventImpl(alienDocument, 0, "", "", alienDocument.getModificationStamp(), false)); @@ -225,6 +235,7 @@ public class PsiDocumentManagerImplTest extends PlatformTestCase { assertTrue(getPsiDocumentManager().isCommitted(document)); ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override public void run() { document.insertString(0, "class X {}"); } @@ -250,6 +261,7 @@ public class PsiDocumentManagerImplTest extends PlatformTestCase { }; ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override public void run() { document.insertString(0, "/**/"); boolean executed = getPsiDocumentManager().cancelAndRunWhenAllCommitted("xxx", action); @@ -268,6 +280,7 @@ public class PsiDocumentManagerImplTest extends PlatformTestCase { count.set(0); ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override public void run() { document.insertString(0, "/**/"); boolean executed = getPsiDocumentManager().performWhenAllCommitted(action); @@ -291,8 +304,51 @@ public class PsiDocumentManagerImplTest extends PlatformTestCase { } } - @Override - protected boolean isRunInWriteAction() { - return false; + public void testDocumentFromAlienProjectGetsCommittedInBackground() throws Exception { + LightVirtualFile virtualFile = createFile(); + PsiFile file = getPsiManager().findFile(virtualFile); + + final Document document = getPsiDocumentManager().getDocument(file); + + File temp = createTempDirectory(); + final Project alienProject = createProject(new File(temp, "alien.ipr"), DebugUtil.currentStackTrace()); + boolean succ2 = ProjectManagerEx.getInstanceEx().openProject(alienProject); + assertTrue(succ2); + + + try { + PsiManager alienManager = PsiManager.getInstance(alienProject); + + final PsiFile alienFile = alienManager.findFile(virtualFile); + assertNotNull(alienFile); + final PsiDocumentManagerImpl alienDocManager = (PsiDocumentManagerImpl)PsiDocumentManager.getInstance(alienProject); + final Document alienDocument = alienDocManager.getDocument(alienFile); + assertSame(document, alienDocument); + assertEquals(0, alienDocManager.getUncommittedDocuments().length); + assertEquals(0, getPsiDocumentManager().getUncommittedDocuments().length); + + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + document.setText("xxx"); + assertOrderedEquals(getPsiDocumentManager().getUncommittedDocuments(), document); + assertOrderedEquals(alienDocManager.getUncommittedDocuments(), alienDocument); + } + }); + assertEquals("xxx", document.getText()); + assertEquals("xxx", alienDocument.getText()); + + while (!getPsiDocumentManager().isCommitted(document)) { + UIUtil.dispatchAllInvocationEvents(); + } + long start = System.currentTimeMillis(); + while (!alienDocManager.isCommitted(alienDocument) && System.currentTimeMillis()-start < 20000) { + UIUtil.dispatchAllInvocationEvents(); + } + assertTrue("Still not committed: "+alienDocument, alienDocManager.isCommitted(alienDocument)); + } + finally { + ProjectUtil.closeAndDispose(alienProject); + } } } diff --git a/java/testFramework/src/com/intellij/codeInsight/completion/CompletionAutoPopupTestCase.groovy b/java/testFramework/src/com/intellij/codeInsight/completion/CompletionAutoPopupTestCase.groovy index ced2ee95db92..83f2474442f1 100644 --- a/java/testFramework/src/com/intellij/codeInsight/completion/CompletionAutoPopupTestCase.groovy +++ b/java/testFramework/src/com/intellij/codeInsight/completion/CompletionAutoPopupTestCase.groovy @@ -91,11 +91,13 @@ abstract class CompletionAutoPopupTestCase extends LightCodeInsightFixtureTestCa final static AtomicInteger cnt = new AtomicInteger() protected def joinCommit(Closure c1={}) { final AtomicBoolean committed = new AtomicBoolean() + final AtomicBoolean run = new AtomicBoolean() boolean executed=true; def closureSeq = cnt.getAndIncrement() Runnable r = new Runnable() { @Override public void run() { + run.set(true) ApplicationManager.application.invokeLater { c1(); committed.set(true) @@ -119,7 +121,7 @@ abstract class CompletionAutoPopupTestCase extends LightCodeInsightFixtureTestCa // println("waiting..." + ((PsiDocumentManagerImpl)PsiDocumentManager.getInstance(project)).printStat()) } if (System.currentTimeMillis() - start >= 20000) { - fail("too long waiting for a document to be committed. executed: $executed ;") + fail("too long waiting for documents to be committed. executed: $executed; r: $r; run: $run; ") printThreadDump() } UIUtil.pump(); diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/OptimizedFileManager.java b/jps/jps-builders/src/org/jetbrains/jps/javac/OptimizedFileManager.java index c0ab7d987e54..b2f68adccdee 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/javac/OptimizedFileManager.java +++ b/jps/jps-builders/src/org/jetbrains/jps/javac/OptimizedFileManager.java @@ -1,18 +1,21 @@ package org.jetbrains.jps.javac; -import com.sun.tools.javac.util.Context; -import com.sun.tools.javac.util.DefaultFileManager; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.sun.tools.javac.util.*; import com.sun.tools.javac.util.List; -import com.sun.tools.javac.util.ListBuffer; import javax.lang.model.SourceVersion; import javax.tools.*; -import java.io.File; -import java.io.IOException; +import java.io.*; +import java.lang.ref.SoftReference; import java.lang.reflect.Field; -import java.util.Collections; -import java.util.Map; -import java.util.Set; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.*; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; /** @@ -25,6 +28,7 @@ class OptimizedFileManager extends DefaultFileManager { private boolean myUseZipFileIndex; private final Map myArchives; private final Map myIsFile = new ConcurrentHashMap(); + private final Map> myContentCache = new HashMap>(); public OptimizedFileManager() throws Throwable { super(new Context(), true, null); @@ -42,6 +46,33 @@ class OptimizedFileManager extends DefaultFileManager { } } + @Override + public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException { + final String name = StringUtil.isEmpty(packageName) ? FileUtil.toSystemIndependentName(relativeName) : (packageName.replace('.', '/') + "/" + FileUtil.toSystemIndependentName(relativeName)); + return getFileForInput(location, name); + } + + @Override + public JavaFileObject getJavaFileForInput(Location location, String className, JavaFileObject.Kind kind) throws IOException { + final String name = className.replace('.', '/') + kind.extension; + return getFileForInput(location, name); + } + + @Override + public Iterable getJavaFileObjectsFromFiles(Iterable files) { + java.util.List result; + if (files instanceof Collection) { + result = new ArrayList(((Collection)files).size()); + } + else { + result = new ArrayList(); + } + for (File f: files) { + result.add(new InputFileObject(f)); + } + return result; + } + @Override public Iterable list(Location location, String packageName, Set kinds, boolean recurse) throws IOException { Iterable path = getLocation(location); @@ -123,7 +154,7 @@ class OptimizedFileManager extends DefaultFileManager { if (children != null) { for (File child : children) { if (isValidFile(child.getName(), fileKinds) && isFile(child)) { - final JavaFileObject fe = getRegularFile(child); + final JavaFileObject fe = new InputFileObject(child); result.append(fe); } } @@ -142,7 +173,7 @@ class OptimizedFileManager extends DefaultFileManager { } else { if (isValidFile(name, fileKinds)) { - JavaFileObject fe = getRegularFile(file); + JavaFileObject fe = new InputFileObject(file); result.append(fe); } } @@ -166,12 +197,318 @@ class OptimizedFileManager extends DefaultFileManager { return fileKinds.contains(kind); } + private JavaFileObject getFileForInput(Location location, String name) throws IOException { + Iterable path = getLocation(location); + if (path == null) { + return null; + } + + for (File root : path) { + Archive archive = myArchives.get(root); + final boolean isFile; + if (archive != null) { + isFile = true; + } + else { + isFile = isFile(root); + } + if (isFile) { + if (archive == null) { + try { + archive = openArchive(root); + } + catch (IOException ex) { + log.error("error.reading.file", root, ex.getLocalizedMessage()); + break; + } + } + if (archive.contains(name)) { + int i = name.lastIndexOf('/'); + String dirname = name.substring(0, i+1); + String basename = name.substring(i+1); + return archive.getFileObject(dirname, basename); + } + } + else { + final File f = new File(root, name.replace('/', File.separatorChar)); + if (f.exists()) { + return new InputFileObject(f); + } + } + } + return null; + } + //actually Javac doesn't check if this method returns null. It always get substring of the returned string starting from the last dot. @Override public String inferBinaryName(Location location, JavaFileObject file) { - final String name = file.getName(); - int dot = name.lastIndexOf('.'); - final String relativePath = dot != -1 ? name.substring(0, dot) : name; - return relativePath.replace(File.separatorChar, '.'); + final String name = file.getName(); + int dot = name.lastIndexOf('.'); + final String relativePath = dot != -1 ? name.substring(0, dot) : name; + return relativePath.replace(File.separatorChar, '.'); } + + private class InputFileObject extends BaseFileObject { + /** The file's name. + */ + private String name; + + /** The underlying file. + */ + final File f; + + public InputFileObject(File f) { + this(f.getName(), f); + } + + public InputFileObject(String name, File f) { + this.name = name; + this.f = f; + } + + public InputStream openInputStream() throws IOException { + return new FileInputStream(f); + } + + public Reader openReader(boolean ignoreEncodingErrors) throws IOException { + throw new UnsupportedOperationException(); + } + + public OutputStream openOutputStream() throws IOException { + throw new UnsupportedOperationException(); + } + + public Writer openWriter() throws IOException { + throw new UnsupportedOperationException(); + } + + @Deprecated + public String getName() { + return name; + } + + public boolean isNameCompatible(String cn, JavaFileObject.Kind kind) { + String n = cn + kind.extension; + if (name.equals(n)) { + return true; + } + if (name.equalsIgnoreCase(n)) { + try { + // allow for Windows + return (f.getCanonicalFile().getName().equals(n)); + } + catch (IOException e) { + } + } + return false; + } + + /** @deprecated see bug 6410637 */ + @Deprecated + public String getPath() { + return f.getPath(); + } + + public long getLastModified() { + return f.lastModified(); + } + + public boolean delete() { + return f.delete(); + } + + public CharBuffer getCharContent(boolean ignoreEncodingErrors) throws IOException { + SoftReference r = myContentCache.get(this); + CharBuffer cb = (r == null ? null : r.get()); + if (cb == null) { + InputStream in = new FileInputStream(f); + try { + ByteBuffer bb = makeByteBuffer(in); + JavaFileObject prev = log.useSource(this); + try { + cb = decode(bb, ignoreEncodingErrors); + } + finally { + log.useSource(prev); + } + myByteBufferCache.put(bb); // save for next time + if (!ignoreEncodingErrors) { + myContentCache.put(this, new SoftReference(cb)); + } + } + finally { + in.close(); + } + } + return cb; + } + + //public CharBuffer getCharContent(boolean ignoreEncodingErrors) throws IOException { + // final String encodingName = getEncodingName(); + // SoftReference r = myContentCache.get(this); + // CharBuffer cb = (r == null ? null : r.get()); + // if (cb == null) { + // InputStream in = new FileInputStream(f); + // try { + // JavaFileObject prev = log.useSource(this); + // try { + // final char[] chars = FileUtil.loadFileText(f, encodingName); + // cb = CharBuffer.wrap(chars); + // } + // finally { + // log.useSource(prev); + // } + // if (!ignoreEncodingErrors) { + // myContentCache.put(this, new SoftReference(cb)); + // } + // } + // finally { + // in.close(); + // } + // } + // return cb; + //} + + @Override + public boolean equals(Object other) { + if (!(other instanceof InputFileObject)) { + return false; + } + InputFileObject o = (InputFileObject) other; + try { + return f.equals(o.f) || f.getCanonicalFile().equals(o.f.getCanonicalFile()); + } + catch (IOException e) { + return false; + } + } + + @Override + public int hashCode() { + return f.hashCode(); + } + + public URI toUri() { + try { + return new URI(f.getPath()); + } + catch (URISyntaxException ex) { + return f.toURI(); + } + } + } + + private ByteBuffer makeByteBuffer(InputStream in) throws IOException { + int limit = in.available(); + if (limit < 1024) { + limit = 1024; + } + ByteBuffer result = myByteBufferCache.get(limit); + int position = 0; + while (in.available() != 0) { + if (position >= limit) { + // expand buffer + result = ByteBuffer.allocate(limit <<= 1).put((ByteBuffer)result.flip()); + } + final int count = in.read(result.array(), position, limit - position); + if (count < 0) { + break; + } + result.position(position += count); + } + return (ByteBuffer)result.flip(); + } + + private CharBuffer decode(ByteBuffer inbuf, boolean ignoreEncodingErrors) { + CharsetDecoder decoder; + String encodingName = getEncodingName(); + try { + Charset charset = (this.charset == null) ? Charset.forName(encodingName) : this.charset; + decoder = charset.newDecoder(); + + CodingErrorAction action; + if (ignoreEncodingErrors) { + action = CodingErrorAction.REPLACE; + } + else { + action = CodingErrorAction.REPORT; + } + + decoder.onMalformedInput(action).onUnmappableCharacter(action); + } + catch (IllegalCharsetNameException e) { + log.error("unsupported.encoding", encodingName); + return (CharBuffer)CharBuffer.allocate(1).flip(); + } + catch (UnsupportedCharsetException e) { + log.error("unsupported.encoding", encodingName); + return (CharBuffer)CharBuffer.allocate(1).flip(); + } + + // slightly overestimate the buffer size to avoid reallocation. + final float factor = decoder.averageCharsPerByte() * 0.8f + decoder.maxCharsPerByte() * 0.2f; + CharBuffer dest = CharBuffer.allocate(10 + (int)(inbuf.remaining() * factor)); + + while (true) { + CoderResult result = decoder.decode(inbuf, dest, true); + dest.flip(); + + if (result.isUnderflow()) { // done reading + // make sure there is at least one extra character + if (dest.limit() == dest.capacity()) { + dest = CharBuffer.allocate(dest.capacity()+1).put(dest); + dest.flip(); + } + return dest; + } + else if (result.isOverflow()) { // buffer too small; expand + int newCapacity = 10 + dest.capacity() + (int)(inbuf.remaining()*decoder.maxCharsPerByte()); + dest = CharBuffer.allocate(newCapacity).put(dest); + } + else if (result.isMalformed() || result.isUnmappable()) { + // bad character in input + + // report coding error (warn only pre 1.5) + if (!getSource().allowEncodingErrors()) { + log.error(new JCDiagnostic.SimpleDiagnosticPosition(dest.limit()), "illegal.char.for.encoding", charset == null ? encodingName : charset.name()); + } + else { + log.warning(new JCDiagnostic.SimpleDiagnosticPosition(dest.limit()), "illegal.char.for.encoding", charset == null ? encodingName : charset.name()); + } + + // skip past the coding error + inbuf.position(inbuf.position() + result.length()); + + // undo the flip() to prepare the output buffer + // for more translation + dest.position(dest.limit()); + dest.limit(dest.capacity()); + dest.put((char)0xfffd); // backward compatible + } + else { + throw new AssertionError(result); + } + } + // unreached + } + + private static class ByteBufferCache { + private ByteBuffer cached; + + ByteBuffer get(int capacity) { + if (capacity < 20480) { + capacity = 20480; + } + ByteBuffer result = (cached != null && cached.capacity() >= capacity) ? + (ByteBuffer)cached.clear() : + ByteBuffer.allocate(capacity + capacity>>1); + cached = null; + return result; + } + void put(ByteBuffer x) { + cached = x; + } + } + private final ByteBufferCache myByteBufferCache = new ByteBufferCache(); + } diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/OptimizedFileManager17.java b/jps/jps-builders/src/org/jetbrains/jps/javac/OptimizedFileManager17.java index e1f56bfb00a3..ec894f6ab35b 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/javac/OptimizedFileManager17.java +++ b/jps/jps-builders/src/org/jetbrains/jps/javac/OptimizedFileManager17.java @@ -1,18 +1,23 @@ package org.jetbrains.jps.javac; -import com.sun.tools.javac.file.*; +import com.sun.tools.javac.file.BaseFileObject; +import com.sun.tools.javac.file.JavacFileManager; +import com.sun.tools.javac.file.RelativePath; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; import javax.lang.model.SourceVersion; import javax.tools.*; -import java.io.File; -import java.io.IOException; +import java.io.*; +import java.lang.ref.Reference; +import java.lang.ref.SoftReference; import java.lang.reflect.Field; -import java.util.Arrays; -import java.util.Map; -import java.util.Set; +import java.net.URI; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharsetDecoder; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; /** @@ -32,6 +37,21 @@ class OptimizedFileManager17 extends com.sun.tools.javac.file.JavacFileManager { myArchives = (Map) archivesField.get(this); } + @Override + public Iterable getJavaFileObjectsFromFiles(Iterable files) { + java.util.List result; + if (files instanceof Collection) { + result = new ArrayList(((Collection)files).size()); + } + else { + result = new ArrayList(); + } + for (File f: files) { + result.add(new InputFileObject(this, f)); + } + return result; + } + @Override public Iterable list(Location location, String packageName, Set kinds, boolean recurse) throws IOException { Iterable path = getLocation(location); @@ -113,7 +133,7 @@ class OptimizedFileManager17 extends com.sun.tools.javac.file.JavacFileManager { for (File f: files) { String fileName = f.getName(); if (isValidFile(fileName, fileKinds) && isFile(f)) { - JavaFileObject fe = getRegularFile(f); + JavaFileObject fe = new InputFileObject(this, f); resultList.append(fe); } } @@ -135,7 +155,7 @@ class OptimizedFileManager17 extends com.sun.tools.javac.file.JavacFileManager { } else { if (isValidFile(fileName, fileKinds)) { - JavaFileObject fe = getRegularFile(file); + JavaFileObject fe = new InputFileObject(this, file); resultList.append(fe); } } @@ -160,4 +180,163 @@ class OptimizedFileManager17 extends com.sun.tools.javac.file.JavacFileManager { return fileKinds.contains(getKind(name)); } + private class InputFileObject extends BaseFileObject { + private String name; + final File file; + private Reference absFileRef; + + public InputFileObject(JavacFileManager fileManager, File f) { + this(fileManager, f.getName(), f); + } + + public InputFileObject(JavacFileManager fileManager, String name, File f) { + super(fileManager); + this.name = name; + this.file = f; + } + + + @Override + public URI toUri() { + return file.toURI().normalize(); + } + + @Override + public String getName() { + return file.getPath(); + } + + @Override + public String getShortName() { + return name; + } + + @Override + public JavaFileObject.Kind getKind() { + return getKind(name); + } + + @Override + public InputStream openInputStream() throws IOException { + return new FileInputStream(file); + } + + @Override + public OutputStream openOutputStream() throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public Writer openWriter() throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public long getLastModified() { + return file.lastModified(); + } + + @Override + public boolean delete() { + return file.delete(); + } + + @Override + protected CharsetDecoder getDecoder(boolean ignoreEncodingErrors) { + return fileManager.getDecoder(fileManager.getEncodingName(), ignoreEncodingErrors); + } + + @Override + protected String inferBinaryName(Iterable path) { + String fPath = file.getPath(); + //System.err.println("RegularFileObject " + file + " " +r.getPath()); + for (File dir: path) { + //System.err.println("dir: " + dir); + String dPath = dir.getPath(); + if (dPath.length() == 0) + dPath = System.getProperty("user.dir"); + if (!dPath.endsWith(File.separator)) + dPath += File.separator; + if (fPath.regionMatches(true, 0, dPath, 0, dPath.length()) + && new File(fPath.substring(0, dPath.length())).equals(new File(dPath))) { + String relativeName = fPath.substring(dPath.length()); + return removeExtension(relativeName).replace(File.separatorChar, '.'); + } + } + return null; + } + + @Override + public boolean isNameCompatible(String cn, JavaFileObject.Kind kind) { + cn.getClass(); + // null check + if (kind == Kind.OTHER && getKind() != kind) { + return false; + } + String n = cn + kind.extension; + if (name.equals(n)) { + return true; + } + if (name.equalsIgnoreCase(n)) { + return file.getName().equals(n); + } + return false; + } + + /** + * Check if two file objects are equal. + * Two RegularFileObjects are equal if the absolute paths of the underlying + * files are equal. + */ + @Override + public boolean equals(Object other) { + if (this == other) + return true; + + if (!(other instanceof InputFileObject)) + return false; + + InputFileObject o = (InputFileObject) other; + return getAbsoluteFile().equals(o.getAbsoluteFile()); + } + + @Override + public int hashCode() { + return getAbsoluteFile().hashCode(); + } + + private File getAbsoluteFile() { + File absFile = (absFileRef == null ? null : absFileRef.get()); + if (absFile == null) { + absFile = file.getAbsoluteFile(); + absFileRef = new SoftReference(absFile); + } + return absFile; + } + + public CharBuffer getCharContent(boolean ignoreEncodingErrors) throws IOException { + CharBuffer cb = fileManager.getCachedContent(this); + if (cb == null) { + InputStream in = new FileInputStream(file); + try { + ByteBuffer bb = fileManager.makeByteBuffer(in); + JavaFileObject prev = fileManager.log.useSource(this); + try { + cb = fileManager.decode(bb, ignoreEncodingErrors); + } finally { + fileManager.log.useSource(prev); + } + fileManager.recycleByteBuffer(bb); + if (!ignoreEncodingErrors) { + fileManager.cache(this, cb); + } + } finally { + in.close(); + } + } + return cb; + } + } + + } diff --git a/jps/lib/optimizedFileManager.jar b/jps/lib/optimizedFileManager.jar index 1fbeab2400ee..9765565bf9b7 100644 Binary files a/jps/lib/optimizedFileManager.jar and b/jps/lib/optimizedFileManager.jar differ diff --git a/platform/core-impl/src/com/intellij/openapi/editor/impl/CharArray.java b/platform/core-impl/src/com/intellij/openapi/editor/impl/CharArray.java index 141d5eae26c7..5e1867dfc861 100644 --- a/platform/core-impl/src/com/intellij/openapi/editor/impl/CharArray.java +++ b/platform/core-impl/src/com/intellij/openapi/editor/impl/CharArray.java @@ -15,34 +15,38 @@ */ package com.intellij.openapi.editor.impl; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.event.DocumentEvent; -import com.intellij.openapi.editor.impl.event.DocumentEventImpl; +import com.intellij.util.ArrayUtil; import com.intellij.util.LocalTimeCounter; import com.intellij.util.text.CharArrayCharSequence; import com.intellij.util.text.CharArrayUtil; import com.intellij.util.text.CharSequenceBackedByArray; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.lang.ref.Reference; import java.lang.ref.SoftReference; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; /** * @author cdr */ abstract class CharArray implements CharSequenceBackedByArray { - - @SuppressWarnings("UseOfArchaicSystemPropertyAccessors") - private static final boolean DISABLE_DEFERRED_PROCESSING = Boolean.getBoolean("idea.document.deny.deferred.changes"); - @SuppressWarnings("UseOfArchaicSystemPropertyAccessors") - private static final boolean DEBUG_DEFERRED_PROCESSING = Boolean.getBoolean("idea.document.debug.bulk.processing"); - private static final Logger LOG = Logger.getInstance("#" + CharArray.class.getName()); + @SuppressWarnings("UseOfArchaicSystemPropertyAccessors") + private static final boolean DISABLE_DEFERRED_PROCESSING = Boolean.getBoolean("idea.document.deny.deferred.changes"); + + @SuppressWarnings("UseOfArchaicSystemPropertyAccessors") + private static final boolean DEBUG_DEFERRED_PROCESSING = LOG.isDebugEnabled() || Boolean.getBoolean("idea.document.debug.bulk.processing") + || ApplicationManager.getApplication() != null && ApplicationManager.getApplication().isUnitTestMode(); /** * We can't exclude possibility of situation when 'defer changes' state is {@link #setDeferredChangeMode(boolean) entered} * but not exited, hence, we want to perform automatic flushing if necessary in order to avoid memory leaks. This constant holds @@ -51,179 +55,232 @@ abstract class CharArray implements CharSequenceBackedByArray { */ private static final int MAX_DEFERRED_CHANGES_NUMBER = 10000; - private final AtomicReference myDeferredChangesStorage = new AtomicReference(); + private final TextChangesStorage myDeferredChangesStorage; - private int myStart; - /** - * This class implements {@link #subSequence(int, int)} by creating object of the same class that partially shares the same - * data as the object on which the method is called. So, this field may define interested end offset (if it's non-negative). - */ - private int myEnd = -1; - private int myCount = 0; + private volatile int myStart; // start offset in myArray (used as an optimization when call substring()) + private volatile int myCount; - private CharSequence myOriginalSequence; - private char[] myArray; - private SoftReference myStringRef; // buffers String value - for not to generate it every time - private int myBufferSize; - private int myDeferredShift; - private boolean myDeferredChangeMode; + private volatile CharSequence myOriginalSequence; + private volatile char[] myArray; + private volatile Reference myStringRef; // buffers String value - for not to generate it every time + private volatile int myBufferSize; + private volatile int myDeferredShift; + private volatile boolean myDeferredChangeMode; + // this lock is for mutual exclusion during read action access + // (some fields are changed in read action too) + private final Lock lock = new ReentrantLock(); // We had a problems with bulk document text processing, hence, debug facilities were introduced. The fields group below work with them. // The main idea is to hold all history of bulk processing iteration in order to be able to retrieve it from client and reproduce the // problem. - /** Flag the identifies if current char array should debug bulk processing. */ - private final boolean myDebugDeferredProcessing; - + private final boolean myDebug = isDebug(); + + boolean isDebug() { + return DEBUG_DEFERRED_PROCESSING; + } + /** * Duplicate instance of the current char array that is used during debug processing as follows - apply every text change * from the bulk changes group to this instance immediately in order to be able to check if the current 'deferred change-aware' * instance functionally behaves at the same way as 'straightforward' one. */ - private CharArray myDebugArray; + private CharArray myDebugArray; - /** Holds deferred changes create during the current bulk processing iteration. */ - private List myDebugDeferredChanges; - - /** Document text on bulk processing start. */ - private String myDebugTextOnBatchUpdateStart; + /** + * Holds deferred changes create during the current bulk processing iteration. + */ + private List myDebugDeferredChanges; - // max chars to hold, bufferSize == 0 means unbounded - CharArray(int bufferSize) { - this(bufferSize, new TextChangesStorage(), null, -1, -1); - } + /** + * Document text on bulk processing start. + */ + private String myDebugTextOnBatchUpdateStart; - private CharArray(final int bufferSize, @NotNull TextChangesStorage deferredChangesStorage, @Nullable char[] data, int start, int end) { - this(bufferSize, deferredChangesStorage, data, start, end, DEBUG_DEFERRED_PROCESSING); - } - - private CharArray(final int bufferSize, @NotNull TextChangesStorage deferredChangesStorage, @Nullable char[] data, int start, int end, - boolean debugDeferredProcessing) - { + // bufferSize == 0 means unbounded + CharArray(final int bufferSize, @NotNull char[] data, int length) { myBufferSize = bufferSize; - myDeferredChangesStorage.set(deferredChangesStorage); - if (data == null) { - myOriginalSequence = ""; - } - else { - myArray = data; - myCount = end - start; - } - if (start >= 0 && end >= 0) { - myStart = start; - myEnd = end; - } + myDeferredChangesStorage = new TextChangesStorage(); + myArray = Arrays.copyOf(data, length); + myCount = length; - myDebugDeferredProcessing = debugDeferredProcessing; - if (myDebugDeferredProcessing) { - - myDebugArray = new CharArray(bufferSize, new TextChangesStorage(), data == null ? null : Arrays.copyOf(data, data.length), - start, end, false) - { + if (myDebug) { + myDebugArray = new CharArray(bufferSize, data, length) { @NotNull @Override - protected DocumentEvent beforeChangedUpdate(DocumentImpl subj, - int offset, + protected DocumentEvent beforeChangedUpdate(int offset, CharSequence oldString, CharSequence newString, boolean wholeTextReplaced) { - return new DocumentEventImpl(subj, offset, oldString, newString, -1, wholeTextReplaced); + return CharArray.this.beforeChangedUpdate(offset, oldString, newString, wholeTextReplaced); } @Override protected void afterChangedUpdate(@NotNull DocumentEvent event, long newModificationStamp) { } + + @Override + protected void assertWriteAccess() { + } + + @Override + protected void assertReadAccess() { + } + + @Override + boolean isDebug() { + return false; + } }; myDebugDeferredChanges = new ArrayList(); } + assertConsistency(); } public void setBufferSize(int bufferSize) { + assert bufferSize >= 0 : bufferSize; myBufferSize = bufferSize; + assertConsistency(); + } + + private DocumentEvent startChange(int offset, + @Nullable CharSequence oldString, + @Nullable CharSequence newString, + boolean wholeTextReplaced) { + assert myStart == 0; // can't change substring + assertWriteAccess(); + assertConsistency(); + + return beforeChangedUpdate(offset, oldString, newString, wholeTextReplaced); } @NotNull - protected abstract DocumentEvent beforeChangedUpdate(DocumentImpl subj, - int offset, + protected abstract DocumentEvent beforeChangedUpdate(int offset, @Nullable CharSequence oldString, @Nullable CharSequence newString, boolean wholeTextReplaced); protected abstract void afterChangedUpdate(@NotNull DocumentEvent event, long newModificationStamp); - public void setText(@Nullable final DocumentImpl subj, final CharSequence chars) { - myOriginalSequence = chars; - myArray = null; - myCount = chars.length(); - myStringRef = null; - TextChangesStorage storage = myDeferredChangesStorage.get(); - storage.getLock().lock(); - try { - if (isSubSequence()) { - myDeferredChangesStorage.set(new TextChangesStorage()); - myStart = 0; - myEnd = -1; - } - else { - storage.clear(); - } - } - finally { - storage.getLock().unlock(); - } - - if (subj != null) { - trimToSize(subj); - } + protected abstract void assertWriteAccess(); + protected abstract void assertReadAccess(); - if (myDebugDeferredProcessing) { - myDebugArray.setText(subj, chars); + private void setText(@NotNull CharSequence chars) { + assertConsistency(); + myOriginalSequence = chars.toString(); + myArray = null; + myStringRef = null; + myCount = chars.length(); + assert myStart == 0; // can't change substring + myDeferredChangesStorage.clear(); + trimToSize(); + + if (myDebug) { + myDebugArray.setText(chars); myDebugDeferredChanges.clear(); } + assertConsistency(); + } + + private void assertConsistency() { + if (isDeferredChangeMode()) { + assert myOriginalSequence == null; + } + CharSequence originalSequence = myOriginalSequence; + int origLen = originalSequence == null ? -1 : originalSequence.length(); + String string = myStringRef == null ? null : myStringRef.get(); + int stringLen = string == null ? -1 : string.length(); + char[] array = myArray; + assert origLen == stringLen || origLen==-1 || stringLen==-1; + + int count = myCount + myDeferredShift; + assert count == origLen || origLen==-1; + assert count == stringLen || stringLen==-1; + if (array != null) { + assert myCount <= array.length; + } + if (myDebug) { + if (array != null && originalSequence != null) { + assert new String(array, myStart, myCount).equals(originalSequence); + } + if (!isDeferredChangeMode() && array != null && string != null) { + assert new String(array, myStart, myCount).equals(string); + } + if (originalSequence != null && string != null) { + assert string.equals(originalSequence.toString()); + } + + myDebugArray.assertConsistency(); + + String str = myStringRef == null ? null : myStringRef.get(); + if (str == null) { + if (hasDeferredChanges()) { + str = doSubString(0, myCount + myDeferredShift).toString(); + } + else if (myOriginalSequence != null) { + str = myOriginalSequence.toString(); + } + else { + str = new String(myArray, myStart, myCount); + } + } + assert count == str.length(); + if (isDeferredChangeMode()) { + String expected = myDebugArray.toString(); + checkStrings("toString()", expected, str); + } + } } - public void replace(DocumentImpl subj, - int startOffset, int endOffset, CharSequence toDelete, CharSequence newString, long newModificationStamp, + public void replace(int startOffset, + int endOffset, + @NotNull CharSequence toDelete, + @NotNull CharSequence newString, + long newModificationStamp, boolean wholeTextReplaced) { - final DocumentEvent event = beforeChangedUpdate(subj, startOffset, toDelete, newString, wholeTextReplaced); + final DocumentEvent event = startChange(startOffset, toDelete, newString, wholeTextReplaced); + startOffset += myStart; endOffset += myStart; doReplace(startOffset, endOffset, newString); afterChangedUpdate(event, newModificationStamp); + assertConsistency(); } - private void doReplace(int startOffset, int endOffset, CharSequence newString) { + private void doReplace(int startOffset, int endOffset, @NotNull CharSequence newString) { prepareForModification(); if (isDeferredChangeMode()) { storeChange(new TextChangeImpl(newString, startOffset, endOffset)); - if (myDebugDeferredProcessing) { + if (myDebug) { myDebugArray.doReplace(startOffset, endOffset, newString); } - return; } - - int newLength = newString.length(); - int oldLength = endOffset - startOffset; + else { + int newLength = newString.length(); + int oldLength = endOffset - startOffset; - CharArrayUtil.getChars(newString, myArray, startOffset, Math.min(newLength, oldLength)); + CharArrayUtil.getChars(newString, myArray, startOffset, Math.min(newLength, oldLength)); + myStringRef = null; - if (newLength > oldLength) { - doInsert(newString.subSequence(oldLength, newLength), endOffset); - } - else if (newLength < oldLength) { - doRemove(startOffset + newLength, startOffset + oldLength); + if (newLength > oldLength) { + doInsert(newString.subSequence(oldLength, newLength), endOffset); + } + else if (newLength < oldLength) { + doRemove(startOffset + newLength, startOffset + oldLength); + } } } - public void remove(DocumentImpl subj, int startIndex, int endIndex, CharSequence toDelete) { - DocumentEvent event = beforeChangedUpdate(subj, startIndex, toDelete, null, false); + public void remove(int startIndex, int endIndex, @NotNull CharSequence toDelete) { + DocumentEvent event = startChange(startIndex, toDelete, null, false); startIndex += myStart; endIndex += myStart; doRemove(startIndex, endIndex); afterChangedUpdate(event, LocalTimeCounter.currentTime()); + assertConsistency(); } - private void doRemove(final int startIndex, final int endIndex) { + private void doRemove(int startIndex, int endIndex) { if (startIndex == endIndex) { return; } @@ -231,46 +288,49 @@ abstract class CharArray implements CharSequenceBackedByArray { if (isDeferredChangeMode()) { storeChange(new TextChangeImpl("", startIndex, endIndex)); - if (myDebugDeferredProcessing) { + if (myDebug) { myDebugArray.doRemove(startIndex, endIndex); } - return; } - - if (endIndex < myCount) { - System.arraycopy(myArray, endIndex, myArray, startIndex, myCount - endIndex); + else { + if (endIndex < myCount) { + System.arraycopy(myArray, endIndex, myArray, startIndex, myCount - endIndex); + myStringRef = null; + } + myCount -= endIndex - startIndex; } - myCount -= endIndex - startIndex; } - public void insert(DocumentImpl subj, CharSequence s, int startIndex) { - DocumentEvent event = beforeChangedUpdate(subj, startIndex, null, s, false); + public void insert(@NotNull CharSequence s, int startIndex) { + DocumentEvent event = startChange(startIndex, null, s, false); startIndex += myStart; doInsert(s, startIndex); afterChangedUpdate(event, LocalTimeCounter.currentTime()); - trimToSize(subj); + trimToSize(); + assertConsistency(); } - private void doInsert(final CharSequence s, final int startIndex) { + private void doInsert(@NotNull CharSequence s, final int startIndex) { prepareForModification(); if (isDeferredChangeMode()) { storeChange(new TextChangeImpl(s, startIndex)); - if (myDebugDeferredProcessing) { + if (myDebug) { myDebugArray.doInsert(s, startIndex); } - return; } - - int insertLength = s.length(); - myArray = relocateArray(myArray, myCount + insertLength); - if (startIndex < myCount) { - System.arraycopy(myArray, startIndex, myArray, startIndex + insertLength, myCount - startIndex); + else { + int insertLength = s.length(); + myArray = resizeArray(myArray, myCount + insertLength); + if (startIndex < myCount) { + System.arraycopy(myArray, startIndex, myArray, startIndex + insertLength, myCount - startIndex); + } + + CharArrayUtil.getChars(s, myArray, startIndex); + myCount += insertLength; + myStringRef = null; } - - CharArrayUtil.getChars(s, myArray, startIndex); - myCount += insertLength; } /** @@ -281,77 +341,68 @@ abstract class CharArray implements CharSequenceBackedByArray { */ private void storeChange(@NotNull TextChangeImpl change) { if (!change.isWithinBounds(length())) { - LOG.error(String.format( - "Invalid change attempt detected - given change bounds are not within the current char array. Change: %d:%d-%d", - change.getText().length(), change.getStart(), change.getEnd() - ), dumpState()); + LOG.error( + "Invalid change attempt detected - given change bounds are not within the current char array. Change: " + + change.getText().length()+":" + change.getStart()+"-" + change.getEnd(), dumpState()); return; } - TextChangesStorage storage = myDeferredChangesStorage.get(); - storage.getLock().lock(); - try { - doStoreChange(change); + if (myDeferredChangesStorage.size() >= MAX_DEFERRED_CHANGES_NUMBER) { + flushDeferredChanged(); } - finally { - storage.getLock().unlock(); - } - } - - private void doStoreChange(@NotNull TextChangeImpl change) { - TextChangesStorage storage = myDeferredChangesStorage.get(); - if (storage.size() >= MAX_DEFERRED_CHANGES_NUMBER) { - flushDeferredChanged(storage); - } - storage.store(change); + myDeferredChangesStorage.store(change); myDeferredShift += change.getDiff(); - if (myDebugDeferredProcessing) { + if (myDebug) { myDebugDeferredChanges.add(change); } } - + private void prepareForModification() { if (myOriginalSequence != null) { myArray = new char[myOriginalSequence.length()]; CharArrayUtil.getChars(myOriginalSequence, myArray, 0); + myCount = myArray.length; myOriginalSequence = null; + myStart = 0; } myStringRef = null; + + assertConsistency(); } + @NotNull public CharSequence getCharArray() { - if (myOriginalSequence != null) return myOriginalSequence; - return this; + assertReadAccess(); + assertConsistency(); + CharSequence originalSequence = myOriginalSequence; + return originalSequence == null ? this : originalSequence; } public String toString() { - String str = myStringRef != null ? myStringRef.get() : null; + assertConsistency(); + String str = myStringRef == null ? null : myStringRef.get(); if (str == null) { - if (myOriginalSequence != null) { - str = myOriginalSequence.toString(); - } - else if (!hasDeferredChanges()) { - str = new String(myArray, myStart, myCount); - } - else { + if (hasDeferredChanges()) { str = substring(0, length()).toString(); } + else if (myOriginalSequence != null) { + str = myOriginalSequence.toString(); + } + else { + str = new String(myArray, myStart, myCount); + } myStringRef = new SoftReference(str); } - if (myDebugDeferredProcessing && isDeferredChangeMode()) { - String expected = myDebugArray.toString(); - checkStrings("toString()", expected, str); - } return str; } @Override public final int length() { final int result = myCount + myDeferredShift; - if (myDebugDeferredProcessing && isDeferredChangeMode()) { + if (myDebug && isDeferredChangeMode()) { int expected = myDebugArray.length(); if (expected != result) { - dumpDebugInfo(String.format("Incorrect length() processing. Expected: '%s', actual: '%s'", expected, result)); + dumpDebugInfo("Incorrect length() processing. Expected: '" + expected + "', actual: '" + result + "'"); } } return result; @@ -363,144 +414,113 @@ abstract class CharArray implements CharSequenceBackedByArray { throw new IndexOutOfBoundsException("Wrong offset: " + i + "; count:" + length()); } i += myStart; - if (myOriginalSequence != null) return myOriginalSequence.charAt(i); final char result; if (hasDeferredChanges()) { - TextChangesStorage storage = myDeferredChangesStorage.get(); - storage.getLock().lock(); - try { - result = storage.charAt(myArray, i); - } - finally { - storage.getLock().unlock(); - } + result = myDeferredChangesStorage.charAt(myArray, i); + } + else if (myOriginalSequence != null) { + result = myOriginalSequence.charAt(i); } else { result = myArray[i]; } - if (myDebugDeferredProcessing && isDeferredChangeMode()) { + if (myDebug && isDeferredChangeMode()) { char expected = myDebugArray.charAt(i); if (expected != result) { - dumpDebugInfo( - String.format("Incorrect charAt() processing for index %d. Expected: '%c', actual: '%c'", i, expected, result) - ); + dumpDebugInfo("Incorrect charAt() processing for index " + i + ". Expected: '" + expected + "', actual: '" + result + "'"); } } return result; } @Override + @NotNull public CharSequence subSequence(final int start, final int end) { + assertReadAccess(); + assertConsistency(); if (start == 0 && end == length()) return this; if (myOriginalSequence != null) { return myOriginalSequence.subSequence(start, end); } - if (hasDeferredChanges()) { - return new CharArray(myBufferSize, myDeferredChangesStorage.get(), myArray, myStart + start, myStart + end) { - @NotNull - @Override - protected DocumentEvent beforeChangedUpdate(DocumentImpl subj, - int offset, - CharSequence oldString, - CharSequence newString, - boolean wholeTextReplaced) { - return new DocumentEventImpl(subj, offset, oldString, newString, LocalTimeCounter.currentTime(), wholeTextReplaced); - } - - @Override - protected void afterChangedUpdate(@NotNull DocumentEvent event, long newModificationStamp) { - } - - @Override - public char[] getChars() { - char[] chars = CharArray.this.getChars(); - char[] result = new char[end - start]; - System.arraycopy(chars, start, result, 0, result.length); - return result; - } - }; - } - else { - // We don't use the same approach as with 'defer changes' mode because the former is the new experimental one and this one - // is rather mature, hence, we just minimizes the risks that something is wrong within the new approach. - return new CharArrayCharSequence(myArray, start, end); - } + flushDeferredChanged(); + return new CharArrayCharSequence(myArray, start, end); } - private boolean isSubSequence() { - return myEnd >= 0; - } - @Override + @NotNull public char[] getChars() { - if (myOriginalSequence != null) { - if (myArray == null) { - myArray = CharArrayUtil.fromSequence(myOriginalSequence); - } - } - flushDeferredChanged(myDeferredChangesStorage.get()); - if (myDebugDeferredProcessing && isDeferredChangeMode()) { - char[] expected = myDebugArray.getChars(); - for (int i = 0, max = length(); i < max; i++) { - if (myArray[i] != expected[i]) { - dumpDebugInfo(String.format("getChars(). Index: %d, expected: %c, actual: %c", i, expected[i], myArray[i])); - break; + assertReadAccess(); + assertConsistency(); + char[] array = myArray; + CharSequence originalSequence = myOriginalSequence; + if (hasDeferredChanges() || originalSequence != null && array == null) { + // slow track + lock.lock(); + try { + flushDeferredChanged(); + if (myOriginalSequence != null && myArray == null) { + myArray = array = ArrayUtil.realloc(CharArrayUtil.fromSequence(myOriginalSequence), myOriginalSequence.length()); + myStringRef = null; } } + finally { + lock.unlock(); + } + assertConsistency(); } - return myArray; + return array; } @Override - public void getChars(final char[] dst, final int dstOffset) { - flushDeferredChanged(myDeferredChangesStorage.get()); - if (myOriginalSequence != null) { - CharArrayUtil.getChars(myOriginalSequence,dst, dstOffset); - } - else { + public void getChars(@NotNull final char[] dst, final int dstOffset) { + assertReadAccess(); + assertConsistency(); + flushDeferredChanged(); + if (myOriginalSequence == null) { System.arraycopy(myArray, myStart, dst, dstOffset, length()); } + else { + CharArrayUtil.getChars(myOriginalSequence, dst, dstOffset); + } - if (myDebugDeferredProcessing && isDeferredChangeMode()) { + if (myDebug && isDeferredChangeMode()) { char[] expected = new char[dst.length]; myDebugArray.getChars(expected, dstOffset); for (int i = dstOffset, j = myStart; i < dst.length && j < myArray.length; i++, j++) { if (expected[i] != myArray[j]) { - dumpDebugInfo(String.format("getChars(char[], int). Given array of length %d, offset %d. Found char '%c' at index %d, " + - "expected to find '%c'", dst.length, dstOffset, myArray[j], i, expected[i])); + dumpDebugInfo("getChars(char[], int). Given array of length " + dst.length + ", offset " + dstOffset + ". Found char '" + myArray[j] + + "' at index " + i + ", expected to find '" + expected[i] + "'"); break; } } } } + @NotNull public CharSequence substring(final int start, final int end) { + assertReadAccess(); + final CharSequence result = doSubString(start, end); + + assertConsistency(); + return result; + } + + private CharSequence doSubString(int start, int end) { if (start == end) return ""; final CharSequence result; if (myOriginalSequence == null) { - TextChangesStorage storage = myDeferredChangesStorage.get(); - storage.getLock().lock(); - try { - result = storage.substring(myArray, start + myStart, end + myStart); - } - finally { - storage.getLock().unlock(); - } + result = myDeferredChangesStorage.substring(myArray, start + myStart, end + myStart); } else { result = myOriginalSequence.subSequence(start, end); } - - if (myDebugDeferredProcessing && isDeferredChangeMode()) { - String expected = myDebugArray.substring(start, end).toString(); - checkStrings(String.format("substring(%d, %d)", start, end), expected, result.toString()); - } return result; } - private static char[] relocateArray(char[] array, int index) { - if (index < array.length) { + @NotNull + private static char[] resizeArray(@NotNull char[] array, int newSize) { + if (newSize < array.length) { return array; } @@ -508,7 +528,7 @@ abstract class CharArray implements CharSequenceBackedByArray { if (newArraySize == 0) { newArraySize = 16; } - while (newArraySize <= index) { + while (newArraySize <= newSize) { newArraySize = newArraySize * 12 / 10 + 1; } char[] newArray = new char[newArraySize]; @@ -516,24 +536,26 @@ abstract class CharArray implements CharSequenceBackedByArray { return newArray; } - private void trimToSize(DocumentImpl subj) { + private void trimToSize() { if (myBufferSize != 0 && length() > myBufferSize) { - flushDeferredChanged(myDeferredChangesStorage.get()); + flushDeferredChanged(); + // make a copy - remove(subj, 0, myCount - myBufferSize, getCharArray().subSequence(0, myCount - myBufferSize).toString()); + int endIndex = myCount - myBufferSize; + String toDelete = getCharArray().subSequence(0, endIndex).toString(); + remove(0, endIndex, toDelete); } } /** - * @return true if this object is at {@link #setDeferredChangeMode(boolean) defer changes} mode; - * false otherwise + * @return true if this object is in the defer changes mode, see {@link #setDeferredChangeMode(boolean)}; */ public boolean isDeferredChangeMode() { - return !DISABLE_DEFERRED_PROCESSING && myDeferredChangeMode; + return myDeferredChangeMode; } public boolean hasDeferredChanges() { - return !myDeferredChangesStorage.get().isEmpty(); + return !myDeferredChangesStorage.isEmpty(); } /** @@ -552,9 +574,9 @@ abstract class CharArray implements CharSequenceBackedByArray { * *
  • * Number of target changes are applied to the current object via standard API - * ({@link #insert(DocumentImpl, CharSequence, int) insert}, - * {@link #remove(DocumentImpl, int, int, CharSequence) remove} and - * {@link #replace(DocumentImpl, int, int, CharSequence, CharSequence, long, boolean) replace}); + * ({@link #insert(CharSequence, int) insert}, + * {@link #remove(int, int, CharSequence) remove} and + * {@link #replace(int, int, java.lang.CharSequence, java.lang.CharSequence, long, boolean)}); *
  • *
  • * Client of this class indicates that 'massive change time' is over by calling this method with 'false' @@ -571,108 +593,109 @@ abstract class CharArray implements CharSequenceBackedByArray { * @param deferredChangeMode flag that defines if 'defer changes' mode should be used by the current object */ public void setDeferredChangeMode(boolean deferredChangeMode) { - if (deferredChangeMode && myDebugDeferredProcessing) { - myDebugArray.setText(null, myDebugTextOnBatchUpdateStart = toString()); - myDebugDeferredChanges.clear(); - } - myDeferredChangeMode = deferredChangeMode; - if (!deferredChangeMode) { - flushDeferredChanged(myDeferredChangesStorage.get()); + if (!DISABLE_DEFERRED_PROCESSING) { + if (deferredChangeMode) { + if (myDebug) { + myDebugArray.setText(myDebugTextOnBatchUpdateStart = toString()); + myDebugDeferredChanges.clear(); + } + prepareForModification(); + myDeferredChangeMode = deferredChangeMode; + } + else { + myDeferredChangeMode = deferredChangeMode; + flushDeferredChanged(); + } } + assertConsistency(); } - private void flushDeferredChanged(@NotNull TextChangesStorage storage) { - storage.getLock().lock(); - try { - doFlushDeferredChanged(); - } - finally { - storage.getLock().unlock(); - } - } - - private void doFlushDeferredChanged() { - TextChangesStorage storage = myDeferredChangesStorage.get(); - List changes = storage.getChanges(); + private void flushDeferredChanged() { + List changes = myDeferredChangesStorage.getChanges(); if (changes.isEmpty()) { return; } - char[] beforeMerge = null; - final boolean inPlace; - if (myDebugDeferredProcessing) { - beforeMerge = new char[myArray.length]; - System.arraycopy(myArray, 0, beforeMerge, 0, myArray.length); - } - - BulkChangesMerger changesMerger = BulkChangesMerger.INSTANCE; - if (myArray.length < length()) { - myArray = changesMerger.mergeToCharArray(myArray, myCount, changes); - inPlace = false; - } - else { - changesMerger.mergeInPlace(myArray, myCount, changes); - inPlace = true; - } + lock.lock(); + try { + char[] beforeMerge = null; + if (myDebug) { + beforeMerge = new char[myArray.length]; + System.arraycopy(myArray, 0, beforeMerge, 0, myArray.length); + } - if (myDebugDeferredProcessing) { - for (int i = 0, max = length(); i < max; i++) { - if (myArray[i] != myDebugArray.myArray[i]) { - dumpDebugInfo(String.format( - "flushDeferredChanged(). Index %d, expected: '%c', actual '%c'. Text before merge: '%s', merge inplace: %b", - i, myDebugArray.myArray[i], myArray[i], Arrays.toString(beforeMerge), inPlace)); - break; + BulkChangesMerger changesMerger = BulkChangesMerger.INSTANCE; + final boolean inPlace; + if (myArray.length < length()) { + myArray = changesMerger.mergeToCharArray(myArray, myCount, changes); + inPlace = false; + } + else { + changesMerger.mergeInPlace(myArray, myCount, changes); + inPlace = true; + } + + myCount += myDeferredShift; + myDeferredShift = 0; + myDeferredChangesStorage.clear(); + myDeferredChangeMode = false; + myStringRef = null; + + if (myDebug) { + for (int i = 0, max = length(); i < max; i++) { + if (myArray[i] != myDebugArray.myArray[i]) { + dumpDebugInfo("flushDeferredChanged(). Index " + i + ", expected: '" + myDebugArray.myArray[i]+"', actual '" + + myArray[i]+"'. Text before merge: '" + Arrays.toString(beforeMerge)+"', merge inplace: "+inPlace); + break; + } } } } - - myCount += myDeferredShift; - myDeferredShift = 0; - storage.clear(); - myDeferredChangeMode = false; + finally { + lock.unlock(); + } + assertConsistency(); } + @NonNls @NotNull - public String dumpState() { - return String.format( - "deferred changes mode: %b, length: %d (data array length: %d, deferred shift: %d); view offsets: [%d; %d]; deferred changes: %s", - isDeferredChangeMode(), length(), myCount, myDeferredShift, myStart, myEnd, myDeferredChangesStorage - ); + private String dumpState() { + return "deferred changes mode: " + isDeferredChangeMode()+", length: " + length()+" (data array length: " + myCount+ + ", deferred shift: " + myDeferredShift+"); view offsets: [" + myStart+"; "+myCount+"]; deferred changes: "+myDeferredChangesStorage; } - private void checkStrings(@NotNull String operation, @NotNull String expected, @NotNull String actual) { + private void checkStrings(@NonNls @NotNull String operation, @NotNull String expected, @NotNull String actual) { if (expected.equals(actual)) { return; } for (int i = 0, max = Math.min(expected.length(), actual.length()); i < max; i++) { if (actual.charAt(i) != expected.charAt(i)) { - dumpDebugInfo(String.format( - "Incorrect %s processing. Expected length: %d, actual length: %d. Unmatched symbol at %d - expected: '%c', " + - "actual: '%c', expected document: '%s', actual document: '%s'", - operation, expected.length(), actual.length(), i, expected.charAt(i), actual.charAt(i), expected, actual - )); + dumpDebugInfo( + "Incorrect " + + operation+" processing. Expected length: " + + expected.length()+", actual length: " + + actual.length()+". Unmatched symbol at " + + i+" - expected: '" + + expected.charAt(i)+"', " + + "actual: '" + + actual.charAt(i)+"', expected document: '" + + expected+"', actual document: '" + + actual+"'" + ); return; } } - dumpDebugInfo(String.format( - "Incorrect %s processing. Expected length: %d, actual length: %d, expected: '%s', actual: '%s'", - operation, expected.length(), actual.length(), expected, actual - )); + dumpDebugInfo("Incorrect " + operation+" processing. Expected length: " + expected.length()+", actual length: " + + actual.length()+", expected: '" + expected+"', actual: '" + actual+"'"); } - private void dumpDebugInfo(@NotNull String problem) { - //LOG.error(String.format( - // "/***********************************************************\n" + - // " * Please email idea.log to Denis.Zhdanov@jetbrains.com\n" + - // " ***********************************************************/\n" + - // "Incorrect CharArray processing detected: '%s'. Start: %d, end: %d, text on batch update start: '%s', deferred changes history: %s, " - // + "current deferred changes: %s", - // problem, myStart, myEnd, myDebugTextOnBatchUpdateStart, myDebugDeferredChanges, myDeferredChangesStorage - //)); - LOG.error(String.format( - "Incorrect CharArray processing detected: '%s'. Start: %d, end: %d, text on batch update start: '%s', deferred changes history: %s, " - + "current deferred changes: %s", - problem, myStart, myEnd, myDebugTextOnBatchUpdateStart, myDebugDeferredChanges, myDeferredChangesStorage - )); + private void dumpDebugInfo(@NonNls @NotNull String problem) { + LOG.error( + "Incorrect CharArray processing detected: " + problem + + ". Start: " + myStart + + ", count: " + myCount + ", text on batch update start: " + + myDebugTextOnBatchUpdateStart + ", deferred changes history: " + + myDebugDeferredChanges + ", current deferred changes: " + myDeferredChangesStorage + ); } } diff --git a/platform/core-impl/src/com/intellij/openapi/editor/impl/DocumentImpl.java b/platform/core-impl/src/com/intellij/openapi/editor/impl/DocumentImpl.java index 715ff4833c70..ef820d74b40c 100644 --- a/platform/core-impl/src/com/intellij/openapi/editor/impl/DocumentImpl.java +++ b/platform/core-impl/src/com/intellij/openapi/editor/impl/DocumentImpl.java @@ -29,12 +29,10 @@ import com.intellij.openapi.editor.ex.*; import com.intellij.openapi.editor.impl.event.DocumentEventImpl; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.util.ShutDownTracker; -import com.intellij.openapi.util.TextRange; -import com.intellij.openapi.util.UserDataHolderBase; +import com.intellij.openapi.util.*; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.ArrayUtil; import com.intellij.util.LocalTimeCounter; import com.intellij.util.Processor; import com.intellij.util.containers.ContainerUtil; @@ -59,7 +57,7 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { private ReadonlyFragmentModificationHandler myReadonlyFragmentModificationHandler; private final LineSet myLineSet = new LineSet(); - private final CharArray myText = new MyCharArray(); + private final CharArray myText; private boolean myIsReadOnly = false; private boolean isStripTrailingSpacesEnabled = true; @@ -74,26 +72,20 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { private boolean myEventsHandling = false; private final boolean myAssertWriteAccess; private volatile boolean myDoingBulkUpdate = false; - private boolean myAcceptSlashR = false; + private volatile boolean myAcceptSlashR = false; private boolean myChangeInProgress; - public DocumentImpl(String text) { - this((CharSequence)text); + public DocumentImpl(@NotNull String text) { + this(text, false); + } + public DocumentImpl(@NotNull CharSequence chars) { + this(chars, false); } - public DocumentImpl(CharSequence chars) { - this(); + public DocumentImpl(@NotNull CharSequence chars, boolean forUseInNonAWTThread) { assertValidSeparators(chars); - myText.setText(this, chars); - DocumentEvent event = new DocumentEventImpl(this, 0, null, null, -1, true); - myLineSet.documentCreated(event); - } - - private DocumentImpl() { - this(false); - } - - public DocumentImpl(boolean forUseInNonAWTThread) { + myText = new MyCharArray(CharArrayUtil.fromSequence(chars), chars.length()); + myLineSet.documentCreated(this); setCyclicBufferSize(0); setModificationStamp(LocalTimeCounter.currentTime()); myAssertWriteAccess = !forUseInNonAWTThread; @@ -115,7 +107,7 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { @Override @NotNull public char[] getChars() { - return CharArrayUtil.fromSequence(getCharsSequence()); + return ArrayUtil.realloc(CharArrayUtil.fromSequence(getCharsSequence()), myText.length()); } @Override @@ -351,7 +343,7 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { throwGuardedFragment(marker, offset, null, s.toString()); } - myText.insert(this, s, offset); + myText.insert(s, offset); } @Override @@ -370,7 +362,7 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { throwGuardedFragment(marker, startOffset, sToDelete.toString(), null); } - myText.remove(this, startOffset, endOffset, sToDelete); + myText.remove(startOffset, endOffset, sToDelete); } @Override @@ -406,10 +398,6 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { newEndInString--; endOffset--; } - //if (newEndInString - newStartInString == 0 && startOffset == endOffset) { - //setModificationStamp(newModificationStamp); - //return; - //} s = s.subSequence(newStartInString, newEndInString); CharSequence sToDelete = myText.substring(startOffset, endOffset); @@ -418,7 +406,7 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { throwGuardedFragment(guard, startOffset, sToDelete.toString(), s.toString()); } - myText.replace(this, startOffset, endOffset, sToDelete, s, newModificationStamp, wholeTextReplaced); + myText.replace(startOffset, endOffset, sToDelete, s, newModificationStamp, wholeTextReplaced); } private void assertBounds(final int startOffset, final int endOffset) { @@ -442,7 +430,7 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { } } - private void assertValidSeparators(final CharSequence s) { + private void assertValidSeparators(@NotNull CharSequence s) { if (myAcceptSlashR) return; StringUtil.assertValidSeparators(s); } @@ -535,9 +523,6 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { } DocumentEvent event = new DocumentEventImpl(this, offset, oldString, newString, myModificationStamp, wholeTextReplaced); - //System.out.printf("%nbefore change: offset=%d, old text='%s', new text='%s'%n document: id=%d, modification stamp=%d%nDocument:'%s'%n", - // event.getOffset(), event.getOldFragment(), event.getNewFragment(), System.identityHashCode(this), - // getModificationStamp(), getText()); if (!ShutDownTracker.isShutdownHookRunning()) { DocumentListener[] listeners = getCachedListeners(); @@ -556,8 +541,6 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { } private void changedUpdate(DocumentEvent event, long newModificationStamp) { - //System.out.printf("after change: document id=%d, new modification stamp=%d%ndocument='%s'%n", System.identityHashCode(this), - // getModificationStamp(), getText()); try { if (LOG.isDebugEnabled()) LOG.debug(event.toString()); @@ -583,48 +566,49 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { @Override public String getText() { - assertReadAccessToDocumentsAllowed(); - return myText.toString(); + return ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public String compute() { + return myText.toString(); + } + }); } @NotNull @Override - public String getText(@NotNull TextRange range) { - assertReadAccessToDocumentsAllowed(); - return myText.substring(range.getStartOffset(), range.getEndOffset()).toString(); + public String getText(@NotNull final TextRange range) { + return ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public String compute() { + return myText.substring(range.getStartOffset(), range.getEndOffset()).toString(); + } + }); } @Override public int getTextLength() { - assertReadAccessToDocumentsAllowed(); return myText.length(); } - private static void assertReadAccessToDocumentsAllowed() { - /* - final ApplicationEx application = ApplicationManagerEx.getApplicationEx(); - if (application != null) { - application.assertReadAccessToDocumentsAllowed(); - } - */ - } - -/* - This method should be used very carefully - only to read the array, and to be sure, that nobody changes - text, while this array is processed. - Really it is used only to optimize paint in Editor. - [Valentin] 25.04.2001: More really, it is used in 61 places in 29 files across the project :-))) -*/ - + /** + This method should be used very carefully - only to read the array, and to be sure, that nobody changes + text, while this array is processed. + Really it is used only to optimize paint in Editor. + [Valentin] 25.04.2001: More really, it is used in 61 places in 29 files across the project :-))) + */ CharSequence getCharsNoThreadCheck() { - return myText.getCharArray(); + return getCharsSequence(); } @Override @NotNull public CharSequence getCharsSequence() { - assertReadAccessToDocumentsAllowed(); - return myText.getCharArray(); + return ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public CharSequence compute() { + return myText.getCharArray(); + } + }); } @@ -656,11 +640,8 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { } @Override - public int getLineNumber(int offset) { - assertReadAccessToDocumentsAllowed(); - int lineIndex = myLineSet.findLineIndex(offset); - assert lineIndex >= 0; - return lineIndex; + public int getLineNumber(final int offset) { + return myLineSet.findLineIndex(offset); } @Override @@ -670,12 +651,9 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { } @Override - public final int getLineStartOffset(int line) { - assertReadAccessToDocumentsAllowed(); + public final int getLineStartOffset(final int line) { if (line == 0) return 0; // otherwise it crashed for zero-length document - int lineStart = myLineSet.getLineStart(line); - assert lineStart >= 0; - return lineStart; + return myLineSet.getLineStart(line); } @Override @@ -821,25 +799,41 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { return result.toString(); } - private static class MyCharArray extends CharArray { - public MyCharArray() { - super(0); + private class MyCharArray extends CharArray { + private MyCharArray(@NotNull char[] chars, int length) { + super(0, chars, length); } @Override @NotNull - protected DocumentEvent beforeChangedUpdate(DocumentImpl subj, - int offset, + protected DocumentEvent beforeChangedUpdate(int offset, CharSequence oldString, CharSequence newString, boolean wholeTextReplaced) { - return subj.beforeChangedUpdate(offset, oldString, newString, wholeTextReplaced); + return DocumentImpl.this.beforeChangedUpdate(offset, oldString, newString, wholeTextReplaced); } @Override protected void afterChangedUpdate(@NotNull DocumentEvent event, long newModificationStamp) { ((DocumentImpl)event.getDocument()).changedUpdate(event, newModificationStamp); } + + @Override + protected void assertWriteAccess() { + DocumentImpl.this.assertWriteAccess(); + } + + @Override + protected void assertReadAccess() { + DocumentImpl.assertReadAccess(); + } + } + + private static void assertReadAccess() { + final Application application = ApplicationManager.getApplication(); + if (application != null) { + application.assertReadAccessAllowed(); + } } } diff --git a/platform/core-impl/src/com/intellij/openapi/editor/impl/LineSet.java b/platform/core-impl/src/com/intellij/openapi/editor/impl/LineSet.java index 03a901e606db..f92cb7cfe669 100644 --- a/platform/core-impl/src/com/intellij/openapi/editor/impl/LineSet.java +++ b/platform/core-impl/src/com/intellij/openapi/editor/impl/LineSet.java @@ -16,12 +16,14 @@ package com.intellij.openapi.editor.impl; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.editor.ex.LineIterator; import com.intellij.openapi.editor.ex.util.SegmentArrayWithData; import com.intellij.openapi.editor.impl.event.DocumentEventImpl; import com.intellij.openapi.util.text.LineTokenizer; import com.intellij.util.text.MergingCharSequence; +import org.jetbrains.annotations.NotNull; /** * Data structure specialized for working with document text lines, i.e. stores information about line mapping to document @@ -35,7 +37,9 @@ public class LineSet{ private static final int SEPARATOR_MASK = 0x3; public int findLineIndex(int offset) { - return mySegments.findSegmentIndex(offset); + int lineIndex = mySegments.findSegmentIndex(offset); + assert lineIndex >= 0; + return lineIndex; } public LineIterator createIterator() { @@ -43,7 +47,9 @@ public class LineSet{ } final int getLineStart(int index) { - return mySegments.getSegmentStart(index); + int lineStart = mySegments.getSegmentStart(index); + assert lineStart >= 0; + return lineStart; } final int getLineEnd(int index) { @@ -65,8 +71,8 @@ public class LineSet{ return mySegments.getSegmentCount(); } - public void documentCreated(DocumentEvent e) { - initSegments(e.getDocument().getCharsSequence(), false); + public void documentCreated(@NotNull Document document) { + initSegments(document.getCharsSequence(), false); } public void changedUpdate(DocumentEvent e1) { diff --git a/platform/core-impl/src/com/intellij/openapi/editor/impl/RangeMarkerTree.java b/platform/core-impl/src/com/intellij/openapi/editor/impl/RangeMarkerTree.java index eb979312e590..c82d44e5a1bf 100644 --- a/platform/core-impl/src/com/intellij/openapi/editor/impl/RangeMarkerTree.java +++ b/platform/core-impl/src/com/intellij/openapi/editor/impl/RangeMarkerTree.java @@ -37,7 +37,7 @@ import java.util.concurrent.atomic.AtomicInteger; */ public class RangeMarkerTree extends IntervalTreeImpl { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.editor.impl.RangeMarkerTree"); - private static final boolean DEBUG = LOG.isDebugEnabled() || ApplicationManager.getApplication().isUnitTestMode() || ApplicationManager.getApplication().isInternal(); + private static final boolean DEBUG = LOG.isDebugEnabled() || ApplicationManager.getApplication() != null && (ApplicationManager.getApplication().isUnitTestMode() || ApplicationManager.getApplication().isInternal()); private final PrioritizedDocumentListener myListener; private final Document myDocument; diff --git a/platform/core-impl/src/com/intellij/openapi/editor/impl/TextChangesStorage.java b/platform/core-impl/src/com/intellij/openapi/editor/impl/TextChangesStorage.java index bf3235f31472..db052d9f5fab 100644 --- a/platform/core-impl/src/com/intellij/openapi/editor/impl/TextChangesStorage.java +++ b/platform/core-impl/src/com/intellij/openapi/editor/impl/TextChangesStorage.java @@ -17,13 +17,12 @@ package com.intellij.openapi.editor.impl; import com.intellij.openapi.editor.TextChange; import com.intellij.util.text.CharArrayUtil; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; /** * Allows to store and retrieve {@link TextChange} objects assuming that they are applied to the same text. @@ -36,9 +35,7 @@ import java.util.concurrent.locks.ReentrantLock; * @since 3/2/11 11:55 AM */ public class TextChangesStorage { - private final List myChanges = new ArrayList(); - private final Lock myLock = new ReentrantLock(); /** * @return list of changes stored previously via {@link #store(TextChange)}. Note that the changes offsets relate to initial @@ -47,22 +44,15 @@ public class TextChangesStorage { */ @NotNull public List getChanges() { - List result = new ArrayList(); + if (myChanges.isEmpty()) return Collections.emptyList(); + List result = new ArrayList(myChanges.size()); + for (ChangeEntry changeEntry : myChanges) { result.add(changeEntry.change); } return result; } - /** - * @return lock object associated with the current storage object. We use {@link Lock} in preference to 'synchronised' here - * because it shows better performance - */ - @NotNull - public Lock getLock() { - return myLock; - } - /** * Allows to ask the storage for the list of changes that have intersections with the target text range (identified by the given * arguments). @@ -176,8 +166,7 @@ public class TextChangesStorage { int clientShift = 0; // 'Client text' shift before the given change to store. I.e. this value can be subtracted from the // given change's start/end offsets in order to get original document range affected by the given change. int changeDiff = change.getText().length() - (change.getEnd() - change.getStart()); - boolean updateClientOffsetOnly = false; - + if (insertionIndex < 0) { insertionIndex = -insertionIndex - 1; if (insertionIndex >= myChanges.size()) { @@ -200,7 +189,8 @@ public class TextChangesStorage { ChangeEntry changeEntry = myChanges.get(insertionIndex); clientShift = changeEntry.clientStartOffset - changeEntry.change.getStart(); } - + + boolean updateClientOffsetOnly = false; for (int i = insertionIndex; i < myChanges.size(); i++) { ChangeEntry changeEntry = myChanges.get(i); int storedClientStart = changeEntry.change.getStart() + clientShift; @@ -366,8 +356,8 @@ public class TextChangesStorage { } } else { - int clientShift = 0; changeIndex = -changeIndex - 1; + int clientShift = 0; if (changeIndex > 0 && changeIndex <= myChanges.size()) { ChangeEntry changeEntry = myChanges.get(changeIndex - 1); clientShift = changeEntry.clientStartOffset - changeEntry.change.getStart() + changeEntry.change.getDiff(); @@ -519,6 +509,7 @@ public class TextChangesStorage { return clientStartOffset + change.getText().length(); } + @NonNls @Override public String toString() { return "client start offset: " + clientStartOffset + ", change: " + change; diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/text/DiffLog.java b/platform/core-impl/src/com/intellij/psi/impl/source/text/DiffLog.java index acd90c20fbeb..c6d0d198b9c3 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/source/text/DiffLog.java +++ b/platform/core-impl/src/com/intellij/psi/impl/source/text/DiffLog.java @@ -94,16 +94,18 @@ public class DiffLog implements DiffTreeChangeBuilder { private final ASTNode myOldChild; private final ASTNode myNewChild; - public ReplaceEntry(ASTNode oldChild, ASTNode newChild) { - myOldChild = oldChild; - myNewChild = newChild; + public ReplaceEntry(@NotNull ASTNode oldNode, @NotNull ASTNode newNode) { + myOldChild = oldNode; + myNewChild = newNode; + ASTNode parent = oldNode.getTreeParent(); + assert parent != null : "old:" + oldNode + " new:" + newNode; } @Override void doActualPsiChange(@NotNull PsiFile file, @NotNull ASTDiffBuilder astDiffBuilder) { ASTNode oldNode = myOldChild; - ASTNode parent = oldNode.getTreeParent(); ASTNode newNode = myNewChild; + ASTNode parent = oldNode.getTreeParent(); assert parent != null : "old:" + oldNode + " new:" + newNode; final PsiElement psiParent = parent.getPsi(); diff --git a/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionParameters.java b/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionParameters.java index 56eb0c10af62..61fa75a9ff2a 100644 --- a/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionParameters.java +++ b/platform/lang-api/src/com/intellij/codeInsight/completion/CompletionParameters.java @@ -97,6 +97,10 @@ public class CompletionParameters { return myInvocationCount; } + public boolean isAutoPopup() { + return myInvocationCount == 0; + } + public boolean isRelaxedMatching() { return myRelaxedMatching; } diff --git a/platform/lang-api/src/com/intellij/psi/search/scope/packageSet/CustomScopesProviderEx.java b/platform/lang-api/src/com/intellij/psi/search/scope/packageSet/CustomScopesProviderEx.java new file mode 100644 index 000000000000..85ce78d981f0 --- /dev/null +++ b/platform/lang-api/src/com/intellij/psi/search/scope/packageSet/CustomScopesProviderEx.java @@ -0,0 +1,40 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.search.scope.packageSet; + +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * User: anna + * Date: 3/14/12 + */ +public abstract class CustomScopesProviderEx implements CustomScopesProvider { + @Nullable + public NamedScope getCustomScope(String name) { + final List predefinedScopes = getCustomScopes(); + return findPredefinedScope(name, predefinedScopes); + } + + @Nullable + public static NamedScope findPredefinedScope(String name, List predefinedScopes) { + for (NamedScope scope : predefinedScopes) { + if (name.equals(scope.getName())) return scope; + } + return null; + } +} diff --git a/platform/lang-api/src/com/intellij/psi/search/scope/packageSet/NamedScopesHolder.java b/platform/lang-api/src/com/intellij/psi/search/scope/packageSet/NamedScopesHolder.java index bad77d1febbe..86d25079de64 100644 --- a/platform/lang-api/src/com/intellij/psi/search/scope/packageSet/NamedScopesHolder.java +++ b/platform/lang-api/src/com/intellij/psi/search/scope/packageSet/NamedScopesHolder.java @@ -179,17 +179,18 @@ public abstract class NamedScopesHolder implements PersistentStateComponent predefinedScopes = getPredefinedScopes(); - for (NamedScope scope : predefinedScopes) { - if (name.equals(scope.getName())) return scope; - } - return null; + return getPredefinedScope(name); } @NotNull public List getPredefinedScopes(){ return Collections.emptyList(); } + + @Nullable + public NamedScope getPredefinedScope(String name) { + return null; + } public Project getProject() { return myProject; diff --git a/platform/lang-impl/src/com/intellij/codeEditor/printing/PrintManager.java b/platform/lang-impl/src/com/intellij/codeEditor/printing/PrintManager.java index 2720b18d5e32..1bd976a11255 100644 --- a/platform/lang-impl/src/com/intellij/codeEditor/printing/PrintManager.java +++ b/platform/lang-impl/src/com/intellij/codeEditor/printing/PrintManager.java @@ -28,6 +28,7 @@ import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ProjectUtil; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; @@ -35,6 +36,7 @@ import com.intellij.psi.PsiFile; import java.awt.print.*; import java.util.ArrayList; +import java.util.Collections; class PrintManager { public static void executePrint(DataContext dataContext) { @@ -159,13 +161,13 @@ class PrintManager { private static void addToPsiFileList(PsiDirectory psiDirectory, ArrayList filesList, boolean isRecursive) { PsiFile[] files = psiDirectory.getFiles(); - for (PsiFile file : files) { - filesList.add(file); - } + Collections.addAll(filesList, files); if(isRecursive) { PsiDirectory[] directories = psiDirectory.getSubdirectories(); for (PsiDirectory directory : directories) { - addToPsiFileList(directory, filesList, isRecursive); + if (!ProjectUtil.DIRECTORY_BASED_PROJECT_DIR.equals(directory.getName())) { + addToPsiFileList(directory, filesList, isRecursive); + } } } } diff --git a/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java b/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java index debad04a65d1..5876d384ce68 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java @@ -150,6 +150,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo myPendingFoldRegions.clear(); final FoldingModel model = myEditor.getFoldingModel(); model.runBatchFoldingOperation(new Runnable() { + @Override public void run() { for (FoldRegion region : model.getAllFoldRegions()) { model.removeFoldRegion(region); @@ -197,6 +198,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo myHyperlinkInfo = hyperlinkInfo; } + @Override public HyperlinkInfo getHyperlinkInfo() { return myHyperlinkInfo; } @@ -311,10 +313,12 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo }; } + @Override public void attachToProcess(final ProcessHandler processHandler) { myState = myState.attachTo(this, processHandler); } + @Override public void clear() { if (myEditor == null) return; synchronized (LOCK) { @@ -329,6 +333,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo cancelHeavyAlarm(); } + @Override public void scrollTo(final int offset) { if (myEditor == null) return; class ScrollRunnable extends MyFlushRunnable { @@ -355,6 +360,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo public void requestScrollingToEnd() { if (myEditor == null) return; final MyFlushRunnable scrollRunnable = new MyFlushRunnable() { + @Override public void doRun() { flushDeferredText(); if (myEditor == null) return; @@ -382,6 +388,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo ApplicationManager.getApplication().assertIsDispatchThread(); } + @Override public void setOutputPaused(final boolean value) { myOutputPaused = value; if (!value) { @@ -389,16 +396,19 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo } } + @Override public boolean isOutputPaused() { return myOutputPaused; } + @Override public boolean hasDeferredOutput() { synchronized (LOCK) { return myBuffer.getLength() > 0; } } + @Override public void performWhenNoDeferredOutput(final Runnable runnable) { //Q: implement in another way without timer? if (!hasDeferredOutput()) { @@ -408,6 +418,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo if (mySpareTimeAlarm.isDisposed()) return; mySpareTimeAlarm.addRequest( new Runnable() { + @Override public void run() { performWhenNoDeferredOutput(runnable); } @@ -418,6 +429,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo } } + @Override public JComponent getComponent() { if (myMainPanel == null) { myMainPanel = new JPanel(new BorderLayout()); @@ -462,6 +474,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo } + @Override public void dispose() { myState = myState.dispose(); if (myEditor != null) { @@ -495,6 +508,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo } } + @Override public void print(String s, final ConsoleViewContentType contentType) { printHyperlink(s, contentType, null); } @@ -538,12 +552,14 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo } } + @Override public int getContentSize() { synchronized (LOCK) { return myContentSize; } } + @Override public boolean canPause() { return true; } @@ -573,6 +589,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo cancelHeavyAlarm(); } CommandProcessor.getInstance().executeCommand(myProject, new Runnable() { + @Override public void run() { document.deleteString(0, document.getTextLength()); } @@ -604,6 +621,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo final boolean isAtEndOfDocument = myEditor.getCaretModel().getOffset() == document.getTextLength(); boolean cycleUsed = myBuffer.isUseCyclicBuffer() && document.getTextLength() + text.length() > myBuffer.getCyclicBufferSize(); CommandProcessor.getInstance().executeCommand(myProject, new Runnable() { + @Override public void run() { int offset = myEditor.getCaretModel().getOffset(); boolean preserveCurrentVisualArea = offset < document.getTextLength(); @@ -679,6 +697,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo return; } myFlushUserInputAlarm.addRequest(new Runnable() { + @Override public void run() { if (myState.isRunning()) { try { @@ -692,6 +711,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo }, 0); } + @Override public Object getData(final String dataId) { if (PlatformDataKeys.NAVIGATABLE.is(dataId)) { if (myEditor == null) { @@ -718,6 +738,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo return null; } + @Override public void setHelpId(final String helpId) { myHelpId = helpId; } @@ -726,16 +747,19 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo myUpdateFoldingsEnabled = updateFoldingsEnabled; } + @Override public void addMessageFilter(final Filter filter) { myCustomFilter.addFilter(filter); } + @Override public void printHyperlink(final String hyperlinkText, final HyperlinkInfo info) { printHyperlink(hyperlinkText, ConsoleViewContentType.NORMAL_OUTPUT, info); } private EditorEx createEditor() { return ApplicationManager.getApplication().runReadAction(new Computable() { + @Override public EditorEx compute() { return doCreateEditor(); } @@ -746,6 +770,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo final EditorEx editor = createRealEditor(); editor.addEditorMouseListener(new EditorPopupHandler() { + @Override public void invokePopup(final EditorMouseEvent event) { final MouseEvent mouseEvent = event.getMouseEvent(); popupInvoked(mouseEvent); @@ -765,9 +790,11 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo final EditorEx editor = ConsoleViewUtil.setupConsoleEditor(myProject, true, false); editor.getDocument().addDocumentListener(new DocumentListener() { + @Override public void beforeDocumentChange(DocumentEvent event) { } + @Override public void documentChanged(DocumentEvent event) { if (event.getNewLength() == 0) { // string has been removed, adjust token ranges @@ -880,9 +907,9 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo final int startLine = Math.max(0, line1); final Document document = myEditor.getDocument(); - final Document documentCopy = new DocumentImpl(true); final int startOffset = document.getLineStartOffset(startLine); - documentCopy.setText(new String(document.getText(new TextRange(startOffset, document.getLineEndOffset(endLine))))); + String text = new String(document.getText(new TextRange(startOffset, document.getLineEndOffset(endLine)))); + final Document documentCopy = new DocumentImpl(text,true); documentCopy.setReadOnly(true); myJLayeredPane.startUpdating(); @@ -940,6 +967,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo myFoldingAlarm.cancelAllRequests(); final Runnable runnable = new Runnable() { + @Override public void run() { if (myEditor == null || myEditor.isDisposed()) { return; @@ -948,6 +976,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo assertIsDispatchThread(); final FoldingModel model = myEditor.getFoldingModel(); final Runnable operation = new Runnable() { + @Override public void run() { assertIsDispatchThread(); for (FoldRegion region : myPendingFoldRegions) { @@ -1039,6 +1068,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo e.getPresentation().setEnabled(enabled); } + @Override public void actionPerformed(final AnActionEvent e) { final ConsoleView consoleView = e.getData(LangDataKeys.CONSOLE_VIEW); if (consoleView != null) { @@ -1057,12 +1087,14 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo private class MyHighlighter extends DocumentAdapter implements EditorHighlighter { private HighlighterClient myEditor; + @Override public HighlighterIterator createIterator(final int startOffset) { final int startIndex = ConsoleUtil.findTokenInfoIndexByOffset(myTokens, startOffset); return new HighlighterIterator() { private int myIndex = startIndex; + @Override public TextAttributes getTextAttributes() { if (myFileType != null && getTokenInfo().contentType == ConsoleViewContentType.USER_INPUT) { return ConsoleViewContentType.NORMAL_OUTPUT.getAttributes(); @@ -1070,30 +1102,37 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo return getTokenInfo() == null ? null : getTokenInfo().attributes; } + @Override public int getStart() { return getTokenInfo() == null ? 0 : getTokenInfo().startOffset; } + @Override public int getEnd() { return getTokenInfo() == null ? 0 : getTokenInfo().endOffset; } + @Override public IElementType getTokenType() { return null; } + @Override public void advance() { myIndex++; } + @Override public void retreat() { myIndex--; } + @Override public boolean atEnd() { return myIndex < 0 || myIndex >= myTokens.size(); } + @Override public Document getDocument() { return myEditor.getDocument(); } @@ -1104,14 +1143,17 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo }; } + @Override public void setText(final CharSequence text) { } + @Override public void setEditor(final HighlighterClient editor) { LOG.assertTrue(myEditor == null, "Highlighters cannot be reused with different editors"); myEditor = editor; } + @Override public void setColorScheme(EditorColorsScheme scheme) { } } @@ -1122,6 +1164,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo super(originalAction); } + @Override public void execute(@NotNull final Editor editor, final char charTyped, @NotNull final DataContext dataContext) { final ConsoleViewImpl consoleView = editor.getUserData(CONSOLE_VIEW_IN_EDITOR_VIEW); if (consoleView == null || !consoleView.myState.isRunning() || consoleView.isViewer) { @@ -1141,6 +1184,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo } private abstract static class ConsoleAction extends AnAction implements DumbAware { + @Override public void actionPerformed(final AnActionEvent e) { final DataContext context = e.getDataContext(); final ConsoleViewImpl console = getRunningConsole(context); @@ -1149,6 +1193,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo protected abstract void execute(ConsoleViewImpl console, final DataContext context); + @Override public void update(final AnActionEvent e) { final ConsoleViewImpl console = getRunningConsole(e.getDataContext()); e.getPresentation().setEnabled(console != null); @@ -1168,6 +1213,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo } private static class EnterHandler extends ConsoleAction { + @Override public void execute(final ConsoleViewImpl consoleView, final DataContext context) { synchronized (consoleView.LOCK) { String str = consoleView.myBuffer.getUserInput(); @@ -1184,6 +1230,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo } private static class PasteHandler extends ConsoleAction { + @Override public void execute(final ConsoleViewImpl consoleView, final DataContext context) { final Transferable content = CopyPasteManager.getInstance().getContents(); if (content == null) return; @@ -1207,6 +1254,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo } private static class BackSpaceHandler extends ConsoleAction { + @Override public void execute(final ConsoleViewImpl consoleView, final DataContext context) { final Editor editor = consoleView.myEditor; @@ -1237,6 +1285,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo } private static class DeleteHandler extends ConsoleAction { + @Override public void execute(final ConsoleViewImpl consoleView, final DataContext context) { final Editor editor = consoleView.myEditor; @@ -1266,6 +1315,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo } } + @Override public JComponent getPreferredFocusableComponent() { //ensure editor created getComponent(); @@ -1274,14 +1324,17 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo // navigate up/down in stack trace + @Override public boolean hasNextOccurence() { return calcNextOccurrence(1) != null; } + @Override public boolean hasPreviousOccurence() { return calcNextOccurrence(-1) != null; } + @Override public OccurenceInfo goNextOccurence() { return calcNextOccurrence(1); } @@ -1300,14 +1353,17 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo }); } + @Override public OccurenceInfo goPreviousOccurence() { return calcNextOccurrence(-1); } + @Override public String getNextOccurenceActionName() { return ExecutionBundle.message("down.the.stack.trace"); } + @Override public String getPreviousOccurenceActionName() { return ExecutionBundle.message("up.the.stack.trace"); } @@ -1316,6 +1372,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo customActions.add(action); } + @Override @NotNull public AnAction[] createConsoleActions() { //Initializing prev and next occurrences actions @@ -1433,9 +1490,11 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo myEditor.getContentComponent().setEnabled(enabled); } + @Override public void addChangeListener(final ChangeListener listener, final Disposable parent) { myListeners.add(listener); Disposer.register(parent, new Disposable() { + @Override public void dispose() { myListeners.remove(listener); } @@ -1732,6 +1791,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo private final class MyClearRunnable extends MyFlushRunnable { + @Override public void doRun() { flushDeferredText(true); } diff --git a/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/RunnerContentUi.java b/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/RunnerContentUi.java index ee32bf865fe1..b3519b762f96 100644 --- a/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/RunnerContentUi.java +++ b/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/RunnerContentUi.java @@ -1053,6 +1053,9 @@ public class RunnerContentUi implements ContentUI, Disposable, CellTransform.Fac if (myOriginal != null) { myOriginal.myChildren.remove(this); } + myMinimizedButtonsPlaceholder.clear(); + myCommonActionsPlaceholder.clear(); + myContextActions.clear(); } public void restoreLayout() { diff --git a/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/RunnerLayoutUiImpl.java b/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/RunnerLayoutUiImpl.java index 36e66742cf50..7b5ec5a58e89 100644 --- a/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/RunnerLayoutUiImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/RunnerLayoutUiImpl.java @@ -60,7 +60,7 @@ public class RunnerLayoutUiImpl implements Disposable.Parent, RunnerLayoutUi, La myContentUI = new RunnerContentUi(project, this, ActionManager.getInstance(), IdeFocusManager.getInstance(project), myLayout, runnerTitle + " - " + sessionName); - + Disposer.register(this, myContentUI); myContentPanel = new MyContent(); myViewsContentManager = getContentFactory().createContentManager(myContentUI.getContentUI(), false, project); diff --git a/platform/lang-impl/src/com/intellij/openapi/editor/ex/util/LayeredLexerEditorHighlighter.java b/platform/lang-impl/src/com/intellij/openapi/editor/ex/util/LayeredLexerEditorHighlighter.java index 35ea4a661d46..d94699cbb86f 100644 --- a/platform/lang-impl/src/com/intellij/openapi/editor/ex/util/LayeredLexerEditorHighlighter.java +++ b/platform/lang-impl/src/com/intellij/openapi/editor/ex/util/LayeredLexerEditorHighlighter.java @@ -70,6 +70,7 @@ public class LayeredLexerEditorHighlighter extends LexerEditorHighlighter { } } + @Override public MappingSegments getSegments() { return (MappingSegments)super.getSegments(); } @@ -113,6 +114,7 @@ public class LayeredLexerEditorHighlighter extends LexerEditorHighlighter { } } + @Override public void setText(final CharSequence text) { // do NOT synchronize before updateLayers due to deadlock with PsiLock updateLayers(); @@ -132,6 +134,7 @@ public class LayeredLexerEditorHighlighter extends LexerEditorHighlighter { } }; + @Override public void addToken(final int i, final int startOffset, final int endOffset, final int data, final IElementType tokenType) { getSegments().setElementLight(i, startOffset, endOffset, data); final Mapper mapper = getMappingDocument(tokenType); @@ -151,6 +154,7 @@ public class LayeredLexerEditorHighlighter extends LexerEditorHighlighter { protected boolean updateLayers() { return false; } + @Override public void documentChanged(DocumentEvent e) { // do NOT synchronize before updateLayers due to deadlock with PsiLock final boolean b = updateLayers(); @@ -166,6 +170,7 @@ public class LayeredLexerEditorHighlighter extends LexerEditorHighlighter { } } + @Override public HighlighterIterator createIterator(int startOffset) { // do NOT synchronize before updateLayers due to deadlock with PsiLock final boolean b = updateLayers(); @@ -181,6 +186,7 @@ public class LayeredLexerEditorHighlighter extends LexerEditorHighlighter { private class MappingSegments extends SegmentArrayWithData { MappedRange[] myRanges = new MappedRange[INITIAL_SIZE]; + @Override public void removeAll() { if (mySegmentCount != 0) { Arrays.fill(myRanges, null); @@ -191,6 +197,7 @@ public class LayeredLexerEditorHighlighter extends LexerEditorHighlighter { super.removeAll(); } + @Override public void setElementAt(int i, int startOffset, int endOffset, int data) { setElementLight(i, startOffset, endOffset, (short)data); final MappedRange range = myRanges[i]; @@ -207,6 +214,7 @@ public class LayeredLexerEditorHighlighter extends LexerEditorHighlighter { myRanges = reallocateArray(myRanges, i+1); } + @Override public void remove(int startIndex, int endIndex) { Map mins = new FactoryMap() { @Override @@ -238,6 +246,7 @@ public class LayeredLexerEditorHighlighter extends LexerEditorHighlighter { super.remove(startIndex, endIndex); } + @Override public void replace(int startOffset, @NotNull SegmentArrayWithData data, int len) { super.replace(startOffset, data, len); for (int i = startOffset; i < startOffset + len; i++) { @@ -245,6 +254,7 @@ public class LayeredLexerEditorHighlighter extends LexerEditorHighlighter { } } + @Override public void insert(@NotNull SegmentArrayWithData segmentArray, final int startIndex) { synchronized (LayeredLexerEditorHighlighter.this) { super.insert(segmentArray, startIndex); @@ -305,7 +315,7 @@ public class LayeredLexerEditorHighlighter extends LexerEditorHighlighter { private Mapper(LayerDescriptor descriptor) { - doc = new DocumentImpl(true); + doc = new DocumentImpl("",true); mySyntaxHighlighter = descriptor.getLayerHighlighter(); myBackground = descriptor.getBackgroundKey(); @@ -330,14 +340,17 @@ public class LayeredLexerEditorHighlighter extends LexerEditorHighlighter { return new LimitedRangeHighlighterIterator(highlighter.createIterator(rangeStart + shift), rangeStart, rangeEnd); } + @Override public Project getProject() { return getClient().getProject(); } + @Override public void repaint(int start, int end) { // TODO: map ranges to outer document } + @Override public Document getDocument() { return LayeredLexerEditorHighlighter.this.getDocument(); } @@ -480,6 +493,7 @@ public class LayeredLexerEditorHighlighter extends LexerEditorHighlighter { } } + @Override public TextAttributes getTextAttributes() { if (myCurrentMapper != null) { return myCurrentMapper.getAttributes(getTokenType()); @@ -488,6 +502,7 @@ public class LayeredLexerEditorHighlighter extends LexerEditorHighlighter { return myBaseIterator.getTextAttributes(); } + @Override public int getStart() { if (myLayerIterator != null) { return myLayerIterator.getStart() + myLayerStartOffset; @@ -495,6 +510,7 @@ public class LayeredLexerEditorHighlighter extends LexerEditorHighlighter { return myBaseIterator.getStart(); } + @Override public int getEnd() { if (myLayerIterator != null) { return myLayerIterator.getEnd() + myLayerStartOffset; @@ -502,10 +518,12 @@ public class LayeredLexerEditorHighlighter extends LexerEditorHighlighter { return myBaseIterator.getEnd(); } + @Override public IElementType getTokenType() { return myLayerIterator != null ? myLayerIterator.getTokenType() : myBaseIterator.getTokenType(); } + @Override public void advance() { if (myLayerIterator != null) { myLayerIterator.advance(); @@ -515,6 +533,7 @@ public class LayeredLexerEditorHighlighter extends LexerEditorHighlighter { initLayer(0); } + @Override public void retreat() { if (myLayerIterator != null) { myLayerIterator.retreat(); @@ -525,10 +544,12 @@ public class LayeredLexerEditorHighlighter extends LexerEditorHighlighter { initLayer(myBaseIterator.atEnd() ? 0 : myBaseIterator.getEnd() - myBaseIterator.getStart() - 1); } + @Override public boolean atEnd() { return myBaseIterator.atEnd(); } + @Override public Document getDocument() { return myBaseIterator.getDocument(); } diff --git a/platform/lang-impl/src/com/intellij/packageDependencies/ChangeListsScopesProvider.java b/platform/lang-impl/src/com/intellij/packageDependencies/ChangeListsScopesProvider.java index b6fdf732590a..7229c7dd08dc 100644 --- a/platform/lang-impl/src/com/intellij/packageDependencies/ChangeListsScopesProvider.java +++ b/platform/lang-impl/src/com/intellij/packageDependencies/ChangeListsScopesProvider.java @@ -18,10 +18,7 @@ package com.intellij.packageDependencies; import com.intellij.ide.IdeBundle; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.project.Project; -import com.intellij.openapi.vcs.changes.Change; -import com.intellij.openapi.vcs.changes.ChangeList; -import com.intellij.openapi.vcs.changes.ChangeListManager; -import com.intellij.openapi.vcs.changes.ContentRevision; +import com.intellij.openapi.vcs.changes.*; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.search.scope.packageSet.*; import org.jetbrains.annotations.NotNull; @@ -34,7 +31,7 @@ import java.util.List; /** * @author anna */ -public class ChangeListsScopesProvider implements CustomScopesProvider { +public class ChangeListsScopesProvider extends CustomScopesProviderEx { private Project myProject; public static ChangeListsScopesProvider getInstance(Project project) { @@ -55,22 +52,40 @@ public class ChangeListsScopesProvider implements CustomScopesProvider { final List result = new ArrayList(); result.add(createScope(changeListManager.getAffectedFiles(), IdeBundle.message("scope.modified.files"))); for (ChangeList list : changeListManager.getChangeListsCopy()) { - final List files = new ArrayList(); - final Collection changes = list.getChanges(); - for (Change change : changes) { - final ContentRevision afterRevision = change.getAfterRevision(); - if (afterRevision != null) { - final VirtualFile vFile = afterRevision.getFile().getVirtualFile(); - if (vFile != null) { - files.add(vFile); - } - } - } - result.add(createScope(files, list.getName())); + result.add(createChangeListScope(list)); } return result; } + private static NamedScope createChangeListScope(@NotNull ChangeList list) { + final List files = new ArrayList(); + final Collection changes = list.getChanges(); + for (Change change : changes) { + final ContentRevision afterRevision = change.getAfterRevision(); + if (afterRevision != null) { + final VirtualFile vFile = afterRevision.getFile().getVirtualFile(); + if (vFile != null) { + files.add(vFile); + } + } + } + return createScope(files, list.getName()); + } + + @Override + public NamedScope getCustomScope(String name) { + if (myProject.isDefault()) return null; + final ChangeListManager changeListManager = ChangeListManager.getInstance(myProject); + if (IdeBundle.message("scope.modified.files").equals(name)) { + return createScope(changeListManager.getAffectedFiles(), IdeBundle.message("scope.modified.files")); + } + final LocalChangeList changeList = changeListManager.getChangeList(name); + if (changeList != null) { + return createChangeListScope(changeList); + } + return null; + } + private static NamedScope createScope(final List files, String changeListName) { return new NamedScope(changeListName, new PackageSetBase() { @Override diff --git a/platform/lang-impl/src/com/intellij/packageDependencies/DefaultScopesProvider.java b/platform/lang-impl/src/com/intellij/packageDependencies/DefaultScopesProvider.java index 8bef41c38291..5bf03306d1fb 100644 --- a/platform/lang-impl/src/com/intellij/packageDependencies/DefaultScopesProvider.java +++ b/platform/lang-impl/src/com/intellij/packageDependencies/DefaultScopesProvider.java @@ -35,7 +35,7 @@ import java.util.List; * @author anna * @author Konstantin Bulenkov */ -public class DefaultScopesProvider implements CustomScopesProvider { +public class DefaultScopesProvider extends CustomScopesProviderEx { private final NamedScope myProblemsScope; private final Project myProject; private final List myScopes; diff --git a/platform/lang-impl/src/com/intellij/packageDependencies/DependencyValidationManagerImpl.java b/platform/lang-impl/src/com/intellij/packageDependencies/DependencyValidationManagerImpl.java index 2d58e06331a5..215abbaeca25 100644 --- a/platform/lang-impl/src/com/intellij/packageDependencies/DependencyValidationManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/packageDependencies/DependencyValidationManagerImpl.java @@ -80,6 +80,22 @@ public class DependencyValidationManagerImpl extends DependencyValidationManager return predefinedScopes; } + @Override + public NamedScope getPredefinedScope(String name) { + final CustomScopesProvider[] scopesProviders = myProject.getExtensions(CustomScopesProvider.CUSTOM_SCOPES_PROVIDER); + if (scopesProviders != null) { + for (CustomScopesProvider scopesProvider : scopesProviders) { + final NamedScope scope = scopesProvider instanceof CustomScopesProviderEx + ? ((CustomScopesProviderEx)scopesProvider).getCustomScope(name) + : CustomScopesProviderEx.findPredefinedScope(name, scopesProvider.getCustomScopes()); + if (scope != null) { + return scope; + } + } + } + return null; + } + public boolean hasRules() { return !myRules.isEmpty(); } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/DocumentCommitThread.java b/platform/lang-impl/src/com/intellij/psi/impl/DocumentCommitThread.java index c7872b9d3ae1..8ffa8b89fd4e 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/DocumentCommitThread.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/DocumentCommitThread.java @@ -15,6 +15,7 @@ */ package com.intellij.psi.impl; +import com.intellij.codeInsight.daemon.impl.DaemonProgressIndicator; import com.intellij.ide.startup.impl.StartupManagerImpl; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; @@ -30,8 +31,8 @@ import com.intellij.openapi.progress.util.ProgressIndicatorBase; import com.intellij.openapi.project.Project; import com.intellij.openapi.startup.StartupManager; import com.intellij.openapi.util.Comparing; -import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.UserDataHolderEx; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.wm.ex.ProgressIndicatorEx; import com.intellij.pom.PomManager; import com.intellij.pom.PomModel; import com.intellij.pom.event.PomModelEvent; @@ -48,7 +49,6 @@ import com.intellij.psi.impl.source.PsiFileImpl; import com.intellij.psi.impl.source.text.DiffLog; import com.intellij.psi.impl.source.tree.FileElement; import com.intellij.psi.text.BlockSupport; -import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; import com.intellij.util.Processor; import com.intellij.util.SmartList; @@ -60,19 +60,16 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; import javax.swing.*; -import java.util.Arrays; -import java.util.List; +import java.text.SimpleDateFormat; +import java.util.*; -/** - * User: cdr - */ public class DocumentCommitThread implements Runnable, Disposable { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.DocumentCommitThread"); - private static final Key COMMIT_STAGE = new Key("Commit stage"); private final Queue documentsToCommit = new Queue(10); + private final List documentsToApplyInEDT = new ArrayList(10); // guarded by documentsToCommit private volatile boolean isDisposed; - private ProgressIndicator myProgressIndicator; // guarded by documentsToCommit + private CommitTask currentTask; // guarded by documentsToCommit private volatile boolean threadFinished; private volatile boolean myEnabled = true; // true if we can do commits. set to false temporarily during the write action. @@ -87,7 +84,22 @@ public class DocumentCommitThread implements Runnable, Disposable { @Override public void dispose() { - stopThread(); + isDisposed = true; + synchronized (documentsToCommit) { + documentsToCommit.clear(); + } + cancel("Stop thread"); + wakeUpQueue(); + while (!threadFinished) { + wakeUpQueue(); + synchronized (documentsToCommit) { + try { + documentsToCommit.wait(10); + } + catch (InterruptedException ignored) { + } + } + } } public void disable(@NonNls Object reason) { @@ -109,119 +121,85 @@ public class DocumentCommitThread implements Runnable, Disposable { } } - private void stopThread() { - isDisposed = true; - synchronized (documentsToCommit) { - documentsToCommit.clear(); - } - cancel("Stop thread"); - wakeUpQueue(); - while (!threadFinished) { - wakeUpQueue(); - synchronized (documentsToCommit) { - try { - documentsToCommit.wait(10); - } - catch (InterruptedException ignored) { - } - } - } - } - private void cancel(@NonNls Object reason) { - log("Canceled", null, false, myProgressIndicator, "Reason: ", reason); - - useIndicator(null); + startNewTask(null, reason); } - public boolean queueCommit(@NotNull Project project, @NotNull Document document, @NonNls @NotNull Object reason) { - log("queueCommit called", document, false, reason); + public void queueCommit(@NotNull final Project project, @NotNull final Document document, @NonNls @NotNull Object reason) { assert !isDisposed : "already disposed"; - if (!project.isInitialized()) return false; + if (!project.isInitialized()) return; PsiFile psiFile = PsiDocumentManager.getInstance(project).getCachedPsiFile(document); - if (psiFile == null) return false; + if (psiFile == null) return; - boolean added = doQueue(document, project, getCommitStage(document), reason); - log("doQueue called", document, false, added); - return added; + doQueue(project, document, reason); } - private boolean doQueue(@NotNull Document document, - @NotNull Project project, - CommitStage start, - @NonNls @NotNull Object reason) { + private void doQueue(Project project, Document document, Object reason) { synchronized (documentsToCommit) { - if (!changeCommitStage(document, start, CommitStage.QUEUED_TO_COMMIT, false)) return false; + ProgressIndicatorEx indicator = new DaemonProgressIndicator(); + CommitTask newTask = new CommitTask(document, project, indicator, reason); + + markRemovedFromDocsToCommit(newTask); + markRemovedCurrentTask(newTask); + markRemovedFromDocsToApplyInEDT(newTask); + + documentsToCommit.addLast(newTask); + log("Queued", newTask, false, reason); - Object[] documentTasks = documentsToCommit.toArray(); - for (Object o : documentTasks) { - assert o != null : "Null element in:" + documentsToCommit; - CommitTask task = (CommitTask)o; - if (task.document == document) { - ProgressIndicator current = document.getUserData(COMMIT_PROGRESS); - if (current == null) { - // already queued, not started yet - return true; - } - else { - // cancel current commit process to re-queue - current.cancel(); - removeCommitFromQueue(document); - break; - } - } - } - ProgressIndicator indicator = new ProgressIndicatorBase(); - indicator.start(); - documentsToCommit.addLast(new CommitTask(document, project, indicator, reason)); - log("Queued", document, false, reason); wakeUpQueue(); - return true; } } private final StringBuilder log = new StringBuilder(); - void log(@NonNls String msg, Document document, boolean synchronously, @NonNls Object... args) { - if (debug()) { - @NonNls - String s = (SwingUtilities.isEventDispatchThread() ? "- " : "-") + - msg + (synchronously ? " (sync)" : "") + - (document == null ? "" : "; Document: " + System.identityHashCode(document) + - "; stage: " + getCommitStage(document)) - + "; my indic="+myProgressIndicator + " ||"; + void log(@NonNls String msg, CommitTask task, boolean synchronously, @NonNls Object... args) { + if (true) return; - for (Object arg : args) { + String indent = new SimpleDateFormat("mm:ss:SSSS").format(new Date()) + + (SwingUtilities.isEventDispatchThread() ? "- " : Thread.currentThread().getName().equals("Document commit thread") ? "- >" : "-"); + @NonNls + String s = indent + + msg + (synchronously ? " (sync)" : "") + + (task == null ? "" : "; task: " + task+" ("+System.identityHashCode(task)+")"); + + for (Object arg : args) { + if (!StringUtil.isEmpty(String.valueOf(arg))) { s += "; "+arg; } - System.out.println(s); - synchronized (log) { - log.append(s).append("\n"); - if (log.length() > 1000000) { - log.delete(0, 1000000); - } + } + if (task != null) { + Collection unc = task.project.isDisposed() ? Collections.emptyList() : + ((PsiDocumentManagerImpl)PsiDocumentManager.getInstance(task.project)).getUncommittedDocumentsUnsafe(); + if (!unc.isEmpty()) { + s += "; Uncommitted: " + unc; } } + + System.err.println(s); + + log.append(s).append("\n"); + if (log.length() > 1000000) { + log.delete(0, 1000000); + } } - private static boolean debug() { - return false; - } + // cancels all pending commits @TestOnly - public String getLog() { - return log.toString(); - } - private void clearLog() { - log.setLength(0); + public void cancelAll() { + synchronized (documentsToCommit) { + cancel("cancel all in tests"); + markRemovedFromDocsToCommit(null); + documentsToCommit.clear(); + markRemovedFromDocsToApplyInEDT(null); + markRemovedCurrentTask(null); + } } @TestOnly public void clearQueue() { - synchronized (documentsToCommit) { - documentsToCommit.clear(); - } - clearLog(); + cancelAll(); + log.setLength(0); disable("end of test"); wakeUpQueue(); } @@ -229,137 +207,196 @@ public class DocumentCommitThread implements Runnable, Disposable { private static class CommitTask { private final Document document; private final Project project; - // running = false means document was removed from the queue, should ignore. - // canceled = true means commit was canceled, should reschedule for later. - private final ProgressIndicator indicator; // progress to commit this doc under. + // when queued it's not started + // when dequeued it's started + // when failed it's canceled + private final ProgressIndicatorEx indicator; // progress to commit this doc under. private final Object reason; + private boolean removed; // task marked as removed, should be ignored. private CommitTask(@NotNull Document document, @NotNull Project project, - @NotNull ProgressIndicator indicator, + @NotNull ProgressIndicatorEx indicator, @NotNull Object reason) { this.document = document; this.project = project; this.indicator = indicator; this.reason = reason; } + + @NonNls + @Override + public String toString() { + return "Project: " + project.getName() + + ", Doc: "+ document +" ("+ StringUtil.first(document.getText(), 12, true).replaceAll("\n"," ")+")" + +(indicator.isCanceled() ? " (Canceled)" : "") + (removed ? "Removed" : ""); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof CommitTask)) return false; + + CommitTask task = (CommitTask)o; + + return document.equals(task.document) && project.equals(task.project); + } + + @Override + public int hashCode() { + int result = document.hashCode(); + result = 31 * result + project.hashCode(); + return result; + } } - private static final Key COMMIT_PROGRESS = Key.create("COMMIT_PROGRESS"); - private void removeCommitFromQueue(@NotNull Document document) { - synchronized (documentsToCommit) { - ProgressIndicator indicator = document.getUserData(COMMIT_PROGRESS); - - if (indicator != null && indicator.isRunning()) { - indicator.stop(); // mark document as removed - - log("Removed from queue", document, false); - } - // let our thread know that queue must be polled again - wakeUpQueue(); + private void markRemovedCurrentTask(@Nullable CommitTask newTask) { + CommitTask task = currentTask; + if (task != null && (task.equals(newTask) || newTask == null)) { + task.removed = true; + cancel("Sync commit intervened"); } } + private void markRemovedFromDocsToApplyInEDT(@Nullable("null means all") CommitTask newTask) { + for (int i = documentsToApplyInEDT.size() - 1; i >= 0; i--) { + CommitTask task = documentsToApplyInEDT.get(i); + if (newTask == null || task.equals(newTask)) { + log("Marked as Removed in EDT apply queue", task, false); + task.removed = true; + } + } + } + private void removeFromDocsToApplyInEDT(CommitTask newTask) { + for (int i = documentsToApplyInEDT.size() - 1; i >= 0; i--) { + CommitTask task = documentsToApplyInEDT.get(i); + if (task.equals(newTask)) { + task.removed = true; + documentsToApplyInEDT.remove(i); + log("Marked and Removed from EDT apply queue (sync commit called)", task, true); + } + } + } + + private void markRemovedFromDocsToCommit(@Nullable("null means all") final CommitTask newTask) { + processAll(new Processor() { + @Override + public boolean process(CommitTask task) { + if (newTask == null || task.equals(newTask)) { + task.removed = true; + log("marker as Removed in background queue", task, true); + } + return true; + } + }); + } + @Override public void run() { threadFinished = false; - while (!isDisposed) { - try { - boolean success = false; - Document document = null; - Project project = null; - ProgressIndicator indicator = null; + try { + while (!isDisposed) { try { - CommitTask task; - synchronized (documentsToCommit) { - if (!myEnabled || documentsToCommit.isEmpty()) { - documentsToCommit.wait(); - continue; - } - task = documentsToCommit.pullFirst(); - document = task.document; - indicator = task.indicator; - project = task.project; - - log("Pulled", document, false, indicator); - - CommitStage commitStage = getCommitStage(document); - Document[] uncommitted = null; - if (commitStage != CommitStage.QUEUED_TO_COMMIT - || project.isDisposed() || !ArrayUtil.contains(document, uncommitted = PsiDocumentManager.getInstance(project).getUncommittedDocuments())) { - List documents = uncommitted == null ? null : Arrays.asList(uncommitted); - log("Abandon and proceeding to next",document, false, commitStage, documents); - continue; - } - if (indicator.isRunning()) { - useIndicator(indicator); - document.putUserData(COMMIT_PROGRESS, indicator); - } - else { - success = true; // document has been marked as removed, e.g. by synchronous commit - } - } - - Runnable finishRunnable = null; - if (!success && !indicator.isCanceled()) { - try { - finishRunnable = commit(document, project, null, indicator, false, task.reason); - success = finishRunnable != null; - log("DCT.commit returned", document, false, finishRunnable, indicator); - } - finally { - document.putUserData(COMMIT_PROGRESS, null); - } - } - - synchronized (documentsToCommit) { - if (indicator.isCanceled()) { - success = false; - } - if (success) { - assert !ApplicationManager.getApplication().isDispatchThread(); - UIUtil.invokeLaterIfNeeded(finishRunnable); - log("Invoked later finishRunnable", document, false, success, finishRunnable, indicator); - } - } + pollQueue(); } - catch (ProcessCanceledException e) { - cancel(e); // leave queue unchanged - log("PCE", document, false, e); - success = false; - } - catch (InterruptedException e) { - // app must be closing - int i = 0; - log("IE", document, false, e); - cancel(e); - } - catch (Throwable e) { + catch(Throwable e) { + //e.printStackTrace(); LOG.error(e); - cancel(e); } - synchronized (documentsToCommit) { - if (!success && indicator.isRunning()) { // running means sync commit has not intervened - // reset status for queue back successfully - changeCommitStage(document, CommitStage.WAITING_FOR_PSI_APPLY, CommitStage.QUEUED_TO_COMMIT, false); - changeCommitStage(document, CommitStage.COMMITTED, CommitStage.QUEUED_TO_COMMIT, false); - doQueue(document, project, CommitStage.QUEUED_TO_COMMIT, "re-added on failure"); - } - } - } - catch(Throwable e) { - e.printStackTrace(); - //LOG.error(e); } } - threadFinished = true; + finally { + threadFinished = true; + } // ping the thread waiting for close wakeUpQueue(); log("Good bye", null, false); } + private void pollQueue() { + boolean success = false; + Document document = null; + Project project = null; + CommitTask task = null; + try { + ProgressIndicator indicator; + synchronized (documentsToCommit) { + if (!myEnabled || documentsToCommit.isEmpty()) { + documentsToCommit.wait(); + return; + } + task = documentsToCommit.pullFirst(); + document = task.document; + indicator = task.indicator; + project = task.project; + + log("Pulled", task, false, indicator); + + if (project.isDisposed() || !((PsiDocumentManagerImpl)PsiDocumentManager.getInstance(project)).getUncommittedDocumentsUnsafe().contains(document)) { + log("Abandon and proceed to next",task, false); + return; + } + + if (task.removed) { + return; // document has been marked as removed, e.g. by synchronous commit + } + + startNewTask(task, "Pulled new task"); + + // transfer to documentsToApplyInEDT + documentsToApplyInEDT.add(task); + } + + Runnable finishRunnable = null; + if (indicator.isCanceled()) { + success = false; + } + else { + final CommitTask commitTask = task; + final Runnable[] result = new Runnable[1]; + ((ProgressManagerImpl)ProgressManager.getInstance()).executeProcessUnderProgress(new Runnable() { + @Override + public void run() { + result[0] = commitUnderProgress(commitTask, null, false); + } + }, commitTask.indicator); + finishRunnable = result[0]; + success = finishRunnable != null; + log("commit returned", task, false, finishRunnable, indicator); + } + + if (success) { + assert !ApplicationManager.getApplication().isDispatchThread(); + UIUtil.invokeLaterIfNeeded(finishRunnable); + log("Invoked later finishRunnable", task, false, success, finishRunnable, indicator); + } + } + catch (ProcessCanceledException e) { + cancel(e); // leave queue unchanged + log("PCE", task, false, e); + success = false; + } + catch (InterruptedException e) { + // app must be closing + log("IE", task, false, e); + cancel(e); + } + catch (Throwable e) { + LOG.error(e); + cancel(e); + } + synchronized (documentsToCommit) { + if (!success && !task.removed) { // sync commit has not intervened + // reset status for queue back successfully + doQueue(project, document, "re-added on failure"); + } + currentTask = null; // do not cancel, it's being invokeLatered + } + } + public void commitSynchronously(@NotNull Document document, @NotNull Project project, PsiFile excludeFile) { assert !isDisposed; + ApplicationManager.getApplication().assertWriteAccessAllowed(); if (!project.isInitialized() && !project.isDefault()) { @NonNls String s = project + "; Disposed: "+project.isDisposed()+"; Open: "+project.isOpen(); @@ -373,69 +410,59 @@ public class DocumentCommitThread implements Runnable, Disposable { throw new RuntimeException(s); } - ApplicationManager.getApplication().assertWriteAccessAllowed(); + ProgressIndicatorBase indicator = new ProgressIndicatorBase(); + CommitTask task = new CommitTask(document, project, indicator, "Sync commit"); synchronized (documentsToCommit) { - setCommitStage(document, CommitStage.ABOUT_TO_BE_SYNC_COMMITTED, true); - removeCommitFromQueue(document); + markRemovedFromDocsToCommit(task); + markRemovedCurrentTask(task); + removeFromDocsToApplyInEDT(task); } - ProgressIndicatorBase indicator = new ProgressIndicatorBase(); - indicator.start(); - log("About to commit sync", document, true, indicator); - Runnable finish = commit(document, project, excludeFile, indicator, true, "Sync commit"); - log("Committed sync", document, true, finish, indicator); + log("About to commit sync", task, true, indicator); + Runnable finish = commitUnderProgress(task, excludeFile, true); + log("Committed sync", task, true, finish, indicator); assert finish != null; + finish.run(); + + // let our thread know that queue must be polled again + wakeUpQueue(); } - private Runnable commit(@NotNull final Document document, - @NotNull final Project project, - final PsiFile excludeFile, - @NotNull final ProgressIndicator indicator, - final boolean synchronously, - @NotNull final Object reason) { - final Runnable[] success = new Runnable[1]; - ((ProgressManagerImpl)ProgressManager.getInstance()).executeProcessUnderProgress(new Runnable() { - @Override - public void run() { - success[0] = commitUnderProgress(document, project, excludeFile, indicator, synchronously, reason); + private void startNewTask(CommitTask task, Object reason) { + synchronized (documentsToCommit) { // sync to prevent overwriting + CommitTask cur = currentTask; + if (cur != null) { + cur.indicator.cancel(); } - }, indicator); - return success[0]; - } - - private void useIndicator(ProgressIndicator indicator) { - synchronized (documentsToCommit) { // sync to prevent overwriting indicator - assert indicator == null || myProgressIndicator != indicator; - if (myProgressIndicator != null) { - myProgressIndicator.cancel(); - } - myProgressIndicator = indicator; + //log("Start new task", task, false, cur == null ? "" : cur.indicator + " canceled", reason); + currentTask = task; } } // returns finish commit Runnable (to be invoked later in EDT), or null on failure - private Runnable commitUnderProgress(@NotNull final Document document, - @NotNull final Project project, + @Nullable + private Runnable commitUnderProgress(@NotNull final CommitTask task, final PsiFile excludeFile, - @NotNull final ProgressIndicator indicator, - final boolean synchronously, - @NotNull final Object reason) { - final List> finishRunnables = new SmartList>(); + final boolean synchronously) { + final Project project = task.project; + final Document document = task.document; + final List> finishProcessors = new SmartList>(); Runnable runnable = new Runnable() { @Override public void run() { + ApplicationManager.getApplication().assertReadAccessAllowed(); if (project.isDisposed()) return; final PsiDocumentManagerImpl documentManager = (PsiDocumentManagerImpl)PsiDocumentManager.getInstance(project); - final FileViewProvider viewProvider = documentManager.getCachedViewProvider(document); + FileViewProvider viewProvider = documentManager.getCachedViewProvider(document); if (viewProvider == null) return; - final List psiFiles = viewProvider.getAllFiles(); + List psiFiles = viewProvider.getAllFiles(); for (PsiFile file : psiFiles) { if (file.isValid() && file != excludeFile) { - Processor finishRunnable = doCommit(document, file, indicator, synchronously, documentManager); - if (finishRunnable != null) { - finishRunnables.add(finishRunnable); + Processor finishProcessor = doCommit(task, file, synchronously, documentManager); + if (finishProcessor != null) { + finishProcessors.add(finishProcessor); } } } @@ -447,58 +474,66 @@ public class DocumentCommitThread implements Runnable, Disposable { } else { if (!ApplicationManagerEx.getApplicationEx().tryRunReadAction(runnable)) { - log("Could not start readaction", document, synchronously, ApplicationManager.getApplication().isReadAccessAllowed(), Thread.currentThread()); + log("Could not start read action", task, synchronously, ApplicationManager.getApplication().isReadAccessAllowed(), Thread.currentThread()); return null; } } - boolean canceled = indicator.isCanceled(); - if (synchronously) { - assert !canceled; - } - if (canceled || !indicator.isRunning()) { - assert !synchronously; - return null; - } - if (!synchronously && !changeCommitStage(document, CommitStage.QUEUED_TO_COMMIT, CommitStage.WAITING_FOR_PSI_APPLY, synchronously)) { + boolean canceled = task.indicator.isCanceled(); + assert !synchronously || !canceled; + if (canceled || task.removed) { return null; } Runnable finishRunnable = new Runnable() { @Override public void run() { + ApplicationManager.getApplication().assertIsDispatchThread(); + + Project project = task.project; if (project.isDisposed()) return; + Document document = task.document; + + synchronized (documentsToCommit) { + boolean isValid = !task.removed; + for (int i = documentsToApplyInEDT.size() - 1; i >= 0; i--) { + CommitTask queuedTask = documentsToApplyInEDT.get(i); + boolean taskIsValid = !queuedTask.removed; + if (task == queuedTask) { // find the same task in the queue + documentsToApplyInEDT.remove(i); + isValid &= taskIsValid; + log("Task matched, removed from documentsToApplyInEDT", queuedTask, false, task); + } + else if (!taskIsValid) { + documentsToApplyInEDT.remove(i); + log("Task invalid, removed from documentsToApplyInEDT", queuedTask, false); + } + } + if (!isValid) { + log("Marked as already committed in EDT apply queue, return", task, true); + return; + } + } + PsiDocumentManagerImpl documentManager = (PsiDocumentManagerImpl)PsiDocumentManager.getInstance(project); + Collection uncommitted = documentManager.getUncommittedDocumentsUnsafe(); + FileViewProvider viewProvider = documentManager.getCachedViewProvider(document); + //if (!documentManager.getSynchronizer().isInSynchronization(document) && !uncommitted.contains(document)) return; // already committed, must be the sync commit - CommitStage stage = getCommitStage(document); - log("Finish", document, synchronously, project); - if (stage != (synchronously ? CommitStage.ABOUT_TO_BE_SYNC_COMMITTED : CommitStage.WAITING_FOR_PSI_APPLY)) { - return; // there must be a synchronous commit sneaked in between queued commit and finish commit, or just document changed meanwhile + log("Executing later finishCommit", task, false); + boolean success = documentManager.finishCommit(document, finishProcessors, synchronously, task.reason); + if (synchronously) { + assert success; } - - boolean success = false; - try { - success = documentManager.finishCommit(document, finishRunnables, synchronously, reason); - log("Finished", document, synchronously, success, Arrays.asList(documentManager.getUncommittedDocuments())); - if (synchronously) { - assert success; - } - } - finally { - if (success) { - success = synchronously || changeCommitStage(document, CommitStage.WAITING_FOR_PSI_APPLY, CommitStage.COMMITTED, false); - } - } - List unc = Arrays.asList(documentManager.getUncommittedDocuments()); - log("after call finish commit",document, synchronously, unc, success); + Collection unc = documentManager.getUncommittedDocumentsUnsafe(); + log("after call finishCommit",task, synchronously, success); if (synchronously || success) { assert !unc.contains(document) : unc; } if (!success) { // add document back to the queue - boolean addedBack = queueCommit(project, document, "Re-added back"); - assert addedBack; + queueCommit(project, document, "Re-added back"); } } }; @@ -506,11 +541,11 @@ public class DocumentCommitThread implements Runnable, Disposable { } @Nullable("returns runnable to execute under write action in AWT to finish the commit") - private Processor doCommit(@NotNull final Document document, + private Processor doCommit(@NotNull final CommitTask task, @NotNull final PsiFile file, - @NotNull ProgressIndicator indicator, final boolean synchronously, @NotNull PsiDocumentManager documentManager) { + Document document = task.document; ((PsiDocumentManagerImpl)documentManager).clearTreeHardRef(document); final TextBlock textBlock = TextBlock.get(file); if (textBlock.isEmpty()) return null; @@ -525,7 +560,7 @@ public class DocumentCommitThread implements Runnable, Disposable { file.putUserData(BlockSupport.DO_NOT_REPARSE_INCREMENTALLY, data); } final String oldPsiText = - ApplicationManagerEx.getApplicationEx().isInternal() && !ApplicationManagerEx.getApplicationEx().isUnitTestMode() + ApplicationManagerEx.getApplicationEx().isInternal() && ApplicationManagerEx.getApplicationEx().isUnitTestMode() ? myTreeElementBeingReparsedSoItWontBeCollected.getText() : null; int startOffset; @@ -544,13 +579,13 @@ public class DocumentCommitThread implements Runnable, Disposable { } assertBeforeCommit(document, file, textBlock, chars, oldPsiText, myTreeElementBeingReparsedSoItWontBeCollected); BlockSupport blockSupport = BlockSupport.getInstance(file.getProject()); - final DiffLog diffLog = blockSupport.reparseRange(file, startOffset, endOffset, lengthShift, chars, indicator); + final DiffLog diffLog = blockSupport.reparseRange(file, startOffset, endOffset, lengthShift, chars, task.indicator); return new Processor() { @Override public boolean process(Document document) { ApplicationManager.getApplication().assertWriteAccessAllowed(); - log("Finishing", document, synchronously, document.getModificationStamp(), startDocModificationTimeStamp); + log("Finishing", task, synchronously, document.getModificationStamp(), startDocModificationTimeStamp); //if (file.getModificationStamp() != startPsiModificationTimeStamp) return; // optimistic locking failed if (document.getModificationStamp() != startDocModificationTimeStamp) { return false; // optimistic locking failed @@ -578,18 +613,20 @@ public class DocumentCommitThread implements Runnable, Disposable { SmartPointerManagerImpl.synchronizePointers(file); } + //System.out.println("committed "+task+"; tree length of "+myTreeElementBeingReparsedSoItWontBeCollected+" is " +myTreeElementBeingReparsedSoItWontBeCollected.getTextLength()); + return true; } }; } - private static void assertBeforeCommit(Document document, - PsiFile file, - TextBlock textBlock, - CharSequence chars, + private static void assertBeforeCommit(@NotNull Document document, + @NotNull PsiFile file, + @NotNull TextBlock textBlock, + @NotNull CharSequence chars, String oldPsiText, - FileElement myTreeElementBeingReparsedSoItWontBeCollected) { + @NotNull FileElement myTreeElementBeingReparsedSoItWontBeCollected) { int startOffset = textBlock.getStartOffset(); int psiEndOffset = textBlock.getPsiEndOffset(); if (oldPsiText != null) { @@ -661,7 +698,7 @@ public class DocumentCommitThread implements Runnable, Disposable { } } - public static void doActualPsiChange(@NotNull final PsiFile file, final DiffLog diffLog){ + public static void doActualPsiChange(@NotNull final PsiFile file, @NotNull final DiffLog diffLog){ file.getViewProvider().beforeContentsSynchronized(); try { @@ -690,30 +727,17 @@ public class DocumentCommitThread implements Runnable, Disposable { } } - @NotNull - static CommitStage getCommitStage(@NotNull Document doc) { - CommitStage stage = doc.getUserData(COMMIT_STAGE); - if (stage == null) { - stage = ((UserDataHolderEx)doc).putUserDataIfAbsent(COMMIT_STAGE, CommitStage.DIRTY); + private boolean processAll(final Processor processor) { + final boolean[] result = {true}; + synchronized (documentsToCommit) { + documentsToCommit.process(new Processor() { + @Override + public boolean process(CommitTask commitTask) { + result[0] &= processor.process(commitTask); + return true; + } + }); } - return stage; - } - - private void setCommitStage(@NotNull Document document, @NotNull CommitStage stage, boolean synchronously) { - document.putUserData(COMMIT_STAGE, stage); - log("Set stage", document, synchronously); - } - - private boolean changeCommitStage(@NotNull Document document, - CommitStage expected, - @NotNull CommitStage stage, - boolean synchronously) { - boolean replaced = ((UserDataHolderEx)document).replace(COMMIT_STAGE, expected, stage); - log("Changed stage", document, synchronously, expected, stage, replaced); - return replaced; - } - - private static enum CommitStage { - DIRTY, QUEUED_TO_COMMIT, WAITING_FOR_PSI_APPLY, COMMITTED, ABOUT_TO_BE_SYNC_COMMITTED + return result[0]; } } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/PsiDocumentManagerImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/PsiDocumentManagerImpl.java index 3ce6ebc02c86..4f0f49ca26d1 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/PsiDocumentManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/PsiDocumentManagerImpl.java @@ -90,32 +90,34 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec myDocumentCommitThread = documentCommitThread; mySmartPointerManager = (SmartPointerManagerImpl)smartPointerManager; mySynchronizer = new PsiToDocumentSynchronizer(this, bus); - myPsiManager.addPsiTreeChangeListener(mySynchronizer); - editorFactory.getEventMulticaster().addDocumentListener(this, myProject); - bus.connect().subscribe(AppTopics.FILE_DOCUMENT_SYNC, new FileDocumentManagerAdapter() { - @Override - public void fileContentLoaded(final VirtualFile virtualFile, Document document) { - PsiFile psiFile = ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public PsiFile compute() { - return getCachedPsiFile(virtualFile); - } - }); - fireDocumentCreated(document, psiFile); - } - }); - ApplicationManager.getApplication().addApplicationListener(new ApplicationAdapter() { - @Override - public void beforeWriteActionStart(Object action) { - documentCommitThread.disable("Write action started: "+ action); - } + if (!project.isDefault()) { + myPsiManager.addPsiTreeChangeListener(mySynchronizer); + editorFactory.getEventMulticaster().addDocumentListener(this, myProject); + bus.connect().subscribe(AppTopics.FILE_DOCUMENT_SYNC, new FileDocumentManagerAdapter() { + @Override + public void fileContentLoaded(final VirtualFile virtualFile, Document document) { + PsiFile psiFile = ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public PsiFile compute() { + return getCachedPsiFile(virtualFile); + } + }); + fireDocumentCreated(document, psiFile); + } + }); + ApplicationManager.getApplication().addApplicationListener(new ApplicationAdapter() { + @Override + public void beforeWriteActionStart(Object action) { + documentCommitThread.disable("Write action started: "+ action); + } - @Override - public void writeActionFinished(Object action) { - documentCommitThread.enable("Write action finished: "+action); - } - }, myProject); - documentCommitThread.enable("project open"); + @Override + public void writeActionFinished(Object action) { + documentCommitThread.enable("Write action finished: "+action); + } + }, myProject); + documentCommitThread.enable("project open"); + } } @Override @@ -268,6 +270,7 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec @Override public boolean performWhenAllCommitted(@NotNull final Runnable action) { ApplicationManager.getApplication().assertIsDispatchThread(); + assert !myProject.isDisposed() : "Already disposed: " + myProject; if (myUncommittedDocuments.isEmpty()) { action.run(); return true; @@ -278,6 +281,7 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec actionsWhenAllDocumentsAreCommitted.put(PERFORM_ALWAYS_KEY, actions); } actions.add(action); + myDocumentCommitThread.log("PDI: added performWhenAllCommitted", null, false, action, myUncommittedDocuments); return false; } @@ -329,10 +333,10 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec } boolean finishCommit(@NotNull final Document document, - @NotNull final List> finishRunnables, + @NotNull final List> finishProcessors, final boolean synchronously, @NotNull Object reason) { - if (myProject.isDisposed()) return false; + assert !myProject.isDisposed() : "Already disposed"; final boolean[] ok = {true}; ApplicationManager.getApplication().runWriteAction(new CommitToPsiFileAction(document, myProject) { @Override @@ -345,7 +349,7 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec try { final FileViewProvider viewProvider = getCachedViewProvider(document); if (viewProvider != null) { - for (Processor finishRunnable : finishRunnables) { + for (Processor finishRunnable : finishProcessors) { success = finishRunnable.process(document); if (synchronously) { assert success; @@ -359,13 +363,13 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec ok[0] = success; } finally { - myDocumentCommitThread.log("in PDI.finishDoc: ", document, synchronously, success, myUncommittedDocuments); + myDocumentCommitThread.log("in PDI.finishDoc: ", null, synchronously, success, myUncommittedDocuments); if (success) { myUncommittedDocuments.remove(document); - myDocumentCommitThread.log("in PDI.finishDoc: removed doc", document, synchronously, success, myUncommittedDocuments); + myDocumentCommitThread.log("in PDI.finishDoc: removed doc", null, synchronously, success, myUncommittedDocuments); } myIsCommitInProgress = false; - myDocumentCommitThread.log("in PDI.finishDoc: exit", document, synchronously, success, myUncommittedDocuments); + myDocumentCommitThread.log("in PDI.finishDoc: exit", null, synchronously, success, myUncommittedDocuments); } } }); @@ -413,7 +417,7 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec finally { myIsCommitInProgress = false; } - assert !myUncommittedDocuments.contains(document) : "Document :"+System.identityHashCode(document); + assert !myUncommittedDocuments.contains(document) : "Document :"+ document; } }); } @@ -438,7 +442,7 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec List keys = new ArrayList(actionsWhenAllDocumentsAreCommitted.keySet()); for (Object key : keys) { Runnable action = actionsWhenAllDocumentsAreCommitted.remove(key); - myDocumentCommitThread.log("Running after commit runnable: ",document, false, key, action); + myDocumentCommitThread.log("Running after commit runnable: ",null, false, key, action); action.run(); } } @@ -567,9 +571,14 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec @Override @NotNull public Document[] getUncommittedDocuments() { + ApplicationManager.getApplication().assertIsDispatchThread(); return myUncommittedDocuments.toArray(new Document[myUncommittedDocuments.size()]); } + public Collection getUncommittedDocumentsUnsafe() { + return myUncommittedDocuments; + } + @Override public boolean isUncommited(@NotNull Document document) { return !isCommitted(document); @@ -662,6 +671,7 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec if (commitNecessary) { myUncommittedDocuments.add(document); + myDocumentCommitThread.log("PDI: added to uncommitted", null, false, document, event, myUncommittedDocuments); myDocumentCommitThread.queueCommit(myProject, document, event); } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/MultiHostRegistrarImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/MultiHostRegistrarImpl.java index daf7011d93e9..fa48c43c19df 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/MultiHostRegistrarImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/MultiHostRegistrarImpl.java @@ -207,7 +207,7 @@ public class MultiHostRegistrarImpl implements MultiHostRegistrar, ModificationT decodedDocument = new DocumentImpl(outChars); } else { - decodedDocument = new DocumentImpl(true); + decodedDocument = new DocumentImpl("", true); decodedDocument.setAcceptSlashR(true); decodedDocument.replaceString(0,0,outChars); } diff --git a/platform/lang-impl/src/com/intellij/ui/TextFieldWithAutoCompletionContributor.java b/platform/lang-impl/src/com/intellij/ui/TextFieldWithAutoCompletionContributor.java index 4544c0318a7d..4f8a070af8fc 100644 --- a/platform/lang-impl/src/com/intellij/ui/TextFieldWithAutoCompletionContributor.java +++ b/platform/lang-impl/src/com/intellij/ui/TextFieldWithAutoCompletionContributor.java @@ -54,7 +54,7 @@ public class TextFieldWithAutoCompletionContributor extends CompletionContrib } @Override - public void fillCompletionVariants(CompletionParameters parameters, CompletionResultSet result) { + public void fillCompletionVariants(final CompletionParameters parameters, CompletionResultSet result) { PsiFile file = parameters.getOriginalFile(); final TextFieldWithAutoCompletionListProvider provider = file.getUserData(KEY); @@ -84,7 +84,7 @@ public class TextFieldWithAutoCompletionContributor extends CompletionContrib result = result.withPrefixMatcher(prefixMatcher); } - Collection items = provider.getItems(prefix, true); + Collection items = provider.getItems(prefix, true, parameters); addCompletionElements(result, provider, items, -10000); Future> @@ -92,7 +92,7 @@ public class TextFieldWithAutoCompletionContributor extends CompletionContrib ApplicationManager.getApplication().executeOnPooledThread(new Callable>() { @Override public Collection call() { - return provider.getItems(prefix, false); + return provider.getItems(prefix, false, parameters); } }); diff --git a/platform/lang-impl/src/com/intellij/ui/TextFieldWithAutoCompletionListProvider.java b/platform/lang-impl/src/com/intellij/ui/TextFieldWithAutoCompletionListProvider.java index 22a57a83dc0d..459e493aff15 100644 --- a/platform/lang-impl/src/com/intellij/ui/TextFieldWithAutoCompletionListProvider.java +++ b/platform/lang-impl/src/com/intellij/ui/TextFieldWithAutoCompletionListProvider.java @@ -60,7 +60,7 @@ public abstract class TextFieldWithAutoCompletionListProvider implements Comp } @NotNull - public Collection getItems(String prefix, boolean cached) { + public Collection getItems(String prefix, boolean cached, CompletionParameters parameters) { if (prefix == null) { return Collections.emptyList(); } diff --git a/platform/platform-impl/src/com/intellij/notification/EventLog.java b/platform/platform-impl/src/com/intellij/notification/EventLog.java index 2e80052db39e..2ac011e9a5b9 100644 --- a/platform/platform-impl/src/com/intellij/notification/EventLog.java +++ b/platform/platform-impl/src/com/intellij/notification/EventLog.java @@ -122,7 +122,7 @@ public class EventLog implements Notifications { } public static LogEntry formatForLog(@NotNull final Notification notification, String indent) { - DocumentImpl logDoc = new DocumentImpl(true); + DocumentImpl logDoc = new DocumentImpl("",true); AtomicBoolean showMore = new AtomicBoolean(false); Map links = new LinkedHashMap(); List lineSeparators = new ArrayList(); @@ -197,8 +197,7 @@ public class EventLog implements Notifications { } private static String getStatusText(DocumentImpl logDoc, AtomicBoolean showMore, List lineSeparators, boolean hasHtml) { - DocumentImpl statusDoc = new DocumentImpl(true); - statusDoc.setText(logDoc.getText()); + DocumentImpl statusDoc = new DocumentImpl(logDoc.getText(),true); List statusSeparators = new ArrayList(); for (RangeMarker separator : lineSeparators) { if (separator.isValid()) { @@ -420,6 +419,7 @@ public class EventLog implements Notifications { return project.getComponent(ProjectTracker.class); } public static class FactoryItself implements ToolWindowFactory, DumbAware { + @Override public void createToolWindowContent(final Project project, ToolWindow toolWindow) { final Editor editor = getProjectComponent(project).myConsole.getConsoleEditor(); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorFactoryImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorFactoryImpl.java index 00b6e1c44c86..0332cc8fdaaa 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorFactoryImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorFactoryImpl.java @@ -140,7 +140,7 @@ public class EditorFactoryImpl extends EditorFactory { @NotNull public Document createDocument(boolean allowUpdatesWithoutWriteAction) { - DocumentImpl document = new DocumentImpl(allowUpdatesWithoutWriteAction); + DocumentImpl document = new DocumentImpl("",allowUpdatesWithoutWriteAction); myEditorEventMulticaster.registerDocument(document); return document; } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileChooser/impl/FileChooserUtil.java b/platform/platform-impl/src/com/intellij/openapi/fileChooser/impl/FileChooserUtil.java index 2bc3bc53d1ab..f3b5d27f2af8 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileChooser/impl/FileChooserUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileChooser/impl/FileChooserUtil.java @@ -42,10 +42,10 @@ public final class FileChooserUtil { } else if (toSelect != null && lastPath != null) { if (Boolean.TRUE.equals(descriptor.getUserData(PathChooserDialog.PREFER_LAST_OVER_EXPLICIT))) { - return toSelect; + return lastPath; } else { - return lastPath; + return toSelect; } } else if (toSelect == null) { diff --git a/platform/platform-resources-en/src/messages/ProjectBundle.properties b/platform/platform-resources-en/src/messages/ProjectBundle.properties index 6b1beb9407e5..bdd837f78bd4 100644 --- a/platform/platform-resources-en/src/messages/ProjectBundle.properties +++ b/platform/platform-resources-en/src/messages/ProjectBundle.properties @@ -281,7 +281,7 @@ project.module.compile.output.path=Use module compile output path project.compiler.output=Project compiler output:
    This path is used to store all project compilation results.
    \ A directory corresponding to each module is created under this path.
    This directory contains two subdirectories: Production and Test for production code and test sources, respectively.
    \ A module specific compiler output path can be configured for each of the modules as required -find.usages.action.text=Find usages +find.usages.action.text=Find Usages dependencies.used.in.popup.title=Used in project.language.level=Project language level:
    This language level is default for all project modules.
    \ A module specific language level can be configured for each of the modules as required diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/CharArrayTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/CharArrayTest.java index 16c399aa0eb0..20357ba9d1e5 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/CharArrayTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/CharArrayTest.java @@ -20,6 +20,8 @@ import com.intellij.openapi.editor.impl.event.DocumentEventImpl; import com.intellij.openapi.util.Pair; import com.intellij.util.LocalTimeCounter; import com.intellij.util.containers.Stack; +import com.intellij.util.text.CharSequenceBackedByArray; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jmock.Expectations; import org.jmock.Mockery; @@ -46,7 +48,6 @@ import static org.junit.Assert.*; * @since 03/01/2011 */ public class CharArrayTest { - @Rule public TestWatcher configReader = new TestWatcher() { @Override @@ -81,7 +82,7 @@ public class CharArrayTest { init(10); if (myConfig != null) { - myArray.insert(myDocument, myConfig.text(), 0); + myArray.insert(myConfig.text(), 0); myArray.setDeferredChangeMode(myConfig.deferred()); } } @@ -95,27 +96,24 @@ public class CharArrayTest { @Test public void deferredReplace() { replace(1, 3, "abc"); - checkText("1abc4"); assertTrue(myArray.hasDeferredChanges()); - + checkText("1abc4"); + replace(2, 3, "XY"); checkText("1aXYc4"); - assertTrue(myArray.hasDeferredChanges()); - + replace(3, 6, "ABC"); checkText("1aXABC"); - assertTrue(myArray.hasDeferredChanges()); - + myArray.setDeferredChangeMode(false); checkText("1aXABC"); - assertFalse(myArray.hasDeferredChanges()); } @Config(text = "01234567", deferred = true) @Test public void subSequenceWithDeferredChangeBeforeIt() { replace(0, 2, "abc"); - CharArray subsSequence = (CharArray)myArray.subSequence(5, 6); + CharSequenceBackedByArray subsSequence = (CharSequenceBackedByArray)myArray.subSequence(5, 6); assertArrayEquals("4".toCharArray(), subsSequence.getChars()); } @@ -123,7 +121,7 @@ public class CharArrayTest { @Test public void subSequenceWithDeferredChangeIntersectingFromLeft() { replace(0, 2, "abc"); - CharArray subsSequence = (CharArray)myArray.subSequence(2, 4); + CharSequenceBackedByArray subsSequence = (CharSequenceBackedByArray)myArray.subSequence(2, 4); assertArrayEquals("c2".toCharArray(), subsSequence.getChars()); } @@ -131,7 +129,7 @@ public class CharArrayTest { @Test public void subSequenceWithDeferredChangeIntersectingFromRight() { replace(4, 6, "abc"); - CharArray subsSequence = (CharArray)myArray.subSequence(3, 5); + CharSequenceBackedByArray subsSequence = (CharSequenceBackedByArray)myArray.subSequence(3, 5); assertArrayEquals("3a".toCharArray(), subsSequence.getChars()); } @@ -139,27 +137,34 @@ public class CharArrayTest { @Test public void subSequenceWithDeferredChangeAfterIt() { replace(6, 8, "abc"); - CharArray subsSequence = (CharArray)myArray.subSequence(1, 2); + CharSequenceBackedByArray subsSequence = (CharSequenceBackedByArray)myArray.subSequence(1, 2); assertArrayEquals("1".toCharArray(), subsSequence.getChars()); } private void init(int size) { - myArray = new CharArray(size) { + myArray = new CharArray(size, new char[0], 0) { @NotNull @Override - protected DocumentEvent beforeChangedUpdate(DocumentImpl subj, int offset, CharSequence oldString, CharSequence newString, - boolean wholeTextReplaced) - { - return new DocumentEventImpl(subj, offset, oldString, newString, LocalTimeCounter.currentTime(), wholeTextReplaced); + protected DocumentEvent beforeChangedUpdate(int offset, CharSequence oldString, CharSequence newString, + boolean wholeTextReplaced) { + return new DocumentEventImpl(myDocument, offset, oldString, newString, LocalTimeCounter.currentTime(), wholeTextReplaced); } @Override protected void afterChangedUpdate(@NotNull DocumentEvent event, long newModificationStamp) { } + + @Override + protected void assertWriteAccess() { + } + + @Override + protected void assertReadAccess() { + } }; } - private void checkText(@NotNull String expected) { + private void checkText(@NonNls @NotNull String expected) { // Test as a whole. assertEquals(expected, myArray.toString()); assertEquals(expected.length(), myArray.length()); @@ -192,8 +197,7 @@ public class CharArrayTest { } private void checkSubSequence(@NotNull String expected, @NotNull CharSequence actual, - @NotNull Stack> history) - { + @NotNull Stack> history) { assertEquals(expected.length(), actual.length()); for (int i = 0; i < expected.length(); i++) { char expectedChar = expected.charAt(i); @@ -223,9 +227,9 @@ public class CharArrayTest { } } - private void replace(int startOffset, int endOffset, String newText) { + private void replace(int startOffset, int endOffset, @NonNls String newText) { myArray.replace( - myDocument, startOffset, endOffset, myArray.substring(startOffset, endOffset), newText, LocalTimeCounter.currentTime(), + startOffset, endOffset, myArray.substring(startOffset, endOffset), newText, LocalTimeCounter.currentTime(), startOffset == 0 && endOffset == myArray.length() ); } diff --git a/platform/testFramework/src/com/intellij/testFramework/AbstractJunitVcsTestCase.java b/platform/testFramework/src/com/intellij/testFramework/AbstractJunitVcsTestCase.java new file mode 100644 index 000000000000..ec85d371dfb1 --- /dev/null +++ b/platform/testFramework/src/com/intellij/testFramework/AbstractJunitVcsTestCase.java @@ -0,0 +1,28 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.testFramework; + +import org.junit.Rule; +import org.junit.rules.TestName; + +public abstract class AbstractJunitVcsTestCase extends AbstractVcsTestCase { + @Rule + public TestName name= new TestName(); + + protected String getTestName() { + return name.getMethodName(); + } +} diff --git a/platform/testFramework/src/com/intellij/testFramework/AbstractVcsTestCase.java b/platform/testFramework/src/com/intellij/testFramework/AbstractVcsTestCase.java index fb3830b820bf..57ed171e60bb 100644 --- a/platform/testFramework/src/com/intellij/testFramework/AbstractVcsTestCase.java +++ b/platform/testFramework/src/com/intellij/testFramework/AbstractVcsTestCase.java @@ -134,8 +134,9 @@ public abstract class AbstractVcsTestCase { return result; } - protected void initProject(final File clientRoot) throws Exception { - final TestFixtureBuilder testFixtureBuilder = IdeaTestFixtureFactory.getFixtureFactory().createFixtureBuilder(); + protected void initProject(final File clientRoot, String testName) throws Exception { + String name = getClass().getName() + "." + testName; + final TestFixtureBuilder testFixtureBuilder = IdeaTestFixtureFactory.getFixtureFactory().createFixtureBuilder(name); myProjectFixture = testFixtureBuilder.getFixture(); testFixtureBuilder.addModule(EmptyModuleFixtureBuilder.class).addContentRoot(clientRoot.toString()); myProjectFixture.setUp(); diff --git a/platform/testFramework/src/com/intellij/testFramework/fixtures/CodeInsightFixtureTestCase.java b/platform/testFramework/src/com/intellij/testFramework/fixtures/CodeInsightFixtureTestCase.java index d4e56c07982a..4ec6f7d5b10a 100644 --- a/platform/testFramework/src/com/intellij/testFramework/fixtures/CodeInsightFixtureTestCase.java +++ b/platform/testFramework/src/com/intellij/testFramework/fixtures/CodeInsightFixtureTestCase.java @@ -38,7 +38,8 @@ public abstract class CodeInsightFixtureTestCase protected void setUp() throws Exception { super.setUp(); - final TestFixtureBuilder projectBuilder = IdeaTestFixtureFactory.getFixtureFactory().createFixtureBuilder(); + String name = getClass().getName() + "." + getName(); + final TestFixtureBuilder projectBuilder = IdeaTestFixtureFactory.getFixtureFactory().createFixtureBuilder(name); myFixture = IdeaTestFixtureFactory.getFixtureFactory().createCodeInsightFixture(projectBuilder.getFixture()); final T moduleFixtureBuilder = projectBuilder.addModule(getModuleBuilderClass()); moduleFixtureBuilder.addSourceContentRoot(myFixture.getTempDirPath()); diff --git a/platform/util/src/com/intellij/util/text/CharArrayUtil.java b/platform/util/src/com/intellij/util/text/CharArrayUtil.java index 145e554c7d46..9cdd22360386 100644 --- a/platform/util/src/com/intellij/util/text/CharArrayUtil.java +++ b/platform/util/src/com/intellij/util/text/CharArrayUtil.java @@ -121,6 +121,11 @@ public class CharArrayUtil { return null; } + /** + * @return the underlying char[] array if any, or the new chara array if not + * NOTE RETURNED ARRAY LENGTH MAY HAVE BE DIFFERENT FROM THE seq.length() + */ + @NotNull public static char[] fromSequence(CharSequence seq) { if (seq instanceof CharSequenceBackedByArray) { return ((CharSequenceBackedByArray)seq).getChars(); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/FragmentedDiffRequestFromChange.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/FragmentedDiffRequestFromChange.java index 0b84302699fb..05621764e935 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/FragmentedDiffRequestFromChange.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/FragmentedDiffRequestFromChange.java @@ -175,10 +175,9 @@ public class FragmentedDiffRequestFromChange { } private Document documentFromRevision(final ContentRevision cr) throws VcsException { - final Document oldDocument = new DocumentImpl(true); + final Document oldDocument = new DocumentImpl(StringUtil.convertLineSeparators(notNullContentRevision(cr)),true); // todo !!! a question how to show line separators in diff etc // todo currently document doesn't allow to put \r as separator - oldDocument.replaceString(0, oldDocument.getTextLength(), StringUtil.convertLineSeparators(notNullContentRevision(cr))); oldDocument.setReadOnly(true); return oldDocument; } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.java index 7df945280376..7f45bdc8aa32 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.java @@ -293,6 +293,7 @@ public class LineStatusTracker { private int myLinesBeforeChange; private final VcsDirtyScopeManager myVcsDirtyScopeManager = VcsDirtyScopeManager.getInstance(myProject); + @Override public void beforeDocumentChange(DocumentEvent e) { myApplication.assertWriteAccessAllowed(); @@ -346,6 +347,7 @@ public class LineStatusTracker { return result; } + @Override public void documentChanged(final DocumentEvent e) { myApplication.assertWriteAccessAllowed(); @@ -646,7 +648,7 @@ public class LineStatusTracker { } public static LineStatusTracker createOn(@Nullable VirtualFile virtualFile, final Document doc, final Project project) { - final Document document = new DocumentImpl(true); + final Document document = new DocumentImpl("",true); return new LineStatusTracker(doc, document, project, virtualFile); } diff --git a/plugins/android/src/org/jetbrains/android/logcat/EditLogFilterDialog.java b/plugins/android/src/org/jetbrains/android/logcat/EditLogFilterDialog.java index 939aea4cdc24..bf66d629eeca 100644 --- a/plugins/android/src/org/jetbrains/android/logcat/EditLogFilterDialog.java +++ b/plugins/android/src/org/jetbrains/android/logcat/EditLogFilterDialog.java @@ -1,6 +1,7 @@ package org.jetbrains.android.logcat; import com.android.ddmlib.Log; +import com.intellij.codeInsight.completion.CompletionParameters; import com.intellij.ide.ui.ListCellRendererWrapper; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; @@ -56,10 +57,10 @@ class EditLogFilterDialog extends DialogWrapper { myTagField = new TextFieldWithAutoCompletion(project, new TextFieldWithAutoCompletion.StringsCompletionProvider(null, null) { @NotNull @Override - public Collection getItems(String prefix, boolean cached) { + public Collection getItems(String prefix, boolean cached, CompletionParameters parameters) { parseExistingMessagesIfNecessary(); setItems(Arrays.asList(myUsedTags)); - return super.getItems(prefix, cached); + return super.getItems(prefix, cached, parameters); } }, true); @@ -69,10 +70,10 @@ class EditLogFilterDialog extends DialogWrapper { myPidField = new TextFieldWithAutoCompletion(project, new TextFieldWithAutoCompletion.StringsCompletionProvider(null, null) { @NotNull @Override - public Collection getItems(String prefix, boolean cached) { + public Collection getItems(String prefix, boolean cached, CompletionParameters parameters) { parseExistingMessagesIfNecessary(); setItems(Arrays.asList(myUsedPids)); - return super.getItems(prefix, cached); + return super.getItems(prefix, cached, parameters); } @Override diff --git a/plugins/git4idea/tests/git4idea/branch/GitBranchOperationsTest.java b/plugins/git4idea/tests/git4idea/branch/GitBranchOperationsTest.java index af4cc1dc72f0..ba0a5c17a6fd 100644 --- a/plugins/git4idea/tests/git4idea/branch/GitBranchOperationsTest.java +++ b/plugins/git4idea/tests/git4idea/branch/GitBranchOperationsTest.java @@ -63,7 +63,7 @@ public class GitBranchOperationsTest extends AbstractVcsTestCase { private TempDirTestFixture myTempDirFixture; @BeforeMethod - public void setup() throws Exception { + public void setup(final Method testMethod) throws Exception { final IdeaTestFixtureFactory fixtureFactory = IdeaTestFixtureFactory.getFixtureFactory(); myTempDirFixture = fixtureFactory.createTempDirTestFixture(); myTempDirFixture.setUp(); @@ -75,7 +75,7 @@ public class GitBranchOperationsTest extends AbstractVcsTestCase { @Override public void run() { try { - initProject(projectDir); + initProject(projectDir, testMethod.getName()); initRepositories(VcsUtil.getVirtualFile(projectDir)); } catch (Exception e) { diff --git a/plugins/git4idea/tests/git4idea/history/GitHistoryUtilsTest.java b/plugins/git4idea/tests/git4idea/history/GitHistoryUtilsTest.java index 80694d9e99f1..a86c0bb02ead 100644 --- a/plugins/git4idea/tests/git4idea/history/GitHistoryUtilsTest.java +++ b/plugins/git4idea/tests/git4idea/history/GitHistoryUtilsTest.java @@ -27,7 +27,6 @@ import com.intellij.util.Consumer; import com.intellij.vcsUtil.VcsUtil; import git4idea.GitFileRevision; import git4idea.GitRevisionNumber; -import git4idea.history.GitHistoryUtils; import git4idea.history.browser.GitCommit; import git4idea.history.browser.SHAHash; import git4idea.history.wholeTree.AbstractHash; @@ -38,6 +37,7 @@ import org.testng.annotations.Test; import java.io.File; import java.io.IOException; +import java.lang.reflect.Method; import java.util.*; import static org.testng.Assert.*; @@ -59,8 +59,8 @@ public class GitHistoryUtilsTest extends GitTest { @BeforeMethod @Override - public void setUp() throws Exception { - super.setUp(); + public void setUp(Method testMethod) throws Exception { + super.setUp(testMethod); myRevisions = new ArrayList(7); myRevisionsAfterRename = new ArrayList(4); diff --git a/plugins/git4idea/tests/git4idea/history/GitLogParserTest.java b/plugins/git4idea/tests/git4idea/history/GitLogParserTest.java index 6c048bca9e8b..590f24a45c88 100644 --- a/plugins/git4idea/tests/git4idea/history/GitLogParserTest.java +++ b/plugins/git4idea/tests/git4idea/history/GitLogParserTest.java @@ -30,6 +30,7 @@ import org.jetbrains.annotations.Nullable; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; +import java.lang.reflect.Method; import java.util.*; import static git4idea.history.GitLogParser.*; @@ -106,8 +107,8 @@ public class GitLogParserTest extends GitTest { @BeforeMethod - protected void setUp() throws Exception { - super.setUp(); + protected void setUp(Method testMethod) throws Exception { + super.setUp(testMethod); myRoot = new LightVirtualFile(); myRecord = RECORD1; // for single record tests } diff --git a/plugins/git4idea/tests/git4idea/repo/GitRepositoryTest.java b/plugins/git4idea/tests/git4idea/repo/GitRepositoryTest.java index 7ff940527714..95da8f264fcd 100644 --- a/plugins/git4idea/tests/git4idea/repo/GitRepositoryTest.java +++ b/plugins/git4idea/tests/git4idea/repo/GitRepositoryTest.java @@ -25,6 +25,7 @@ import org.testng.annotations.Test; import java.io.File; import java.io.IOException; +import java.lang.reflect.Method; import java.util.concurrent.atomic.AtomicBoolean; import static git4idea.repo.GitRepository.State.*; @@ -45,8 +46,8 @@ public class GitRepositoryTest extends GitTest { private GitRepository myRepository; @BeforeMethod - public void setUp() throws Exception { - super.setUp(); + public void setUp(Method testMethod) throws Exception { + super.setUp(testMethod); myRepository = GitRepository.getFullInstance(myRepo.getVFRootDir(), myProject, myProject); myReader = new GitRepositoryReader(new File(VfsUtil.virtualToIoFile(myRepository.getRoot()), ".git")); } diff --git a/plugins/git4idea/tests/git4idea/tests/ChangeListManagerUpdateOnFileChangeTest.java b/plugins/git4idea/tests/git4idea/tests/ChangeListManagerUpdateOnFileChangeTest.java index 43e0e4a17fe7..ba42ad3faef2 100644 --- a/plugins/git4idea/tests/git4idea/tests/ChangeListManagerUpdateOnFileChangeTest.java +++ b/plugins/git4idea/tests/git4idea/tests/ChangeListManagerUpdateOnFileChangeTest.java @@ -22,6 +22,7 @@ import org.testng.annotations.Test; import java.io.File; import java.io.IOException; +import java.lang.reflect.Method; import java.util.HashSet; import java.util.Set; @@ -44,8 +45,8 @@ public class ChangeListManagerUpdateOnFileChangeTest extends GitTest { @Override @BeforeMethod - public void setUp() throws Exception { - super.setUp(); + public void setUp(Method testMethod) throws Exception { + super.setUp(testMethod); myChangeListManager = ChangeListManagerImpl.getInstanceImpl(myProject); // we'll need a file in all tests => testing file creation here diff --git a/plugins/git4idea/tests/git4idea/tests/GitBranchTest.java b/plugins/git4idea/tests/git4idea/tests/GitBranchTest.java index 12dbde65af00..3c8d28d4918c 100644 --- a/plugins/git4idea/tests/git4idea/tests/GitBranchTest.java +++ b/plugins/git4idea/tests/git4idea/tests/GitBranchTest.java @@ -23,6 +23,7 @@ import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.io.IOException; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -44,8 +45,8 @@ public class GitBranchTest extends GitTest { @BeforeMethod @Override - public void setUp() throws Exception { - super.setUp(); + public void setUp(Method testMethod) throws Exception { + super.setUp(testMethod); GitTestUtil.createFileStructure(myProject, myRepo, "a.txt", "b.txt", "dir/c.txt", "dir/subdir/d.txt"); myRepo.commit(); myRepo.push("origin", "master"); diff --git a/plugins/git4idea/tests/git4idea/tests/GitChangeProviderTest.java b/plugins/git4idea/tests/git4idea/tests/GitChangeProviderTest.java index 3c260a7de2f5..eac7726cf963 100644 --- a/plugins/git4idea/tests/git4idea/tests/GitChangeProviderTest.java +++ b/plugins/git4idea/tests/git4idea/tests/GitChangeProviderTest.java @@ -29,6 +29,7 @@ import git4idea.status.GitChangeProvider; import git4idea.test.GitTestUtil; import org.testng.annotations.BeforeMethod; +import java.lang.reflect.Method; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -54,8 +55,8 @@ public class GitChangeProviderTest extends GitTest { @BeforeMethod @Override - protected void setUp() throws Exception { - super.setUp(); + protected void setUp(Method testMethod) throws Exception { + super.setUp(testMethod); myChangeProvider = (GitChangeProvider) GitVcs.getInstance(myProject).getChangeProvider(); myFiles = GitTestUtil.createFileStructure(myProject, myRepo, "a.txt", "b.txt", "dir/c.txt", "dir/subdir/d.txt"); diff --git a/plugins/git4idea/tests/git4idea/tests/GitChangeProviderUnversionedTest.java b/plugins/git4idea/tests/git4idea/tests/GitChangeProviderUnversionedTest.java index 923cc7c323c7..4d78012eeecd 100644 --- a/plugins/git4idea/tests/git4idea/tests/GitChangeProviderUnversionedTest.java +++ b/plugins/git4idea/tests/git4idea/tests/GitChangeProviderUnversionedTest.java @@ -32,6 +32,7 @@ import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.io.File; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; @@ -48,8 +49,8 @@ public class GitChangeProviderUnversionedTest extends GitChangeProviderTest { @BeforeMethod @Override - protected void setUp() throws Exception { - super.setUp(); + protected void setUp(Method testMethod) throws Exception { + super.setUp(testMethod); } @Test diff --git a/plugins/git4idea/tests/git4idea/tests/GitLineSeparatorsConverterTest.java b/plugins/git4idea/tests/git4idea/tests/GitLineSeparatorsConverterTest.java index 9236026f245a..77df9aa07f36 100644 --- a/plugins/git4idea/tests/git4idea/tests/GitLineSeparatorsConverterTest.java +++ b/plugins/git4idea/tests/git4idea/tests/GitLineSeparatorsConverterTest.java @@ -57,8 +57,8 @@ public class GitLineSeparatorsConverterTest extends GitTest { @BeforeMethod @Override - protected void setUp() throws Exception { - super.setUp(); + protected void setUp(Method testMethod) throws Exception { + super.setUp(testMethod); myChangeListManager = ChangeListManagerImpl.getInstanceImpl(myProject); mySettings = GitVcsSettings.getInstance(myProject); myCodeStyleSeparator = CodeStyleFacade.getInstance(myProject).getLineSeparator(); diff --git a/plugins/git4idea/tests/git4idea/tests/GitTest.java b/plugins/git4idea/tests/git4idea/tests/GitTest.java index d3e10ab0fe69..26f627a174b5 100644 --- a/plugins/git4idea/tests/git4idea/tests/GitTest.java +++ b/plugins/git4idea/tests/git4idea/tests/GitTest.java @@ -31,6 +31,7 @@ import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import java.io.File; +import java.lang.reflect.Method; import java.util.Map; import static org.testng.Assert.assertNotNull; @@ -60,7 +61,7 @@ public abstract class GitTest extends AbstractVcsTestCase { protected GitVcs myVcs; @BeforeMethod - protected void setUp() throws Exception { + protected void setUp(final Method testMethod) throws Exception { myProjectDirFixture = IdeaTestFixtureFactory.getFixtureFactory().createTempDirTestFixture(); myProjectDirFixture.setUp(); myProjectDir = new File(myProjectDirFixture.getTempDirPath()); @@ -69,7 +70,7 @@ public abstract class GitTest extends AbstractVcsTestCase { @Override public void run() { try { - initProject(myProjectDir); + initProject(myProjectDir, testMethod.getName()); initRepositories(); activateVCS(GitVcs.NAME); } catch (Exception e) { diff --git a/plugins/git4idea/tests/git4idea/tests/rebase/GitRebaserReorderCommitsTest.java b/plugins/git4idea/tests/git4idea/tests/rebase/GitRebaserReorderCommitsTest.java index e69af71ed481..00277378863d 100644 --- a/plugins/git4idea/tests/git4idea/tests/rebase/GitRebaserReorderCommitsTest.java +++ b/plugins/git4idea/tests/git4idea/tests/rebase/GitRebaserReorderCommitsTest.java @@ -22,6 +22,7 @@ import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.io.IOException; +import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collections; @@ -38,8 +39,8 @@ public class GitRebaserReorderCommitsTest extends GitTest { private VirtualFile myRoot; private String myFirstCommit; - @BeforeMethod @Override protected void setUp() throws Exception { - super.setUp(); + @BeforeMethod @Override protected void setUp(Method testMethod) throws Exception { + super.setUp(testMethod); myRebaser = new GitRebaser(myProject, null); myRoot = myRepo.getVFRootDir(); myFirstCommit = makeCommit(); diff --git a/plugins/gradle/resources/i18n/GradleBundle.properties b/plugins/gradle/resources/i18n/GradleBundle.properties index 18704d71fd5d..4993111dcfad 100644 --- a/plugins/gradle/resources/i18n/GradleBundle.properties +++ b/plugins/gradle/resources/i18n/GradleBundle.properties @@ -89,6 +89,8 @@ gradle.action.import.entity.text=Import gradle.action.import.entity.description=Import target gradle entity gradle.action.show.conflict.text=Show conflict gradle.action.show.conflict.description=Show conflict details +gradle.action.rebuild.sync.tree.text=Rebuild Gradle project changes tree +gradle.action.rebuild.sync.tree.description=Rebuilds Gradle project structure changes tree gradle.settings.color.text.sample.conflict.node.name=node-with-conflicting-setup gradle.settings.color.text.sample.node.sync.name=node-with-same-setup diff --git a/plugins/gradle/src/META-INF/plugin.xml b/plugins/gradle/src/META-INF/plugin.xml index 3ff74f965ad0..a6919bcbbf21 100644 --- a/plugins/gradle/src/META-INF/plugin.xml +++ b/plugins/gradle/src/META-INF/plugin.xml @@ -127,6 +127,8 @@ + + \ No newline at end of file diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleAbstractSyncTreeFilterAction.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/action/AbstractGradleSyncTreeFilterAction.java similarity index 96% rename from plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleAbstractSyncTreeFilterAction.java rename to plugins/gradle/src/org/jetbrains/plugins/gradle/action/AbstractGradleSyncTreeFilterAction.java index 8ed13ba1956c..b9a3afddefb3 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleAbstractSyncTreeFilterAction.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/action/AbstractGradleSyncTreeFilterAction.java @@ -22,7 +22,7 @@ import java.awt.*; * @author Denis Zhdanov * @since 3/7/12 3:48 PM */ -public abstract class GradleAbstractSyncTreeFilterAction extends ToggleAction { +public abstract class AbstractGradleSyncTreeFilterAction extends ToggleAction { @NotNull private final MyFilter myFilter; @NotNull private final TextAttributesKey myAttributesKey; @@ -30,7 +30,7 @@ public abstract class GradleAbstractSyncTreeFilterAction extends ToggleAction { private Color myColor; private boolean myIconChanged; - protected GradleAbstractSyncTreeFilterAction(@NotNull AttributesDescriptor descriptor) { + protected AbstractGradleSyncTreeFilterAction(@NotNull AttributesDescriptor descriptor) { myFilter = new MyFilter(descriptor.getKey()); myAttributesKey = descriptor.getKey(); getTemplatePresentation().setText(descriptor.getDisplayName()); diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleConfirmedSyncTreeFilterAction.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleConfirmedSyncTreeFilterAction.java index de21d9c421f8..e56032167348 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleConfirmedSyncTreeFilterAction.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleConfirmedSyncTreeFilterAction.java @@ -6,7 +6,7 @@ import org.jetbrains.plugins.gradle.config.GradleColorAndFontDescriptorsProvider * @author Denis Zhdanov * @since 3/7/12 6:07 PM */ -public class GradleConfirmedSyncTreeFilterAction extends GradleAbstractSyncTreeFilterAction { +public class GradleConfirmedSyncTreeFilterAction extends AbstractGradleSyncTreeFilterAction { public GradleConfirmedSyncTreeFilterAction() { super(GradleColorAndFontDescriptorsProvider.CONFIRMED); diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleConflictSyncTreeFilterAction.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleConflictSyncTreeFilterAction.java index 3ef8e0957b20..49c1f906c43b 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleConflictSyncTreeFilterAction.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleConflictSyncTreeFilterAction.java @@ -6,7 +6,7 @@ import org.jetbrains.plugins.gradle.config.GradleColorAndFontDescriptorsProvider * @author Denis Zhdanov * @since 3/7/12 6:08 PM */ -public class GradleConflictSyncTreeFilterAction extends GradleAbstractSyncTreeFilterAction { +public class GradleConflictSyncTreeFilterAction extends AbstractGradleSyncTreeFilterAction { public GradleConflictSyncTreeFilterAction() { super(GradleColorAndFontDescriptorsProvider.CONFLICT); diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleLocalSyncTreeFilterAction.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleLocalSyncTreeFilterAction.java index f0a59e30c5f4..3ff4ff6cfdb6 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleLocalSyncTreeFilterAction.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleLocalSyncTreeFilterAction.java @@ -6,7 +6,7 @@ import org.jetbrains.plugins.gradle.config.GradleColorAndFontDescriptorsProvider * @author Denis Zhdanov * @since 3/7/12 3:54 PM */ -public class GradleLocalSyncTreeFilterAction extends GradleAbstractSyncTreeFilterAction { +public class GradleLocalSyncTreeFilterAction extends AbstractGradleSyncTreeFilterAction { public GradleLocalSyncTreeFilterAction() { super(GradleColorAndFontDescriptorsProvider.GRADLE_LOCAL); diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleRebuildStructureChangesTreeAction.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleRebuildStructureChangesTreeAction.java new file mode 100644 index 000000000000..ac2c7bfca55c --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleRebuildStructureChangesTreeAction.java @@ -0,0 +1,71 @@ +package org.jetbrains.plugins.gradle.action; + +import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.wm.ToolWindow; +import com.intellij.openapi.wm.ToolWindowManager; +import com.intellij.ui.content.Content; +import com.intellij.ui.content.ContentManager; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.gradle.sync.GradleProjectStructureTreeModel; +import org.jetbrains.plugins.gradle.ui.GradleDataKeys; +import org.jetbrains.plugins.gradle.util.GradleBundle; +import org.jetbrains.plugins.gradle.util.GradleConstants; + +import javax.swing.*; + +/** + * @author Denis Zhdanov + * @since 3/15/12 11:23 AM + */ +public class GradleRebuildStructureChangesTreeAction extends AnAction { + + public GradleRebuildStructureChangesTreeAction() { + getTemplatePresentation().setText(GradleBundle.message("gradle.action.rebuild.sync.tree.text")); + getTemplatePresentation().setDescription(GradleBundle.message("gradle.action.rebuild.sync.tree.description")); + } + + @Override + public void actionPerformed(AnActionEvent e) { + final GradleProjectStructureTreeModel model = getModel(e.getDataContext()); + if (model != null) { + model.rebuild(); + } + } + + @Nullable + private static GradleProjectStructureTreeModel getModel(@NotNull DataContext context) { + final GradleProjectStructureTreeModel model = GradleDataKeys.SYNC_TREE_MODEL.getData(context); + if (model != null) { + return model; + } + + final Project project = PlatformDataKeys.PROJECT.getData(context); + if (project == null) { + return null; + } + + final ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(project); + final ToolWindow toolWindow = toolWindowManager.getToolWindow(GradleConstants.TOOL_WINDOW_ID); + if (toolWindow == null) { + return null; + } + + final ContentManager contentManager = toolWindow.getContentManager(); + if (contentManager == null) { + return null; + } + + for (Content content : contentManager.getContents()) { + final JComponent component = content.getComponent(); + if (component instanceof DataProvider) { + final Object data = ((DataProvider)component).getData(GradleDataKeys.SYNC_TREE_MODEL.getName()); + if (data instanceof GradleProjectStructureTreeModel) { + return (GradleProjectStructureTreeModel)data; + } + } + } + return null; + } +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/action/IntellijLocalSyncTreeFilterAction.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/action/IntellijLocalSyncTreeFilterAction.java index 46bc43a9d51b..4a95797a88c1 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/action/IntellijLocalSyncTreeFilterAction.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/action/IntellijLocalSyncTreeFilterAction.java @@ -1,14 +1,12 @@ package org.jetbrains.plugins.gradle.action; -import com.intellij.openapi.options.colors.AttributesDescriptor; -import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.gradle.config.GradleColorAndFontDescriptorsProvider; /** * @author Denis Zhdanov * @since 3/7/12 3:59 PM */ -public class IntellijLocalSyncTreeFilterAction extends GradleAbstractSyncTreeFilterAction { +public class IntellijLocalSyncTreeFilterAction extends AbstractGradleSyncTreeFilterAction { public IntellijLocalSyncTreeFilterAction() { super(GradleColorAndFontDescriptorsProvider.INTELLIJ_LOCAL); diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleConstants.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleConstants.java index 348a4c857903..d43481149b38 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleConstants.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleConstants.java @@ -14,6 +14,8 @@ public class GradleConstants { @NonNls public static final String EXTENSION = "gradle"; @NonNls public static final String DEFAULT_SCRIPT_NAME = "build.gradle"; + + @NonNls public static final String TOOL_WINDOW_ID = "JetGradle"; @NonNls public static final String TOOL_WINDOW_TOOLBAR_PLACE = "GRADLE_SYNC_CHANGES_TOOLBAR"; @NonNls public static final String SYNC_TREE_CONTEXT_MENU_PLACE = "GRADLE_SYNC_TREE_CONTEXT_MENU_PLACE"; diff --git a/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgAddTest.java b/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgAddTest.java index afcf45aa8a7c..80c8cd0d94af 100644 --- a/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgAddTest.java +++ b/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgAddTest.java @@ -17,6 +17,8 @@ import com.intellij.openapi.vfs.VirtualFile; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; +import java.lang.reflect.Method; + import static org.zmlx.hg4idea.test.HgTestOutputParser.added; /** @@ -26,8 +28,8 @@ public class HgAddTest extends HgSingleUserTest { @BeforeMethod @Override - protected void setUp() throws Exception { - super.setUp(); + protected void setUp(Method testMethod) throws Exception { + super.setUp(testMethod); doNothingSilently(VcsConfiguration.StandardConfirmation.ADD); } diff --git a/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgAnnotateCommandTest.java b/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgAnnotateCommandTest.java index aee72fd237a6..2a0048d805fb 100644 --- a/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgAnnotateCommandTest.java +++ b/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgAnnotateCommandTest.java @@ -54,8 +54,8 @@ public class HgAnnotateCommandTest extends HgSingleUserTest { @BeforeMethod @Override - protected void setUp() throws Exception { - super.setUp(); + protected void setUp(Method testMethod) throws Exception { + super.setUp(testMethod); } @BeforeClass diff --git a/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgMergeProviderTest.java b/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgMergeProviderTest.java index 3aceb2cfe38d..ca486678b082 100644 --- a/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgMergeProviderTest.java +++ b/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgMergeProviderTest.java @@ -27,6 +27,7 @@ import org.testng.annotations.Test; import org.zmlx.hg4idea.HgVcs; import java.io.IOException; +import java.lang.reflect.Method; import java.util.concurrent.atomic.AtomicReference; import static org.testng.Assert.assertNotNull; @@ -43,8 +44,8 @@ public class HgMergeProviderTest extends HgCollaborativeTest { @BeforeMethod @Override - protected void setUp() throws Exception { - super.setUp(); + protected void setUp(Method testMethod) throws Exception { + super.setUp(testMethod); myMergeProvider = HgVcs.getInstance(myProject).getMergeProvider(); assertNotNull(myMergeProvider); } diff --git a/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgTest.java b/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgTest.java index 12e09f87fa54..b99b33e9f6bb 100644 --- a/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgTest.java +++ b/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgTest.java @@ -32,6 +32,7 @@ import org.zmlx.hg4idea.HgFile; import org.zmlx.hg4idea.HgVcs; import java.io.*; +import java.lang.reflect.Method; import static org.testng.Assert.assertTrue; @@ -56,7 +57,7 @@ public abstract class HgTest extends AbstractVcsTestCase { private HgTestRepository myMainRepo; @BeforeMethod - protected void setUp() throws Exception { + protected void setUp(final Method testMethod) throws Exception { // setting hg executable String exec = System.getenv(HG_EXECUTABLE_PATH); if (exec != null) { @@ -75,7 +76,7 @@ public abstract class HgTest extends AbstractVcsTestCase { @Override public void run() { try { - initProject(myProjectDir); + initProject(myProjectDir, testMethod.getName()); activateVCS(HgVcs.VCS_NAME); } catch (Exception e) { e.printStackTrace(); diff --git a/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgUpdateTest.java b/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgUpdateTest.java index ccf62f252c91..7ba973ef84ff 100644 --- a/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgUpdateTest.java +++ b/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgUpdateTest.java @@ -35,6 +35,7 @@ import org.zmlx.hg4idea.provider.update.HgRegularUpdater; import java.io.File; import java.io.IOException; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -54,8 +55,8 @@ public class HgUpdateTest extends HgCollaborativeTest { @BeforeMethod @Override - protected void setUp() throws Exception { - super.setUp(); + protected void setUp(Method testMethod) throws Exception { + super.setUp(testMethod); projectRepoVirtualFile = myRepo.getDir(); projectRepo = new File(myRepo.getDir().getPath()); remoteRepo = new File(myParentRepo.getDir().getPath()); diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/execution/MavenRunnerParameters.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/execution/MavenRunnerParameters.java index bf50f9f4c5a2..7521b5a66afe 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/execution/MavenRunnerParameters.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/execution/MavenRunnerParameters.java @@ -107,6 +107,7 @@ public class MavenRunnerParameters implements Cloneable { } public void setGoals(@Nullable List goals) { + if (myGoals == goals) return; // Called from XML Serializer myGoals.clear(); if (goals != null) { diff --git a/plugins/maven/src/test/java/org/jetbrains/idea/maven/MavenTestCase.java b/plugins/maven/src/test/java/org/jetbrains/idea/maven/MavenTestCase.java index 71047a304bba..162b134445d2 100644 --- a/plugins/maven/src/test/java/org/jetbrains/idea/maven/MavenTestCase.java +++ b/plugins/maven/src/test/java/org/jetbrains/idea/maven/MavenTestCase.java @@ -125,7 +125,7 @@ public abstract class MavenTestCase extends UsefulTestCase { } protected void setUpFixtures() throws Exception { - myTestFixture = IdeaTestFixtureFactory.getFixtureFactory().createFixtureBuilder().getFixture(); + myTestFixture = IdeaTestFixtureFactory.getFixtureFactory().createFixtureBuilder(getName()).getFixture(); myTestFixture.setUp(); } diff --git a/plugins/maven/src/test/java/org/jetbrains/idea/maven/execution/MavenRunConfigurationTest.java b/plugins/maven/src/test/java/org/jetbrains/idea/maven/execution/MavenRunConfigurationTest.java index f4ad8fc7f869..3376b3073147 100644 --- a/plugins/maven/src/test/java/org/jetbrains/idea/maven/execution/MavenRunConfigurationTest.java +++ b/plugins/maven/src/test/java/org/jetbrains/idea/maven/execution/MavenRunConfigurationTest.java @@ -26,7 +26,7 @@ public class MavenRunConfigurationTest extends IdeaTestCase { public void testSaveLoadRunnerParameters() { MavenRunConfiguration.MavenSettings s = new MavenRunConfiguration.MavenSettings(myProject); s.myRunnerParameters.setWorkingDirPath("some path"); - s.myRunnerParameters.setGoals(Arrays.asList("clean validate")); + s.myRunnerParameters.setGoals(Arrays.asList("clean", "validate")); s.myRunnerParameters.setProfilesMap(ImmutableMap.of("prof1", true, "prof2", true, "prof3", false)); Element xml = XmlSerializer.serialize(s); diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/IgnoredFilesTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/IgnoredFilesTest.java index 0cb4fb37aab7..dc45dc51aeb2 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/IgnoredFilesTest.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/IgnoredFilesTest.java @@ -63,7 +63,7 @@ public class IgnoredFilesTest extends SvnTestCase { myClientRoot = new File(myTempDirFixture.getTempDirPath(), "clientroot"); myClientRoot.mkdir(); - initProject(myClientRoot); + initProject(myClientRoot, IgnoredFilesTest.this.getTestName()); ((StartupManagerImpl)StartupManager.getInstance(myProject)).runPostStartupActivities(); diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnTestCase.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnTestCase.java index c0f0ecac7392..e2a1a1082a98 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnTestCase.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnTestCase.java @@ -18,7 +18,7 @@ import com.intellij.openapi.vcs.VcsShowConfirmationOption; import com.intellij.openapi.vcs.changes.*; import com.intellij.openapi.vcs.changes.pending.MockChangeListManagerGate; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.testFramework.AbstractVcsTestCase; +import com.intellij.testFramework.AbstractJunitVcsTestCase; import com.intellij.testFramework.PlatformTestCase; import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory; import com.intellij.testFramework.fixtures.TempDirTestFixture; @@ -35,7 +35,7 @@ import java.util.List; /** * @author yole */ -public abstract class SvnTestCase extends AbstractVcsTestCase { +public abstract class SvnTestCase extends AbstractJunitVcsTestCase { protected TempDirTestFixture myTempDirFixture; private File myWcRoot; protected String myRepoUrl; @@ -76,7 +76,7 @@ public abstract class SvnTestCase extends AbstractVcsTestCase { myRepoUrl = "file:///" + FileUtil.toSystemIndependentName(svnRoot.getPath()); - initProject(myWcRoot); + initProject(myWcRoot, SvnTestCase.this.getTestName()); activateVCS(SvnVcs.VCS_NAME); verify(runSvn("co", myRepoUrl, ".")); diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/IgnoredFilesTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/IgnoredFilesTest.java index df9f31a874e5..9bd8172bc615 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/IgnoredFilesTest.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/IgnoredFilesTest.java @@ -64,7 +64,7 @@ public class IgnoredFilesTest extends SvnTestCase { myClientRoot = new File(myTempDirFixture.getTempDirPath(), "clientroot"); myClientRoot.mkdir(); - initProject(myClientRoot); + initProject(myClientRoot, IgnoredFilesTest.this.getTestName()); ((StartupManagerImpl)StartupManager.getInstance(myProject)).runPostStartupActivities(); diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnTestCase.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnTestCase.java index 49ce0b4bb452..2085565f8ab8 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnTestCase.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnTestCase.java @@ -18,7 +18,7 @@ import com.intellij.openapi.vcs.VcsShowConfirmationOption; import com.intellij.openapi.vcs.changes.*; import com.intellij.openapi.vcs.changes.pending.MockChangeListManagerGate; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.testFramework.AbstractVcsTestCase; +import com.intellij.testFramework.AbstractJunitVcsTestCase; import com.intellij.testFramework.PlatformTestCase; import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory; import com.intellij.testFramework.fixtures.TempDirTestFixture; @@ -37,7 +37,7 @@ import java.util.List; /** * @author yole */ -public abstract class SvnTestCase extends AbstractVcsTestCase { +public abstract class SvnTestCase extends AbstractJunitVcsTestCase { protected TempDirTestFixture myTempDirFixture; private File myWcRoot; protected String myRepoUrl; @@ -78,7 +78,7 @@ public abstract class SvnTestCase extends AbstractVcsTestCase { myRepoUrl = "file:///" + FileUtil.toSystemIndependentName(svnRoot.getPath()); verify(runSvn("co", myRepoUrl, ".")); - initProject(myWcRoot); + initProject(myWcRoot, SvnTestCase.this.getTestName()); activateVCS(SvnVcs.VCS_NAME); myGate = new MockChangeListManagerGate(ChangeListManager.getInstance(myProject)); diff --git a/plugins/svn4ideaOld/testSource/org/jetbrains/idea/svn/IgnoredFilesTest.java b/plugins/svn4ideaOld/testSource/org/jetbrains/idea/svn/IgnoredFilesTest.java index 302b62185140..5bef91e4b6e0 100644 --- a/plugins/svn4ideaOld/testSource/org/jetbrains/idea/svn/IgnoredFilesTest.java +++ b/plugins/svn4ideaOld/testSource/org/jetbrains/idea/svn/IgnoredFilesTest.java @@ -49,7 +49,7 @@ public class IgnoredFilesTest extends AbstractVcsTestCase { myClientRoot = new File(myTempDirFixture.getTempDirPath(), "clientroot"); myClientRoot.mkdir(); - initProject(myClientRoot); + initProject(myClientRoot, IgnoredFilesTest.this.getTestName()); ((StartupManagerImpl)StartupManager.getInstance(myProject)).runPostStartupActivities(); diff --git a/plugins/svn4ideaOld/testSource/org/jetbrains/idea/svn/SvnTestCase.java b/plugins/svn4ideaOld/testSource/org/jetbrains/idea/svn/SvnTestCase.java index 7454a6411628..4e06b57fffa3 100644 --- a/plugins/svn4ideaOld/testSource/org/jetbrains/idea/svn/SvnTestCase.java +++ b/plugins/svn4ideaOld/testSource/org/jetbrains/idea/svn/SvnTestCase.java @@ -76,7 +76,7 @@ public abstract class SvnTestCase extends AbstractVcsTestCase { myRepoUrl = "file:///" + FileUtil.toSystemIndependentName(svnRoot.getPath()); verify(runSvn("co", myRepoUrl, ".")); - initProject(myWcRoot); + initProject(myWcRoot, SvnTestCase.this.getTestName()); activateVCS(SvnVcs.VCS_NAME); myGate = new MockChangeListManagerGate(ChangeListManager.getInstance(myProject)); diff --git a/plugins/tasks/tasks-api/src/com/intellij/tasks/TaskManager.java b/plugins/tasks/tasks-api/src/com/intellij/tasks/TaskManager.java index 099ffd5a7ac3..c7c6889c5750 100644 --- a/plugins/tasks/tasks-api/src/com/intellij/tasks/TaskManager.java +++ b/plugins/tasks/tasks-api/src/com/intellij/tasks/TaskManager.java @@ -41,6 +41,8 @@ public abstract class TaskManager { */ public abstract List getIssues(String query); + public abstract List getIssues(String query, boolean forceRequest); + /** * Returns already cached issues. * @return cached issues. diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/ActivateTaskDialog.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/ActivateTaskDialog.java index dbc2e3ee601a..8228c0998cf3 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/ActivateTaskDialog.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/ActivateTaskDialog.java @@ -17,6 +17,7 @@ package com.intellij.tasks.actions; import com.intellij.CommonBundle; +import com.intellij.codeInsight.completion.CompletionParameters; import com.intellij.codeInsight.completion.InsertHandler; import com.intellij.codeInsight.completion.InsertionContext; import com.intellij.codeInsight.lookup.LookupElement; @@ -294,11 +295,12 @@ public class ActivateTaskDialog extends DialogWrapper { } public static class MyTextFieldWithAutoCompletionListProvider extends TextFieldWithAutoCompletionListProvider { - private final TaskSearchSupport mySearchSupport; - public MyTextFieldWithAutoCompletionListProvider(@Nullable final Project project) { + private final Project myProject; + + public MyTextFieldWithAutoCompletionListProvider(Project project) { super(null); - mySearchSupport = new TaskSearchSupport(project); + myProject = project; } @Override @@ -308,8 +310,8 @@ public class ActivateTaskDialog extends DialogWrapper { @NotNull @Override - public List getItems(final String prefix, final boolean cached) { - return mySearchSupport.getItems(prefix, cached); + public List getItems(final String prefix, final boolean cached, CompletionParameters parameters) { + return new TaskSearchSupport(myProject).getItems(prefix, cached, parameters.isAutoPopup()); } @Override @@ -335,7 +337,7 @@ public class ActivateTaskDialog extends DialogWrapper { @Override public void handleInsert(InsertionContext context, LookupElement item) { Document document = context.getEditor().getDocument(); - String s = task.getId() + ": " + task.getSummary(); + String s = ((TaskManagerImpl)TaskManager.getManager(context.getProject())).getChangelistName(task); s = StringUtil.convertLineSeparators(s); document.replaceString(context.getStartOffset(), context.getTailOffset(), s); context.getEditor().getCaretModel().moveToOffset(context.getStartOffset() + s.length()); diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/TaskSearchSupport.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/TaskSearchSupport.java index 5e0f2b5810c6..62743136fc9b 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/TaskSearchSupport.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/actions/TaskSearchSupport.java @@ -39,9 +39,9 @@ public class TaskSearchSupport { myManager = (TaskManagerImpl)TaskManager.getManager(project); } - public List getItems(String pattern, boolean cached) { + public List getItems(String pattern, boolean cached, boolean autopopup) { final Matcher matcher = getMatcher(pattern); - return ContainerUtil.mapNotNull(getTasks(pattern, cached), new NullableFunction() { + return ContainerUtil.mapNotNull(getTasks(pattern, cached, autopopup), new NullableFunction() { public Task fun(Task task) { return matcher.matches(task.getId()) || matcher.matches(task.getSummary()) ? task : null; } @@ -64,7 +64,7 @@ public class TaskSearchSupport { return myMatcher; } - private List getTasks(String pattern, boolean cached) { - return cached ? myManager.getCachedIssues() : myManager.getIssues(pattern); + private List getTasks(String pattern, boolean cached, boolean autopopup) { + return cached ? myManager.getCachedIssues() : myManager.getIssues(pattern, !autopopup); } } diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/TaskChangelistSupport.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/TaskChangelistSupport.java index d4d5c8bf3840..f41a6b130681 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/TaskChangelistSupport.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/TaskChangelistSupport.java @@ -45,7 +45,7 @@ public class TaskChangelistSupport implements EditChangelistSupport { final ActivateTaskDialog.MyTextFieldWithAutoCompletionListProvider completionProvider = new ActivateTaskDialog.MyTextFieldWithAutoCompletionListProvider(myProject); - TextFieldWithAutoCompletionContributor.installCompletion(document, myProject, completionProvider, true); + TextFieldWithAutoCompletionContributor.installCompletion(document, myProject, completionProvider, false); } public Consumer addControls(JPanel bottomPanel, LocalChangeList initial) { diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/TaskManagerImpl.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/TaskManagerImpl.java index bf164b1fe19a..ab2bba3438e0 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/TaskManagerImpl.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/TaskManagerImpl.java @@ -230,7 +230,12 @@ public class TaskManagerImpl extends TaskManager implements ProjectComponent, Pe @Override public List getIssues(String query) { - List tasks = getIssuesFromRepositories(query, 50, 0, true); + return getIssues(query, true); + } + + @Override + public List getIssues(String query, boolean forceRequest) { + List tasks = getIssuesFromRepositories(query, 50, 0, forceRequest); synchronized (myIssueCache) { myTemporaryCache.clear(); myTemporaryCache.putAll(ContainerUtil.assignKeys(tasks.iterator(), KEY_CONVERTOR)); diff --git a/plugins/tasks/tasks-tests/test/com/intellij/tasks/TaskCompletionTest.java b/plugins/tasks/tasks-tests/test/com/intellij/tasks/TaskCompletionTest.java index 5cfcececca68..51cd69711c72 100644 --- a/plugins/tasks/tasks-tests/test/com/intellij/tasks/TaskCompletionTest.java +++ b/plugins/tasks/tasks-tests/test/com/intellij/tasks/TaskCompletionTest.java @@ -27,15 +27,15 @@ public class TaskCompletionTest extends LightCodeInsightFixtureTestCase { } public void testTaskCompletion() throws Exception { - doTest("", "TEST-001: Test task"); + doTest("", "TEST-001 Test task"); } public void testPrefix() throws Exception { - doTest("TEST-", "TEST-001: Test task"); + doTest("TEST-", "TEST-001 Test task"); } public void testSecondWord() throws Exception { - doTest("my TEST-", "my TEST-001: Test task"); + doTest("my TEST-", "my TEST-001 Test task"); } public void testNumberCompletion() throws Exception { @@ -63,12 +63,18 @@ public class TaskCompletionTest extends LightCodeInsightFixtureTestCase { } public void testSIOOBE() throws Exception { - doTest(" my", " TEST-001: Test task my"); + doTest(" my", " TEST-001 Test task my"); } - private void doTest(String text, String after) { + private void doTest(String text, final String after) { configureFile(text); - configureRepository(new LocalTaskImpl("TEST-001", "Test task")); + final TestRepository repository = configureRepository(); + repository.setTasks(new LocalTaskImpl("TEST-001", "Test task") { + @Override + public TaskRepository getRepository() { + return repository; + } + }); myFixture.completeBasic(); myFixture.checkResult(after); }