Remove duplication in SideBySideCommentManager and UnifiedCommentManager
In the change that added the new unified diff screen [1],
SideBySideCommentManager and UnifiedCommentManager had a lot of
duplicate code. Extract common code to reduce duplication.
[1]: https://gerrit-review.googlesource.com/#/c/49171/
Change-Id: I3396d72b9146ddd4944a8226c9860022b0879c95
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentBox.java
index 4fe2914..70ef947 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentBox.java
@@ -57,7 +57,7 @@
CommentBox(CommentGroup group, CommentRange range) {
this.group = group;
if (range != null) {
- DiffScreen screen = group.getManager().getDiffScreen();
+ DiffScreen screen = group.getManager().host;
int startCmLine =
screen.getCmLine(range.startLine() - 1, group.getSide());
int endCmLine = screen.getCmLine(range.endLine() - 1, group.getSide());
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentManager.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentManager.java
index 7f4e446..a26b1ce 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentManager.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentManager.java
@@ -14,6 +14,7 @@
package com.google.gerrit.client.diff;
+import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.changes.CommentInfo;
import com.google.gerrit.client.patches.SkippedLine;
import com.google.gerrit.client.rpc.CallbackGroup;
@@ -27,11 +28,15 @@
import net.codemirror.lib.Pos;
import net.codemirror.lib.TextMarker.FromTo;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
/** Tracks comment widgets for {@link DiffScreen}. */
abstract class CommentManager {
@@ -39,19 +44,23 @@
private final PatchSet.Id revision;
private final String path;
private final CommentLinkProcessor commentLinkProcessor;
-
+ final SortedMap<Integer, CommentGroup> sideA;
+ final SortedMap<Integer, CommentGroup> sideB;
private final Map<String, PublishedBox> published;
private final Set<DraftBox> unsavedDrafts;
+ final DiffScreen host;
private boolean attached;
private boolean expandAll;
private boolean open;
CommentManager(
+ DiffScreen host,
PatchSet.Id base,
PatchSet.Id revision,
String path,
CommentLinkProcessor clp,
boolean open) {
+ this.host = host;
this.base = base;
this.revision = revision;
this.path = path;
@@ -60,6 +69,8 @@
published = new HashMap<>();
unsavedDrafts = new HashSet<>();
+ sideA = new TreeMap<>();
+ sideB = new TreeMap<>();
}
void setAttached(boolean attached) {
@@ -142,27 +153,290 @@
return fromTo;
}
- abstract void insertNewDraft(DisplaySide side, int line);
+ abstract CommentGroup group(DisplaySide side, int cmLinePlusOne);
- abstract Runnable newDraftCallback(final CodeMirror cm);
+ /**
+ * Create a new {@link DraftBox} at the specified line and focus it.
+ *
+ * @param side which side the draft will appear on.
+ * @param line the line the draft will be at. Lines are 1-based. Line 0 is a
+ * special case creating a file level comment.
+ */
+ void insertNewDraft(DisplaySide side, int line) {
+ if (line == 0) {
+ host.skipManager.ensureFirstLineIsVisible();
+ }
- abstract DraftBox addDraftBox(DisplaySide side, CommentInfo info);
+ CommentGroup group = group(side, line);
+ if (0 < group.getBoxCount()) {
+ CommentBox last = group.getCommentBox(group.getBoxCount() - 1);
+ if (last instanceof DraftBox) {
+ ((DraftBox)last).setEdit(true);
+ } else {
+ ((PublishedBox)last).doReply();
+ }
+ } else {
+ addDraftBox(side, CommentInfo.create(
+ getPath(),
+ getStoredSideFromDisplaySide(side),
+ line,
+ null)).setEdit(true);
+ }
+ }
- abstract void setExpandAllComments(boolean b);
+ abstract String getTokenSuffixForActiveLine(CodeMirror cm);
- abstract Runnable commentNav(CodeMirror src, Direction dir);
+ Runnable signInCallback(final CodeMirror cm) {
+ return new Runnable() {
+ @Override
+ public void run() {
+ String token = host.getToken();
+ if (cm.extras().hasActiveLine()) {
+ token += "@" + getTokenSuffixForActiveLine(cm);
+ }
+ Gerrit.doSignIn(token);
+ }
+ };
+ }
- abstract void clearLine(DisplaySide side, int line, CommentGroup group);
+ abstract void newDraft(CodeMirror cm);
- abstract void renderPublished(DisplaySide forSide, JsArray<CommentInfo> in);
+ Runnable newDraftCallback(final CodeMirror cm) {
+ if (!Gerrit.isSignedIn()) {
+ return signInCallback(cm);
+ }
- abstract List<SkippedLine> splitSkips(int context, List<SkippedLine> skips);
+ return new Runnable() {
+ @Override
+ public void run() {
+ if (cm.extras().hasActiveLine()) {
+ newDraft(cm);
+ }
+ }
+ };
+ }
- abstract void newDraftOnGutterClick(CodeMirror cm, String gutterClass, int line);
+ DraftBox addDraftBox(DisplaySide side, CommentInfo info) {
+ int cmLinePlusOne = host.getCmLine(info.line() - 1, side) + 1;
+ CommentGroup group = group(side, cmLinePlusOne);
+ DraftBox box = new DraftBox(
+ group,
+ getCommentLinkProcessor(),
+ getPatchSetIdFromSide(side),
+ info,
+ isExpandAll());
- abstract Runnable toggleOpenBox(final CodeMirror cm);
+ if (info.inReplyTo() != null) {
+ PublishedBox r = getPublished().get(info.inReplyTo());
+ if (r != null) {
+ r.setReplyBox(box);
+ }
+ }
- abstract Runnable openCloseAll(final CodeMirror cm);
+ group.add(box);
+ box.setAnnotation(host.getDiffTable().scrollbar.draft(
+ host.getCmFromSide(side),
+ Math.max(0, cmLinePlusOne - 1)));
+ return box;
+ }
- abstract DiffScreen getDiffScreen();
+ void setExpandAllComments(boolean b) {
+ setExpandAll(b);
+ for (CommentGroup g : sideA.values()) {
+ g.setOpenAll(b);
+ }
+ for (CommentGroup g : sideB.values()) {
+ g.setOpenAll(b);
+ }
+ }
+
+ abstract SortedMap<Integer, CommentGroup> getMapForNav(DisplaySide side);
+
+ Runnable commentNav(final CodeMirror src, final Direction dir) {
+ return new Runnable() {
+ @Override
+ public void run() {
+ // Every comment appears in both side maps as a linked pair.
+ // It is only necessary to search one side to find a comment
+ // on either side of the editor pair.
+ SortedMap<Integer, CommentGroup> map = getMapForNav(src.side());
+ int line = src.extras().hasActiveLine()
+ ? src.getLineNumber(src.extras().activeLine()) + 1
+ : 0;
+
+ CommentGroup g;
+ if (dir == Direction.NEXT) {
+ map = map.tailMap(line + 1);
+ if (map.isEmpty()) {
+ return;
+ }
+ g = map.get(map.firstKey());
+ while (g.getBoxCount() == 0) {
+ map = map.tailMap(map.firstKey() + 1);
+ if (map.isEmpty()) {
+ return;
+ }
+ g = map.get(map.firstKey());
+ }
+ } else {
+ map = map.headMap(line);
+ if (map.isEmpty()) {
+ return;
+ }
+ g = map.get(map.lastKey());
+ while (g.getBoxCount() == 0) {
+ map = map.headMap(map.lastKey());
+ if (map.isEmpty()) {
+ return;
+ }
+ g = map.get(map.lastKey());
+ }
+ }
+
+ CodeMirror cm = g.getCm();
+ double y = cm.heightAtLine(g.getLine() - 1, "local");
+ cm.setCursor(Pos.create(g.getLine() - 1));
+ cm.scrollToY(y - 0.5 * cm.scrollbarV().getClientHeight());
+ cm.focus();
+ }
+ };
+ }
+
+ void clearLine(DisplaySide side, int line, CommentGroup group) {
+ SortedMap<Integer, CommentGroup> map = map(side);
+ if (map.get(line) == group) {
+ map.remove(line);
+ }
+ }
+
+ void render(CommentsCollections in, boolean expandAll) {
+ if (in.publishedBase != null) {
+ renderPublished(DisplaySide.A, in.publishedBase);
+ }
+ if (in.publishedRevision != null) {
+ renderPublished(DisplaySide.B, in.publishedRevision);
+ }
+ if (in.draftsBase != null) {
+ renderDrafts(DisplaySide.A, in.draftsBase);
+ }
+ if (in.draftsRevision != null) {
+ renderDrafts(DisplaySide.B, in.draftsRevision);
+ }
+ if (expandAll) {
+ setExpandAllComments(true);
+ }
+ for (CommentGroup g : sideA.values()) {
+ g.init(host.getDiffTable());
+ }
+ for (CommentGroup g : sideB.values()) {
+ g.init(host.getDiffTable());
+ g.handleRedraw();
+ }
+ setAttached(true);
+ }
+
+ void renderPublished(DisplaySide forSide, JsArray<CommentInfo> in) {
+ for (CommentInfo info : Natives.asList(in)) {
+ DisplaySide side = displaySide(info, forSide);
+ if (side != null) {
+ int cmLinePlusOne = host.getCmLine(info.line() - 1, side) + 1;
+ CommentGroup group = group(side, cmLinePlusOne);
+ PublishedBox box = new PublishedBox(
+ group,
+ getCommentLinkProcessor(),
+ getPatchSetIdFromSide(side),
+ info,
+ side,
+ isOpen());
+ group.add(box);
+ box.setAnnotation(host.getDiffTable().scrollbar.comment(
+ host.getCmFromSide(side),
+ cmLinePlusOne - 1));
+ getPublished().put(info.id(), box);
+ }
+ }
+ }
+
+ abstract Collection<Integer> getLinesWithCommentGroups();
+
+ private static void checkAndAddSkip(List<SkippedLine> out, SkippedLine s) {
+ if (s.getSize() > 1) {
+ out.add(s);
+ }
+ }
+
+ List<SkippedLine> splitSkips(int context, List<SkippedLine> skips) {
+ if (sideA.containsKey(0) || sideB.containsKey(0)) {
+ // Special case of file comment; cannot skip first line.
+ for (SkippedLine skip : skips) {
+ if (skip.getStartA() == 0) {
+ skip.incrementStart(1);
+ break;
+ }
+ }
+ }
+
+ for (int boxLine : getLinesWithCommentGroups()) {
+ List<SkippedLine> temp = new ArrayList<>(skips.size() + 2);
+ for (SkippedLine skip : skips) {
+ int startLine = host.getCmLine(skip.getStartB(), DisplaySide.B);
+ int deltaBefore = boxLine - startLine;
+ int deltaAfter = startLine + skip.getSize() - boxLine;
+ if (deltaBefore < -context || deltaAfter < -context) {
+ temp.add(skip); // Size guaranteed to be greater than 1
+ } else if (deltaBefore > context && deltaAfter > context) {
+ SkippedLine before = new SkippedLine(
+ skip.getStartA(), skip.getStartB(),
+ skip.getSize() - deltaAfter - context);
+ skip.incrementStart(deltaBefore + context);
+ checkAndAddSkip(temp, before);
+ checkAndAddSkip(temp, skip);
+ } else if (deltaAfter > context) {
+ skip.incrementStart(deltaBefore + context);
+ checkAndAddSkip(temp, skip);
+ } else if (deltaBefore > context) {
+ skip.reduceSize(deltaAfter + context);
+ checkAndAddSkip(temp, skip);
+ }
+ }
+ if (temp.isEmpty()) {
+ return temp;
+ }
+ skips = temp;
+ }
+ return skips;
+ }
+
+ abstract void newDraftOnGutterClick(CodeMirror cm, String gutterClass,
+ int line);
+
+ abstract CommentGroup getCommentGroupOnActiveLine(CodeMirror cm);
+
+ Runnable toggleOpenBox(final CodeMirror cm) {
+ return new Runnable() {
+ @Override
+ public void run() {
+ CommentGroup group = getCommentGroupOnActiveLine(cm);
+ if (group != null) {
+ group.openCloseLast();
+ }
+ }
+ };
+ }
+
+ Runnable openCloseAll(final CodeMirror cm) {
+ return new Runnable() {
+ @Override
+ public void run() {
+ CommentGroup group = getCommentGroupOnActiveLine(cm);
+ if (group != null) {
+ group.openCloseAll();
+ }
+ }
+ };
+ }
+
+ SortedMap<Integer, CommentGroup> map(DisplaySide side) {
+ return side == DisplaySide.A ? sideA : sideB;
+ }
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DisplaySide.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DisplaySide.java
index 4dfcd8c..c7ee678 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DisplaySide.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DisplaySide.java
@@ -16,5 +16,9 @@
/** Enum representing the side on a side-by-side view */
public enum DisplaySide {
- A, B
+ A, B;
+
+ DisplaySide otherSide() {
+ return this == A ? B : A;
+ }
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PublishedBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PublishedBox.java
index e497e8b..dc2e3a2 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PublishedBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PublishedBox.java
@@ -161,7 +161,7 @@
void doReply() {
if (!Gerrit.isSignedIn()) {
- Gerrit.doSignIn(getCommentManager().getDiffScreen().getToken());
+ Gerrit.doSignIn(getCommentManager().host.getToken());
} else if (replyBox == null) {
addReplyBox(false);
} else {
@@ -179,7 +179,7 @@
void onQuote(ClickEvent e) {
e.stopPropagation();
if (!Gerrit.isSignedIn()) {
- Gerrit.doSignIn(getCommentManager().getDiffScreen().getToken());
+ Gerrit.doSignIn(getCommentManager().host.getToken());
}
addReplyBox(true);
}
@@ -188,7 +188,7 @@
void onReplyDone(ClickEvent e) {
e.stopPropagation();
if (!Gerrit.isSignedIn()) {
- Gerrit.doSignIn(getCommentManager().getDiffScreen().getToken());
+ Gerrit.doSignIn(getCommentManager().host.getToken());
} else if (replyBox == null) {
done.setEnabled(false);
CommentInfo input = CommentInfo.createReply(comment);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideCommentManager.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideCommentManager.java
index e18f7a5..a2af3a1c 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideCommentManager.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySideCommentManager.java
@@ -16,321 +16,67 @@
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.changes.CommentInfo;
-import com.google.gerrit.client.patches.SkippedLine;
-import com.google.gerrit.client.rpc.Natives;
import com.google.gerrit.client.ui.CommentLinkProcessor;
import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gwt.core.client.JsArray;
import net.codemirror.lib.CodeMirror;
-import net.codemirror.lib.CodeMirror.LineHandle;
-import net.codemirror.lib.Pos;
import net.codemirror.lib.TextMarker.FromTo;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Collection;
import java.util.SortedMap;
-import java.util.TreeMap;
/** Tracks comment widgets for {@link SideBySide}. */
class SideBySideCommentManager extends CommentManager {
- private final SideBySide host;
- private final SortedMap<Integer, SideBySideCommentGroup> sideA;
- private final SortedMap<Integer, SideBySideCommentGroup> sideB;
-
SideBySideCommentManager(SideBySide host,
PatchSet.Id base, PatchSet.Id revision,
String path,
CommentLinkProcessor clp,
boolean open) {
- super(base, revision, path, clp, open);
-
- this.host = host;
- sideA = new TreeMap<>();
- sideB = new TreeMap<>();
+ super(host, base, revision, path, clp, open);
}
@Override
- SideBySide getDiffScreen() {
- return host;
- }
-
- @Override
- void setExpandAllComments(boolean b) {
- setExpandAll(b);
- for (SideBySideCommentGroup g : sideA.values()) {
- g.setOpenAll(b);
- }
- for (SideBySideCommentGroup g : sideB.values()) {
- g.setOpenAll(b);
- }
- }
-
- @Override
- Runnable commentNav(final CodeMirror src, final Direction dir) {
- return new Runnable() {
- @Override
- public void run() {
- // Every comment appears in both side maps as a linked pair.
- // It is only necessary to search one side to find a comment
- // on either side of the editor pair.
- SortedMap<Integer, SideBySideCommentGroup> map = map(src.side());
- int line = src.extras().hasActiveLine()
- ? src.getLineNumber(src.extras().activeLine()) + 1
- : 0;
- if (dir == Direction.NEXT) {
- map = map.tailMap(line + 1);
- if (map.isEmpty()) {
- return;
- }
- line = map.firstKey();
- } else {
- map = map.headMap(line);
- if (map.isEmpty()) {
- return;
- }
- line = map.lastKey();
- }
-
- SideBySideCommentGroup g = map.get(line);
- if (g.getBoxCount() == 0) {
- g = g.getPeer();
- }
-
- CodeMirror cm = g.getCm();
- double y = cm.heightAtLine(g.getLine() - 1, "local");
- cm.setCursor(Pos.create(g.getLine() - 1));
- cm.scrollToY(y - 0.5 * cm.scrollbarV().getClientHeight());
- cm.focus();
- }
- };
- }
-
- void render(CommentsCollections in, boolean expandAll) {
- if (in.publishedBase != null) {
- renderPublished(DisplaySide.A, in.publishedBase);
- }
- if (in.publishedRevision != null) {
- renderPublished(DisplaySide.B, in.publishedRevision);
- }
- if (in.draftsBase != null) {
- renderDrafts(DisplaySide.A, in.draftsBase);
- }
- if (in.draftsRevision != null) {
- renderDrafts(DisplaySide.B, in.draftsRevision);
- }
- if (expandAll) {
- setExpandAllComments(true);
- }
- for (SideBySideCommentGroup g : sideA.values()) {
- g.init(host.getDiffTable());
- }
- for (SideBySideCommentGroup g : sideB.values()) {
- g.init(host.getDiffTable());
- g.handleRedraw();
- }
- setAttached(true);
- }
-
- @Override
- void renderPublished(DisplaySide forSide, JsArray<CommentInfo> in) {
- for (CommentInfo info : Natives.asList(in)) {
- DisplaySide side = displaySide(info, forSide);
- if (side != null) {
- SideBySideCommentGroup group = group(side, info.line());
- PublishedBox box = new PublishedBox(
- group,
- getCommentLinkProcessor(),
- getPatchSetIdFromSide(side),
- info,
- side,
- isOpen());
- group.add(box);
- box.setAnnotation(host.getDiffTable().scrollbar.comment(
- host.getCmFromSide(side),
- Math.max(0, info.line() - 1)));
- getPublished().put(info.id(), box);
- }
- }
- }
-
- @Override
- void newDraftOnGutterClick(CodeMirror cm, String gutterClass, int line) {
- insertNewDraft(cm.side(), line);
- }
-
- /**
- * Create a new {@link DraftBox} at the specified line and focus it.
- *
- * @param side which side the draft will appear on.
- * @param line the line the draft will be at. Lines are 1-based. Line 0 is a
- * special case creating a file level comment.
- */
- @Override
- void insertNewDraft(DisplaySide side, int line) {
- if (line == 0) {
- host.skipManager.ensureFirstLineIsVisible();
- }
-
- SideBySideCommentGroup group = group(side, line);
- if (0 < group.getBoxCount()) {
- CommentBox last = group.getCommentBox(group.getBoxCount() - 1);
- if (last instanceof DraftBox) {
- ((DraftBox)last).setEdit(true);
- } else {
- ((PublishedBox)last).doReply();
- }
- } else {
- addDraftBox(side, CommentInfo.create(
- getPath(),
- getStoredSideFromDisplaySide(side),
- line,
- null)).setEdit(true);
- }
- }
-
- @Override
- DraftBox addDraftBox(DisplaySide side, CommentInfo info) {
- SideBySideCommentGroup group = group(side, info.line());
- DraftBox box = new DraftBox(
- group,
- getCommentLinkProcessor(),
- getPatchSetIdFromSide(side),
- info,
- isExpandAll());
-
- if (info.inReplyTo() != null) {
- PublishedBox r = getPublished().get(info.inReplyTo());
- if (r != null) {
- r.setReplyBox(box);
- }
- }
-
- group.add(box);
- box.setAnnotation(host.getDiffTable().scrollbar.draft(
- host.getCmFromSide(side),
- Math.max(0, info.line() - 1)));
- return box;
- }
-
- @Override
- List<SkippedLine> splitSkips(int context, List<SkippedLine> skips) {
- if (sideB.containsKey(0)) {
- // Special case of file comment; cannot skip first line.
- for (SkippedLine skip : skips) {
- if (skip.getStartB() == 0) {
- skip.incrementStart(1);
- }
- }
- }
-
- // TODO: This is not optimal, but shouldn't be too costly in most cases.
- // Maybe rewrite after done keeping track of diff chunk positions.
- for (int boxLine : sideB.tailMap(1).keySet()) {
- List<SkippedLine> temp = new ArrayList<>(skips.size() + 2);
- for (SkippedLine skip : skips) {
- int startLine = skip.getStartB();
- int deltaBefore = boxLine - startLine;
- int deltaAfter = startLine + skip.getSize() - boxLine;
- if (deltaBefore < -context || deltaAfter < -context) {
- temp.add(skip); // Size guaranteed to be greater than 1
- } else if (deltaBefore > context && deltaAfter > context) {
- SkippedLine before = new SkippedLine(
- skip.getStartA(), skip.getStartB(),
- skip.getSize() - deltaAfter - context);
- skip.incrementStart(deltaBefore + context);
- checkAndAddSkip(temp, before);
- checkAndAddSkip(temp, skip);
- } else if (deltaAfter > context) {
- skip.incrementStart(deltaBefore + context);
- checkAndAddSkip(temp, skip);
- } else if (deltaBefore > context) {
- skip.reduceSize(deltaAfter + context);
- checkAndAddSkip(temp, skip);
- }
- }
- if (temp.isEmpty()) {
- return temp;
- }
- skips = temp;
- }
- return skips;
- }
-
- private static void checkAndAddSkip(List<SkippedLine> out, SkippedLine s) {
- if (s.getSize() > 1) {
- out.add(s);
- }
+ SortedMap<Integer, CommentGroup> getMapForNav(DisplaySide side) {
+ return map(side);
}
@Override
void clearLine(DisplaySide side, int line, CommentGroup group) {
- SortedMap<Integer, SideBySideCommentGroup> map = map(side);
- if (map.get(line) == group) {
- map.remove(line);
- }
+ super.clearLine(side, line, group);
}
@Override
- Runnable toggleOpenBox(final CodeMirror cm) {
- return new Runnable() {
- @Override
- public void run() {
- if (cm.extras().hasActiveLine()) {
- SideBySideCommentGroup w = map(cm.side()).get(
- cm.getLineNumber(cm.extras().activeLine()) + 1);
- if (w != null) {
- w.openCloseLast();
- }
- }
- }
- };
- }
-
- @Override
- Runnable openCloseAll(final CodeMirror cm) {
- return new Runnable() {
- @Override
- public void run() {
- if (cm.extras().hasActiveLine()) {
- SideBySideCommentGroup w = map(cm.side()).get(
- cm.getLineNumber(cm.extras().activeLine()) + 1);
- if (w != null) {
- w.openCloseAll();
- }
- }
- }
- };
- }
-
- @Override
- Runnable newDraftCallback(final CodeMirror cm) {
+ void newDraftOnGutterClick(CodeMirror cm, String gutterClass, int line) {
if (!Gerrit.isSignedIn()) {
- return new Runnable() {
- @Override
- public void run() {
- String token = host.getToken();
- if (cm.extras().hasActiveLine()) {
- LineHandle handle = cm.extras().activeLine();
- int line = cm.getLineNumber(handle) + 1;
- token += "@" + (cm.side() == DisplaySide.A ? "a" : "") + line;
- }
- Gerrit.doSignIn(token);
- }
- };
+ signInCallback(cm).run();
+ } else {
+ insertNewDraft(cm.side(), line);
}
-
- return new Runnable() {
- @Override
- public void run() {
- if (cm.extras().hasActiveLine()) {
- newDraft(cm);
- }
- }
- };
}
- private void newDraft(CodeMirror cm) {
+ @Override
+ CommentGroup getCommentGroupOnActiveLine(CodeMirror cm) {
+ CommentGroup group = null;
+ if (cm.extras().hasActiveLine()) {
+ group = map(cm.side())
+ .get(cm.getLineNumber(cm.extras().activeLine()) + 1);
+ }
+ return group;
+ }
+
+ @Override
+ Collection<Integer> getLinesWithCommentGroups() {
+ return sideB.tailMap(1).keySet();
+ }
+
+ @Override
+ String getTokenSuffixForActiveLine(CodeMirror cm) {
+ return (cm.side() == DisplaySide.A ? "a" : "")
+ + (cm.getLineNumber(cm.extras().activeLine()) + 1);
+ }
+
+ @Override
+ void newDraft(CodeMirror cm) {
int line = cm.getLineNumber(cm.extras().activeLine()) + 1;
if (cm.somethingSelected()) {
FromTo fromTo = adjustSelection(cm);
@@ -346,8 +92,9 @@
}
}
- private SideBySideCommentGroup group(DisplaySide side, int line) {
- SideBySideCommentGroup w = map(side).get(line);
+ @Override
+ CommentGroup group(DisplaySide side, int line) {
+ SideBySideCommentGroup w = (SideBySideCommentGroup) map(side).get(line);
if (w != null) {
return w;
}
@@ -382,8 +129,4 @@
private SideBySideCommentGroup newGroup(DisplaySide side, int line) {
return new SideBySideCommentGroup(this, host.getCmFromSide(side), side, line);
}
-
- private SortedMap<Integer, SideBySideCommentGroup> map(DisplaySide side) {
- return side == DisplaySide.A ? sideA : sideB;
- }
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedCommentManager.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedCommentManager.java
index 9fae92c..8968bc7 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedCommentManager.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedCommentManager.java
@@ -19,334 +19,116 @@
import com.google.gerrit.client.diff.LineMapper.LineOnOtherInfo;
import com.google.gerrit.client.diff.UnifiedChunkManager.LineRegionInfo;
import com.google.gerrit.client.diff.UnifiedChunkManager.RegionType;
-import com.google.gerrit.client.patches.SkippedLine;
-import com.google.gerrit.client.rpc.Natives;
import com.google.gerrit.client.ui.CommentLinkProcessor;
import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gwt.core.client.JsArray;
import net.codemirror.lib.CodeMirror;
-import net.codemirror.lib.CodeMirror.LineHandle;
import net.codemirror.lib.Pos;
import net.codemirror.lib.TextMarker.FromTo;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
-import java.util.TreeSet;
/** Tracks comment widgets for {@link Unified}. */
class UnifiedCommentManager extends CommentManager {
- private final Unified host;
- private final SortedMap<Integer, UnifiedCommentGroup> sideA;
- private final SortedMap<Integer, UnifiedCommentGroup> sideB;
+
+ private final SortedMap<Integer, CommentGroup> mergedMap;
+
+ // In Unified, a CodeMirror line can have up to two CommentGroups - one for
+ // the base side and one for the revision, so we need to keep track of the
+ // duplicates and replace the entries in mergedMap on draft removal.
+ private final Map<Integer, CommentGroup> duplicates;
UnifiedCommentManager(Unified host,
PatchSet.Id base, PatchSet.Id revision,
String path,
CommentLinkProcessor clp,
boolean open) {
- super(base, revision, path, clp, open);
-
- this.host = host;
- sideA = new TreeMap<>();
- sideB = new TreeMap<>();
+ super(host, base, revision, path, clp, open);
+ mergedMap = new TreeMap<>();
+ duplicates = new HashMap<>();
}
@Override
- Unified getDiffScreen() {
- return host;
+ SortedMap<Integer, CommentGroup> getMapForNav(DisplaySide side) {
+ return mergedMap;
}
@Override
- void setExpandAllComments(boolean b) {
- setExpandAll(b);
- for (UnifiedCommentGroup g : sideA.values()) {
- g.setOpenAll(b);
- }
- for (UnifiedCommentGroup g : sideB.values()) {
- g.setOpenAll(b);
- }
- }
+ void clearLine(DisplaySide side, int line, CommentGroup group) {
+ super.clearLine(side, line, group);
- @Override
- Runnable commentNav(final CodeMirror src, final Direction dir) {
- return new Runnable() {
- @Override
- public void run() {
- SortedMap<Integer, UnifiedCommentGroup> map = map(src.side());
- int line = src.extras().hasActiveLine()
- ? src.getLineNumber(src.extras().activeLine()) + 1
- : 0;
- if (dir == Direction.NEXT) {
- map = map.tailMap(line + 1);
- if (map.isEmpty()) {
- return;
- }
- line = map.firstKey();
- } else {
- map = map.headMap(line);
- if (map.isEmpty()) {
- return;
- }
- line = map.lastKey();
- }
-
- UnifiedCommentGroup g = map.get(line);
- CodeMirror cm = g.getCm();
- double y = cm.heightAtLine(g.getLine() - 1, "local");
- cm.setCursor(Pos.create(g.getLine() - 1));
- cm.scrollToY(y - 0.5 * cm.scrollbarV().getClientHeight());
- cm.focus();
- }
- };
- }
-
- void render(CommentsCollections in, boolean expandAll) {
- if (in.publishedBase != null) {
- renderPublished(DisplaySide.A, in.publishedBase);
- }
- if (in.publishedRevision != null) {
- renderPublished(DisplaySide.B, in.publishedRevision);
- }
- if (in.draftsBase != null) {
- renderDrafts(DisplaySide.A, in.draftsBase);
- }
- if (in.draftsRevision != null) {
- renderDrafts(DisplaySide.B, in.draftsRevision);
- }
- if (expandAll) {
- setExpandAllComments(true);
- }
- for (CommentGroup g : sideA.values()) {
- g.init(host.getDiffTable());
- }
- for (CommentGroup g : sideB.values()) {
- g.init(host.getDiffTable());
- g.handleRedraw();
- }
- setAttached(true);
- }
-
- @Override
- void renderPublished(DisplaySide forSide, JsArray<CommentInfo> in) {
- for (CommentInfo info : Natives.asList(in)) {
- DisplaySide side = displaySide(info, forSide);
- if (side != null) {
- int cmLinePlusOne = host.getCmLine(info.line() - 1, side) + 1;
- UnifiedCommentGroup group = group(side, cmLinePlusOne);
- PublishedBox box = new PublishedBox(
- group,
- getCommentLinkProcessor(),
- getPatchSetIdFromSide(side),
- info,
- side,
- isOpen());
- group.add(box);
- box.setAnnotation(getDiffScreen().getDiffTable().scrollbar.comment(
- host.getCm(),
- cmLinePlusOne));
- getPublished().put(info.id(), box);
+ if (mergedMap.get(line) == group) {
+ mergedMap.remove(line);
+ if (duplicates.containsKey(line)) {
+ mergedMap.put(line, duplicates.remove(line));
}
}
}
@Override
- void newDraftOnGutterClick(CodeMirror cm, String gutterClass, int cmLinePlusOne) {
- DisplaySide side = gutterClass.equals(UnifiedTable.style.lineNumbersLeft())
- ? DisplaySide.A
- : DisplaySide.B;
- insertNewDraft(side, cmLinePlusOne);
- }
-
- /**
- * Create a new {@link DraftBox} at the specified line and focus it.
- *
- * @param side which side the draft will appear on.
- * @param cmLinePlusOne the line the draft will be at, plus one.
- * Lines are 1-based. Line 0 is a special case creating a file level comment.
- */
- @Override
- void insertNewDraft(DisplaySide side, int cmLinePlusOne) {
- if (cmLinePlusOne == 0) {
- getDiffScreen().skipManager.ensureFirstLineIsVisible();
- }
-
- CommentGroup group = group(side, cmLinePlusOne);
- if (0 < group.getBoxCount()) {
- CommentBox last = group.getCommentBox(group.getBoxCount() - 1);
- if (last instanceof DraftBox) {
- ((DraftBox)last).setEdit(true);
- } else {
- ((PublishedBox)last).doReply();
- }
+ void newDraftOnGutterClick(CodeMirror cm, String gutterClass,
+ int cmLinePlusOne) {
+ if (!Gerrit.isSignedIn()) {
+ signInCallback(cm).run();
} else {
- LineRegionInfo info = host.getLineRegionInfoFromCmLine(cmLinePlusOne - 1);
+ LineRegionInfo info =
+ ((Unified) host).getLineRegionInfoFromCmLine(cmLinePlusOne - 1);
+ DisplaySide side =
+ gutterClass.equals(UnifiedTable.style.lineNumbersLeft())
+ ? DisplaySide.A
+ : DisplaySide.B;
int line = info.line;
if (info.getSide() != side) {
line = host.lineOnOther(info.getSide(), line).getLine();
}
- addDraftBox(side, CommentInfo.create(
- getPath(),
- getStoredSideFromDisplaySide(side),
- line + 1,
- null)).setEdit(true);
+ insertNewDraft(side, line + 1);
}
}
@Override
- DraftBox addDraftBox(DisplaySide side, CommentInfo info) {
- int cmLinePlusOne = host.getCmLine(info.line() - 1, side) + 1;
- UnifiedCommentGroup group = group(side, cmLinePlusOne);
- DraftBox box = new DraftBox(
- group,
- getCommentLinkProcessor(),
- getPatchSetIdFromSide(side),
- info,
- isExpandAll());
-
- if (info.inReplyTo() != null) {
- PublishedBox r = getPublished().get(info.inReplyTo());
- if (r != null) {
- r.setReplyBox(box);
- }
+ CommentGroup getCommentGroupOnActiveLine(CodeMirror cm) {
+ CommentGroup group = null;
+ if (cm.extras().hasActiveLine()) {
+ int cmLinePlusOne = cm.getLineNumber(cm.extras().activeLine()) + 1;
+ LineRegionInfo info =
+ ((Unified) host).getLineRegionInfoFromCmLine(cmLinePlusOne - 1);
+ CommentGroup forSide = map(info.getSide()).get(cmLinePlusOne);
+ group = forSide == null
+ ? map(info.getSide().otherSide()).get(cmLinePlusOne)
+ : forSide;
}
-
- group.add(box);
- box.setAnnotation(getDiffScreen().getDiffTable().scrollbar.draft(
- host.getCm(),
- cmLinePlusOne));
- return box;
+ return group;
}
@Override
- List<SkippedLine> splitSkips(int context, List<SkippedLine> skips) {
- if (sideA.containsKey(0) || sideB.containsKey(0)) {
- // Special case of file comment; cannot skip first line.
- for (SkippedLine skip : skips) {
- if (skip.getStartA() == 0) {
- skip.incrementStart(1);
- }
- }
- }
-
- TreeSet<Integer> allBoxLines = new TreeSet<>(sideA.tailMap(1).keySet());
- allBoxLines.addAll(sideB.tailMap(1).keySet());
- for (int boxLine : allBoxLines) {
- List<SkippedLine> temp = new ArrayList<>(skips.size() + 2);
- for (SkippedLine skip : skips) {
- int startLine = host.getCmLine(skip.getStartA(), DisplaySide.A);
- int deltaBefore = boxLine - startLine;
- int deltaAfter = startLine + skip.getSize() - boxLine;
- if (deltaBefore < -context || deltaAfter < -context) {
- temp.add(skip); // Size guaranteed to be greater than 1
- } else if (deltaBefore > context && deltaAfter > context) {
- SkippedLine before = new SkippedLine(
- skip.getStartA(), skip.getStartB(),
- skip.getSize() - deltaAfter - context);
- skip.incrementStart(deltaBefore + context);
- checkAndAddSkip(temp, before);
- checkAndAddSkip(temp, skip);
- } else if (deltaAfter > context) {
- skip.incrementStart(deltaBefore + context);
- checkAndAddSkip(temp, skip);
- } else if (deltaBefore > context) {
- skip.reduceSize(deltaAfter + context);
- checkAndAddSkip(temp, skip);
- }
- }
- if (temp.isEmpty()) {
- return temp;
- }
- skips = temp;
- }
- return skips;
- }
-
- private static void checkAndAddSkip(List<SkippedLine> out, SkippedLine s) {
- if (s.getSize() > 1) {
- out.add(s);
- }
+ Collection<Integer> getLinesWithCommentGroups() {
+ return mergedMap.tailMap(1).keySet();
}
@Override
- void clearLine(DisplaySide side, int cmLinePlusOne, CommentGroup group) {
- SortedMap<Integer, UnifiedCommentGroup> map = map(side);
- if (map.get(cmLinePlusOne) == group) {
- map.remove(cmLinePlusOne);
- }
+ String getTokenSuffixForActiveLine(CodeMirror cm) {
+ int cmLinePlusOne = cm.getLineNumber(cm.extras().activeLine()) + 1;
+ LineRegionInfo info =
+ ((Unified) host).getLineRegionInfoFromCmLine(cmLinePlusOne - 1);
+ return (info.getSide() == DisplaySide.A ? "a" : "") + cmLinePlusOne;
}
@Override
- Runnable toggleOpenBox(final CodeMirror cm) {
- return new Runnable() {
- @Override
- public void run() {
- if (cm.extras().hasActiveLine()) {
- UnifiedCommentGroup w = map(cm.side()).get(
- cm.getLineNumber(cm.extras().activeLine()) + 1);
- if (w != null) {
- w.openCloseLast();
- }
- }
- }
- };
- }
-
- @Override
- Runnable openCloseAll(final CodeMirror cm) {
- return new Runnable() {
- @Override
- public void run() {
- if (cm.extras().hasActiveLine()) {
- CommentGroup w = map(cm.side()).get(
- cm.getLineNumber(cm.extras().activeLine()) + 1);
- if (w != null) {
- w.openCloseAll();
- }
- }
- }
- };
- }
-
- @Override
- Runnable newDraftCallback(final CodeMirror cm) {
- if (!Gerrit.isSignedIn()) {
- return new Runnable() {
- @Override
- public void run() {
- String token = host.getToken();
- if (cm.extras().hasActiveLine()) {
- LineHandle handle = cm.extras().activeLine();
- int line = cm.getLineNumber(handle) + 1;
- token += "@" + line;
- }
- Gerrit.doSignIn(token);
- }
- };
- }
-
- return new Runnable() {
- @Override
- public void run() {
- if (cm.extras().hasActiveLine()) {
- newDraft(cm);
- }
- }
- };
- }
-
- private void newDraft(CodeMirror cm) {
+ void newDraft(CodeMirror cm) {
if (cm.somethingSelected()) {
FromTo fromTo = adjustSelection(cm);
Pos from = fromTo.from();
Pos to = fromTo.to();
- UnifiedChunkManager manager = host.getChunkManager();
+ Unified unified = (Unified) host;
+ UnifiedChunkManager manager = unified.getChunkManager();
LineRegionInfo fromInfo =
- host.getLineRegionInfoFromCmLine(from.line());
+ unified.getLineRegionInfoFromCmLine(from.line());
LineRegionInfo toInfo =
- host.getLineRegionInfoFromCmLine(to.line());
+ unified.getLineRegionInfoFromCmLine(to.line());
DisplaySide side = toInfo.getSide();
// Handle special cases in selections that span multiple regions. Force
@@ -398,33 +180,33 @@
cm.setSelection(cm.getCursor());
} else {
int cmLine = cm.getLineNumber(cm.extras().activeLine());
- LineRegionInfo info = host.getLineRegionInfoFromCmLine(cmLine);
+ LineRegionInfo info =
+ ((Unified) host).getLineRegionInfoFromCmLine(cmLine);
insertNewDraft(info.getSide(), cmLine + 1);
}
}
- private UnifiedCommentGroup group(DisplaySide side, int cmLinePlusOne) {
- UnifiedCommentGroup w = map(side).get(cmLinePlusOne);
- if (w != null) {
- return w;
+ @Override
+ CommentGroup group(DisplaySide side, int cmLinePlusOne) {
+ Map<Integer, CommentGroup> map = map(side);
+ CommentGroup existing = map.get(cmLinePlusOne);
+ if (existing != null) {
+ return existing;
}
- UnifiedCommentGroup g = new UnifiedCommentGroup(this, host.getCm(), side, cmLinePlusOne);
- if (side == DisplaySide.A) {
- sideA.put(cmLinePlusOne, g);
- } else {
- sideB.put(cmLinePlusOne, g);
+ UnifiedCommentGroup g = new UnifiedCommentGroup(
+ this, host.getCmFromSide(side), side, cmLinePlusOne);
+ map.put(cmLinePlusOne, g);
+ if (mergedMap.containsKey(cmLinePlusOne)) {
+ duplicates.put(cmLinePlusOne, mergedMap.remove(cmLinePlusOne));
}
+ mergedMap.put(cmLinePlusOne, g);
if (isAttached()) {
- g.init(getDiffScreen().getDiffTable());
+ g.init(host.getDiffTable());
g.handleRedraw();
}
return g;
}
-
- private SortedMap<Integer, UnifiedCommentGroup> map(DisplaySide side) {
- return side == DisplaySide.A ? sideA : sideB;
- }
}