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