comparison src/org/tmatesoft/hg/internal/AnnotateFacility.java @ 545:15b406c7cd9d

First round of annotate file is functional
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Fri, 15 Feb 2013 22:15:13 +0100
parents 7f5998a9619d
children cd78e8b9d7bc
comparison
equal deleted inserted replaced
544:7f5998a9619d 545:15b406c7cd9d
17 package org.tmatesoft.hg.internal; 17 package org.tmatesoft.hg.internal;
18 18
19 import static org.tmatesoft.hg.repo.HgRepository.NO_REVISION; 19 import static org.tmatesoft.hg.repo.HgRepository.NO_REVISION;
20 20
21 import org.tmatesoft.hg.core.Nodeid; 21 import org.tmatesoft.hg.core.Nodeid;
22 import org.tmatesoft.hg.internal.PatchGenerator.ChunkSequence;
23 import org.tmatesoft.hg.internal.PatchGenerator.LineSequence; 22 import org.tmatesoft.hg.internal.PatchGenerator.LineSequence;
24 import org.tmatesoft.hg.repo.HgDataFile; 23 import org.tmatesoft.hg.repo.HgDataFile;
25 import org.tmatesoft.hg.repo.HgInvalidStateException; 24 import org.tmatesoft.hg.repo.HgInvalidStateException;
26 import org.tmatesoft.hg.repo.HgRepository;
27 import org.tmatesoft.hg.util.CancelledException; 25 import org.tmatesoft.hg.util.CancelledException;
28 26
29 /** 27 /**
30 * 28 *
31 * @author Artem Tikhomirov 29 * @author Artem Tikhomirov
35 public class AnnotateFacility { 33 public class AnnotateFacility {
36 34
37 /** 35 /**
38 * Annotates changes of the file against its parent(s) 36 * Annotates changes of the file against its parent(s)
39 */ 37 */
40 public void annotateChange(HgDataFile df, int changestRevisionIndex, Inspector insp) { 38 public void annotateChange(HgDataFile df, int changesetRevisionIndex, Inspector insp) {
41 Nodeid fileRev = df.getRepo().getManifest().getFileRevision(changestRevisionIndex, df.getPath()); 39 // TODO detect if file is text/binary (e.g. looking for chars < ' ' and not \t\r\n\f
40 Nodeid fileRev = df.getRepo().getManifest().getFileRevision(changesetRevisionIndex, df.getPath());
42 int fileRevIndex = df.getRevisionIndex(fileRev); 41 int fileRevIndex = df.getRevisionIndex(fileRev);
43 int[] fileRevParents = new int[2]; 42 int[] fileRevParents = new int[2];
44 df.parents(fileRevIndex, fileRevParents, null, null); 43 df.parents(fileRevIndex, fileRevParents, null, null);
45 if (fileRevParents[0] != NO_REVISION && fileRevParents[1] != NO_REVISION) { 44 try {
46 // merge 45 if (fileRevParents[0] != NO_REVISION && fileRevParents[1] != NO_REVISION) {
47 } else if (fileRevParents[0] == fileRevParents[1]) { 46 // merge
48 // may be equal iff both are unset 47 } else if (fileRevParents[0] == fileRevParents[1]) {
49 assert fileRevParents[0] == NO_REVISION; 48 // may be equal iff both are unset
50 // everything added 49 assert fileRevParents[0] == NO_REVISION;
51 insp.added(null); 50 // everything added
52 } else { 51 ByteArrayChannel c;
53 int soleParent = fileRevParents[0] == NO_REVISION ? fileRevParents[1] : fileRevParents[0]; 52 df.content(fileRevIndex, c = new ByteArrayChannel());
54 assert soleParent != NO_REVISION; 53 BlameBlockInspector bbi = new BlameBlockInspector(insp, NO_REVISION, changesetRevisionIndex);
55 try { 54 LineSequence cls = LineSequence.newlines(c.toArray());
55 bbi.begin(LineSequence.newlines(new byte[0]), cls);
56 bbi.match(0, cls.chunkCount()-1, 0);
57 bbi.end();
58 } else {
59 int soleParent = fileRevParents[0] == NO_REVISION ? fileRevParents[1] : fileRevParents[0];
60 assert soleParent != NO_REVISION;
56 ByteArrayChannel c1, c2; 61 ByteArrayChannel c1, c2;
57 df.content(soleParent, c1 = new ByteArrayChannel()); 62 df.content(soleParent, c1 = new ByteArrayChannel());
58 df.content(fileRevIndex, c2 = new ByteArrayChannel()); 63 df.content(fileRevIndex, c2 = new ByteArrayChannel());
59 int parentChangesetRevIndex = df.getChangesetRevisionIndex(soleParent); 64 int parentChangesetRevIndex = df.getChangesetRevisionIndex(soleParent);
60 PatchGenerator<LineSequence> pg = new PatchGenerator<LineSequence>(); 65 PatchGenerator<LineSequence> pg = new PatchGenerator<LineSequence>();
61 pg.init(LineSequence.newlines(c1.toArray()), LineSequence.newlines(c2.toArray())); 66 pg.init(LineSequence.newlines(c1.toArray()), LineSequence.newlines(c2.toArray()));
62 pg.findMatchingBlocks(new BlameBlockInspector(insp)); 67 pg.findMatchingBlocks(new BlameBlockInspector(insp, parentChangesetRevIndex, changesetRevisionIndex));
63 } catch (CancelledException ex) { 68 }
64 // TODO likely it was bad idea to throw cancelled exception from content() 69 } catch (CancelledException ex) {
65 // deprecate and provide alternative? 70 // TODO likely it was bad idea to throw cancelled exception from content()
66 HgInvalidStateException ise = new HgInvalidStateException("ByteArrayChannel never throws CancelledException"); 71 // deprecate and provide alternative?
67 ise.initCause(ex); 72 HgInvalidStateException ise = new HgInvalidStateException("ByteArrayChannel never throws CancelledException");
68 throw ise; 73 ise.initCause(ex);
69 } 74 throw ise;
70 } 75 }
71 } 76 }
72 77
73 @Callback 78 @Callback
74 public interface Inspector { 79 public interface Inspector {
75 void same(Block block); 80 void same(EqualBlock block);
76 void added(AddBlock block); 81 void added(AddBlock block);
77 void changed(ChangeBlock block); 82 void changed(ChangeBlock block);
78 void deleted(DeleteBlock block); 83 void deleted(DeleteBlock block);
79 } 84 }
80 85
86 @Callback
87 public interface InspectorEx extends Inspector { // XXX better name
88 // XXX perhaps, shall pass object instead of separate values for future extension?
89 void start(int originLineCount, int targetLineCount);
90 void done();
91 }
92
81 public interface Block { 93 public interface Block {
94 int originChangesetIndex();
95 int targetChangesetIndex();
82 // boolean isMergeRevision(); 96 // boolean isMergeRevision();
83 // int fileRevisionIndex(); 97 // int fileRevisionIndex();
84 // int originFileRevisionIndex(); 98 // int originFileRevisionIndex();
85 // String[] lines(); 99 // String[] lines();
86 // byte[] data(); 100 // byte[] data();
87 } 101 }
88 102
103 public interface EqualBlock extends Block {
104 int originStart();
105 int targetStart();
106 int length();
107 }
108
89 public interface AddBlock extends Block { 109 public interface AddBlock extends Block {
90 int insertedAt(); // line index in the old file 110 int insertedAt(); // line index in the old file
91 int firstAddedLine(); 111 int firstAddedLine();
92 int totalAddedLines(); 112 int totalAddedLines();
93 String[] addedLines(); 113 String[] addedLines();
101 public interface ChangeBlock extends AddBlock, DeleteBlock { 121 public interface ChangeBlock extends AddBlock, DeleteBlock {
102 } 122 }
103 123
104 static class BlameBlockInspector extends PatchGenerator.DeltaInspector<LineSequence> { 124 static class BlameBlockInspector extends PatchGenerator.DeltaInspector<LineSequence> {
105 private final Inspector insp; 125 private final Inspector insp;
106 126 private final int csetP1;
107 public BlameBlockInspector(Inspector inspector) { 127 private final int csetTarget;
128
129 public BlameBlockInspector(Inspector inspector, int parentCset1, int targetCset) {
108 assert inspector != null; 130 assert inspector != null;
109 insp = inspector; 131 insp = inspector;
132 csetP1 = parentCset1;
133 csetTarget = targetCset;
134 }
135
136 @Override
137 public void begin(LineSequence s1, LineSequence s2) {
138 super.begin(s1, s2);
139 if (insp instanceof InspectorEx) {
140 ((InspectorEx) insp).start(s1.chunkCount() - 1, s2.chunkCount() - 1);
141 }
142 }
143
144 @Override
145 public void end() {
146 super.end();
147 if(insp instanceof InspectorEx) {
148 ((InspectorEx) insp).done();
149 }
110 } 150 }
111 151
112 @Override 152 @Override
113 protected void changed(int s1From, int s1To, int s2From, int s2To) { 153 protected void changed(int s1From, int s1To, int s2From, int s2To) {
114 insp.changed(new BlockImpl2(seq1, seq2, s1From, s1To-s1From, s2From, s2To - s2From, s1From, s2From)); 154 BlockImpl2 block = new BlockImpl2(seq1, seq2, s1From, s1To-s1From, s2From, s2To - s2From, s1From, s2From);
155 block.setOriginAndTarget(csetP1, csetTarget);
156 insp.changed(block);
115 } 157 }
116 158
117 @Override 159 @Override
118 protected void added(int s1InsertPoint, int s2From, int s2To) { 160 protected void added(int s1InsertPoint, int s2From, int s2To) {
119 insp.added(new BlockImpl2(null, seq2, -1, -1, s2From, s2To - s2From, s1InsertPoint, -1)); 161 BlockImpl2 block = new BlockImpl2(null, seq2, -1, -1, s2From, s2To - s2From, s1InsertPoint, -1);
162 block.setOriginAndTarget(csetP1, csetTarget);
163 insp.added(block);
120 } 164 }
121 165
122 @Override 166 @Override
123 protected void deleted(int s2DeletePoint, int s1From, int s1To) { 167 protected void deleted(int s2DeletePoint, int s1From, int s1To) {
124 insp.deleted(new BlockImpl2(seq1, null, s1From, s1To - s1From, -1, -1, -1, s2DeletePoint)); 168 BlockImpl2 block = new BlockImpl2(seq1, null, s1From, s1To - s1From, -1, -1, -1, s2DeletePoint);
169 block.setOriginAndTarget(csetP1, csetTarget);
170 insp.deleted(block);
125 } 171 }
126 172
127 @Override 173 @Override
128 protected void unchanged(int s1From, int s2From, int length) { 174 protected void unchanged(int s1From, int s2From, int length) {
129 insp.same(new BlockImpl(seq2, s2From, length)); 175 BlockImpl1 block = new BlockImpl1(s1From, s2From, length);
130 } 176 block.setOriginAndTarget(csetP1, csetTarget);
131 } 177 insp.same(block);
132 178 }
179 }
180
133 static class BlockImpl implements Block { 181 static class BlockImpl implements Block {
134 private final ChunkSequence seq; 182
135 private final int start; 183 private int originCset;
184 private int targetCset;
185
186 void setOriginAndTarget(int originChangesetIndex, int targetChangesetIndex) {
187 // XXX perhaps, shall be part of Inspector API, rather than Block's
188 // as they don't change between blocks (although the moment about merged revisions)
189 // is not yet clear to me
190 originCset = originChangesetIndex;
191 targetCset = targetChangesetIndex;
192 }
193
194 public int originChangesetIndex() {
195 return originCset;
196 }
197
198 public int targetChangesetIndex() {
199 return targetCset;
200 }
201 }
202
203 static class BlockImpl1 extends BlockImpl implements EqualBlock {
204 private final int start1, start2;
136 private final int length; 205 private final int length;
137 206
138 BlockImpl() { 207 BlockImpl1(int blockStartSeq1, int blockStartSeq2, int blockLength) {
139 // FIXME delete this cons 208 start1 = blockStartSeq1;
140 seq = null; 209 start2 = blockStartSeq2;
141 start = length = -1;
142 }
143
144 BlockImpl(ChunkSequence s, int blockStart, int blockLength) {
145 seq = s;
146 start = blockStart;
147 length = blockLength; 210 length = blockLength;
148 } 211 }
149 212
150 } 213 public int originStart() {
151 214 return start1;
152 static class BlockImpl2 implements ChangeBlock { 215 }
153 216
154 private final ChunkSequence oldSeq; 217 public int targetStart() {
155 private final ChunkSequence newSeq; 218 return start2;
219 }
220
221 public int length() {
222 return length;
223 }
224
225 @Override
226 public String toString() {
227 return String.format("@@ [%d..%d) == [%d..%d) @@", start1, start1+length, start2, start2+length);
228 }
229 }
230
231 static class BlockImpl2 extends BlockImpl implements ChangeBlock {
232
233 private final LineSequence oldSeq;
234 private final LineSequence newSeq;
156 private final int s1Start; 235 private final int s1Start;
157 private final int s1Len; 236 private final int s1Len;
158 private final int s2Start; 237 private final int s2Start;
159 private final int s2Len; 238 private final int s2Len;
160 private final int s1InsertPoint; 239 private final int s1InsertPoint;
161 private final int s2DeletePoint; 240 private final int s2DeletePoint;
162 241
163 public BlockImpl2(ChunkSequence s1, ChunkSequence s2, int s1Start, int s1Len, int s2Start, int s2Len, int s1InsertPoint, int s2DeletePoint) { 242 public BlockImpl2(LineSequence s1, LineSequence s2, int s1Start, int s1Len, int s2Start, int s2Len, int s1InsertPoint, int s2DeletePoint) {
164 oldSeq = s1; 243 oldSeq = s1;
165 newSeq = s2; 244 newSeq = s2;
166 this.s1Start = s1Start; 245 this.s1Start = s1Start;
167 this.s1Len = s1Len; 246 this.s1Len = s1Len;
168 this.s2Start = s2Start; 247 this.s2Start = s2Start;
208 for (int i = 0; i < count; i++) { 287 for (int i = 0; i < count; i++) {
209 rv[i] = String.format("LINE %d", startFrom + i+1); 288 rv[i] = String.format("LINE %d", startFrom + i+1);
210 } 289 }
211 return rv; 290 return rv;
212 } 291 }
292
293 @Override
294 public String toString() {
295 if (s2DeletePoint == -1) {
296 return String.format("@@ -%d,0 +%d,%d @@", insertedAt(), firstAddedLine(), totalAddedLines());
297 } else if (s1InsertPoint == -1) {
298 // delete only
299 return String.format("@@ -%d,%d +%d,0 @@", firstRemovedLine(), totalRemovedLines(), removedAt());
300 }
301 return String.format("@@ -%d,%d +%d,%d @@", firstRemovedLine(), totalRemovedLines(), firstAddedLine(), totalAddedLines());
302 }
213 } 303 }
214 } 304 }