Skip to content

Commit

Permalink
refactor: convert to TypeScript (#7)
Browse files Browse the repository at this point in the history
## Breaking Changes

- Add `SourceRange`, `SourcePosition`, `SourceLocation` as types
- Remove `SourceLocation` and `SourcePosition` class. It becomes just object
- Make `source.indice` private

## Features

- Support TypeScript 
  - You can remove `@types/structured-source`

fix #5
  • Loading branch information
azu authored Jan 8, 2023
1 parent 0647df2 commit 868c449
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 116 deletions.
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,25 @@ npm install structured-source
## Usage

```js
const StructuredSource = require('structured-source');
import assert from "node:assert";
import { StructuredSource } from "structured-source";

let src = new StructuredSource('aaa\u2028aaaa\u2029aaaaa\n');
const src = new StructuredSource('aaa\u2028aaaa\u2029aaaaa\n');

// positionToIndex({ line: number, column: number) -> number
assert(src.positionToIndex({ line: 1, column: 2 }) === 2);

// indexToPosition(number) -> { line: number, column: number }
assert.deepEqual(src.indexToPosition(2), { line: 1, column: 2 });
assert.deepStrictEqual(src.indexToPosition(2), { line: 1, column: 2 });

// rangeToLocation([ number, number ]) -> { start: { line: number, column: number}, end: { line: number, column: number } }
assert.deepEqual(src.rangeToLocation([0, 2]), {
assert.deepStrictEqual(src.rangeToLocation([0, 2]), {
start: { line: 1, column: 0 },
end: { line: 1, column: 2 }
});

// locationToRange({ start: { line: number, column: number}, end: { line: number, column: number } }) -> [ number, number ]
assert.deepEqual(src.locationToRange({
assert.deepStrictEqual(src.locationToRange({
start: { line: 1, column: 0 },
end: { line: 1, column: 2 }
}), [0, 2]);
Expand Down
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
"web": "http://github.com/Constellation"
}
],
"main": "lib/index.js",
"types": "lib/index.d.ts",
"main": "lib/structured-source.js",
"types": "lib/structured-source.d.ts",
"files": [
"lib"
],
Expand All @@ -38,7 +38,7 @@
"watch": "tsc -p . --watch"
},
"dependencies": {
"boundary": "^1.0.1"
"boundary": "^2.0.0"
},
"devDependencies": {
"@types/mocha": "^10.0.1",
Expand Down
29 changes: 0 additions & 29 deletions src/index.js

This file was deleted.

58 changes: 31 additions & 27 deletions src/structured-source.js → src/structured-source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,33 +24,31 @@

import { upperBound } from 'boundary';

export class Position {
constructor(line, column) {
this.line = line;
this.column = column;
}
export type SourceRange = readonly [number, number];
export type SourcePosition = {
readonly line: number,
readonly column: number,
}

export class SourceLocation {
constructor(start, end) {
this.start = start;
this.end = end;
}
export type SourceLocation = {
readonly end: SourcePosition,
readonly start: SourcePosition,
}

/**
* StructuredSource
* @class
*/
export default class StructuredSource {
export class StructuredSource {
private readonly indice: number[];

/**
* @constructs StructuredSource
* @param {string} source - source code text.
*/
constructor(source) {
this.indice = [ 0 ];
constructor(source: string) {
this.indice = [0];
let regexp = /[\r\n\u2028\u2029]/g;
let length = source.length;
const length = source.length;
regexp.lastIndex = 0;
while (true) {
let result = regexp.exec(source);
Expand All @@ -59,7 +57,7 @@ export default class StructuredSource {
}
let index = result.index;
if (source.charCodeAt(index) === 0x0D /* '\r' */ &&
source.charCodeAt(index + 1) === 0x0A /* '\n' */) {
source.charCodeAt(index + 1) === 0x0A /* '\n' */) {
index += 1;
}
let nextIndex = index + 1;
Expand All @@ -73,31 +71,34 @@ export default class StructuredSource {
}
}

get line() {
get line(): number {
return this.indice.length;
}

/**
* @param {SourceLocation} loc - location indicator.
* @return {[ number, number ]} range.
*/
locationToRange(loc) {
return [ this.positionToIndex(loc.start), this.positionToIndex(loc.end) ];
locationToRange(loc: SourceLocation): SourceRange {
return [this.positionToIndex(loc.start), this.positionToIndex(loc.end)];
}

/**
* @param {[ number, number ]} range - pair of indice.
* @return {SourceLocation} location.
*/
rangeToLocation(range) {
return new SourceLocation(this.indexToPosition(range[0]), this.indexToPosition(range[1]));
rangeToLocation(range: SourceRange): SourceLocation {
return {
start: this.indexToPosition(range[0]),
end: this.indexToPosition(range[1])
};
}

/**
* @param {Position} pos - position indicator.
* @param {SourcePosition} pos - position indicator.
* @return {number} index.
*/
positionToIndex(pos) {
positionToIndex(pos: SourcePosition): number {
// Line number starts with 1.
// Column number starts with 0.
let start = this.indice[pos.line - 1];
Expand All @@ -106,11 +107,14 @@ export default class StructuredSource {

/**
* @param {number} index - index to the source code.
* @return {Position} position.
* @return {SourcePosition} position.
*/
indexToPosition(index) {
let startLine = upperBound(this.indice, index);
return new Position(startLine, index - this.indice[startLine - 1]);
indexToPosition(index: number): SourcePosition {
const startLine = upperBound(this.indice, index);
return {
line: startLine,
column: index - this.indice[startLine - 1]
};
}
}

Expand Down
90 changes: 48 additions & 42 deletions test/source.js → test/structured-source.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,51 +22,57 @@
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

import StructuredSource from '../src/index.js'
import { StructuredSource } from '../src/structured-source'
import assert from 'assert'

describe('StructuredSource', () => {
it('constructor', () => {
{
let src = new StructuredSource('');
assert.deepEqual(src.indice, [ 0 ]);
assert(src.line === 1);
const src = new StructuredSource('');
// @ts-expect-error: for testing
assert.deepStrictEqual(src.indice, [0]);
assert.strictEqual(src.line , 1);
}

{
let src = new StructuredSource('\n');
assert.deepEqual(src.indice, [ 0, 1 ]);
assert(src.line === 2);
const src = new StructuredSource('\n');
// @ts-expect-error: for testing
assert.deepStrictEqual(src.indice, [0, 1]);
assert.strictEqual(src.line , 2);
}

{
let src = new StructuredSource('\r\n');
assert.deepEqual(src.indice, [ 0, 2 ]);
assert(src.line === 2);
const src = new StructuredSource('\r\n');
// @ts-expect-error: for testing
assert.deepStrictEqual(src.indice, [0, 2]);
assert.strictEqual(src.line , 2);
}

{
let src = new StructuredSource('\n\r');
assert.deepEqual(src.indice, [ 0, 1, 2 ]);
assert(src.line === 3);
const src = new StructuredSource('\n\r');
// @ts-expect-error: for testing
assert.deepStrictEqual(src.indice, [0, 1, 2]);
assert.strictEqual(src.line , 3);
}

{
let src = new StructuredSource('aaa\naaaa\raaaaa');
assert.deepEqual(src.indice, [ 0, 4, 9 ]);
assert(src.line === 3);
const src = new StructuredSource('aaa\naaaa\raaaaa');
// @ts-expect-error: for testing
assert.deepStrictEqual(src.indice, [0, 4, 9]);
assert.strictEqual(src.line , 3);
}

{
let src = new StructuredSource('aaa\u2028aaaa\u2029aaaaa\n');
assert.deepEqual(src.indice, [ 0, 4, 9, 15 ]);
assert(src.line === 4);
const src = new StructuredSource('aaa\u2028aaaa\u2029aaaaa\n');
// @ts-expect-error: for testing
assert.deepStrictEqual(src.indice, [0, 4, 9, 15]);
assert.strictEqual(src.line , 4);
}
});

it('positionToIndex', () => {
{
let src = new StructuredSource('aaa\u2028aaaa\u2029aaaaa\n');
const src = new StructuredSource('aaa\u2028aaaa\u2029aaaaa\n');
assert(src.positionToIndex({ line: 1, column: 2 }) === 2);
assert(src.positionToIndex({ line: 2, column: 2 }) === 6);
assert(src.positionToIndex({ line: 2, column: 5 }) === 9); // out of source column is calculated.
Expand All @@ -79,39 +85,39 @@ describe('StructuredSource', () => {

it('indexToPosition', () => {
{
let src = new StructuredSource('aaa\u2028aaaa\u2029aaaaa\n');
assert.deepEqual(src.indexToPosition(2), { line: 1, column: 2 });
assert.deepEqual(src.indexToPosition(6), { line: 2, column: 2 });
assert.deepEqual(src.indexToPosition(9), { line: 3, column: 0 });
assert.deepEqual(src.indexToPosition(15), { line: 4, column: 0 });
assert.deepEqual(src.indexToPosition(25), { line: 4, column: 10 });
assert.deepEqual(src.indexToPosition(30), { line: 4, column: 15 });
assert.deepEqual(src.indexToPosition(0), { line: 1, column: 0 });
const src = new StructuredSource('aaa\u2028aaaa\u2029aaaaa\n');
assert.deepStrictEqual(src.indexToPosition(2), { line: 1, column: 2 });
assert.deepStrictEqual(src.indexToPosition(6), { line: 2, column: 2 });
assert.deepStrictEqual(src.indexToPosition(9), { line: 3, column: 0 });
assert.deepStrictEqual(src.indexToPosition(15), { line: 4, column: 0 });
assert.deepStrictEqual(src.indexToPosition(25), { line: 4, column: 10 });
assert.deepStrictEqual(src.indexToPosition(30), { line: 4, column: 15 });
assert.deepStrictEqual(src.indexToPosition(0), { line: 1, column: 0 });
}

{
let src = new StructuredSource('');
assert.deepEqual(src.indexToPosition(2), { line: 1, column: 2 });
assert.deepEqual(src.indexToPosition(6), { line: 1, column: 6 });
assert.deepEqual(src.indexToPosition(0), { line: 1, column: 0 });
const src = new StructuredSource('');
assert.deepStrictEqual(src.indexToPosition(2), { line: 1, column: 2 });
assert.deepStrictEqual(src.indexToPosition(6), { line: 1, column: 6 });
assert.deepStrictEqual(src.indexToPosition(0), { line: 1, column: 0 });
}
});

it('rangeToLocation', () => {
{
let src = new StructuredSource('aaa\u2028aaaa\u2029aaaaa\n');
assert.deepEqual(src.rangeToLocation([0, 2]), {
const src = new StructuredSource('aaa\u2028aaaa\u2029aaaaa\n');
assert.deepStrictEqual(src.rangeToLocation([0, 2]), {
start: { line: 1, column: 0 },
end: { line: 1, column: 2 }
});
assert.deepEqual(src.rangeToLocation([0, 45]), {
assert.deepStrictEqual(src.rangeToLocation([0, 45]), {
start: { line: 1, column: 0 },
end: { line: 4, column: 30 }
});
}
{
let src = new StructuredSource('');
assert.deepEqual(src.rangeToLocation([0, 2]), {
const src = new StructuredSource('');
assert.deepStrictEqual(src.rangeToLocation([0, 2]), {
start: { line: 1, column: 0 },
end: { line: 1, column: 2 }
});
Expand All @@ -120,19 +126,19 @@ describe('StructuredSource', () => {

it('locationToRange', () => {
{
let src = new StructuredSource('aaa\u2028aaaa\u2029aaaaa\n');
assert.deepEqual(src.locationToRange({
const src = new StructuredSource('aaa\u2028aaaa\u2029aaaaa\n');
assert.deepStrictEqual(src.locationToRange({
start: { line: 1, column: 0 },
end: { line: 1, column: 2 }
}), [0, 2]);
assert.deepEqual(src.locationToRange({
assert.deepStrictEqual(src.locationToRange({
start: { line: 1, column: 0 },
end: { line: 4, column: 30 }
}), [0, 45]);
}
{
let src = new StructuredSource('');
assert.deepEqual(src.locationToRange({
const src = new StructuredSource('');
assert.deepStrictEqual(src.locationToRange({
start: { line: 1, column: 0 },
end: { line: 1, column: 2 }
}), [0, 2]);
Expand Down
2 changes: 1 addition & 1 deletion test/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@
"../src/**/*",
"./**/*"
]
}
}
Loading

0 comments on commit 868c449

Please sign in to comment.