Mercurial > hg4j
comparison src/org/tmatesoft/hg/internal/AnnotateFacility.java @ 548:ab21ac7dd833
Line-by-line annotation API and support code in place
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Mon, 18 Feb 2013 19:58:51 +0100 |
parents | cd78e8b9d7bc |
children | 83afa680555d |
comparison
equal
deleted
inserted
replaced
547:66fc86e8c0dd | 548:ab21ac7dd833 |
---|---|
15 * contact TMate Software at support@hg4j.com | 15 * contact TMate Software at support@hg4j.com |
16 */ | 16 */ |
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 import static org.tmatesoft.hg.repo.HgRepository.TIP; | |
20 | 21 |
21 import org.tmatesoft.hg.core.Nodeid; | 22 import org.tmatesoft.hg.core.Nodeid; |
22 import org.tmatesoft.hg.internal.PatchGenerator.LineSequence; | 23 import org.tmatesoft.hg.internal.PatchGenerator.LineSequence; |
23 import org.tmatesoft.hg.repo.HgDataFile; | 24 import org.tmatesoft.hg.repo.HgDataFile; |
24 import org.tmatesoft.hg.repo.HgInvalidStateException; | 25 import org.tmatesoft.hg.repo.HgInvalidStateException; |
26 import org.tmatesoft.hg.repo.HgRepository; | |
25 import org.tmatesoft.hg.util.CancelledException; | 27 import org.tmatesoft.hg.util.CancelledException; |
26 | 28 |
27 /** | 29 /** |
28 * | 30 * |
29 * @author Artem Tikhomirov | 31 * @author Artem Tikhomirov |
30 * @author TMate Software Ltd. | 32 * @author TMate Software Ltd. |
31 */ | 33 */ |
32 @Experimental(reason="work in progress") | 34 @Experimental(reason="work in progress") |
33 public class AnnotateFacility { | 35 public class AnnotateFacility { |
34 | 36 |
37 /** | |
38 * Annotate file revision, line by line. | |
39 */ | |
40 public void annotate(HgDataFile df, int changesetRevisionIndex, LineInspector insp) { | |
41 if (!df.exists()) { | |
42 return; | |
43 } | |
44 Nodeid fileRev = df.getRepo().getManifest().getFileRevision(changesetRevisionIndex, df.getPath()); | |
45 int fileRevIndex = df.getRevisionIndex(fileRev); | |
46 int[] fileRevParents = new int[2]; | |
47 FileAnnotation fa = new FileAnnotation(insp); | |
48 do { | |
49 // also covers changesetRevisionIndex == TIP, #implAnnotateChange doesn't tolerate constants | |
50 changesetRevisionIndex = df.getChangesetRevisionIndex(fileRevIndex); | |
51 df.parents(fileRevIndex, fileRevParents, null, null); | |
52 implAnnotateChange(df, changesetRevisionIndex, fileRevIndex, fileRevParents, fa); | |
53 fileRevIndex = fileRevParents[0]; | |
54 } while (fileRevIndex != NO_REVISION); | |
55 } | |
56 | |
35 /** | 57 /** |
36 * Annotates changes of the file against its parent(s) | 58 * Annotates changes of the file against its parent(s) |
37 */ | 59 */ |
38 public void annotateChange(HgDataFile df, int changesetRevisionIndex, BlockInspector insp) { | 60 public void annotateChange(HgDataFile df, int changesetRevisionIndex, BlockInspector insp) { |
39 // TODO detect if file is text/binary (e.g. looking for chars < ' ' and not \t\r\n\f | 61 // 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()); | 62 Nodeid fileRev = df.getRepo().getManifest().getFileRevision(changesetRevisionIndex, df.getPath()); |
41 int fileRevIndex = df.getRevisionIndex(fileRev); | 63 int fileRevIndex = df.getRevisionIndex(fileRev); |
42 int[] fileRevParents = new int[2]; | 64 int[] fileRevParents = new int[2]; |
43 df.parents(fileRevIndex, fileRevParents, null, null); | 65 df.parents(fileRevIndex, fileRevParents, null, null); |
66 if (changesetRevisionIndex == TIP) { | |
67 changesetRevisionIndex = df.getChangesetRevisionIndex(fileRevIndex); | |
68 } | |
69 implAnnotateChange(df, changesetRevisionIndex, fileRevIndex, fileRevParents, insp); | |
70 } | |
71 | |
72 private void implAnnotateChange(HgDataFile df, int csetRevIndex, int fileRevIndex, int[] fileParentRevs, BlockInspector insp) { | |
44 try { | 73 try { |
45 if (fileRevParents[0] != NO_REVISION && fileRevParents[1] != NO_REVISION) { | 74 if (fileParentRevs[0] != NO_REVISION && fileParentRevs[1] != NO_REVISION) { |
46 // merge | 75 // merge |
47 } else if (fileRevParents[0] == fileRevParents[1]) { | 76 } else if (fileParentRevs[0] == fileParentRevs[1]) { |
48 // may be equal iff both are unset | 77 // may be equal iff both are unset |
49 assert fileRevParents[0] == NO_REVISION; | 78 assert fileParentRevs[0] == NO_REVISION; |
50 // everything added | 79 // everything added |
51 ByteArrayChannel c; | 80 ByteArrayChannel c; |
52 df.content(fileRevIndex, c = new ByteArrayChannel()); | 81 df.content(fileRevIndex, c = new ByteArrayChannel()); |
53 BlameBlockInspector bbi = new BlameBlockInspector(insp, NO_REVISION, changesetRevisionIndex); | 82 BlameBlockInspector bbi = new BlameBlockInspector(insp, NO_REVISION, csetRevIndex); |
54 LineSequence cls = LineSequence.newlines(c.toArray()); | 83 LineSequence cls = LineSequence.newlines(c.toArray()); |
55 bbi.begin(LineSequence.newlines(new byte[0]), cls); | 84 bbi.begin(LineSequence.newlines(new byte[0]), cls); |
56 bbi.match(0, cls.chunkCount()-1, 0); | 85 bbi.match(0, cls.chunkCount()-1, 0); |
57 bbi.end(); | 86 bbi.end(); |
58 } else { | 87 } else { |
59 int soleParent = fileRevParents[0] == NO_REVISION ? fileRevParents[1] : fileRevParents[0]; | 88 int soleParent = fileParentRevs[0] == NO_REVISION ? fileParentRevs[1] : fileParentRevs[0]; |
60 assert soleParent != NO_REVISION; | 89 assert soleParent != NO_REVISION; |
61 ByteArrayChannel c1, c2; | 90 ByteArrayChannel c1, c2; |
62 df.content(soleParent, c1 = new ByteArrayChannel()); | 91 df.content(soleParent, c1 = new ByteArrayChannel()); |
63 df.content(fileRevIndex, c2 = new ByteArrayChannel()); | 92 df.content(fileRevIndex, c2 = new ByteArrayChannel()); |
64 int parentChangesetRevIndex = df.getChangesetRevisionIndex(soleParent); | 93 int parentChangesetRevIndex = df.getChangesetRevisionIndex(soleParent); |
65 PatchGenerator<LineSequence> pg = new PatchGenerator<LineSequence>(); | 94 PatchGenerator<LineSequence> pg = new PatchGenerator<LineSequence>(); |
66 pg.init(LineSequence.newlines(c1.toArray()), LineSequence.newlines(c2.toArray())); | 95 pg.init(LineSequence.newlines(c1.toArray()), LineSequence.newlines(c2.toArray())); |
67 pg.findMatchingBlocks(new BlameBlockInspector(insp, parentChangesetRevIndex, changesetRevisionIndex)); | 96 pg.findMatchingBlocks(new BlameBlockInspector(insp, parentChangesetRevIndex, csetRevIndex)); |
68 } | 97 } |
69 } catch (CancelledException ex) { | 98 } catch (CancelledException ex) { |
70 // TODO likely it was bad idea to throw cancelled exception from content() | 99 // TODO likely it was bad idea to throw cancelled exception from content() |
71 // deprecate and provide alternative? | 100 // deprecate and provide alternative? |
72 HgInvalidStateException ise = new HgInvalidStateException("ByteArrayChannel never throws CancelledException"); | 101 HgInvalidStateException ise = new HgInvalidStateException("ByteArrayChannel never throws CancelledException"); |
121 public interface ChangeBlock extends AddBlock, DeleteBlock { | 150 public interface ChangeBlock extends AddBlock, DeleteBlock { |
122 } | 151 } |
123 | 152 |
124 @Callback | 153 @Callback |
125 public interface LineInspector { | 154 public interface LineInspector { |
155 /** | |
156 * Not necessarily invoked sequentially by line numbers | |
157 */ | |
126 void line(int lineNumber, int changesetRevIndex, LineDescriptor ld); | 158 void line(int lineNumber, int changesetRevIndex, LineDescriptor ld); |
127 } | 159 } |
128 | 160 |
129 public interface LineDescriptor { | 161 public interface LineDescriptor { |
130 int totalLines(); | 162 int totalLines(); |