r/swift • • 5h ago

Project Building incremental Swift syntax highlighting and symbol navigation with TextKit 2

I’ve been working on a native code editor for iPhone and iPad, and one of the more interesting problems has been making Swift files comfortable to read and edit without reprocessing the entire document after every keystroke.

The editor uses UIKit and TextKit 2, with a custom NSTextContentStorage implementation managing the source text and attributed rendering.

For syntax highlighting, I use a Swift TextMate grammar and store the parsed output in a token tree. The initial document load parses the complete file, while interactive edits are handled incrementally.

After an edit, the editor:

  1. Invalidates the affected syntax range.
  2. Continues parsing from the nearest valid state.
  3. Propagates highlighting changes in bounded chunks.
  4. Applies attributes only to ranges that need rendering.
  5. Cancels stale work when another edit changes the document revision.

Keeping a syntax revision number turned out to be important. Background parsing may finish after the user has already changed the document again, so each result is checked against the current revision before it can update the editor.

The other feature shown in the screenshot is Swift symbol navigation.

Instead of using SourceKit, the current implementation builds a lightweight outline from the scopes emitted by the TextMate grammar. It identifies declarations such as:

  • class
  • struct
  • enum
  • protocol
  • extension
  • func
  • Initializers and subscripts

The symbols are arranged into a hierarchy using their scope depth and indentation. Selecting a symbol jumps directly to its line in the editor.

This approach is intentionally syntax-based rather than semantic. It works well for navigating a single source file, but it does not understand types, overload resolution, cross-file references, or compiler diagnostics.

That trade-off has been useful on iPhone and iPad: the outline remains lightweight and provides a quick way to move around long Swift files without trying to reproduce Xcode.

I’m considering where to take it next. Possible additions include stored properties, MARK sections, protocol conformances, and improved handling of nested declarations.

For a lightweight mobile Swift editor, which symbols would you expect to see in the outline?

2 Upvotes

5 comments sorted by

2

u/kokerali 2h ago

I'd prioritize MARK sections and keeping each member visibly attached to its enclosing type or extension. On a phone, two entries both called reset() aren't very useful unless the parent stays visible while filtering. I'd make MARK headings collapsible and keep matching symbols under their parent when searching, rather than flattening the results. That would add navigation structure without making the outline look like it understands protocol conformance semantically.

1

u/Ok-Welder-8005 24m ago

That makes sense. The outline already keeps members under their enclosing type or extension in the normal view, but preserving that context while filtering is just as important—especially when multiple members share the same name. Collapsible MARK sections also sound like a good fit for the existing syntax-based hierarchy. Thanks, this gives me a much clearer direction for the next iteration.

1

u/DismalCockroach1928 4h ago

I'd include computed properties alongside stored ones, so body in a SwiftUI view is a jump target too. That keeps a big piece of the view's code reachable from the outline without needing type information.

1

u/Ok-Welder-8005 4h ago

Good point — computed properties are already included, so body in a SwiftUI view appears in the symbol list and can be used as a jump target. I agree that it’s especially useful for navigating larger views without needing semantic type information.