Merge remote-tracking branch 'origin/master'

This commit is contained in:
Maxim.Medvedev
2012-03-15 15:11:35 +04:00
87 changed files with 2029 additions and 967 deletions
@@ -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();
@@ -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));
@@ -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);
}
}
}
@@ -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;
}
}
@@ -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;
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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;
}
@@ -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;
}
}
@@ -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() {
@@ -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);
@@ -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();
}
@@ -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;
@@ -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);
}
@@ -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;
}
@@ -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>
@@ -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();
@@ -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();
@@ -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
+2
View File
@@ -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>
@@ -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());
@@ -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);
@@ -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);
@@ -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);
@@ -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,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());
@@ -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();
}
@@ -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);
}