Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add getPosAtLineNumber #893

Open
wants to merge 1 commit into
base: latest
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/common/lib/ts-morph-common.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -966,6 +966,7 @@ export declare class StringUtils {
static endsWithNewLine(str: string | undefined): boolean;
static insertAtLastNonWhitespace(str: string, insertText: string): string;
static getLineNumberAtPos(str: string, pos: number): number;
static getPosAtLineNumber(str: string, line: number): number;
static getLengthFromLineStartAtPos(str: string, pos: number): number;
static getLineStartFromPos(str: string, pos: number): number;
static getLineEndFromPos(str: string, pos: number): number;
Expand Down
51 changes: 51 additions & 0 deletions packages/common/src/tests/utils/stringUtilsTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,57 @@ describe(nameof(StringUtils), () => {
});
});

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

it("should not throw if providing a line number the amount of lines", () => {
expect(() => StringUtils.getPosAtLineNumber("", 1)).to.not.throw();
});

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

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

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

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

it("should get the pos for the specified line number when right after the newline when mixing newlines", () => {
let str = "testing\r\nthis\nout\nmore\r\nandmore\n";
const pos = str.length;
str += "out\r\nmore and more";
expect(StringUtils.getPosAtLineNumber(str, 6)).to.equal(pos);
});

function testSymmetry(newLineType: string) {
let str = `testing${newLineType}this${newLineType}out`;
const posAtLine = StringUtils.getPosAtLineNumber(str, 2);
const lineAtPos = StringUtils.getLineNumberAtPos(str, posAtLine);
expect(lineAtPos).to.equal(2);
}

it("should be symmetric with getLineNumberAtPos when using \\n newlines", () => {
testSymmetry("\n");
});

it("should be symmetric with getLineNumberAtPos when using \\r\\n newlines", () => {
testSymmetry("\r\n");
});
});

describe(nameof(StringUtils.getLengthFromLineStartAtPos), () => {
it("should throw if providing a negative pos", () => {
expect(() => StringUtils.getLengthFromLineStartAtPos("", -1)).to.throw(errors.ArgumentOutOfRangeError);
Expand Down
12 changes: 12 additions & 0 deletions packages/common/src/utils/StringUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,18 @@ export class StringUtils {
return count + 1; // convert count to line number
}

static getPosAtLineNumber(str: string, line: number) {
const lines = str.split('\n');
errors.throwIfOutOfRange(line, [1, lines.length], nameof(line));
let count = 0;

for (let i = 0; i < line - 1; i++) { // line number is 1-indexed
count += lines[i].length + 1; // include newline
}

return count; // convert count to pos
}

static getLengthFromLineStartAtPos(str: string, pos: number) {
errors.throwIfOutOfRange(pos, [0, str.length], nameof(pos));
return pos - StringUtils.getLineStartFromPos(str, pos);
Expand Down
6 changes: 6 additions & 0 deletions packages/ts-morph/lib/ts-morph.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7203,6 +7203,12 @@ export declare class SourceFile extends SourceFileBase<ts.SourceFile> {
line: number;
column: number;
};
/**
* Gets the position (1-indexed) at the provided line and column number.
* @param line - Line number in the source file.
* @param col - Column number in the source file.
*/
getPosAtLineAndColumn(line: number, col: number): number;
/**
* Gets the character count from the start of the line to the provided position.
* @param pos - Position.
Expand Down
12 changes: 11 additions & 1 deletion packages/ts-morph/src/compiler/ast/module/SourceFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ export class SourceFile extends SourceFileBase<ts.SourceFile> {
}

/**
* Gets the line and column number at the provided position (1-indexed).
* Gets the line and column number (1-indexed) at the provided position.
* @param pos - Position in the source file.
*/
getLineAndColumnAtPos(pos: number) {
Expand All @@ -173,6 +173,16 @@ export class SourceFile extends SourceFileBase<ts.SourceFile> {
};
}

/**
* Gets the position at the provided line and column number (1-indexed).
* @param line - Line number in the source file.
* @param col - Column number in the source file.
*/
getPosAtLineAndColumn(line: number, col: number) {
const fullText = this.getFullText();
return StringUtils.getPosAtLineNumber(fullText, line) + col;
}

/**
* Gets the character count from the start of the line to the provided position.
* @param pos - Position.
Expand Down