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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: dedent spacing #147

Closed
wants to merge 1 commit into from
Closed
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
18 changes: 12 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,17 @@ Lib mainly provides two pieces [Parser](#Parser) and [Stringifier](#Stringifier)
Let's go over string parsing:

```js
const { parse } = require('comment-parser/lib')
const { parse } = require('comment-parser/lib');

const source = `
/**
* Description may go
* over few lines followed by @tags
* @param {string} name the name parameter
* @param {any} value the value of any type
*/`
*/`;

const parsed = parse(source)
const parsed = parse(source);
```

Lib source code is written in TypeScript and all data shapes are conveniently available for your IDE of choice. All types described below can be found in [primitives.ts](src/primitives.ts)
Expand Down Expand Up @@ -160,13 +160,14 @@ interface Options {
// escaping chars sequence marking wrapped content literal for the parser
fence: string;
// block and comment description compaction strategy
spacing: 'compact' | 'preserve';
spacing: 'compact' | 'preserve' | 'dedent';
// tokenizer functions extracting name, type, and description out of tag, see Tokenizer
tokenizers: Tokenizer[];
}
```

examples

- [default config](https://syavorsky.github.io/comment-parser/#parse-defaults)
- [line numbers control](https://syavorsky.github.io/comment-parser/#parse-line-numbering)
- [description spacing](https://syavorsky.github.io/comment-parser/#parse-spacing)
Expand All @@ -180,7 +181,11 @@ examples
The stringifier is an important piece used by other tools updating the source code. It goes over `Block.source[].tokens` items and assembles them back to the string. It might be used with various transforms applied before stringifying.

```js
const { parse, stringify, transforms: {flow, align, indent} } = require('comment-parser');
const {
parse,
stringify,
transforms: { flow, align, indent },
} = require('comment-parser');

const source = `
/**
Expand All @@ -193,7 +198,7 @@ const source = `
*/`;

const parsed = parse(source);
const transform = flow(align(), indent(0))
const transform = flow(align(), indent(0));
console.log(stringify(transform(parsed[0])));
```

Expand All @@ -211,6 +216,7 @@ console.log(stringify(transform(parsed[0])));
```

examples

- [format comments](https://syavorsky.github.io/comment-parser/#stringify-formatting)

[suggest more examples](https://github.com/syavorsky/comment-parser/issues/new?title=example+suggestion%3A+...&labels=example,stringifier)
Expand Down
1 change: 1 addition & 0 deletions package-lock.json

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

3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,5 @@
"bugs": {
"url": "https://github.com/syavorsky/comment-parser/issues"
},
"homepage": "https://github.com/syavorsky/comment-parser",
"dependencies": {}
"homepage": "https://github.com/syavorsky/comment-parser"
}
19 changes: 14 additions & 5 deletions src/parser/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Block, Line, Problem, BlockMarkers, Markers } from '../primitives';
import { splitLines } from '../util';
import { splitLines, dedent, compose2, identity } from '../util';
import blockParser from './block-parser';
import sourceParser from './source-parser';
import specParser from './spec-parser';
import specParser, { dedentSpec } from './spec-parser';
import { Tokenizer } from './tokenizers/index';
import tokenizeTag from './tokenizers/tag';
import tokenizeType from './tokenizers/type';
Expand All @@ -17,7 +17,7 @@ export interface Options {
// escaping chars sequence marking wrapped content literal for the parser
fence: string;
// block and comment description compaction strategy
spacing: 'compact' | 'preserve';
spacing: 'compact' | 'preserve' | 'dedent';
// comment description markers
markers: BlockMarkers;
// tokenizer functions extracting name, type, and description out of tag, see Tokenizer
Expand Down Expand Up @@ -57,10 +57,19 @@ export default function getParser({
if (lines.find(notEmpty) === undefined) continue;

const sections = parseBlock(lines);
const specs = sections.slice(1).map(parseSpec);
const specFormatter = spacing === 'dedent' ? dedentSpec : identity;

const specs = sections.slice(1).map(compose2(specFormatter, parseSpec));

const joinedDescription = joinDescription(sections[0], markers);

const description =
spacing === 'dedent' ? dedent(joinedDescription) : joinedDescription;

console.log({ joinedDescription, spacing });

blocks.push({
description: joinDescription(sections[0], markers),
description,
tags: specs,
source: lines,
problems: specs.reduce(
Expand Down
9 changes: 8 additions & 1 deletion src/parser/spec-parser.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Line, Spec } from '../primitives';
import { seedSpec } from '../util';
import { dedent, seedSpec } from '../util';
import { Tokenizer } from './tokenizers/index';

export type Parser = (source: Line[]) => Spec;
Expand All @@ -18,3 +18,10 @@ export default function getParser({ tokenizers }: Options): Parser {
return spec;
};
}

export function dedentSpec(spec: Spec): Spec {
return {
...spec,
description: dedent(spec.description),
};
}
4 changes: 2 additions & 2 deletions src/parser/tokenizers/description.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export type Joiner = (lines: Line[], markers?: BlockMarkers) => string;
* compact - strip surrounding whitespace and concat lines using a single string
* preserve - preserves original whitespace and line breaks as is
*/
export type Spacing = 'compact' | 'preserve' | Joiner;
export type Spacing = 'compact' | 'preserve' | 'dedent' | Joiner;

/**
* Makes no changes to `spec.lines[].tokens` but joins them into `spec.description`
Expand All @@ -32,7 +32,7 @@ export default function descriptionTokenizer(

export function getJoiner(spacing: Spacing): Joiner {
if (spacing === 'compact') return compactJoiner;
if (spacing === 'preserve') return preserveJoiner;
if (spacing === 'preserve' || spacing === 'dedent') return preserveJoiner;

return spacing;
}
Expand Down
5 changes: 3 additions & 2 deletions src/parser/tokenizers/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export type Joiner = (parts: string[]) => string;
* compact - trim surrounding space, replace line breaks with a single space
* preserve - concat as is
*/
export type Spacing = 'compact' | 'preserve' | Joiner;
export type Spacing = 'compact' | 'preserve' | 'dedent' | Joiner;

/**
* Sets splits remaining `Spec.lines[].tokes.description` into `type` and `description`
Expand Down Expand Up @@ -86,6 +86,7 @@ const trim = (x: string) => x.trim();

function getJoiner(spacing: Spacing): Joiner {
if (spacing === 'compact') return (t: string[]) => t.map(trim).join('');
else if (spacing === 'preserve') return (t: string[]) => t.join('\n');
else if (spacing === 'preserve' || spacing === 'dedent')
return (t: string[]) => t.join('\n');
else return spacing;
}
58 changes: 58 additions & 0 deletions src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,61 @@ export function rewireSpecs(block: Block): Block {
block.source = block.source.map((line) => source.get(line.number) || line);
return block;
}

/**
* Dedent a string. Adapted from 'dedent' npm package
* @see https://npm.im/dedent
*/
export function dedent(raw: string): string {
// first, perform interpolation
var result = '';
for (var i = 0; i < raw.length; i++) {
result += raw[i]
// join lines when there is a suppressed newline
.replace(/\\\n[ \t]*/g, '')
// handle escaped backticks
.replace(/\\`/g, '`');

if (i < (arguments.length <= 1 ? 0 : arguments.length - 1)) {
result += arguments.length <= i + 1 ? undefined : arguments[i + 1];
}
}

// now strip indentation
var lines = result.split('\n');
var mindent = null;
lines.forEach(function (l) {
var m = l.match(/^(\s+)\S+/);
if (m) {
var indent = m[1].length;
if (!mindent) {
// this is the first indented line
mindent = indent;
} else {
mindent = Math.min(mindent, indent);
}
}
});

if (mindent !== null) {
result = lines
.map(function (l) {
return l[0] === ' ' ? l.slice(mindent) : l;
})
.join('\n');
}

// dedent eats leading and trailing whitespace too
result = result.trim();

// handle escaped newlines at the end to ensure they don't get stripped too
return result.replace(/\\n/g, '\n');
}

/** I combinator */
export const identity = <T>(x: T): T => x;
/** B combinator - the Bluebird. Compose two unary functions, right-to-left */
export const compose2 =
<T, U = T, V = T>(f: (y: U) => V, g: (x: T) => U) =>
(x: T): V =>
f(g(x));
Loading