Skip to content

Commit

Permalink
fix: support importing a single line of code
Browse files Browse the repository at this point in the history
  • Loading branch information
haocheng6 committed Dec 26, 2023
1 parent f797375 commit 53802a3
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 14 deletions.
47 changes: 34 additions & 13 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,51 @@
import parse from 'fenceparser';

const lineRangeRegex = /^L(\d+)(?:-L(\d+))?$/;

export function isCodeReference(meta: string): boolean {
// @ts-expect-error: the library definition is wrong.
return parse(meta).reference === true;
}

export function parseLineRange(
range: string,
): undefined | number | [number, number] {
if (!range) {
return undefined;
}

const match = range.match(lineRangeRegex);
if (match === null) {
throw new Error(`Invalid line range: ${range}.`);
}

const fromLineStr = match[1];
const toLineStr = match[2];
const fromLine = Number(fromLineStr);
const toLine = Number(toLineStr);

if (toLineStr === undefined) {
return fromLine;
}
return [fromLine, toLine];
}

export async function readCode(
referenceUrl: string,
dedentCode = true,
): Promise<string> {
const url = new URL(referenceUrl);

const range = url.hash.slice(1);
let [fromLine, toLine] = range
.split('-')
.map((line) => (line !== undefined ? Number(line.slice(1)) : undefined));
fromLine ||= 1;
toLine ??= Infinity;
const range = parseLineRange(url.hash.slice(1));
let fromLine: number;
let toLine: number;

if (
fromLine === undefined ||
toLine === undefined ||
isNaN(fromLine) ||
isNaN(toLine)
) {
throw new Error(`URL does not have a valid line range: ${range}.`);
if (range === undefined) {
[fromLine, toLine] = [1, Infinity];
} else if (typeof range === 'number') {
[fromLine, toLine] = [range, range];
} else {
[fromLine, toLine] = range;
}

const [, user, repo, , ...path] = url.pathname.split('/');
Expand Down
5 changes: 5 additions & 0 deletions test/fixtures/input/single_line.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Example

```js reference
https://github.com/user/repo/blob/branch/folder/example.js#L8
```
1 change: 1 addition & 0 deletions test/fixtures/output/example_single_line.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
return undefined;
1 change: 1 addition & 0 deletions test/fixtures/output/example_single_line_dedented.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
return undefined;
11 changes: 11 additions & 0 deletions test/fixtures/output/single_line_dedented.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Example

<div class="imported-github-code">

```js reference
return undefined;
```

<div class="github-code-link"><a href="https://github.com/user/repo/blob/branch/folder/example.js#L8" target="_blank">See full example on GitHub</a></div>

</div>
9 changes: 9 additions & 0 deletions test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@ describe('remarkGithubCodeImport', () => {
);
});

it('imports code from the specified line when the URL in the reference code block specifies a line', async () => {
const processor = remark().use(remarkGithubCodeImport);
const markdown = await getFixtureVFile('input/single_line.md');

expect(String(await processor.process(markdown))).toMatchFileSnapshot(
'./fixtures/output/single_line_dedented.md',
);
});

it('does not dedent the imported code when the `dedentCode` options is false', async () => {
const processor = remark().use(remarkGithubCodeImport, {
dedentCode: false,
Expand Down
49 changes: 48 additions & 1 deletion test/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { isCodeReference, readCode } from '../src/utils.js';
import { isCodeReference, parseLineRange, readCode } from '../src/utils.js';

import { getFixtureString } from './helpers.js';

Expand Down Expand Up @@ -36,6 +36,41 @@ describe('isCodeReference', () => {
);
});

describe('parseLineRange', () => {
it('returns `undefined` when no line range is given', () => {
expect(parseLineRange('')).toBe(undefined);
});

it.each([
'L',
'La',
'L1x',
'-',
'L-L',
'L1-',
'-L10',
'L1x-L10',
'L1-Lx',
'X1-X3',
'L1-L10_',
])('throws an error when an invalid line range (%s) is given', (range) => {
expect(() => parseLineRange(range)).toThrow(
`Invalid line range: ${range}.`,
);
});

it.each([
['L10', 10],
['L1-L3', [1, 3]],
['L10-L10', [10, 10]],
])(
'returns the parsed line range when a valid line range (%s) is given',
(range, result) => {
expect(parseLineRange(range)).toStrictEqual(result);
},
);
});

describe('readCode', () => {
const mockCode = getFixtureString('input/example.js');
const mockUrl = 'https://github.com/user/repo/blob/branch/folder/example.js';
Expand All @@ -58,6 +93,12 @@ describe('readCode', () => {
'./fixtures/output/example_line_range.js',
);
});

it('reads code from the specified line range when a single line is given', async () => {
expect(await readCode(`${mockUrl}#L8`, false)).toMatchFileSnapshot(
'./fixtures/output/example_single_line.js',
);
});
});

describe('when `dedentCode` is true', () => {
Expand All @@ -72,5 +113,11 @@ describe('readCode', () => {
'./fixtures/output/example_line_range_dedented.js',
);
});

it('reads code from the specified line range when a single line is given', async () => {
expect(await readCode(`${mockUrl}#L8`, true)).toMatchFileSnapshot(
'./fixtures/output/example_single_line_dedented.js',
);
});
});
});

0 comments on commit 53802a3

Please sign in to comment.