Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions packages/@d-zero/readtext/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,19 @@ const list = await readList('path/to/file.txt');
const kv = await readList('path/to/file.txt', ' ');
```

### `readListWithPosition` — 元の行番号・列番号付き

`readList` と同じ空行・コメント除外規則だが、各要素の値に加えて元ファイル内の 1-origin の行番号・列番号を保持する。不正な行を警告表示する際など、元の位置に戻す必要がある場合に使う。

```ts
import { readListWithPosition } from '@d-zero/readtext/list';

const items = await readListWithPosition('path/to/file.txt');
// items: [{ value: 'item1', line: 1, column: 1 }, ...]
```

文字列をそのまま渡したい場合は `toListWithPosition` を使う。

### `readGrid` — 区切り文字で 2D 配列

```ts
Expand Down
3 changes: 3 additions & 0 deletions packages/@d-zero/readtext/src/list.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
export { readList } from './read-list.js';
export { readListWithPosition } from './read-list-with-position.js';
export { toKvList } from './to-kv-list.js';
export { toList } from './to-list.js';
export { toListWithPosition } from './to-list-with-position.js';
export type { ListItem } from './types.js';
21 changes: 21 additions & 0 deletions packages/@d-zero/readtext/src/read-list-with-position.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import fs from 'node:fs/promises';

import { toListWithPosition } from './to-list-with-position.js';

/**
* Reads a list file and returns each surviving line tagged with its
* 1-origin line/column position in the source file — the position-aware
* companion to `readList`, for callers that need to report which line of
* the file an invalid entry came from.
* @param filePath - The path to the file to read.
* @returns A promise that resolves to the surviving lines with position info.
* @example
* ```typescript
* const items = await readListWithPosition('/path/to/file.txt');
* // items: [{ value: 'item1', line: 1, column: 1 }, ...]
* ```
*/
export async function readListWithPosition(filePath: string) {
const fileContent = await fs.readFile(filePath, 'utf8');
return toListWithPosition(fileContent);
}
44 changes: 44 additions & 0 deletions packages/@d-zero/readtext/src/to-list-with-position.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { describe, test, expect } from 'vitest';

import { toListWithPosition } from './to-list-with-position.js';

describe('toListWithPosition', () => {
test('assigns 1-origin line numbers, skipping blank and comment lines', () => {
expect(
toListWithPosition(`item1
# comment

item2
`),
).toStrictEqual([
{ value: 'item1', line: 1, column: 1 },
{ value: 'item2', line: 4, column: 1 },
]);
});

test('reports the column where leading whitespace ends', () => {
expect(toListWithPosition(' item1\n\titem2\nitem3')).toStrictEqual([
{ value: 'item1', line: 1, column: 3 },
{ value: 'item2', line: 2, column: 2 },
{ value: 'item3', line: 3, column: 1 },
]);
});

test('treats a whitespace-only line as blank', () => {
expect(toListWithPosition('item1\n \nitem2')).toStrictEqual([
{ value: 'item1', line: 1, column: 1 },
{ value: 'item2', line: 3, column: 1 },
]);
});

test('strips the trailing \\r from CRLF line endings without shifting the column', () => {
expect(toListWithPosition('item1\r\n item2\r\n')).toStrictEqual([
{ value: 'item1', line: 1, column: 1 },
{ value: 'item2', line: 2, column: 3 },
]);
});

test('returns an empty array for text with no surviving lines', () => {
expect(toListWithPosition('\n# comment\n\n')).toStrictEqual([]);
});
});
44 changes: 44 additions & 0 deletions packages/@d-zero/readtext/src/to-list-with-position.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import type { ListItem } from './types.js';

/**
* Splits text into non-empty, non-comment lines (same rules as `toList`),
* keeping each surviving line's 1-origin line/column position in the
* original text.
*
* `toList` discards this position by filtering before returning a plain
* `string[]` — a caller that needs to report "line 3 of the source file was
* invalid" back to a user cannot reconstruct it from the filtered array
* alone, since blank lines and `#` comments have already shifted the index.
* @param text - The raw text to split into lines.
* @returns Surviving lines with their original line/column position.
* @example
* ```typescript
* const items = toListWithPosition('item1\n# comment\n\n item2\n');
* // items: [
* // { value: 'item1', line: 1, column: 1 },
* // { value: 'item2', line: 4, column: 3 },
* // ]
* ```
*/
export function toListWithPosition(text: string): ListItem[] {
const lines = text.split('\n');
const items: ListItem[] = [];

for (const [index, rawLine] of lines.entries()) {
const value = rawLine.trim();

// Empty (post-trim) and comment lines are dropped, matching `toList`.
if (value.length === 0 || value.startsWith('#')) {
continue;
}

const leadingWhitespaceLength = rawLine.length - rawLine.trimStart().length;
items.push({
value,
line: index + 1,
column: leadingWhitespaceLength + 1,
});
}

return items;
}
15 changes: 3 additions & 12 deletions packages/@d-zero/readtext/src/to-list.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,9 @@
import { toListWithPosition } from './to-list-with-position.js';

/**
*
* @param text
*/
export function toList(text: string) {
const lines = text.split('\n');

// Trim
const trimmedLines = lines.map((line) => line.trim());

// Remove empty lines
const nonEmptyLines = trimmedLines.filter((line) => line.length > 0);

// Remove comments
const nonCommentLines = nonEmptyLines.filter((line) => !line.startsWith('#'));

return nonCommentLines;
return toListWithPosition(text).map((item) => item.value);
}
13 changes: 13 additions & 0 deletions packages/@d-zero/readtext/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,16 @@ export type KeyValue = {
key: string;
value: string;
};

/**
* A single non-empty, non-comment line surviving `toListWithPosition`'s
* filtering, tagged with its position in the original source text.
*/
export type ListItem = {
/** The trimmed line content (comments and blank lines already excluded). */
value: string;
/** 1-origin line number in the original source text. */
line: number;
/** 1-origin column of `value`'s first character in the original line (i.e. where leading whitespace ended). */
column: number;
};
Loading