# tools/core/matchers/segment.matcher.ts

> 78 lines of code and 20 definitions.

Tree: Coordination tree
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/coordination#file-coordination-tools-core-matchers-segment-matcher-ts
Source text: https://banes-lab.com/assets/sources/source.52bffbff5b4decdbc3b1f3a679cf1d3fdd5693dc06d9c38734c8bba9a2a58f0f.generated.txt

## Definitions

- `differs` (lexical_declaration, line 4)
- `scalarsMatch` (lexical_declaration, line 8)
- `matchesOne` (lexical_declaration, line 36)
- `inlineMatches` (lexical_declaration, line 26)
- `textMatches` (lexical_declaration, line 22)
- `inlinesMatch` (lexical_declaration, line 31)
- `matchesAt` (lexical_declaration, line 45)
- `hitAt` (lexical_declaration, line 56)
- `matchPattern` (lexical_declaration, line 80, exported)
- `matchAll` (lexical_declaration, line 90, exported)
- `prefixMatches` (lexical_declaration, line 17)
- `prefix` (lexical_declaration, line 18)
- `needle` (lexical_declaration, line 27)
- `unconstrained` (lexical_declaration, line 32)
- `segment` (lexical_declaration, line 51)
- `span` (lexical_declaration, line 62)
- `[first]` (lexical_declaration, line 63)
- `final` (lexical_declaration, line 64)
- `width` (lexical_declaration, line 81, exported)
- `starts` (lexical_declaration, line 86, exported)

## Uses

- [tools/core/predicates/text.predicate.ts](https://banes-lab.com/source/coordination/tools/core/predicates/text.predicate.ts.md)

## Used by

- [tools/core/entrypoints/segment.entrypoint.ts](https://banes-lab.com/source/coordination/tools/core/entrypoints/segment.entrypoint.ts.md)

## Source

```typescript
import type { Document, Hit, Inline, Segment, SegmentPattern, SegmentPredicate } from "../types/segment.types.ts";
import { contains, hasPrefix } from "../predicates/text.predicate.ts";

const differs = function differs<T>(expected: T | undefined, actual: T | undefined): boolean {
    return expected !== undefined && actual !== expected;
};

const scalarsMatch = function scalarsMatch(segment: Segment, predicate: SegmentPredicate): boolean {
    return (
        !differs(predicate.kind, segment.kind) &&
        !differs(predicate.depthEquals, segment.depth) &&
        !differs(predicate.keyEquals, segment.key) &&
        !differs(predicate.valueEquals, segment.value)
    );
};

const prefixMatches = function prefixMatches(segment: Segment, predicate: SegmentPredicate): boolean {
    const prefix = predicate.valueStartsWith;
    return prefix === undefined || (segment.value !== undefined && hasPrefix(segment.value, prefix));
};

const textMatches = function textMatches(segment: Segment, predicate: SegmentPredicate): boolean {
    return predicate.textContains === undefined || contains(segment.text, predicate.textContains);
};

const inlineMatches = function inlineMatches(inline: Inline, predicate: SegmentPredicate): boolean {
    const needle = predicate.inlineValueContains;
    return !differs(predicate.inlineKind, inline.kind) && (needle === undefined || contains(inline.value, needle));
};

const inlinesMatch = function inlinesMatch(segment: Segment, predicate: SegmentPredicate): boolean {
    const unconstrained = predicate.inlineKind === undefined && predicate.inlineValueContains === undefined;
    return unconstrained || segment.inlines.some((inline) => inlineMatches(inline, predicate));
};

const matchesOne = function matchesOne(segment: Segment, predicate: SegmentPredicate): boolean {
    return (
        scalarsMatch(segment, predicate) &&
        prefixMatches(segment, predicate) &&
        textMatches(segment, predicate) &&
        inlinesMatch(segment, predicate)
    );
};

const matchesAt = function matchesAt(
    segments: readonly Segment[],
    predicates: readonly SegmentPredicate[],
    start: number,
): boolean {
    return predicates.every((predicate, offset) => {
        const segment = segments[start + offset];
        return segment !== undefined && matchesOne(segment, predicate);
    });
};

const hitAt = function hitAt(document: Document, pattern: SegmentPattern, index: number): Hit[] {
    const width = pattern.predicates.length;
    if (!matchesAt(document.segments, pattern.predicates, index)) {
        return [];
    }

    const span = document.segments.slice(index, index + width);
    const [first] = span;
    const final = span.at(-1);
    if (first === undefined || final === undefined) {
        return [];
    }

    return [
        {
            index,
            path: document.path,
            patternId: pattern.id,
            segments: span,
            span: { end: final.end, line: first.line, start: first.start },
        },
    ];
};

export const matchPattern = function matchPattern(document: Document, pattern: SegmentPattern): Hit[] {
    const width = pattern.predicates.length;
    if (width === 0) {
        return [];
    }

    const starts = Math.max(0, document.segments.length - width + 1);
    return [...document.segments.keys()].slice(0, starts).flatMap((index) => hitAt(document, pattern, index));
};

export const matchAll = function matchAll(document: Document, patterns: readonly SegmentPattern[]): Hit[] {
    return patterns.flatMap((pattern) => matchPattern(document, pattern));
};
```
