Skip to content

Commit

Permalink
feat: Node.getStartLineNumber and SourceFile.getLineNumberFromPos.
Browse files Browse the repository at this point in the history
  • Loading branch information
dsherret committed Jan 27, 2018
1 parent 714713b commit 64178fa
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 1 deletion.
8 changes: 8 additions & 0 deletions src/compiler/common/Node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,14 @@ export class Node<NodeType extends ts.Node = ts.Node> {
return getPreviousMatchingPos(sourceFileText, this.getStart(includeJsDocComment), char => char === "\n");
}

/**
* Gets the line number at the start of the node.
* @param includeJsDocComment - Whether to include the JS doc comment or not.
*/
getStartLineNumber(includeJsDocComment?: boolean) {
return this.getSourceFile().getLineNumberFromPos(this.getStartLinePos(includeJsDocComment));
}

/**
* 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 @@ -95,6 +95,14 @@ export class SourceFile extends SourceFileBase<ts.SourceFile> {
return FileUtils.getDirPath(this.compilerNode.fileName);
}

/**
* Gets the line number of the provided position.
* @param pos - Position
*/
getLineNumberFromPos(pos: number) {
return StringUtils.getLineNumberFromPos(this.getFullText(), pos);

This comment has been minimized.

Copy link
@cancerberoSgx

cancerberoSgx May 20, 2018

Contributor

Why using in-home implementation StringUtils.getLineNumberFromPos() while we have native ts.getLineAndCharacterOfPosition and ts.getPositionOfLineAndCharacter ? is it because compiler node and simple-ast nodes might not be in sync? I want to add missing getPositionOfLineAndCharacter equivalent in SourceFile and I was planning to use the native method that's why I'm asking. Thanks

This comment has been minimized.

Copy link
@dsherret

dsherret May 21, 2018

Author Owner

I went with my own implementation because the function in StringUtils is more efficient and faster (it doesn't instantiate an array with many strings).

For the purpose of getting the position of a line and character, it would also probably be best to roll with our own implementation that doesn't instantiate anything and instead just inspects the existing string.

By the way, the compiler nodes and wrapped nodes will always be in sync.

This comment has been minimized.

Copy link
@cancerberoSgx

cancerberoSgx May 21, 2018

Contributor

thanks! "the compiler nodes and wrapped nodes will always be in sync." This is good info. In the meanwhile, can I contribute with the reverse equivalent to getPositionOfLineAndCharacter by using the native one ? THanks

This comment has been minimized.

Copy link
@dsherret

dsherret May 21, 2018

Author Owner

@cancerberoSgx for sure! I can optimize it later over time.

}

/**
* Copy this source file to a new file.
* @param filePath - A new file path. Can be relative to the original file or an absolute path.
Expand Down
7 changes: 7 additions & 0 deletions src/tests/compiler/common/nodeTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,13 @@ describe(nameof(Node), () => {
});
});

describe(nameof<Node>(n => n.getStartLineNumber), () => {
it("should get the start line number of the node", () => {
const {firstChild} = getInfoFromText<ClassDeclaration>("\n\nclass MyClass {\n\n prop: string;\n}");
expect(firstChild.getInstanceProperties()[0].getStartLineNumber()).to.equal(5);
});
});

describe(nameof<Node>(n => n.getStart), () => {
function doTest(text: string, expectedPos: number, includeJsDocComment?: boolean) {
const {firstChild} = getInfoFromText(text);
Expand Down
45 changes: 45 additions & 0 deletions src/tests/utils/stringUtilsTests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {expect} from "chai";
import * as errors from "./../../errors";
import {StringUtils} from "./../../utils";

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

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

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

function doTest(newLineType: string) {
let str = `testing${newLineType}this${newLineType}out`;
const pos = str.length;
str += `${newLineType}more and more${newLineType}and more`;
expect(StringUtils.getLineNumberFromPos(str, pos)).to.equal(3);
}

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

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

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

it("should get the line position for the specified pos when right after the newline when mixing newlines", () => {
let str = "testing\r\nthis\nout\rmore\r\nandmore\n";
const pos = str.length;
str += "out\r\nmore and more";
expect(StringUtils.getLineNumberFromPos(str, pos)).to.equal(6);
});
});
});
17 changes: 16 additions & 1 deletion src/utils/StringUtils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export class StringUtils {
import * as errors from "./../errors";

export class StringUtils {
private constructor() {
}

Expand All @@ -20,4 +22,17 @@
static endsWith(str: string, endsWithString: string) {
return str.substr(str.length - endsWithString.length, endsWithString.length) === endsWithString;
}

static getLineNumberFromPos(str: string, pos: number) {
errors.throwIfOutOfRange(pos, [0, str.length + 1], nameof(pos));
// do not allocate a string in this method
let count = 0;

for (let i = 0; i < pos; i++) {
if (str[i] === "\n" || (str[i] === "\r" && str[i + 1] !== "\n"))
count++;
}

return count + 1; // convert count to line number
}
}

0 comments on commit 64178fa

Please sign in to comment.