mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
+14
@@ -538,6 +538,20 @@ public class LayoutTreeComponent implements DnDTarget, Disposable {
|
||||
updateAndSelect(myTree.getRootPackagingNode(), toSelect);
|
||||
}
|
||||
|
||||
public void putElements(@NotNull final String path, @NotNull final List<? extends PackagingElement<?>> elements) {
|
||||
final List<PackagingElement<?>> toSelect = new ArrayList<PackagingElement<?>>();
|
||||
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<? extends PackagingSourceItem> items, final String pathToJar) {
|
||||
final List<PackagingElement<?>> toSelect = new ArrayList<PackagingElement<?>>();
|
||||
final CompositePackagingElement<?> rootElement = getArtifact().getRootElement();
|
||||
|
||||
+1
@@ -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));
|
||||
|
||||
+96
@@ -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<PackagingElement<?>> extracted = new ArrayList<PackagingElement<?>>();
|
||||
for (PackagingSourceItem item : mySourceItemsTree.getSelectedItems()) {
|
||||
final ArtifactEditorContext context = myArtifactEditor.getContext();
|
||||
final List<? extends PackagingElement<?>> elements = item.createElements(context);
|
||||
ArtifactUtil.processElementsWithSubstitutions(elements, context, context.getArtifactType(), PackagingElementPath.EMPTY, new PackagingElementProcessor<PackagingElement<?>>() {
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
@@ -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<String> 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;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-14
@@ -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);
|
||||
|
||||
@@ -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<NamedScope> myScopes;
|
||||
|
||||
|
||||
+3
-2
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+12
@@ -75,3 +75,15 @@ class Aaa {
|
||||
new Aaa("asdasdad") {};
|
||||
}
|
||||
}
|
||||
|
||||
class Anonym {
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public Anonym(String sss) {
|
||||
System.out.println(sss);
|
||||
}
|
||||
public void foo() {
|
||||
new <warning descr="'Anonym(java.lang.String)' is deprecated">Anonym</warning>("asdasd") {};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -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();
|
||||
|
||||
@@ -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<File, Archive> myArchives;
|
||||
private final Map<File, Boolean> myIsFile = new ConcurrentHashMap<File, Boolean>();
|
||||
private final Map<InputFileObject, SoftReference<CharBuffer>> myContentCache = new HashMap<InputFileObject, SoftReference<CharBuffer>>();
|
||||
|
||||
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<? extends JavaFileObject> getJavaFileObjectsFromFiles(Iterable<? extends File> files) {
|
||||
java.util.List<InputFileObject> result;
|
||||
if (files instanceof Collection) {
|
||||
result = new ArrayList<InputFileObject>(((Collection)files).size());
|
||||
}
|
||||
else {
|
||||
result = new ArrayList<InputFileObject>();
|
||||
}
|
||||
for (File f: files) {
|
||||
result.add(new InputFileObject(f));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<JavaFileObject> list(Location location, String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse) throws IOException {
|
||||
Iterable<? extends File> 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<? extends File> 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<CharBuffer> 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<CharBuffer>(cb));
|
||||
}
|
||||
}
|
||||
finally {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
return cb;
|
||||
}
|
||||
|
||||
//public CharBuffer getCharContent(boolean ignoreEncodingErrors) throws IOException {
|
||||
// final String encodingName = getEncodingName();
|
||||
// SoftReference<CharBuffer> 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<CharBuffer>(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();
|
||||
|
||||
}
|
||||
|
||||
@@ -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<File, Archive>) archivesField.get(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<? extends JavaFileObject> getJavaFileObjectsFromFiles(Iterable<? extends File> files) {
|
||||
java.util.List<InputFileObject> result;
|
||||
if (files instanceof Collection) {
|
||||
result = new ArrayList<InputFileObject>(((Collection)files).size());
|
||||
}
|
||||
else {
|
||||
result = new ArrayList<InputFileObject>();
|
||||
}
|
||||
for (File f: files) {
|
||||
result.add(new InputFileObject(this, f));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<JavaFileObject> list(Location location, String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse) throws IOException {
|
||||
Iterable<? extends File> 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<File> 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<? extends File> 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<File>(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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -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 <code>'defer changes'</code> 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<TextChangesStorage> myDeferredChangesStorage = new AtomicReference<TextChangesStorage>();
|
||||
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<String> 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<String> 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<TextChangeImpl> myDebugDeferredChanges;
|
||||
|
||||
/** Document text on bulk processing start. */
|
||||
private String myDebugTextOnBatchUpdateStart;
|
||||
/**
|
||||
* Holds deferred changes create during the current bulk processing iteration.
|
||||
*/
|
||||
private List<TextChangeImpl> 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<TextChangeImpl>();
|
||||
}
|
||||
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<String>(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 <code>true</code> if this object is at {@link #setDeferredChangeMode(boolean) defer changes} mode;
|
||||
* <code>false</code> otherwise
|
||||
* @return <code>true</code> 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 {
|
||||
* </li>
|
||||
* <li>
|
||||
* 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)});
|
||||
* </li>
|
||||
* <li>
|
||||
* Client of this class indicates that <code>'massive change time'</code> is over by calling this method with <code>'false'</code>
|
||||
@@ -571,108 +593,109 @@ abstract class CharArray implements CharSequenceBackedByArray {
|
||||
* @param deferredChangeMode flag that defines if <code>'defer changes'</code> 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<TextChangeImpl> changes = storage.getChanges();
|
||||
private void flushDeferredChanged() {
|
||||
List<TextChangeImpl> 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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String>() {
|
||||
@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<String>() {
|
||||
@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<CharSequence>() {
|
||||
@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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -37,7 +37,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
*/
|
||||
public class RangeMarkerTree<T extends RangeMarkerEx> extends IntervalTreeImpl<T> {
|
||||
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;
|
||||
|
||||
@@ -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<ChangeEntry> myChanges = new ArrayList<ChangeEntry>();
|
||||
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<TextChangeImpl> getChanges() {
|
||||
List<TextChangeImpl> result = new ArrayList<TextChangeImpl>();
|
||||
if (myChanges.isEmpty()) return Collections.emptyList();
|
||||
List<TextChangeImpl> result = new ArrayList<TextChangeImpl>(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;
|
||||
|
||||
@@ -94,16 +94,18 @@ public class DiffLog implements DiffTreeChangeBuilder<ASTNode,ASTNode> {
|
||||
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();
|
||||
|
||||
@@ -97,6 +97,10 @@ public class CompletionParameters {
|
||||
return myInvocationCount;
|
||||
}
|
||||
|
||||
public boolean isAutoPopup() {
|
||||
return myInvocationCount == 0;
|
||||
}
|
||||
|
||||
public boolean isRelaxedMatching() {
|
||||
return myRelaxedMatching;
|
||||
}
|
||||
|
||||
+40
@@ -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<NamedScope> predefinedScopes = getCustomScopes();
|
||||
return findPredefinedScope(name, predefinedScopes);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static NamedScope findPredefinedScope(String name, List<NamedScope> predefinedScopes) {
|
||||
for (NamedScope scope : predefinedScopes) {
|
||||
if (name.equals(scope.getName())) return scope;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+6
-5
@@ -179,17 +179,18 @@ public abstract class NamedScopesHolder implements PersistentStateComponent<Elem
|
||||
for (NamedScope scope : myScopes) {
|
||||
if (name.equals(scope.getName())) return scope;
|
||||
}
|
||||
final List<NamedScope> predefinedScopes = getPredefinedScopes();
|
||||
for (NamedScope scope : predefinedScopes) {
|
||||
if (name.equals(scope.getName())) return scope;
|
||||
}
|
||||
return null;
|
||||
return getPredefinedScope(name);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<NamedScope> getPredefinedScopes(){
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public NamedScope getPredefinedScope(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Project getProject() {
|
||||
return myProject;
|
||||
|
||||
@@ -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<PsiFile> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<EditorEx>() {
|
||||
@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);
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
+22
-1
@@ -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<Mapper, Integer> mins = new FactoryMap<Mapper, Integer>() {
|
||||
@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();
|
||||
}
|
||||
|
||||
+32
-17
@@ -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<NamedScope> result = new ArrayList<NamedScope>();
|
||||
result.add(createScope(changeListManager.getAffectedFiles(), IdeBundle.message("scope.modified.files")));
|
||||
for (ChangeList list : changeListManager.getChangeListsCopy()) {
|
||||
final List<VirtualFile> files = new ArrayList<VirtualFile>();
|
||||
final Collection<Change> 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<VirtualFile> files = new ArrayList<VirtualFile>();
|
||||
final Collection<Change> 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<VirtualFile> files, String changeListName) {
|
||||
return new NamedScope(changeListName, new PackageSetBase() {
|
||||
@Override
|
||||
|
||||
@@ -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<NamedScope> myScopes;
|
||||
|
||||
+16
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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<CommitStage> COMMIT_STAGE = new Key<CommitStage>("Commit stage");
|
||||
|
||||
private final Queue<CommitTask> documentsToCommit = new Queue<CommitTask>(10);
|
||||
private final List<CommitTask> documentsToApplyInEDT = new ArrayList<CommitTask>(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<Document> unc = task.project.isDisposed() ? Collections.<Document>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<ProgressIndicator> 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<CommitTask>() {
|
||||
@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<Document> 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<Processor<Document>> finishRunnables = new SmartList<Processor<Document>>();
|
||||
final boolean synchronously) {
|
||||
final Project project = task.project;
|
||||
final Document document = task.document;
|
||||
final List<Processor<Document>> finishProcessors = new SmartList<Processor<Document>>();
|
||||
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<PsiFile> psiFiles = viewProvider.getAllFiles();
|
||||
List<PsiFile> psiFiles = viewProvider.getAllFiles();
|
||||
for (PsiFile file : psiFiles) {
|
||||
if (file.isValid() && file != excludeFile) {
|
||||
Processor<Document> finishRunnable = doCommit(document, file, indicator, synchronously, documentManager);
|
||||
if (finishRunnable != null) {
|
||||
finishRunnables.add(finishRunnable);
|
||||
Processor<Document> 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<Document> 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<Document> unc = Arrays.asList(documentManager.getUncommittedDocuments());
|
||||
log("after call finish commit",document, synchronously, unc, success);
|
||||
Collection<Document> 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<Document> doCommit(@NotNull final Document document,
|
||||
private Processor<Document> 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<Document>() {
|
||||
@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<CommitTask> processor) {
|
||||
final boolean[] result = {true};
|
||||
synchronized (documentsToCommit) {
|
||||
documentsToCommit.process(new Processor<CommitTask>() {
|
||||
@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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<PsiFile>() {
|
||||
@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<PsiFile>() {
|
||||
@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<Processor<Document>> finishRunnables,
|
||||
@NotNull final List<Processor<Document>> 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<Document> finishRunnable : finishRunnables) {
|
||||
for (Processor<Document> 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<Object> keys = new ArrayList<Object>(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<Document> 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);
|
||||
}
|
||||
|
||||
+1
-1
@@ -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);
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ public class TextFieldWithAutoCompletionContributor<T> extends CompletionContrib
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillCompletionVariants(CompletionParameters parameters, CompletionResultSet result) {
|
||||
public void fillCompletionVariants(final CompletionParameters parameters, CompletionResultSet result) {
|
||||
PsiFile file = parameters.getOriginalFile();
|
||||
final TextFieldWithAutoCompletionListProvider<T> provider = file.getUserData(KEY);
|
||||
|
||||
@@ -84,7 +84,7 @@ public class TextFieldWithAutoCompletionContributor<T> extends CompletionContrib
|
||||
result = result.withPrefixMatcher(prefixMatcher);
|
||||
}
|
||||
|
||||
Collection<T> items = provider.getItems(prefix, true);
|
||||
Collection<T> items = provider.getItems(prefix, true, parameters);
|
||||
addCompletionElements(result, provider, items, -10000);
|
||||
|
||||
Future<Collection<T>>
|
||||
@@ -92,7 +92,7 @@ public class TextFieldWithAutoCompletionContributor<T> extends CompletionContrib
|
||||
ApplicationManager.getApplication().executeOnPooledThread(new Callable<Collection<T>>() {
|
||||
@Override
|
||||
public Collection<T> call() {
|
||||
return provider.getItems(prefix, false);
|
||||
return provider.getItems(prefix, false, parameters);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ public abstract class TextFieldWithAutoCompletionListProvider<T> implements Comp
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Collection<T> getItems(String prefix, boolean cached) {
|
||||
public Collection<T> getItems(String prefix, boolean cached, CompletionParameters parameters) {
|
||||
if (prefix == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@@ -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<RangeMarker, HyperlinkInfo> links = new LinkedHashMap<RangeMarker, HyperlinkInfo>();
|
||||
List<RangeMarker> lineSeparators = new ArrayList<RangeMarker>();
|
||||
@@ -197,8 +197,7 @@ public class EventLog implements Notifications {
|
||||
}
|
||||
|
||||
private static String getStatusText(DocumentImpl logDoc, AtomicBoolean showMore, List<RangeMarker> lineSeparators, boolean hasHtml) {
|
||||
DocumentImpl statusDoc = new DocumentImpl(true);
|
||||
statusDoc.setText(logDoc.getText());
|
||||
DocumentImpl statusDoc = new DocumentImpl(logDoc.getText(),true);
|
||||
List<RangeMarker> statusSeparators = new ArrayList<RangeMarker>();
|
||||
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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+2
-2
@@ -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) {
|
||||
|
||||
@@ -281,7 +281,7 @@ project.module.compile.output.path=Use module compile output path
|
||||
project.compiler.output=<html><b>Project compiler output:</b><br>This path is used to store all project compilation results. <br>\
|
||||
A directory corresponding to each module is created under this path. <br>This directory contains two subdirectories: Production and Test for production code and test sources, respectively.<br>\
|
||||
A module specific compiler output path can be configured for each of the modules as required</html>
|
||||
find.usages.action.text=Find usages
|
||||
find.usages.action.text=Find Usages
|
||||
dependencies.used.in.popup.title=Used in
|
||||
project.language.level=<html><b>Project language level:</b><br>This language level is default for all project modules.<br>\
|
||||
A module specific language level can be configured for each of the modules as required</html>
|
||||
|
||||
+27
-23
@@ -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<Pair<Integer, Integer>> history)
|
||||
{
|
||||
@NotNull Stack<Pair<Integer, Integer>> 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()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -134,8 +134,9 @@ public abstract class AbstractVcsTestCase {
|
||||
return result;
|
||||
}
|
||||
|
||||
protected void initProject(final File clientRoot) throws Exception {
|
||||
final TestFixtureBuilder<IdeaProjectTestFixture> testFixtureBuilder = IdeaTestFixtureFactory.getFixtureFactory().createFixtureBuilder();
|
||||
protected void initProject(final File clientRoot, String testName) throws Exception {
|
||||
String name = getClass().getName() + "." + testName;
|
||||
final TestFixtureBuilder<IdeaProjectTestFixture> testFixtureBuilder = IdeaTestFixtureFactory.getFixtureFactory().createFixtureBuilder(name);
|
||||
myProjectFixture = testFixtureBuilder.getFixture();
|
||||
testFixtureBuilder.addModule(EmptyModuleFixtureBuilder.class).addContentRoot(clientRoot.toString());
|
||||
myProjectFixture.setUp();
|
||||
|
||||
+2
-1
@@ -38,7 +38,8 @@ public abstract class CodeInsightFixtureTestCase<T extends ModuleFixtureBuilder>
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
final TestFixtureBuilder<IdeaProjectTestFixture> projectBuilder = IdeaTestFixtureFactory.getFixtureFactory().createFixtureBuilder();
|
||||
String name = getClass().getName() + "." + getName();
|
||||
final TestFixtureBuilder<IdeaProjectTestFixture> projectBuilder = IdeaTestFixtureFactory.getFixtureFactory().createFixtureBuilder(name);
|
||||
myFixture = IdeaTestFixtureFactory.getFixtureFactory().createCodeInsightFixture(projectBuilder.getFixture());
|
||||
final T moduleFixtureBuilder = projectBuilder.addModule(getModuleBuilderClass());
|
||||
moduleFixtureBuilder.addSourceContentRoot(myFixture.getTempDirPath());
|
||||
|
||||
@@ -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();
|
||||
|
||||
+1
-2
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<String>(project, new TextFieldWithAutoCompletion.StringsCompletionProvider(null, null) {
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<String> getItems(String prefix, boolean cached) {
|
||||
public Collection<String> 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<String>(project, new TextFieldWithAutoCompletion.StringsCompletionProvider(null, null) {
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<String> getItems(String prefix, boolean cached) {
|
||||
public Collection<String> getItems(String prefix, boolean cached, CompletionParameters parameters) {
|
||||
parseExistingMessagesIfNecessary();
|
||||
setItems(Arrays.asList(myUsedPids));
|
||||
return super.getItems(prefix, cached);
|
||||
return super.getItems(prefix, cached, parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<GitTestRevision>(7);
|
||||
myRevisionsAfterRename = new ArrayList<GitTestRevision>(4);
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -127,6 +127,8 @@
|
||||
<reference id="Gradle.SyncTreeFilter.Conflict"/>
|
||||
<reference id="Gradle.SyncTreeFilter.Confirmed"/>
|
||||
</group>
|
||||
|
||||
<action id="Gradle.RebuildChangesTree" class="org.jetbrains.plugins.gradle.action.GradleRebuildStructureChangesTreeAction"/>
|
||||
</actions>
|
||||
|
||||
</idea-plugin>
|
||||
+2
-2
@@ -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());
|
||||
+1
-1
@@ -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);
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
+71
@@ -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;
|
||||
}
|
||||
}
|
||||
+1
-3
@@ -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);
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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());
|
||||
|
||||
+1
@@ -107,6 +107,7 @@ public class MavenRunnerParameters implements Cloneable {
|
||||
}
|
||||
|
||||
public void setGoals(@Nullable List<String> goals) {
|
||||
if (myGoals == goals) return; // Called from XML Serializer
|
||||
myGoals.clear();
|
||||
|
||||
if (goals != null) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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, "."));
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -41,6 +41,8 @@ public abstract class TaskManager {
|
||||
*/
|
||||
public abstract List<Task> getIssues(String query);
|
||||
|
||||
public abstract List<Task> getIssues(String query, boolean forceRequest);
|
||||
|
||||
/**
|
||||
* Returns already cached issues.
|
||||
* @return cached issues.
|
||||
|
||||
@@ -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<Task> {
|
||||
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<Task> getItems(final String prefix, final boolean cached) {
|
||||
return mySearchSupport.getItems(prefix, cached);
|
||||
public List<Task> 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());
|
||||
|
||||
@@ -39,9 +39,9 @@ public class TaskSearchSupport {
|
||||
myManager = (TaskManagerImpl)TaskManager.getManager(project);
|
||||
}
|
||||
|
||||
public List<Task> getItems(String pattern, boolean cached) {
|
||||
public List<Task> getItems(String pattern, boolean cached, boolean autopopup) {
|
||||
final Matcher matcher = getMatcher(pattern);
|
||||
return ContainerUtil.mapNotNull(getTasks(pattern, cached), new NullableFunction<Task, Task>() {
|
||||
return ContainerUtil.mapNotNull(getTasks(pattern, cached, autopopup), new NullableFunction<Task, Task>() {
|
||||
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<Task> getTasks(String pattern, boolean cached) {
|
||||
return cached ? myManager.getCachedIssues() : myManager.getIssues(pattern);
|
||||
private List<Task> getTasks(String pattern, boolean cached, boolean autopopup) {
|
||||
return cached ? myManager.getCachedIssues() : myManager.getIssues(pattern, !autopopup);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<LocalChangeList> addControls(JPanel bottomPanel, LocalChangeList initial) {
|
||||
|
||||
@@ -230,7 +230,12 @@ public class TaskManagerImpl extends TaskManager implements ProjectComponent, Pe
|
||||
|
||||
@Override
|
||||
public List<Task> getIssues(String query) {
|
||||
List<Task> tasks = getIssuesFromRepositories(query, 50, 0, true);
|
||||
return getIssues(query, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Task> getIssues(String query, boolean forceRequest) {
|
||||
List<Task> tasks = getIssuesFromRepositories(query, 50, 0, forceRequest);
|
||||
synchronized (myIssueCache) {
|
||||
myTemporaryCache.clear();
|
||||
myTemporaryCache.putAll(ContainerUtil.assignKeys(tasks.iterator(), KEY_CONVERTOR));
|
||||
|
||||
@@ -27,15 +27,15 @@ public class TaskCompletionTest extends LightCodeInsightFixtureTestCase {
|
||||
}
|
||||
|
||||
public void testTaskCompletion() throws Exception {
|
||||
doTest("<caret>", "TEST-001: Test task<caret>");
|
||||
doTest("<caret>", "TEST-001 Test task<caret>");
|
||||
}
|
||||
|
||||
public void testPrefix() throws Exception {
|
||||
doTest("TEST-<caret>", "TEST-001: Test task<caret>");
|
||||
doTest("TEST-<caret>", "TEST-001 Test task<caret>");
|
||||
}
|
||||
|
||||
public void testSecondWord() throws Exception {
|
||||
doTest("my TEST-<caret>", "my TEST-001: Test task<caret>");
|
||||
doTest("my TEST-<caret>", "my TEST-001 Test task<caret>");
|
||||
}
|
||||
|
||||
public void testNumberCompletion() throws Exception {
|
||||
@@ -63,12 +63,18 @@ public class TaskCompletionTest extends LightCodeInsightFixtureTestCase {
|
||||
}
|
||||
|
||||
public void testSIOOBE() throws Exception {
|
||||
doTest(" <caret> my", " TEST-001: Test task my");
|
||||
doTest(" <caret> 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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user