| // Copyright (C) 2013 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 static com.google.gerrit.client.diff.DisplaySide.A; |
| |
| import com.google.gwt.core.client.JavaScriptObject; |
| import com.google.gwt.dom.client.Element; |
| import java.util.ArrayList; |
| import java.util.Comparator; |
| import java.util.List; |
| import net.codemirror.lib.CodeMirror; |
| import net.codemirror.lib.CodeMirror.LineClassWhere; |
| import net.codemirror.lib.Pos; |
| import net.codemirror.lib.TextMarker; |
| |
| /** Colors modified regions for {@link SideBySide} and {@link Unified}. */ |
| abstract class ChunkManager { |
| static final native void onClick(Element e, JavaScriptObject f) /*-{ e.onclick = f }-*/; |
| |
| final Scrollbar scrollbar; |
| final LineMapper lineMapper; |
| |
| private List<TextMarker> markers; |
| private List<Runnable> undo; |
| |
| ChunkManager(Scrollbar scrollbar) { |
| this.scrollbar = scrollbar; |
| this.lineMapper = new LineMapper(); |
| } |
| |
| abstract DiffChunkInfo getFirst(); |
| |
| List<TextMarker> getMarkers() { |
| return markers; |
| } |
| |
| void reset() { |
| lineMapper.reset(); |
| for (TextMarker m : markers) { |
| m.clear(); |
| } |
| for (Runnable r : undo) { |
| r.run(); |
| } |
| } |
| |
| abstract void render(DiffInfo diff); |
| |
| void render() { |
| markers = new ArrayList<>(); |
| undo = new ArrayList<>(); |
| } |
| |
| void colorLines(CodeMirror cm, String color, int line, int cnt) { |
| colorLines(cm, LineClassWhere.WRAP, color, line, line + cnt); |
| } |
| |
| void colorLines(CodeMirror cm, LineClassWhere where, String className, int start, int end) { |
| if (start < end) { |
| for (int line = start; line < end; line++) { |
| cm.addLineClass(line, where, className); |
| } |
| undo.add( |
| () -> { |
| for (int line = start; line < end; line++) { |
| cm.removeLineClass(line, where, className); |
| } |
| }); |
| } |
| } |
| |
| abstract Runnable diffChunkNav(CodeMirror cm, Direction dir); |
| |
| void diffChunkNavHelper( |
| List<? extends DiffChunkInfo> chunks, DiffScreen host, int res, Direction dir) { |
| if (res < 0) { |
| res = -res - (dir == Direction.PREV ? 1 : 2); |
| } |
| res = res + (dir == Direction.PREV ? -1 : 1); |
| if (res < 0 || chunks.size() <= res) { |
| return; |
| } |
| |
| DiffChunkInfo lookUp = chunks.get(res); |
| // If edit, skip the deletion chunk and set focus on the insertion one. |
| if (lookUp.isEdit() && lookUp.getSide() == A) { |
| res = res + (dir == Direction.PREV ? -1 : 1); |
| if (res < 0 || chunks.size() <= res) { |
| return; |
| } |
| } |
| |
| DiffChunkInfo target = chunks.get(res); |
| CodeMirror targetCm = host.getCmFromSide(target.getSide()); |
| int cmLine = getCmLine(target.getStart(), target.getSide()); |
| targetCm.setCursor(Pos.create(cmLine)); |
| targetCm.focus(); |
| targetCm.scrollToY( |
| targetCm.heightAtLine(cmLine, "local") - 0.5 * targetCm.scrollbarV().getClientHeight()); |
| } |
| |
| Comparator<DiffChunkInfo> getDiffChunkComparator() { |
| // Chunks are ordered by their starting line. If it's a deletion, |
| // use its corresponding line on the revision side for comparison. |
| // In the edit case, put the deletion chunk right before the |
| // insertion chunk. This placement guarantees well-ordering. |
| return new Comparator<DiffChunkInfo>() { |
| @Override |
| public int compare(DiffChunkInfo a, DiffChunkInfo b) { |
| if (a.getSide() == b.getSide()) { |
| return a.getStart() - b.getStart(); |
| } else if (a.getSide() == A) { |
| int comp = lineMapper.lineOnOther(a.getSide(), a.getStart()).getLine() - b.getStart(); |
| return comp == 0 ? -1 : comp; |
| } else { |
| int comp = a.getStart() - lineMapper.lineOnOther(b.getSide(), b.getStart()).getLine(); |
| return comp == 0 ? 1 : comp; |
| } |
| } |
| }; |
| } |
| |
| abstract int getCmLine(int line, DisplaySide side); |
| } |