Skip to content

Commit

Permalink
feat: Add Node.getStartColumn(), Node.getEndColumn(), SourceFile.getC…
Browse files Browse the repository at this point in the history
…olumnAtPos(pos).
  • Loading branch information
dsherret committed Jun 4, 2018
1 parent 3e46a69 commit 5119e83
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 2 deletions.
19 changes: 17 additions & 2 deletions src/compiler/common/Node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -919,7 +919,7 @@ export class Node<NodeType extends ts.Node = ts.Node> {
*/
getStartLinePos(includeJsDocComment?: boolean) {
const sourceFileText = this.sourceFile.getFullText();
return getPreviousMatchingPos(sourceFileText, this.getStart(includeJsDocComment), char => char === "\n");
return getPreviousMatchingPos(sourceFileText, this.getStart(includeJsDocComment), char => char === "\n" || char === "\r");
}

/**
Expand All @@ -935,10 +935,25 @@ export class Node<NodeType extends ts.Node = ts.Node> {
*/
getEndLineNumber() {
const sourceFileText = this.sourceFile.getFullText();
const endLinePos = getPreviousMatchingPos(sourceFileText, this.getEnd(), char => char === "\n");
const endLinePos = getPreviousMatchingPos(sourceFileText, this.getEnd(), char => char === "\n" || char === "\r");
return this.sourceFile.getLineNumberFromPos(endLinePos);
}

/**
* Gets the length from the start of the line to the start of the node.
* @param includeJsDocComment - Whether to include the JS doc comment or not.
*/
getStartColumn(includeJsDocComment?: boolean) {
return this.sourceFile.getColumnAtPos(this.getStart(includeJsDocComment));
}

/**
* Gets the length from the start of the line to the end of the node.
*/
getEndColumn() {
return this.sourceFile.getColumnAtPos(this.getEnd());
}

/**
* Gets if this is the first node on the current line.
*/
Expand Down
8 changes: 8 additions & 0 deletions src/compiler/file/SourceFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,14 @@ export class SourceFile extends SourceFileBase<ts.SourceFile> {
return StringUtils.getLineNumberFromPos(this.getFullText(), pos);
}

/**
* Gets the length from the start of the line to the provided position.
* @param pos - Position.
*/
getColumnAtPos(pos: number) {
return StringUtils.getColumnAtPos(this.getFullText(), pos);
}

/**
* Copy this source file to a new file.
*
Expand Down
34 changes: 34 additions & 0 deletions src/tests/compiler/common/nodeTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,16 @@ class MyClass {
const {firstChild} = getInfoFromText<ClassDeclaration>("\n\nclass MyClass {\n\n prop: string;\n}");
expect(firstChild.getInstanceProperties()[0].getStartLineNumber()).to.equal(5);
});

it("should get the start line number of the node including js docs", () => {
const {firstChild} = getInfoFromText<ClassDeclaration>("\n\n/** Testing*/\nclass MyClass {}");
expect(firstChild.getStartLineNumber(true)).to.equal(3);
});

it("should get the start line number of the node not including js docs", () => {
const {firstChild} = getInfoFromText<ClassDeclaration>("\n\n/** Testing*/\nclass MyClass {}");
expect(firstChild.getStartLineNumber()).to.equal(4);
});
});

describe(nameof<Node>(n => n.getEndLineNumber), () => {
Expand All @@ -337,6 +347,30 @@ class MyClass {
});
});

describe(nameof<Node>(n => n.getStartColumn), () => {
it("should get the column of the node", () => {
const {firstChild} = getInfoFromText<ClassDeclaration>(" class MyClass {}");
expect(firstChild.getStartColumn()).to.equal(4);
});

it("should get the column of the node including js docs", () => {
const {firstChild} = getInfoFromText<ClassDeclaration>(" /** Testing */class MyClass {}");
expect(firstChild.getStartColumn(true)).to.equal(4);
});

it("should get the column of the node not including js docs", () => {
const {firstChild} = getInfoFromText<ClassDeclaration>(" /** Testing */class MyClass {}");
expect(firstChild.getStartColumn()).to.equal(18);
});
});

describe(nameof<Node>(n => n.getEndColumn), () => {
it("should get the column of the end of the node", () => {
const {firstChild} = getInfoFromText<ClassDeclaration>("class MyClass {}");
expect(firstChild.getEndColumn()).to.equal(16);
});
});

describe(nameof<Node>(n => n.getStart), () => {
function doTest(text: string, expectedPos: number, includeJsDocComment?: boolean) {
const {firstChild} = getInfoFromText(text);
Expand Down
43 changes: 43 additions & 0 deletions src/tests/utils/stringUtilsTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,49 @@ describe(nameof(StringUtils), () => {
});
});

describe(nameof(StringUtils.getColumnAtPos), () => {
it("should throw if providing a negative pos", () => {
expect(() => StringUtils.getColumnAtPos("", -1)).to.throw(errors.ArgumentOutOfRangeError);
});

it("should not throw if providing a pos the length of the string", () => {
expect(() => StringUtils.getColumnAtPos("", 1)).to.not.throw();
});

it("should throw if providing a pos greater than the length + 1", () => {
expect(() => StringUtils.getColumnAtPos("", 2)).to.throw(errors.ArgumentOutOfRangeError);
});

function doTest(text: string, pos: number, expected: number) {
expect(StringUtils.getColumnAtPos(text, pos)).to.equal(expected);
}

function doNewlineTest(newLineKind: string) {
let text = "testing" + newLineKind;
const startLinePos = text.length;
text += "more text";
const pos = text.length;
text += newLineKind + "more text";
doTest(text, pos, pos - startLinePos);
}

it("should get for the specified pos when using \r newlines", () => {
doNewlineTest("\r");
});

it("should get for the specified pos when using \n newlines", () => {
doNewlineTest("\n");
});

it("should get for the specified pos when using \r\n newlines", () => {
doNewlineTest("\r\n");
});

it("should get on the first line", () => {
doTest("testing this out", 10, 10);
});
});

describe(nameof(StringUtils.escapeForWithinString), () => {
function doTest(input: string, expected: string) {
expect(StringUtils.escapeForWithinString(input, QuoteKind.Double)).to.equal(expected);
Expand Down
14 changes: 14 additions & 0 deletions src/utils/StringUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,20 @@ export class StringUtils {
return count + 1; // convert count to line number
}

static getColumnAtPos(str: string, pos: number) {
errors.throwIfOutOfRange(pos, [0, str.length + 1], nameof(pos));
const startPos = pos;

while (pos > 0) {
const previousChar = str[pos - 1];
if (previousChar === "\n" || previousChar === "\r")
break;
pos--;
}

return startPos - pos;
}

static escapeForWithinString(str: string, quoteKind: QuoteKind) {
return StringUtils.escapeChar(str, quoteKind).replace(/(\r?\n)/g, "\\$1");
}
Expand Down

0 comments on commit 5119e83

Please sign in to comment.