# core/analyzers/vocabulary.analyzer.ts

> 54 lines of code and 15 definitions.

Tree: GovLab Docs
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/docs#file-docs-core-analyzers-vocabulary-analyzer-ts
Source text: https://banes-lab.com/source/docs/core/analyzers/vocabulary.analyzer.ts.txt

Listed in [core/analyzers](https://banes-lab.com/api/source/docs/core/analyzers.md), after [core/analyzers/text.analyzer.ts](https://banes-lab.com/source/docs/core/analyzers/text.analyzer.ts.md).

## Definitions

- `bannedLanguage` (lexical_declaration, line 50, exported)
- `isStandalone` (lexical_declaration, line 13)
- `termHits` (lexical_declaration, line 19)
- `blankPair` (lexical_declaration, line 31)
- `stripQuotedText` (lexical_declaration, line 46)
- `BLOCKQUOTE_MARK` (lexical_declaration, line 7)
- `QUOTE_PAIRS` (lexical_declaration, line 8)
- `before` (lexical_declaration, line 14)
- `after` (lexical_declaration, line 15)
- `hits` (lexical_declaration, line 20)
- `out` (lexical_declaration, line 32)
- `from` (lexical_declaration, line 33)
- `start` (lexical_declaration, line 35)
- `end` (lexical_declaration, line 36)
- `prose` (lexical_declaration, line 57, exported)

## Contained in

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

## Uses

- [core/parsers/markdown.parser.ts](https://banes-lab.com/source/docs/core/parsers/markdown.parser.ts.md)
- [core/predicates/character.predicate.ts](https://banes-lab.com/source/docs/core/predicates/character.predicate.ts.md)

## Used by

- [core/analyzers/document.analyzer.ts](https://banes-lab.com/source/docs/core/analyzers/document.analyzer.ts.md)
- [core/validators/manifest.prose.validator.ts](https://banes-lab.com/source/docs/core/validators/manifest.prose.validator.ts.md)

## Linked from

- [core/analyzers](https://banes-lab.com/anatomy/docs/folder-docs-core-analyzers.md)
- [core/parsers](https://banes-lab.com/anatomy/docs/folder-docs-core-parsers.md)
- [core/predicates](https://banes-lab.com/anatomy/docs/folder-docs-core-predicates.md)
- [core/validators](https://banes-lab.com/anatomy/docs/folder-docs-core-validators.md)

## Source

```typescript
import { BANNED_TERMS } from "#configuration/constants/vocabulary.constants";
import type { BannedHit } from "#types/markdown.types";
import { isAsciiAlnum } from "#core/predicates/character.predicate";
import { proseLines } from "#core/parsers/markdown.parser";
import { stripInlineCode } from "#core/converters/markdown.converter";

const BLOCKQUOTE_MARK = ">";
const QUOTE_PAIRS: readonly (readonly [string, string])[] = [
    ['"', '"'],
    ["“", "”"],
];

const isStandalone = function isStandalone(lower: string, from: number, term: string): boolean {
    const before = from > 0 ? lower.charAt(from - 1) : "";
    const after = lower.charAt(from + term.length);
    return !isAsciiAlnum(before) && !isAsciiAlnum(after);
};

const termHits = function termHits(lower: string, lineNo: number, term: string): BannedHit[] {
    const hits: BannedHit[] = [];
    let from = lower.indexOf(term);
    while (from !== -1) {
        if (isStandalone(lower, from, term)) {
            hits.push({ col: from + 1, line: lineNo, term });
        }
        from = lower.indexOf(term, from + term.length);
    }
    return hits;
};

const blankPair = function blankPair(line: string, open: string, close: string): string {
    let out = "";
    let from = 0;
    while (from < line.length) {
        const start = line.indexOf(open, from);
        const end = start === -1 ? -1 : line.indexOf(close, start + open.length);
        if (end === -1) {
            return out + line.slice(from);
        }
        out += line.slice(from, start) + " ".repeat(end + close.length - start);
        from = end + close.length;
    }
    return out;
};

const stripQuotedText = function stripQuotedText(line: string): string {
    return QUOTE_PAIRS.reduce((text, [open, close]) => blankPair(text, open, close), line);
};

export const bannedLanguage = function bannedLanguage(
    source: string,
    terms: readonly string[] = BANNED_TERMS,
): BannedHit[] {
    return proseLines(source)
        .filter(({ line }) => !line.trimStart().startsWith(BLOCKQUOTE_MARK))
        .flatMap(({ line, lineNo }) => {
            const prose = stripQuotedText(stripInlineCode(line)).toLowerCase();
            return terms.flatMap((term) => termHits(prose, lineNo, term));
        });
};
```
