Skip to content

Commit 74c19f8

Browse files
author
John Haley
committed
Modify ConvenientLine to work with unicode
This changes how `ConvenientLines` are calculated with a breaking API change. `ConvenientLine` used to assume that all content coming back was ASCII characters and a uniform byte length. This is no longer the case. Now we use the byte length coming back from libgit2 to extract the string from a buffer using the correct byte length. This will give back the correct line everytime. The breaking API change is that we no longer return the new line character with the line nor do we include that character in the `contentLen` calculation either. I'm on the fence about that change but it seems like it's what we ought to be doing instead of force all of the consumers of the library to account for that everywhere.
1 parent 76a08dc commit 74c19f8

4 files changed

Lines changed: 112 additions & 33 deletions

File tree

lib/convenient_line.js

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,59 +3,63 @@ var NodeGit = require("../");
33
function ConvenientLine(raw, i) {
44
this.raw = raw;
55
this.i = i;
6+
this._cache = {};
67
}
78

89
/**
9-
* The content of the line
10-
* @return {String}
10+
* The type of the line (context, addition, deletion, etc...)
11+
* @return {Diff.LINE}
1112
*/
1213
ConvenientLine.prototype.origin = function() {
1314
return this.raw.origin();
1415
};
1516

1617
/**
17-
* The content of the line
18-
* @return {String}
18+
* Line number in old file or -1 for added line
19+
* @return {Number}
1920
*/
2021
ConvenientLine.prototype.oldLineno = function() {
2122
return this.raw.oldLineno();
2223
};
2324

2425
/**
25-
* The content of the line
26-
* @return {String}
26+
* Line number in new file or -1 for deleted line
27+
* @return {Number}
2728
*/
2829
ConvenientLine.prototype.newLineno = function() {
2930
return this.raw.newLineno();
3031
};
3132

3233
/**
33-
* The content of the line
34-
* @return {String}
34+
* Number of newline characters in content
35+
* @return {Number}
3536
*/
3637
ConvenientLine.prototype.numLines = function() {
3738
return this.raw.numLines();
3839
};
3940

4041
/**
41-
* The content of the line
42-
* @return {String}
42+
* Number of characters in the string
43+
* @return {Number}
4344
*/
4445
ConvenientLine.prototype.contentLen = function() {
45-
return this.raw.contentLen();
46-
};
46+
if (!this._cache.contentLen) {
47+
this._cache.contentLen = this.content().length;
48+
}
4749

50+
return this._cache.contentLen;
51+
};
4852

4953
/**
50-
* The content of the line
51-
* @return {String}
54+
* Offset in the original file to the content
55+
* @return {Number}
5256
*/
5357
ConvenientLine.prototype.contentOffset = function() {
5458
return this.raw.contentOffset();
5559
};
5660

5761
/**
58-
* The content of the line
62+
* Pointer to diff text, not NUL-byte terminated
5963
* @return {String}
6064
*/
6165
ConvenientLine.prototype.rawContent = function() {
@@ -67,8 +71,14 @@ ConvenientLine.prototype.rawContent = function() {
6771
* @return {String}
6872
*/
6973
ConvenientLine.prototype.content = function() {
70-
return this.raw.content()
71-
.substring(0, this.raw.contentLen()).replace("\n", "");
74+
if (!this._cache.content) {
75+
this._cache.content = new Buffer(this.raw.content())
76+
.slice(0, this.raw.contentLen())
77+
.toString("utf8")
78+
.replace("\n", "");
79+
}
80+
81+
return this._cache.content;
7282
};
7383

7484
NodeGit.ConvenientLine = ConvenientLine;

test/tests/commit.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -559,30 +559,30 @@ describe("Commit", function() {
559559
//check all hunk lines
560560
assert.equal(lines.length, 12);
561561
assert.equal(lines[0].origin(), Diff.LINE.CONTEXT);
562-
assert.equal(lines[1].contentLen(), 9);
562+
assert.equal(lines[1].contentLen(), 8);
563563

564-
assert.equal(getLineText(lines[1]), "line s\n");
564+
assert.equal(getLineText(lines[1]), "line s");
565565
assert.equal(lines[1].origin(), Diff.LINE.CONTEXT);
566566
assert.equal(lines[2].origin(), Diff.LINE.CONTEXT);
567567

568-
assert.equal(lines[3].contentLen(), 1);
569-
assert.equal(getLineText(lines[3]), "\n");
568+
assert.equal(lines[3].contentLen(), 0);
569+
assert.equal(getLineText(lines[3]), "");
570570
assert.equal(lines[3].origin(), Diff.LINE.ADDITION);
571571

572572
assert.equal(lines[4].origin(), Diff.LINE.CONTEXT);
573573

574-
assert.equal(lines[5].contentLen(), 7);
575-
assert.equal(getLineText(lines[5]), "line v\n");
574+
assert.equal(lines[5].contentLen(), 6);
575+
assert.equal(getLineText(lines[5]), "line v");
576576
assert.equal(lines[5].origin(), Diff.LINE.DELETION);
577-
assert.equal(lines[6].contentLen(), 8);
578-
assert.equal(getLineText(lines[6]), "line v1\n");
577+
assert.equal(lines[6].contentLen(), 7);
578+
assert.equal(getLineText(lines[6]), "line v1");
579579
assert.equal(lines[6].origin(), Diff.LINE.ADDITION);
580580

581581
assert.equal(lines[7].origin(), Diff.LINE.CONTEXT);
582582
assert.equal(lines[8].origin(), Diff.LINE.CONTEXT);
583583

584-
assert.equal(lines[9].contentLen(), 4);
585-
assert.equal(getLineText(lines[9]), "\t\t\t\n");
584+
assert.equal(lines[9].contentLen(), 3);
585+
assert.equal(getLineText(lines[9]), "\t\t\t");
586586
assert.equal(lines[9].origin(), Diff.LINE.ADDITION);
587587

588588
assert.equal(lines[10].origin(), Diff.LINE.CONTEXT);

test/tests/convenient_line.js

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
var assert = require("assert");
2+
var repoSetup = require("../utils/repository_setup");
3+
var promisify = require("promisify-node");
4+
var fse = promisify(require("fs-extra"));
5+
var path = require("path");
6+
var local = path.join.bind(path, __dirname);
7+
8+
describe("ConvenientLine", function() {
9+
var repoPath = local("../repos/convenientLineTest");
10+
var unicodeLine = "Ťḥ𝖎ṧ ℓỈ𝓃ệ çǒ𝚗ẗảḭṋṦ Û𝐧ǐ𝗰ṓḍ𝔢";
11+
var asciiLine = "but this line doesn't";
12+
13+
beforeEach(function() {
14+
var test = this;
15+
16+
return repoSetup.createRepository(repoPath)
17+
.then(function(repo) {
18+
return repoSetup.commitFileToRepo(
19+
repo,
20+
"fileWithUnicodeChars",
21+
unicodeLine + "\n" + asciiLine + "\n"
22+
);
23+
})
24+
.then(function(commit) {
25+
return commit.getDiff();
26+
})
27+
.then(function(diff) {
28+
return diff[0].patches();
29+
})
30+
.then(function(patches) {
31+
return patches[0].hunks();
32+
})
33+
.then(function(hunks) {
34+
return hunks[0].lines();
35+
})
36+
.then(function(lines) {
37+
test.unicodeLine = lines[0];
38+
test.asciiLine = lines[1];
39+
});
40+
});
41+
42+
after(function() {
43+
return fse.remove(repoPath);
44+
});
45+
46+
it("can parse the byte length of a unicode string", function() {
47+
var line = this.unicodeLine;
48+
49+
assert.equal(line.contentLen(), 32);
50+
});
51+
52+
it("can get a line that contains unicode", function() {
53+
var line = this.unicodeLine;
54+
55+
assert.equal(line.content(), unicodeLine);
56+
});
57+
58+
it("can parse the byte length of a ascii string", function() {
59+
var line = this.asciiLine;
60+
61+
assert.equal(line.contentLen(), 21);
62+
});
63+
64+
it("can get a line that contains ascii", function() {
65+
var line = this.asciiLine;
66+
67+
assert.equal(line.content(), asciiLine);
68+
});
69+
});

test/tests/diff.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -114,17 +114,17 @@ describe("Diff", function() {
114114
assert.equal(lines[2].origin(), Diff.LINE.CONTEXT);
115115

116116
var oldContent = "__Before submitting a pull request, please ensure " +
117-
"both unit tests and lint checks pass.__\n";
118-
assert.equal(lines[3].rawContent(), oldContent);
117+
"both unit tests and lint checks pass.__";
118+
assert.equal(lines[3].content(), oldContent);
119119
assert.equal(lines[3].origin(), Diff.LINE.DELETION);
120-
assert.equal(lines[3].contentLen(), 90);
120+
assert.equal(lines[3].contentLen(), oldContent.length);
121121

122122
var newContent = "__Before submitting a pull request, please ensure " +
123123
"both that you've added unit tests to cover your shiny new code, " +
124-
"and that all unit tests and lint checks pass.__\n";
125-
assert.equal(lines[4].rawContent(), newContent);
124+
"and that all unit tests and lint checks pass.__";
125+
assert.equal(lines[4].content(), newContent);
126126
assert.equal(lines[4].origin(), Diff.LINE.ADDITION);
127-
assert.equal(lines[4].contentLen(), 162);
127+
assert.equal(lines[4].contentLen(), newContent.length);
128128
});
129129
});
130130

0 commit comments

Comments
 (0)