textPatternDiagnosticsForDocument function

List<TextPositionDiagnosticRange> textPatternDiagnosticsForDocument({
  1. required TextDocument document,
  2. required Iterable<TextPatternDiagnosticRule> rules,
})

Implementation

List<TextPositionDiagnosticRange> textPatternDiagnosticsForDocument({
  required TextDocument document,
  required Iterable<TextPatternDiagnosticRule> rules,
}) {
  final diagnostics = <TextPositionDiagnosticRange>[];

  for (final rule in rules) {
    final needle = uni.graphemes(rule.pattern).toList(growable: false);
    if (needle.isEmpty) {
      continue;
    }

    final normalizedNeedle = rule.caseSensitive
        ? needle
        : needle.map((token) => token.toLowerCase()).toList(growable: false);

    for (var lineIndex = 0; lineIndex < document.lineCount; lineIndex++) {
      final haystack = document.lineGraphemesAt(lineIndex);
      final normalizedHaystack = rule.caseSensitive
          ? haystack
          : haystack
                .map((token) => token.toLowerCase())
                .toList(growable: false);

      var cursor = 0;
      while (cursor <= normalizedHaystack.length - normalizedNeedle.length) {
        var matched = true;
        for (var index = 0; index < normalizedNeedle.length; index++) {
          if (normalizedHaystack[cursor + index] != normalizedNeedle[index]) {
            matched = false;
            break;
          }
        }
        if (!matched) {
          cursor += 1;
          continue;
        }

        final startColumn = cursor;
        final endColumn = cursor + normalizedNeedle.length;
        if (rule.wholeWord &&
            !_isWholeWordMatch(
              haystack,
              startColumn: startColumn,
              endColumn: endColumn,
            )) {
          cursor += 1;
          continue;
        }

        diagnostics.add(
          TextPositionDiagnosticRange(
            startLine: lineIndex,
            startColumn: startColumn,
            endLine: lineIndex,
            endColumn: endColumn,
            severity: rule.severity,
            code: rule.code,
            message: rule.message,
            source: rule.source,
          ).clamp(document).normalized(),
        );
        cursor = endColumn;
      }
    }
  }

  diagnostics.sort((a, b) {
    final startLineComparison = a.startLine.compareTo(b.startLine);
    if (startLineComparison != 0) {
      return startLineComparison;
    }
    final startColumnComparison = a.startColumn.compareTo(b.startColumn);
    if (startColumnComparison != 0) {
      return startColumnComparison;
    }
    final endLineComparison = a.endLine.compareTo(b.endLine);
    if (endLineComparison != 0) {
      return endLineComparison;
    }
    final endColumnComparison = a.endColumn.compareTo(b.endColumn);
    if (endColumnComparison != 0) {
      return endColumnComparison;
    }
    return _diagnosticSeverityRank(
      b.severity,
    ).compareTo(_diagnosticSeverityRank(a.severity));
  });

  return List<TextPositionDiagnosticRange>.unmodifiable(diagnostics);
}