sweetline 1.2.2 copy "sweetline: ^1.2.2" to clipboard
sweetline: ^1.2.2 copied to clipboard

Flutter/Dart FFI wrapper for the SweetLine native syntax highlighting engine.

sweetline #

SweetLine Flutter/Dart FFI package.

It wraps the SweetLine native C API and provides a Dart-friendly API for:

  • full text syntax highlighting
  • single-line analysis
  • managed document incremental analysis
  • visible-range highlight slice retrieval
  • indent guide analysis

Features #

  • HighlightEngine for syntax compilation and analyzer creation
  • TextAnalyzer for full text and single-line analysis
  • Document + DocumentAnalyzer for incremental updates
  • DocumentHighlight / DocumentHighlightSlice / IndentGuideResult models
  • optional showIndex support
  • optional inline-style output

Supported platforms #

The published package bundles prebuilt native libraries for:

  • Android arm64-v8a
  • Android x86_64
  • Linux aarch64
  • Linux x86_64
  • macOS arm64
  • macOS x86_64
  • Windows x86_64
  • iOS device arm64
  • iOS simulator arm64

Web is not supported.

Installation #

dependencies:
  sweetline: ^1.2.2

Load syntax rules #

SweetLine does not hardcode language grammars in the Dart layer. You need to provide syntax JSON yourself, then compile it into the engine.

Built-in syntax rule JSON files can be downloaded from the SweetLine open source repository's syntaxes/ directory. In most cases you only need to copy the language files your app actually uses into Flutter assets. After compiling those JSON files, prefer createAnalyzerByFileName(...) or loadDocument(...) with real file names so the core can resolve syntax routing automatically.

In Flutter applications, loading syntax JSON from assets is the most direct approach:

flutter:
  assets:
    - assets/syntaxes/dart.json
import 'package:flutter/services.dart' show rootBundle;
import 'package:sweetline/sweetline.dart';

Future<HighlightEngine> createEngine() async {
  final engine = HighlightEngine(
    const HighlightConfig(showIndex: true, inlineStyle: false),
  );

  final dartSyntax = await rootBundle.loadString('assets/syntaxes/dart.json');
  engine.compileSyntaxFromJson(dartSyntax);
  return engine;
}

You can also call compileSyntaxFromFile(...) if you already have a real file path on the current platform.

Quick start #

import 'package:flutter/services.dart' show rootBundle;
import 'package:sweetline/sweetline.dart';

Future<void> main() async {
  final engine = HighlightEngine(
    const HighlightConfig(showIndex: true, inlineStyle: false),
  );

  try {
    final syntaxJson = await rootBundle.loadString('assets/syntaxes/dart.json');
    engine.compileSyntaxFromJson(syntaxJson);

    final analyzer = engine.createAnalyzerByFileName('main.dart');
    if (analyzer == null) {
      throw StateError('no syntax matched main.dart');
    }

    final result = analyzer.analyzeText('class Foo<T> {\n  final int value;\n}');
    for (var line = 0; line < result.lines.length; line++) {
      for (final span in result.lines[line].spans) {
        print(
          'line=$line '
          'column=${span.range.start.column} '
          'length=${span.range.end.column - span.range.start.column} '
          'styleId=${span.styleId}',
        );
      }
    }
  } finally {
    engine.close();
  }
}

Incremental document analysis #

Use DocumentAnalyzer when text changes over time and you need incremental re-analysis.

import 'package:sweetline/sweetline.dart';

void analyzeDocument(HighlightEngine engine, String source) {
  final document = Document('main.dart', source);

  try {
    final analyzer = engine.loadDocument(document);
    if (analyzer == null) {
      throw StateError('failed to load document');
    }

    final fullHighlight = analyzer.analyze();

    final slice = analyzer.analyzeIncrementalInLineRange(
      TextRange(
        const TextPosition(1, 0),
        const TextPosition(1, 0),
      ),
      '  print(value);\n',
      const LineRange(0, 40),
    );

    print(fullHighlight.lines.length);
    print(slice.startLine);
    print(slice.totalLineCount);
  } finally {
    document.close();
  }
}

API overview #

HighlightEngine #

  • compileSyntaxFromJson(String syntaxJson)
  • compileSyntaxFromFile(String path)
  • registerStyleName(String styleName, int styleId)
  • getStyleName(int styleId)
  • defineMacro(String macroName)
  • undefineMacro(String macroName)
  • createAnalyzerByFileName(String fileName)
  • createAnalyzerBySyntaxName(String syntaxName)
  • loadDocument(Document document)

TextAnalyzer #

  • analyzeText(String text)
  • analyzeLine(String text, TextLineInfo info)
  • analyzeIndentGuides(String text)

DocumentAnalyzer #

  • analyze()
  • analyzeIncremental(TextRange range, String newText)
  • analyzeIncrementalInLineRange(TextRange range, String newText, LineRange visibleRange)
  • getHighlightSlice(LineRange visibleRange)
  • analyzeIndentGuides()

Result models #

DocumentHighlight #

Full highlight result:

DocumentHighlight.lines -> List<LineHighlight>
LineHighlight.spans -> List<TokenSpan>

DocumentHighlightSlice #

Highlight result for a specified visible line range:

DocumentHighlightSlice.startLine
DocumentHighlightSlice.totalLineCount
DocumentHighlightSlice.lines

TokenSpan #

Each token span contains:

  • range
  • styleId when inlineStyle == false
  • inlineStyle when inlineStyle == true

InlineStyle #

Inline style contains:

  • foreground
  • background
  • fontAttributes
  • isBold
  • isItalic
  • isStrikethrough

Error handling #

  • compileSyntaxFromJson(...) and compileSyntaxFromFile(...) throw SyntaxCompileError when syntax compilation fails
  • other native failures throw SweetLineException

Lifecycle #

  • call engine.close() when the engine is no longer needed
  • call document.close() when the managed document is no longer needed
  • TextAnalyzer and DocumentAnalyzer are lightweight wrappers; closing the owning engine/document is the important part

Notes #

  • showIndex: true makes TextPosition.index available in returned spans
  • inlineStyle: true returns inline color/font style instead of styleId
  • createAnalyzerByFileName(...) only works after loading syntax rules that declare matching fileNames or fileSuffixes
  • createAnalyzerByFileName(...) should be given a basename such as main.dart, Dockerfile, or build.gradle.kts
1
likes
140
points
251
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Flutter/Dart FFI wrapper for the SweetLine native syntax highlighting engine.

Repository (GitHub)
View/report issues

License

LGPL-2.1 (license)

Dependencies

code_assets, ffi, hooks, logging, native_toolchain_c

More

Packages that depend on sweetline