📝 Performant plain text editor for iOS with syntax highlighting, line numbers, invisible characters and much more.

Overview

👋 Welcome to Runestone - a performant plain text editor for iOS with code editing features

Runestone uses GitHub's Tree-sitter to parse code to a syntax tree which is used for features that require an understanding of the code in the editor, for example syntax highlighting.

Features

  • Syntax highlighting.
  • Line numbers.
  • Highlight the selected line.
  • Show invisible characters (tabs, spaces and line breaks).
  • Insertion of character pairs, e.g. inserting the trailing quotation mark when inserting the leading.
  • Customization of colors and fonts.
  • Toggle line wrapping on and off.
  • Adjust height of lines.
  • Add a page guide.
  • Add vertical and horizontal overscroll.
  • Highlight ranges in the text view.
  • Search the text using regular expressions.
  • Automatically detects if a file is using spaces or tabs for indentation.

🚀 Getting Started

Please refer to the Getting Started article in the documentation.

📖 Documentation

The documentation of all public types is available at docs.runestone.app. The documentation is generated from the Swift code using Apple's DocC documentation compiler.

🏎 Performance

Runestone was built to be fast. Its good performance is by far mostly thanks to Tree-sitter's incremental parsing and AvalonEdit's approach for managing lines in a document.

When judging the performance of Runestone, it is key to build your app in the release configuration. The optimizations applied by the compiler when using the release configuration becomes very apparent when opening large documents.

📱 Projects

The Runestone framework is used by an app of the same name. The Runestone app is a plain text editor for iPhone and iPad that uses all the features of this framework.

Runestone app icon

Download on the App Store

❤️ Acknowledgments

Issues
  • 🐛「Bug」Can't handle Korean input correctly

    🐛「Bug」Can't handle Korean input correctly

    Describe the bug Can't handle Korean input correctly

    Screenshots

    https://user-images.githubusercontent.com/7934444/166901295-93cb41aa-131d-4289-bab3-af121ad6d3c0.MOV

    bug 
    opened by octree 9
  • Always insert text when calling `insertText(_:)` programmatically

    Always insert text when calling `insertText(_:)` programmatically

    I came across another subtle difference in behaviour when switching to Runestone: When the TextView is not the first responder, programatically calling insertText with some text didn't do anything, while my previous implementation that used UITextView did append the text.

    This PR changes the behaviour of TextView.insertText(_:) to also append the text at the end. I kept the behaviour of TextInputView.insertText(_:) the same (i.e., it will not insert the text if there's no marked or selected range) in case that this was on purpose.

    opened by nighthawk 7
  • Automatic theme switches when toggling light/dark mode

    Automatic theme switches when toggling light/dark mode

    Is your feature request related to a problem? Please describe. The theme system allows specifying a single theme, which would be optimised for either light or dark mode. There doesn't yet seem a way to either set a single theme that supports both light and dark mode, or specify a theme per interface style.

    Describe the solution you'd like This could be addressed by providing multiple themes, one per UIUserInterfaceStyle, or by providing support for themes that support multiple interface styles.

    Describe alternatives you've considered

    • I've tried adjusting the Color Set in my asset catalog to provide alternative colours for light and dark mode. This seems to work for a subset of colours, but notable not for the background colours. I'm also not sure if that's fully compatible with the internals of Runestone.
    • Detect this on my view controllers, and then set the new theme.

    Additional context I'm happy to provide a PR for this, but I'm not sure if this is desired and, if so, which approach would be the best fit for this framework.

    enhancement 
    opened by nighthawk 5
  • Option to disable font ligatures

    Option to disable font ligatures

    Is your feature request related to a problem? Please describe. "The problem" is that Runestone uses font ligatures, so it joins characters like == != <= => <==> together.

    Example: (with JetBrains Mono)

    image

    Describe the solution you'd like An option to disable font ligatures.

    Describe alternatives you've considered /

    enhancement 
    opened by amadejpapez 5
  • insertText(...) does not update caret position in TextView

    insertText(...) does not update caret position in TextView

    Describe the bug Inserting characters to a TextView programmatically, for example from a keyboard input view with quick access special keys, seems to be done by calling insertText(_ text: String) method on TextView. This adds the characters and they appear, but the caret is not moved after the newly inserted characters but stays where it was, even though the implementation seems to attempt to move the caret.

    Typing more characters using the native keyboard does move the caret after the new text. Also moving the caret using arrow keys does begin moving from the position where the caret was supposed to be. So it seems like the caret position (selectedRange?) has the correct value, but it is not reflected in the TextView after text insertion.

    I also tried to move the caret programmatically, by adjusting selectedRange's location property, but it results in the caret being moved too many steps ahead.

    To Reproduce

    • Call textView.insertText("a") on an existing TextView instance.
    • New characters appear but the caret stays in the same location before the inserted characters.

    Expected behavior Caret should move to a position after the newly inserted characters.

    bug 
    opened by PasiSalenius 4
  • Allow subclassing TextView and overriding certain methods

    Allow subclassing TextView and overriding certain methods

    This is a great library and works really well, and I enjoyed the challenge of wrapping my head around Tree-sitter!

    I'm using Runestone's TextView in a project that allows writing expressions including Xcode-like autocompletion that is anchored to the selected text. That requires the ability to subclass TextView (to intercept certain keystrokes for interacting with the autocompletion list) and to access the underlying UITextInput protocol (to get the rectangle of the selection, to put the autocompletion list next to it).

    For consistency, I made the various methods that are overridden open or that provide UITextInput-like functionality.

    opened by nighthawk 3
  • Crash with Catalyst

    Crash with Catalyst

    Describe the bug When adding a TextView as a subview in a UIViewController in a Catalyst build there's an immediate crash when tapping into the TextView (and making first responder).

    Error is: Runestone/TextInputView.swift:1287: Fatal error: Positions must be of type IndexedPosition looks like position is nil.

    Screenshots image

    bug 
    opened by rrenna 3
  • Add isEditable and isSelectable property to TextView

    Add isEditable and isSelectable property to TextView

    Closes #34

    • When isEditable is true, text is still selectable and users are able to copy the text.
    • When isSelectable is true then the user will not be able to do anything to the text.
    opened by twodayslate 2
  • Double digit line numbers truncate with a font size greater or equal to 20

    Double digit line numbers truncate with a font size greater or equal to 20

    Describe the bug Double digit line numbers truncate with a font size greater or equal to 20

    To Reproduce

    1. Open the Example project.
    2. Navigate to OneDarkTheme.swift.
    3. Set the font to a size of 20 or greater.
    4. Run the project
    5. Set theme to One Dark
    6. Enter enough new lines to see 10 rows

    Expected behavior

    • Line numbers don't truncate.
    • Line number gutter increases in width to accommodate the double digits at the larger font size.

    Screenshots image

    bug 
    opened by ptrkstr 2
  • Text is not laid out according to textContainerInset assigned to TextView

    Text is not laid out according to textContainerInset assigned to TextView

    Runestone's TextView has textContainerInset property to configure amount of spacing around text, but the width of the text in the TextView does not match this setting. Setting textContainerInset to UIEdgeInsets.zero causes the text to flow over from trailing edge of the view. Same thing happens with and without line numbers.

    To reproduce the issue, configure a TextView with some piece of text in it, so that it spans multiple lines. Enable line wrapping. Configure textContainerInset to zero margins. You should see the text flow over the trailing edge of the TextView.

    IMG_5731

    bug 
    opened by PasiSalenius 2
  • [Feature Request] Open Files from iCloud Drive HIDDEN Folders

    [Feature Request] Open Files from iCloud Drive HIDDEN Folders

    Hello @simonbs !!!!

    My FR for the amazing Runestone is simple to explain: be able to open files (.css in my case) that are located inside iCloud Drive but in a hidden folder

    💡 Let me explain why this is so important to me :)

    My Workflow

    I use Obsidian as my texto editor for .md files on iPadOS using a keyboard and a mouse.

    Based on the way that Obsidian was developed, all the internal content of Obsidian is inside a hidden folder that is located inside a Folder in iCloud Drive

    On iCloud we have this structure:

    iCloudDrive >> Obsidian Folder >> Main Folder >> .obsidian (hidden folder) >> snippets (hidden folder)

    C6061175-3363-417B-A679-DDD3162D3DCF

    The .obsidian/snippets folder has the .css files that re used by users to customize the Obsidian UI

    • I want to use Runestone to edit my .css files that are placed in a hidden folder.

    ✅ This is the reason that i’m asking for this feature 🙏


    I’m aware that the App Textastic already have this Feature.

    BUT I really love Runestone and I want to use it more in my daily workflow.

    Moreover, I want to support your work because I use all your apps and I really like to the attention to details that you have with your apps 💙


    Thanks for reading this !

    I wish you a fantastic launch day 🚀

    enhancement 
    opened by FelipeRearden 2
  • Anyway to get the TreeSitterTree inside the textView?

    Anyway to get the TreeSitterTree inside the textView?

    Hello, I'm integrating the Runestone framework into my rule editor, and it's awesome, especially the highlight and line number stuffs!

    And after integrating the syntax highlight, I try to do some analysis using the tree parsed by the tree-sitter, and I found the tree is stored in textView privately. So, is there any way to get that tree inside the textView?

    enhancement 
    opened by Kaelzs 1
  • Support for UIContentSizeCategoryAdjusting

    Support for UIContentSizeCategoryAdjusting

    Is your feature request related to a problem? Please describe. Currently apps which utilise UILabel, UITextView and UITextField are able to support dynamic type via opt in of adjustsFontForContentSizeCategory. Once a developer enables this property, users are able to have an app experience that caters to their font size preferences for example:

    • Larger font for users with difficulty seeing.
    • Smaller font for users which want to display more content at once.

    Describe the solution you'd like

    • TextView conforms to UIContentSizeCategoryAdjusting.
    • adjustsFontForContentSizeCategory can be set per TextView instance.
    • Internally this updates the fonts for both line number and text editor views.

    Describe alternatives you've considered Without modifying the original source code, a workaround currently is to set a new theme with updated fonts every time a user changes font size. This approach however doesn't seem as performant as only updating internal font properties directly.

    Additional context

    enhancement 
    opened by ptrkstr 1
  • Can we find a way to collaborate on tree-sitter support?

    Can we find a way to collaborate on tree-sitter support?

    First, huge congratulations! This is totally awesome, and shipping is a big deal.

    Thanks for the SwiftTreeSitter shout-out in the acknowledgements, that's very kind of you! Do you think we could ever find a way to collaborate in that area? Seems like there's so much overlapping effort here. Perhaps https://github.com/krzyzanowskim/tree-sitter-xcframework might be a starting point?

    (If you have interest in sharing why you went your own way on tree-sitter bindings, I'd love to hear. But, everyone's allowed to do their own thing and there's definitely no need justify!)

    enhancement 
    opened by mattmassicotte 3
  • View for SwiftUI using `UIViewRepresentable`

    View for SwiftUI using `UIViewRepresentable`

    Is your feature request related to a problem? Please describe. The current TextView is built to be used with UIKit.

    Describe the solution you'd like Create a SwiftUITextView conforming to UIViewRepresentable, to wrap TextView for SwiftUI.

    Additional context I will try to make a PR to solve the problem.

    enhancement 
    opened by NathanFallet 5
Releases(0.1.2)
  • 0.1.2(Jun 17, 2022)

    What's Changed

    • No longer applies captures with unsupported predicates by @simonbs in https://github.com/simonbs/Runestone/pull/9
    • Uses line break instead of cluster break by @simonbs in https://github.com/simonbs/Runestone/pull/10
    • Adds Tree-sitter submoduel with HTTPS by @simonbs in https://github.com/simonbs/Runestone/pull/12
    • Fix small typos by @multigreg in https://github.com/simonbs/Runestone/pull/14
    • Fixes SwiftLint issues by @simonbs in https://github.com/simonbs/Runestone/pull/20
    • Removes unsafe build flag from TreeSitter target by @simonbs in https://github.com/simonbs/Runestone/pull/18
    • Removes TreeSitterLanguages dependency from Example project by @simonbs in https://github.com/simonbs/Runestone/pull/22
    • Renames editorView to scrollView by @simonbs in https://github.com/simonbs/Runestone/pull/23
    • Renames additionalInset to safeAreaInset by @simonbs in https://github.com/simonbs/Runestone/pull/24
    • Renames maximumLineWidth to constrainingLineWidth by @simonbs in https://github.com/simonbs/Runestone/pull/25
    • Improves comments by @simonbs in https://github.com/simonbs/Runestone/pull/26
    • Fixes issue where setupViewHierarchy() didn't remove all subviews by @simonbs in https://github.com/simonbs/Runestone/pull/27
    • Removes unused 'animated' parameter by @simonbs in https://github.com/simonbs/Runestone/pull/29
    • Returns no selection rects when range is empty by @simonbs in https://github.com/simonbs/Runestone/pull/28
    • Ensures caret is visible when moved past trailing edge by @simonbs in https://github.com/simonbs/Runestone/pull/30
    • Caps scroll offset to minimum and maximum offset by @simonbs in https://github.com/simonbs/Runestone/pull/31
    • Enforces constraining line width by @simonbs in https://github.com/simonbs/Runestone/pull/36
    • Splits example project into Swift packages by @simonbs in https://github.com/simonbs/Runestone/pull/37
    • Fixes issue where long pressing to select text would scroll by @simonbs in https://github.com/simonbs/Runestone/pull/38
    • Adds support for showing non-breaking spaces by @simonbs in https://github.com/simonbs/Runestone/pull/39
    • Improves text selection by @simonbs in https://github.com/simonbs/Runestone/pull/33
    • Validates predicates of injection captures by @simonbs in https://github.com/simonbs/Runestone/pull/41
    • Uses correct font for line numbers by @simonbs in https://github.com/simonbs/Runestone/pull/44
    • No longer groups child layers by languages by @simonbs in https://github.com/simonbs/Runestone/pull/45
    • Skip empty captures by @simonbs in https://github.com/simonbs/Runestone/pull/46
    • Enables Catalyst in Example project by @simonbs in https://github.com/simonbs/Runestone/pull/47
    • Fixes crash in compare(_:to:) on Catalyst by @simonbs in https://github.com/simonbs/Runestone/pull/48
    • Removes debug code by @simonbs in https://github.com/simonbs/Runestone/pull/49
    • Fixes issue where carriage return was not rendered by @simonbs in https://github.com/simonbs/Runestone/pull/52
    • Adds support for specifying line endings by @simonbs in https://github.com/simonbs/Runestone/pull/53
    • Updates documentation by @simonbs in https://github.com/simonbs/Runestone/pull/54
    • Only prepares text for insertion when caused by external event by @simonbs in https://github.com/simonbs/Runestone/pull/55
    • Considers Windows-style line breaks (\r\n) a composed character sequence by @simonbs in https://github.com/simonbs/Runestone/pull/56
    • Fixes typo in -sendSelectionChangedToTextSelectionView() by @simonbs in https://github.com/simonbs/Runestone/pull/57
    • Uses line position to restore selected range when replacing text by @simonbs in https://github.com/simonbs/Runestone/pull/58
    • Makes symbol on line ending public by @simonbs in https://github.com/simonbs/Runestone/pull/59
    • Fixes -textInputViewDidChangeSelection(_:) called too early by @simonbs in https://github.com/simonbs/Runestone/pull/61
    • Add isEditable and isSelectable property to TextView by @twodayslate in https://github.com/simonbs/Runestone/pull/60
    • Fix documentation typos by @AndrewBennet in https://github.com/simonbs/Runestone/pull/62
    • Fixes linguist by @simonbs in https://github.com/simonbs/Runestone/pull/63
    • Adds CODEOWNERS by @simonbs in https://github.com/simonbs/Runestone/pull/64
    • Uses byte indices only in -treeSitterLanguageMode(_:bytesAt:) by @simonbs in https://github.com/simonbs/Runestone/pull/65
    • -customRangeOfComposedCharacterSequences(for:) builds upon native range by @simonbs in https://github.com/simonbs/Runestone/pull/66
    • Removes push to main trigger by @simonbs in https://github.com/simonbs/Runestone/pull/67
    • Attempts setting up .spi.yml by @simonbs in https://github.com/simonbs/Runestone/pull/70
    • Adds shields to README by @simonbs in https://github.com/simonbs/Runestone/pull/71
    • Only runs Build and Test workflow when source has changed by @simonbs in https://github.com/simonbs/Runestone/pull/72
    • Adds workflow for building example project by @simonbs in https://github.com/simonbs/Runestone/pull/73
    • Uses correct shields in README by @simonbs in https://github.com/simonbs/Runestone/pull/74
    • Fixes workflow name by @simonbs in https://github.com/simonbs/Runestone/pull/75
    • Adds workflow_dispatch trigger to all workflows by @simonbs in https://github.com/simonbs/Runestone/pull/76
    • Adds GitHub shields to README by @simonbs in https://github.com/simonbs/Runestone/pull/77
    • Adds Twitter shield to README by @simonbs in https://github.com/simonbs/Runestone/pull/78
    • Don't silently absorb error, at least log it in DEBUG mode by @nighthawk in https://github.com/simonbs/Runestone/pull/84
    • Prefer `` when linking to symbols within the package by @eliperkins in https://github.com/simonbs/Runestone/pull/83
    • Only set isPrepared to true after it really is prepared by @nighthawk in https://github.com/simonbs/Runestone/pull/85
    • Fixes issue where caret position is not updated by @simonbs in https://github.com/simonbs/Runestone/pull/86
    • Updates caret position after toggling line wrapping by @simonbs in https://github.com/simonbs/Runestone/pull/88
    • Fixes code style in TextInputStringTokenizer by @simonbs in https://github.com/simonbs/Runestone/pull/89
    • Improves handling of text selection by @simonbs in https://github.com/simonbs/Runestone/pull/90
    • Scrolls to zero content offset when within text container inset by @simonbs in https://github.com/simonbs/Runestone/pull/91
    • Text view would scroll when editing began by @simonbs in https://github.com/simonbs/Runestone/pull/93
    • Clears cached highlighted rects when changing theme by @simonbs in https://github.com/simonbs/Runestone/pull/94
    • Always insert text when calling insertText(_:) programmatically by @nighthawk in https://github.com/simonbs/Runestone/pull/87
    • Allow subclassing TextView and overriding certain methods by @nighthawk in https://github.com/simonbs/Runestone/pull/68
    • Adds UITextInteractions to TextInputView by @simonbs in https://github.com/simonbs/Runestone/pull/95
    • Fixes caret position not updated by @simonbs in https://github.com/simonbs/Runestone/pull/96

    New Contributors

    • @multigreg made their first contribution in https://github.com/simonbs/Runestone/pull/14
    • @twodayslate made their first contribution in https://github.com/simonbs/Runestone/pull/60
    • @AndrewBennet made their first contribution in https://github.com/simonbs/Runestone/pull/62
    • @nighthawk made their first contribution in https://github.com/simonbs/Runestone/pull/84
    • @eliperkins made their first contribution in https://github.com/simonbs/Runestone/pull/83

    Full Changelog: https://github.com/simonbs/Runestone/compare/0.1.0...0.2.0

    Source code(tar.gz)
    Source code(zip)
  • 0.1.1(May 6, 2022)

  • 0.1.0(May 5, 2022)

    This is the initial release of Runestone, an open source framework for creating performant plain text editors on iPhone and iPad with syntax highlighting, line numbers, a page guide, theming and much more.

    The version is published as 0.1.0 to indicate that the public API may not be stable. At this point I've used the framework in my Runestone app for iPhone and iPad so the framework has proven to work but I'm looking for input on the API before considering it stable.

    Source code(tar.gz)
    Source code(zip)
Owner
Simon Støvring
I tinker with iOS automation and poke at private APIs. Working on Runestone, Scriptable, Jayson, and Data Jar.
Simon Støvring
This project Orchid-Fst implements a fast text string dictionary search data structure: Finite state transducer (short for FST) in c++ language.This FST C++ open source project has much significant advantages.

Orchid-Fst 1. Project Overview This project Orchid-Fst implements a fast text string dictionary search data structure: Finite state transducer , which

Bin Ding 8 Jan 5, 2022
An intrusive C++17 implementation of a Red-Black-Tree, a Weight Balanced Tree, a Dynamic Segment Tree and much more!

This is Ygg (short for Yggdrasil), a C++17 implementation of several intrusive data structures: several balanced binary search trees: a red-black Tree

Lukas Barth 93 May 29, 2022
data structures which I've tried to implement in C++, much more incoming 👨‍💻

This repository contains some data structures which I've tried to implement, much more incoming ??‍?? I'm definitely not a C++ expert, so the code is

Wiktor Zając 2 Dec 21, 2021
This is a curve topology verification tool based on Fast Linking Numbers for Topology Verification of Loopy Structures.

Fast Linking Numbers This tool, called verifycurves, takes input models that consist of closed-loop curves, and outputs a topology certificate as a .t

Ante Qu 22 Jan 26, 2022
C++ library to create dynamic structures in plain memory of shared-memory segments

Ищи описание на хабре @mrlolthe1st. #define _CRT_SECURE_NO_WARNINGS #include "shared_structures.h" #include <iostream> #include <fstream> #include <ca

Александр Новожилов 4 Mar 30, 2022
Va1 is a simple character converter. It converts characters into nums, might be used in encryption protocols or as independent algorithm.

Va1 What is it? Va1 is a simple character converter. It converts characters into nums, might be used in encryption protocols or as independent algorit

Mr.Red 1 Dec 22, 2021
Like neofetch, but much faster because written in c. Only Linux.

fastfetch fastfetch is a neofetch like tool for fetching system information and displaying them in a pretty way. It is written in c to achieve much be

Linus Dierheimer 286 Jul 3, 2022
tree-sitter parser and syntax highlighter for the Dwarf Fortress raw language

tree-sitter-dfraw A simple language parser and highlighter made with tree-sitter tokyonight nightfly Using with nvim-treesitter Please refer to the ad

null 2 Apr 1, 2022
A library to convert Uber's H3 geo-index to LatLng vertices for Kotlin Multiplatform Mobile iOS and Android

A library to convert Uber's H3 geo-index to LatLng vertices for Kotlin Multiplatform Mobile iOS and android Features Uber H3 in one interface Common i

LINE 9 Apr 25, 2022
High performance build system for Windows, OSX and Linux. Supporting caching, network distribution and more.

FASTBuild FASTBuild is a build system for Windows, OSX and Linux, supporting distributed compilation and object caching. It is used by many game devel

FASTBuild 929 Jun 23, 2022
A collection of multiple types of lists used during pentesting, collected in one place. List types include usernames, passwords, combos, wordlist and may more..

Access list is a collection of multiple types of lists used during pentesting, collected in one place, created by Undercode This list include a collec

UNDERCODE UTILITIES 7 Jan 6, 2022
A collection of libraries, data structures, and more that I have created to make coding in C less painful.

ctools A collection of libraries, data structures, and more that I have created to make coding in C less painful. Data structures There are many data

null 3 Nov 27, 2021
100daysofDSA - Explore about arrays, linked lists, stacks & queues, graphs, and more to master the foundations of data structures & algorithms!

Explore about arrays, linked lists, stacks & queues, graphs, and more to master the foundations of data structures & algorithms!

null 26 Jun 30, 2022
Nodable is node-able. The goal of Nodable is to provide an original hybrid source code editor, using both textual and nodal paradigm.

Nodable is node-able ! Introduction: The goal of Nodable is to provide an original hybrid source code editor, using both textual and nodal paradigm. I

Bérenger Dalle-Cort 142 May 26, 2022
heuristically and dynamically sample (more) uniformly from large decision trees of unknown shape

PROBLEM STATEMENT When writing a randomized generator for some file format in a general-purpose programming language, we can view the resulting progra

John Regehr 4 Feb 15, 2022
Open-source graph editor, with built-it step-by-step Dijkstra's Algorithm.

Visual Dijkstra - Simple visual graph editor, with built-in step-by-step Dijkstra's algorithm Visual Dijkstra is a free and open-source tool, designed

Samuele Girgenti 27 May 23, 2022
Flexible level editor

Tiled Map Editor - https://www.mapeditor.org/ About Tiled Tiled is a general purpose tile map editor for all tile-based games, such as RPGs, platforme

mapeditor.org 8.9k Jul 1, 2022
What the Package Does (One Line, Title Case)

inside The goal of insidecpp11 is to do fast point in polygon classification, with cpp11. We are comparing a few ways of implementing this, essentiall

diminutive 3 Feb 11, 2022
What the Package Does (One Line, Title Case)

--- output: github_document --- <!-- README.md is generated from README.Rmd. Please edit that file --> ```{r, include = FALSE} knitr::opts_chunk$set

diminutive 2 Feb 11, 2022