Skip to content

Commit b8e26d6

Browse files
committed
Changed how the rules work.
This is a lot more specific now in terms of requirements.
1 parent d85d5d7 commit b8e26d6

2 files changed

Lines changed: 168 additions & 30 deletions

File tree

Lines changed: 120 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,43 @@
11
module.exports = requireDescriptionCompleteSentence;
2-
module.exports.scopes = ['function'];
2+
module.exports.scopes = ['function', 'variable'];
33
module.exports.options = {
44
requireDescriptionCompleteSentence: {allowedValues: [true]}
55
};
66

7-
var RE_START_WITH_UPPER_CASE = /^[A-Z]/;
8-
97
/**
10-
* Checks that a period is next to a word at the end of input.
8+
* Ensures that every sentence starts with an upper case letter.
119
*
12-
* The sentence ending with a new line is also valid.
10+
* This matches when a new line or start of a blank line
11+
* does not start with an upper case letter.
12+
* It also matches a period not fllowed by an upper case letter.
1313
*/
14-
var RE_END_WITH_PERIOD = /\w[.](\n|$)/;
14+
var RE_NEW_LINE_START_WITH_UPPER_CASE = /[*]*((\n[*\s]*\n)|[.])[*\s]*[a-z]/g;
15+
16+
var START_DESCRIPTION = /^[*\s]*[a-z]/g;
1517

1618
var RE_END_DESCRIPTION = /\n/g;
1719

18-
var RE_LAST_WORD = /\w(?!.*\w)/;
20+
/**
21+
* Ensures next lines with uppercase letters have periods.
22+
*
23+
* This checks for the existance of a new line that starts with an
24+
* upper case letter where the previous line does not have a period
25+
* Note that numbers count as word characters.
26+
*/
27+
var RE_NEW_LINE_UPPERCASE = /\w(?![.])(\W)*\n\W*[A-Z]/g;
28+
29+
/**
30+
* Ensures that a sentence followed by a blank line has a period
31+
*
32+
* If the above line did not have a period this would match.
33+
* this also ensures that the last sentence in the description ends with a period.
34+
*/
35+
var RE_END_WITH_PERIOD = /\w(?![.])(\W)*(\n|$)[*\s]*(\n|$)/g;
1936

2037
/**
2138
* Requires description to be a complete sentence in a jsdoc comment.
2239
*
23-
* A complete sentence is defined by starting with an upper letter
40+
* a complete sentence is defined by starting with an upper letter
2441
* and ending with a period.
2542
*
2643
* @param {(FunctionDeclaration|FunctionExpression)} node
@@ -33,30 +50,105 @@ function requireDescriptionCompleteSentence(node, err) {
3350
}
3451

3552
var loc = doc.loc.start;
53+
var lines = doc.description.split(RE_END_DESCRIPTION);
54+
55+
var errors = [];
3656

37-
if (!RE_START_WITH_UPPER_CASE.test(doc.description)) {
38-
err('Sentence must start with an upper case letter.', {
39-
line: loc.line + 1,
40-
column: loc.column + 3
57+
if (START_DESCRIPTION.test(doc.description)) {
58+
var matches = returnAllMatches(doc.description, START_DESCRIPTION);
59+
matches.map(function(match) {
60+
match.message = 'Description must start with an upper case letter.';
61+
match.index = match.start;
4162
});
63+
errors = errors.concat(matches);
4264
}
4365

44-
if (!RE_END_WITH_PERIOD.test(doc.description)) {
45-
var lines = doc.description.split(RE_END_DESCRIPTION);
46-
47-
// Find the location last word in the description.
48-
var line = -1;
49-
var column = -1;
50-
for (var i = lines.length - 1; i >= 0; i--) {
51-
if (lines[i] && lines[i].search(RE_LAST_WORD) >= 0) {
52-
line = i;
53-
column = lines[i].search(RE_LAST_WORD);
54-
break;
55-
}
56-
}
57-
err('Sentence must end with a period.', {
58-
line: loc.line + 1 + line,
59-
column: loc.column + 3 + column
66+
if (RE_NEW_LINE_START_WITH_UPPER_CASE.test(doc.description)) {
67+
var matches1 = returnAllMatches(doc.description, RE_NEW_LINE_START_WITH_UPPER_CASE);
68+
matches1.map(function(match) {
69+
match.message = 'Sentence must start with an upper case letter.';
70+
match.index = match.end - 1;
6071
});
72+
errors = errors.concat(matches1);
73+
}
74+
75+
if (RE_END_WITH_PERIOD.test(doc.description)) {
76+
var matches2 = returnAllMatches(doc.description, RE_END_WITH_PERIOD);
77+
matches2.map(function(match) {
78+
match.message = 'Sentence must end with a period.';
79+
match.index = match.start;
80+
});
81+
errors = errors.concat(matches2);
82+
}
83+
84+
if (RE_NEW_LINE_UPPERCASE.test(doc.description)) {
85+
var matches3 = returnAllMatches(doc.description, RE_NEW_LINE_UPPERCASE);
86+
matches3.map(function(match) {
87+
match.message = 'You started a new line with an upper case letter but ' +
88+
'previous line does not end with a period.';
89+
match.index = match.end - 1;
90+
});
91+
errors = errors.concat(matches3);
92+
}
93+
94+
computeErrors(err, loc, errors, lines);
95+
}
96+
97+
/**
98+
* Given a list of matches it records offenses.
99+
*
100+
* This will only go through the description once for all offenses.
101+
*
102+
* @param {Function} err
103+
* @param {Object} loc
104+
* @param {Array} matches An array of matching offenses.
105+
* @param {number} matches.start The starting index of the match.
106+
* @param {string} matches.message The message of the offence.
107+
* @param {Array} lines The lines in this description.
108+
*/
109+
function computeErrors(err, loc, matches, lines) {
110+
var indexInString = 0;
111+
var currentMatch = 0;
112+
for (var currentLine = 0; currentLine < lines.length &&
113+
currentMatch < matches.length; currentLine++) {
114+
115+
var nextIndexInString = indexInString + lines[currentLine].length;
116+
while (currentMatch < matches.length && matches[currentMatch].index >= indexInString &&
117+
matches[currentMatch].index <= nextIndexInString) {
118+
119+
// currentLine is to account for additional extra characters being added.
120+
var columnOffset = (matches[currentMatch].index - indexInString) - currentLine;
121+
err(matches[currentMatch].message, {
122+
line: loc.line + 1 + currentLine,
123+
column: loc.column + 3 + columnOffset
124+
});
125+
126+
currentMatch++;
127+
}
128+
indexInString = nextIndexInString;
61129
}
62130
}
131+
132+
/**
133+
* Returns all matches of regex in input as an array.
134+
*
135+
* @return {Array} Each element in the array has two values: start and end.
136+
*/
137+
function returnAllMatches(input, regex) {
138+
var match;
139+
var indexes = [];
140+
141+
// resets the last index so that exec does not return null.
142+
regex.lastIndex = 0;
143+
do {
144+
match = regex.exec(input);
145+
if (match === null) {
146+
break;
147+
}
148+
indexes.push({
149+
start: match.index,
150+
end: match.index + match[0].length
151+
});
152+
} while (match !== null);
153+
return indexes;
154+
}

test/lib/rules/validate-jsdoc/require-description-complete-sentence.js

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,27 @@ describe('lib/rules/validate-jsdoc/require-description-complete-sentence', funct
4949
errors: {
5050
line: 2,
5151
column: 18
52+
}
53+
}, {
54+
it: 'should report missing upper case letter followed by period',
55+
code: function () {
56+
/**
57+
* Some description. hola.
58+
* @param {number} p description without hyphen
59+
*/
60+
function fun(p) {
61+
}
5262
},
63+
errors: {
64+
line: 2,
65+
column: 21
66+
}
67+
}, {
5368
it: 'should report missing period at end of multi line description',
5469
code: function () {
5570
/**
5671
* Some description
57-
* That takes up multiple lines
72+
* that takes up multiple lines
5873
* @param {number} p description without hyphen
5974
*/
6075
function fun(p) {
@@ -63,7 +78,22 @@ describe('lib/rules/validate-jsdoc/require-description-complete-sentence', funct
6378
errors: {
6479
line: 3,
6580
column: 30
81+
}
82+
}, {
83+
it: 'should report missing period if upper case letter follows',
84+
code: function () {
85+
/**
86+
* Some description
87+
* That takes up multiple lines.
88+
* @param {number} p description without hyphen
89+
*/
90+
function fun(p) {
91+
}
6692
},
93+
errors: {
94+
line: 3,
95+
column: 3
96+
}
6797
}, {
6898
it: 'should report missing upper case at beginning of description',
6999
code: function () {
@@ -99,12 +129,28 @@ describe('lib/rules/validate-jsdoc/require-description-complete-sentence', funct
99129
function fun(p) {}
100130
},
101131
errors: 1
132+
}, {
133+
it: 'should report missing period at end of first line',
134+
code: function () {
135+
/**
136+
* Some description
137+
*
138+
* More description.
139+
* @param {number} p description without hyphen
140+
*/
141+
function fun(p) {
142+
}
143+
},
144+
errors: {
145+
line: 2,
146+
column: 18
147+
}
102148
}, {
103149
it: 'should not report missing period',
104150
code: function () {
105151
/**
106152
* Some description
107-
* Which is continued on the next line.
153+
* which is continued on the next line.
108154
* @param {number} p description without hyphen
109155
*/
110156
function fun(p) {}

0 commit comments

Comments
 (0)