blob: 1d9b55afc9285f99ebf4d2cde9667960c9dcf2c3 [file] [log] [blame]
// Copyright (C) 2014 The Android Open Source Project
//
// 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.google.gerrit.client.diff;
import com.google.gerrit.client.DiffObject;
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.changes.CommentInfo;
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.ui.CommentLinkProcessor;
import com.google.gerrit.reviewdb.client.PatchSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import net.codemirror.lib.CodeMirror;
import net.codemirror.lib.Pos;
import net.codemirror.lib.TextMarker.FromTo;
/** Tracks comment widgets for {@link Unified}. */
class UnifiedCommentManager extends CommentManager {
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,
DiffObject base,
PatchSet.Id revision,
String path,
CommentLinkProcessor clp,
boolean open) {
super(host, base, revision, path, clp, open);
mergedMap = new TreeMap<>();
duplicates = new HashMap<>();
}
@Override
SortedMap<Integer, CommentGroup> getMapForNav(DisplaySide side) {
return mergedMap;
}
@Override
void clearLine(DisplaySide side, int line, CommentGroup group) {
super.clearLine(side, line, group);
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) {
if (!Gerrit.isSignedIn()) {
signInCallback(cm).run();
} else {
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();
}
insertNewDraft(side, line + 1);
}
}
@Override
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;
}
return group;
}
@Override
Collection<Integer> getLinesWithCommentGroups() {
return mergedMap.tailMap(1).keySet();
}
@Override
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
void newDraft(CodeMirror cm) {
if (cm.somethingSelected()) {
FromTo fromTo = adjustSelection(cm);
Pos from = fromTo.from();
Pos to = fromTo.to();
Unified unified = (Unified) host;
UnifiedChunkManager manager = unified.getChunkManager();
LineRegionInfo fromInfo = unified.getLineRegionInfoFromCmLine(from.line());
LineRegionInfo toInfo = unified.getLineRegionInfoFromCmLine(to.line());
DisplaySide side = toInfo.getSide();
// Handle special cases in selections that span multiple regions. Force
// start line to be on the same side as the end line.
if ((fromInfo.type == RegionType.INSERT || fromInfo.type == RegionType.COMMON)
&& toInfo.type == RegionType.DELETE) {
LineOnOtherInfo infoOnSideA = manager.lineMapper.lineOnOther(DisplaySide.B, fromInfo.line);
int startLineOnSideA = infoOnSideA.getLine();
if (infoOnSideA.isAligned()) {
from.line(startLineOnSideA);
} else {
from.line(startLineOnSideA + 1);
}
from.ch(0);
to.line(toInfo.line);
} else if (fromInfo.type == RegionType.DELETE && toInfo.type == RegionType.INSERT) {
LineOnOtherInfo infoOnSideB = manager.lineMapper.lineOnOther(DisplaySide.A, fromInfo.line);
int startLineOnSideB = infoOnSideB.getLine();
if (infoOnSideB.isAligned()) {
from.line(startLineOnSideB);
} else {
from.line(startLineOnSideB + 1);
}
from.ch(0);
to.line(toInfo.line);
} else if (fromInfo.type == RegionType.DELETE && toInfo.type == RegionType.COMMON) {
int toLineOnSideA = manager.lineMapper.lineOnOther(DisplaySide.B, toInfo.line).getLine();
from.line(fromInfo.line);
// Force the end line to be on the same side as the start line.
to.line(toLineOnSideA);
side = DisplaySide.A;
} else { // Common case
from.line(fromInfo.line);
to.line(toInfo.line);
}
addDraftBox(
side,
CommentInfo.create(
getPath(),
getStoredSideFromDisplaySide(side),
to.line() + 1,
CommentRange.create(fromTo),
false))
.setEdit(true);
cm.setCursor(Pos.create(host.getCmLine(to.line(), side), to.ch()));
cm.setSelection(cm.getCursor());
} else {
int cmLine = cm.getLineNumber(cm.extras().activeLine());
LineRegionInfo info = ((Unified) host).getLineRegionInfoFromCmLine(cmLine);
insertNewDraft(info.getSide(), cmLine + 1);
}
}
@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.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(host.getDiffTable());
g.handleRedraw();
}
return g;
}
}