# core/parsers/knob.parser.ts

> 54 lines of code and 12 definitions.

Tree: GovLab Quality
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/quality#file-quality-core-parsers-knob-parser-ts
Source text: https://banes-lab.com/source/quality/core/parsers/knob.parser.ts.txt

Listed in [core/parsers](https://banes-lab.com/api/source/quality/core/parsers.md), after [core/parsers/invocation.parser.ts](https://banes-lab.com/source/quality/core/parsers/invocation.parser.ts.md) and before [core/parsers/location.parser.ts](https://banes-lab.com/source/quality/core/parsers/location.parser.ts.md).

## Definitions

- `intAt` (lexical_declaration, line 6, exported)
- `isDigitsOnly` (lexical_declaration, line 50, exported)
- `intAfter` (lexical_declaration, line 22, exported)
- `SCAN_STOPS` (lexical_declaration, line 3)
- `RUBY_TAG` (lexical_declaration, line 4)
- `digits` (lexical_declaration, line 14, exported)
- `at` (lexical_declaration, line 27, exported)
- `withoutRubyTags` (lexical_declaration, line 34, exported)
- `result` (lexical_declaration, line 35, exported)
- `i` (lexical_declaration, line 36, exported)
- `trailingIdentifier` (lexical_declaration, line 54, exported)
- `field` (lexical_declaration, line 55, exported)

## Contained in

- [core/parsers](https://banes-lab.com/anatomy/quality/folder-quality-core-parsers.md)

## Used by

- [core/producers/tool.scalastyle.producer.ts](https://banes-lab.com/source/quality/core/producers/tool.scalastyle.producer.ts.md)

## Linked from

- [core/parsers](https://banes-lab.com/anatomy/quality/folder-quality-core-parsers.md)
- [core/producers](https://banes-lab.com/anatomy/quality/folder-quality-core-producers.md)

## Source

```typescript
import { everyChar, isDigit, isIdentifierChar } from "@govlab/constants";

const SCAN_STOPS: ReadonlySet<string> = new Set(["\n", ";", ")"]);
const RUBY_TAG = "!ruby/";

export const intAt = function intAt(text: string, start: number): number | null {
    let i = start;
    while (i < text.length && !isDigit(text[i] ?? "")) {
        if (SCAN_STOPS.has(text[i] ?? "")) {
            return null;
        }
        i += 1;
    }
    let digits = "";
    while (i < text.length && isDigit(text[i] ?? "")) {
        digits += text[i];
        i += 1;
    }
    return digits === "" ? null : Number(digits);
};

export const intAfter = function intAfter(
    text: string,
    marker: string,
    opts: { from: number; window?: number },
): number | null {
    const at = text.indexOf(marker, opts.from);
    if (at === -1 || (typeof opts.window === "number" && at - opts.from > opts.window)) {
        return null;
    }
    return intAt(text, at + marker.length);
};

export const withoutRubyTags = function withoutRubyTags(text: string): string {
    let result = "";
    let i = 0;
    while (i < text.length) {
        if (text.startsWith(RUBY_TAG, i)) {
            while (i < text.length && text[i] !== " " && text[i] !== "\n") {
                i += 1;
            }
        } else {
            result += text[i];
            i += 1;
        }
    }
    return result;
};

export const isDigitsOnly = function isDigitsOnly(raw: string): boolean {
    return raw.length > 0 && everyChar(raw, isDigit);
};

export const trailingIdentifier = function trailingIdentifier(chunk: string): string {
    let field = "";
    for (const ch of chunk) {
        field = isIdentifierChar(ch) ? field + ch : "";
    }
    return field;
};
```
