A SwiftUI TextEditor with syntax highlighting using Highlight.js

Overview

CodeEditor

SwiftUI Swift5.3 macOS iOS Build and Test

A SwiftUI TextEditor View with syntax highlighting using Highlight.js.

It builds on top of Highlightr which does the wrapping of Highlight.js. CodeEditor then packages things up for SwiftUI.

Example usage in SVG Shaper for SwiftUI (used for editing SVG and Swift source):

SVG Shaper Screenshot

(Shaper is not actually using Highlightr, but is otherwise quite similar).

Highlightr example:

Highlight Example

Usage

Adding the Package

The Swift package URL is: https://github.com/ZeeZide/CodeEditor.git

Using it in a SwiftUI App

To use CodeEditor as a source code viewer, simply pass the source code as a string:

struct ContentView: View {

    var body: some View {
        CodeEditor(source: "let a = 42")
    }
}

If it should act as an actual editor, pass in a string Binding:

struct ContentView: View {

    @State private var source = "let a = 42\n"
    
    var body: some View {
        CodeEditor(source: $source, language: .swift, theme: .ocean)
    }
}

Languages and Themes

Highlight.js. supports more than 180 languages and over 80 different themes.

The available languages and themes can be accessed using:

CodeEditor.availableLanguages
CodeEditor.availableThemes

They can be used in a SwiftUI Picker like so:

struct MyEditor: View {
  
    @State private var source   = "let it = be"
    @State private var language = CodeEditor.Language.swift

    var body: some View {
        Picker("Language", selection: $language) {
            ForEach(CodeEditor.availableLanguages) { language in
                Text("\(language.rawValue.capitalized)")
                    .tag(language)
            }
        }
    
        CodeEditor(source: $source, language: language)
    }
}

Note: The CodeEditor doesn't do automatic theme changes if the appearance changes.

Smart Indent and Open/Close Pairing

Inspired by NTYSmartTextView, CodeEditor now also supports (on macOS):

  • smarter indents (preserving the indent of the previous line)
  • soft indents (insert a configurable amount of spaces if the user presses tabs)
  • auto character pairing, e.g. when entering {, the matching } will be auto-added

To enable smart indents, add the smartIndent flag, e.g.:

CodeEditor(source: $source, language: language, 
           flags: [ .selectable, .editable, .smartIndent ])

It is enabled for editors by default.

To configure soft indents, use the indentStyle parameter, e.g.

CodeEditor(source: $source, language: language,
           indentStyle: .softTab(width: 2))

It defaults to tabs, as per system settings.

Auto character pairing is automatic based on the language. E.g. there is a set of defaults for C like languages (e.g. Swift), Python or XML. The defaults can be overridden using the respective static variable in CodeEditor, or the desired pairing can be set explicitly:

", "'": "'" ]) ">
CodeEditor(source: $source, language: language,
           autoPairs: [ "{": "}", "<": ">", "'": "'" ])

Font Sizing

On macOS the editor supports sizing of the font (using Cmd +/Cmd - and the font panel). To enable sizing commands, the WindowScene needs to have the proper commands applied, e.g.:

WindowGroup {
    ContentView()
}
.commands {
    TextFormattingCommands()
}

To persist the size, the fontSize binding is available.

Highlightr and Shaper

Based on the excellent Highlightr. This means that it is using JavaScriptCore as the actual driver. As Highlightr says:

It will never be as fast as a native solution, but it's fast enough to be used on a real time editor.

The editor is similar to (but not exactly the same) the one used by SVG Shaper for SwiftUI, for its SVG and Swift editor parts.

Complete Example

import SwiftUI
import CodeEditor

struct ContentView: View {
  
  #if os(macOS)
    @AppStorage("fontsize") var fontSize = Int(NSFont.systemFontSize)
  #endif
  @State private var source = "let a = 42"
  @State private var language = CodeEditor.Language.swift
  @State private var theme    = CodeEditor.ThemeName.pojoaque

  var body: some View {
    VStack(spacing: 0) {
      HStack {
        Picker("Language", selection: $language) {
          ForEach(CodeEditor.availableLanguages) { language in
            Text("\(language.rawValue.capitalized)")
              .tag(language)
          }
        }
        Picker("Theme", selection: $theme) { theme in
          ForEach(CodeEditor.availableThemes) {
            Text("\(theme.rawValue.capitalized)")
              .tag(theme)
          }
        }
      }
      .padding()
    
      Divider()
    
      #if os(macOS)
        CodeEditor(source: $source, language: language, theme: theme,
                   fontSize: .init(get: { CGFloat(fontSize)  },
                                   set: { fontSize = Int($0) }))
          .frame(minWidth: 640, minHeight: 480)
      #else
        CodeEditor(source: $source, language: language, theme: theme)
      #endif
    }
  }
}

struct ContentView_Previews: PreviewProvider {
  static var previews: some View {
    ContentView()
  }
}

Who

SVGWebView is brought to you by ZeeZide. We like feedback, GitHub stars, cool contract work, presumably any form of praise you can think of.

Comments
  • Add support for observing and modifying the selected text range.

    Add support for observing and modifying the selected text range.

    This PR addresses #2.

    The selected text range can be observed and modified via another Binding now:

     struct ContentView: View {
        static private let initialSource = "let a = 42\n"
    
        @State private var source = Self.initialSource
        @State private var selection = Self.initialSource.endIndex..<Self.initialSource.endIndex
    
        var body: some View {
            CodeEditor(source: $source,
                       selection: $selection,
                       language: .swift,
                       theme: .ocean,
                       autoscroll: false)
            Button("Select All") {
                selection = source.startIndex..<source.endIndex
            }
        }
    }
    

    When autoscroll is true, the editor automatically scrolls to the respective cursor position when selection is modfied from the outside, i.e. programatically.

    opened by theMomax 5
  • [BUG] - CodeEditor is affected by ColorPicker

    [BUG] - CodeEditor is affected by ColorPicker

    When CodeEditor and ColorPicker are used at the same time, the color of CodeEditor changes

    import SwiftUI
    import CodeEditor
    
    struct ContentView: View {
        @State private var source = """
    struct ContentView: View {
        @State private var source = "let a = 42\n"
        @State var color: Color = Color.white
        var body: some View {
            HStack {
                ColorPicker("Color Picker", selection: $color)
                CodeEditor(source: $source, language: .swift, theme: .ocean)
            }
        }
    }
    """
        @State var color: Color = Color.white
        var body: some View {
            HStack {
                ColorPicker("Color Picker", selection: $color)
                CodeEditor(source: $source, language: .swift, theme: .ocean)
            }
        }
    }
    

    https://user-images.githubusercontent.com/84154073/164365639-cad35ec1-ac9e-4ce4-82b8-66e1c7b589e7.mov

    bug 
    opened by SNQ-2001 3
  • How to turn off smart dash

    How to turn off smart dash

    If using lua, then double dash aka -- is comment character. iOS has the feature to merge -- into a longer single dash..... Any idea, how to turn this off in CodeEditor ? I could not find any solution

    opened by gin66 2
  • Keyboard shortcuts

    Keyboard shortcuts

    Hello, Thanks for this great library!

    A beginner's question: what's the easiest way to add keyboard shortcuts? I'd like to execute a closure upon receiving a specific keyboard combination, ex. Cmd-R.

    Thanks!

    opened by iliasaz 2
  • How to use a customized font in IOS?

    How to use a customized font in IOS?

    I switched from SwiftUI's TextEditor to this CodeEditor. it really amazed me. However, I found I cannot specify the font and font size as I did in the TextEditor. The code is as follow,

    CodeEditor(source: $formulaInput, language: .python, theme: .default,
                           autoPairs: ["(" : ")", "{" : "}", "[" : "]"])
                    .font(.custom("SF Mono Bold", size: fontSize))
    

    Can you teach me how to use a customized font? Thank you so much

    opened by InkosiZhong 1
  • Add a Binding to report the currently selected range

    Add a Binding to report the currently selected range

    I want to insert some text in the code editor. Markdown example: Select some text, press Cmd-B for Bold, and have two asterisks inserted before and after the selection. But to do this, I need to get the code editor's selection range. How do I get the selection range?

    enhancement 
    opened by SwiftDevJournal 1
  • Correction of README

    Correction of README

    I noticed that the README was wrong, so I fixed it.

    Picker("Theme", selection: $theme) { theme in
      ForEach(CodeEditor.availableThemes) {
        Text("\(theme.rawValue.capitalized)")
          .tag(theme)
      }
    }
    

    ↓ Change ↓

    Picker("Theme", selection: $theme) {
      ForEach(CodeEditor.availableThemes) { theme in
        Text("\(theme.rawValue.capitalized)")
          .tag(theme)
      }
    }
    
    opened by SNQ-2001 0
  • Add missing delegate function (iOS)

    Add missing delegate function (iOS)

    Summary

    This PR adds missing UITextViewDelegate's function, which fixes #6.

    What was done

    • [x] Implement textViewDidChange(_ textView: UITextView) function in UXCodeTextViewRepresentable.Coordinator class.
    opened by darrarski 0
  • Binding does not work on iOS 15.2

    Binding does not work on iOS 15.2

    Description

    When a user enters text into CodeEditor, the binding is not called. This happens only on iOS. When using CodeEditor in a macOS app, there are no issues.

    Environment

    • Xcode v13.2.1
    • iOS 15.2 Simulator

    Steps to reproduce

    1. Embed CodeEditor in SwiftUI iOS application

      struct ContentView: View {
        @State var text = "TEST"
      
        var body: some View {
          VStack {
            CodeEditor(source: $text)
              .frame(height: 100)
      
            Divider()
      
            Text(text)
              .frame(maxWidth: .infinity, alignment: .leading)
              .multilineTextAlignment(.leading)
      
            Spacer()
          }
        }
      }
      
    2. Run the app in iOS Simulator

    3. Enter some text into CodeEditor

    4. Notice that the text property is not updated.

    opened by darrarski 0
  • Themes don’t work in Swift Playgrounds 4 on iPad

    Themes don’t work in Swift Playgrounds 4 on iPad

    When trying to use a CodeEditor view in my SP4 app, the editor always shows a clear background with a black letter font, no matter what theme I chose.

    opened by LeonardoXUI 0
  • Multiple instances in custom views are messing up the text state on macOS

    Multiple instances in custom views are messing up the text state on macOS

    Hello and first of all thank you for this great library :)

    I'm trying to replicate something similar to Xcode tabs, with some kind of top "tab bar" selecting each code file I want to edit.

    Since SwiftUI's TabView doesn't (currently) allow for tab bar customization, I've created my own using buttons in a HStack. When I click a button, the "main view" (containing the CodeEditor for selected file) changes and shows me file contents.

    The bug I've found is very strange, after changing one of two tabs the global "state" starts to mixup, showing up either wrong file content (the previous one) or resetting contents after window loses focus or when my tab changes.

    System's TextEditor seems to work fine.

    I have a strong suspect this is somehow related to SwiftUI.

    I hope attached video is "self explaining" (window always has focus, but something strange also happens when you focus any other application).

    you can find it here https://github.com/stefanomondino/CodeEditor in the Demo folder.

    https://user-images.githubusercontent.com/1691903/168474027-aedb3b73-5bcb-4183-b755-d745c11941cc.mov

    Also, it's worth nothing that also CodeEditorView (a very similar project) has the same issue.

    Let me know if you have any idea, thanks!

    bug help wanted 
    opened by stefanomondino 8
  • Problems using ColorPicker and CodeEditor at the same time

    Problems using ColorPicker and CodeEditor at the same time

    I found a new issue regarding the relationship between ColorPicker and CodeEditor

    (1) Start ColorPicker (2) Double-tap Text in CodeEditor

    ↓ Result ↓

    The color of the Text is reflected in the ColorPicker

    import SwiftUI
    import CodeEditor
    
    struct ContentView: View {
        @State var source: String = """
    import SwiftUI
    import CodeEditor
    
    struct ContentView: View {
        @State var source: String = ""
        @State var color: Color = Color.white
        var body: some View {
            HStack {
                ColorPicker("", selection: $color)
                CodeEditor(source: $source)
            }
        }
    }
    """
        @State var color: Color = Color.white
        var body: some View {
            ZStack {
                color
                    .edgesIgnoringSafeArea(.all)
                HStack {
                    ColorPicker("ColorPicker", selection: $color)
                    CodeEditor(source: $source, language: .swift, theme: .pojoaque)
                }
            }
        }
    }
    

    https://user-images.githubusercontent.com/84154073/164490460-6978fbbb-2afc-4e30-973b-87aae9435ae5.mp4

    opened by SNQ-2001 5
  • Problems with integrating CodeView into an Action Extension

    Problems with integrating CodeView into an Action Extension

    I am using this to build an App for reading codes on iOS. This package works fine for building SwiftUI Views. However, when I am using it to build an Action Extension, this error below occurs. Can you help me with that? Thanks a lot!

    Undefined symbols for architecture x86_64: "nominal type descriptor for CodeViewer.CodeViewer", referenced from: _symbolic ____y___________y_____y_____GGACy_____y__________y_____SgGGGQo 7SwiftUI4ViewPAAE18navigationBarItems7leading8trailingQrqd___qd_0_tAaBRd__AaBRd_0_r0_lFQO 10CodeViewerAGV AA6HStackV AA6ButtonV AA4TextV AA15ModifiedContentV AA5ImageV AA30_EnvironmentKeyWritingModifierV AA4FontV in EditorView.o _symbolic ____y_____y___________y_____y_____GGACy_____y__________y_____SgGGGQo_______Qo 7SwiftUI4ViewPAAE5sheet11isPresented9onDismiss7contentQrAA7BindingVySbG_yycSgqd__yctAaBRd__lFQO AcAE18navigationBarItems7leading8trailingQrqd___qd_0_tAaBRd__AaBRd_0_r0_lFQO 10CodeViewerAOV AA6HStackV AA6ButtonV AA4TextV AA15ModifiedContentV AA5ImageV AA30_EnvironmentKeyWritingModifierV AA4FontV 15ActionExtension05ThemeC0V in EditorView.o "nominal type descriptor for CodeViewer.CodeWebView.Theme", referenced from: _symbolic _____y_____G 7SwiftUI5StateV 10CodeViewer0D7WebViewC5ThemeO in EditorView.o "CodeViewer.CodeViewer.init(content: SwiftUI.Binding<Swift.String>, mode: CodeViewer.CodeWebView.Mode, darkTheme: CodeViewer.CodeWebView.Theme, lightTheme: CodeViewer.CodeWebView.Theme, isReadOnly: Swift.Bool, fontSize: Swift.Int, textDidChanged: ((Swift.String) -> ())?) -> CodeViewer.CodeViewer", referenced from: ActionExtension.EditorView.body.getter : some in EditorView.o "protocol conformance descriptor for CodeViewer.CodeViewer : SwiftUI.View in CodeViewer", referenced from: lazy protocol witness table accessor for type CodeViewer.CodeViewer and conformance CodeViewer.CodeViewer : SwiftUI.View in CodeViewer in EditorView.o "type metadata accessor for CodeViewer.CodeViewer", referenced from: ActionExtension.EditorView.body.getter : some in EditorView.o lazy protocol witness table accessor for type CodeViewer.CodeViewer and conformance CodeViewer.CodeViewer : SwiftUI.View in CodeViewer in EditorView.o outlined destroy of CodeViewer.CodeViewer in EditorView.o l_get_witness_table qd0__7SwiftUI4ViewHD3_AaBPAAE5sheet11isPresented9onDismiss7contentQrAA7BindingVySbG_yycSgqd__yctAaBRd__lFQOyAcAE18navigationBarItems7leading8trailingQrqd___qd_0_tAaBRd__AaBRd_0_r0_lFQOy10CodeViewerAOV_AA6HStackVyAA6ButtonVyAA4TextVGGATyAA15ModifiedContentVyAA5ImageVAA30_EnvironmentKeyWritingModifierVyAA4FontVSgGGGQo__15ActionExtension05ThemeC0VQo_HO in EditorView.o "type metadata for CodeViewer.CodeWebView.Theme", referenced from: property wrapper backing initializer of ActionExtension.EditorView.(theme in _1FD4E17BFA9D43C14FDD2767A1577070) : CodeViewer.CodeWebView.Theme in EditorView.o ld: symbol(s) not found for architecture x86_64 clang: error: linker command failed with exit code 1 (use -v to see invocation)

    opened by TangZhongham 1
Releases(1.2.2)
  • 1.2.2(Jul 16, 2022)

  • 1.2.1(Apr 21, 2022)

    Bug fixes:

    • block NSColorPanel from changing the text color
    • iOS: disable autocorrection, spellchecking and smart quotes (thanks @gin66!)
    • iOS: fix a bug where the text binding wasn't updated (thanks @darrarski!)

    New features:

    • monitor and set the text selection (thanks @theMomax!)
    • CocoaPods support (thanks @mcblooder!)
    Source code(tar.gz)
    Source code(zip)
  • 1.2.0(May 13, 2021)

    Added the set of "smart" editing features demonstrated by NTYSmartTextView, i.e.:

    • smarter indents (preserving the indent of the previous line when pressing enter)
    • soft indents (insert a configurable amount of spaces if the user presses tabs)
    • auto character pairing, e.g. when entering {, the matching } will be auto-added
    Source code(tar.gz)
    Source code(zip)
  • 1.0.0(May 12, 2021)

Owner
ZeeZide
ZeeZide GmbH ★ Software Consulting and Development
ZeeZide
A wallpaper changer using Wallhaven api. Written with C++/CLR, only 0.5 MB and have a GUI.

WHaven Wallpaper WHaven Wallpaper is a desktop wallpaper change program for change your wallpapers directly from wallhaven. Features You have a search

null 2 Aug 1, 2022
An integrated information center created with dear ImGui using modern C++ design / coding style.

ImGui info-center Introduction An integrated notification and information center created with dear ImGui. Interfaces and variables are designed under

Feej 7 Oct 29, 2022
A powerful and fast search tool using regular expressions

A powerful and fast search tool using regular expressions

Stefan Küng 1.3k Nov 28, 2022
Plugin for Rainmeter: Making widgets using web technology

PluginWebView Plugin to take advantage of Microsoft Edge WebView2 to display web content on a skin. WebView window is attached into skin window so all

null 29 Nov 20, 2022
Electron framework lets you write cross-platform desktop applications using JavaScript, HTML and CSS.

?? Available Translations: ???? ???? ???? ???? ???? ???? ???? ???? . View these docs in other languages at electron/i18n. The Electron framework lets

Electron 104.7k Dec 1, 2022
wxWidgets is a free and open source cross-platform C++ framework for writing advanced GUI applications using native controls.

About wxWidgets is a free and open source cross-platform C++ framework for writing advanced GUI applications using native controls. wxWidgets allows y

null 4.7k Dec 3, 2022
AxeraVision / Simple yet customizable UI using ImGui Framework

[ AxeraVision UI ] This repository features a base UI using the ImGui Framework. Features Simple UI Design Easy Code to Customize Good Starting Base U

Axera.Digital 4 Jun 28, 2022
A readline and libedit replacement that supports UTF-8, syntax highlighting, hints and Windows and is BSD licensed.

Read Evaluate Print Loop ++ A small, portable GNU readline replacement for Linux, Windows and MacOS which is capable of handling UTF-8 characters. Unl

Marcin Konarski 602 Nov 29, 2022
Visual Studio extension for assembly syntax highlighting and code completion in assembly files and the disassembly window

Asm-Dude Assembly syntax highlighting and code assistance for assembly source files and the disassembly window for Visual Studio 2015, 2017 and 2019.

Henk-Jan Lebbink 4k Nov 29, 2022
Syntax highlighting text editor for ImGui

Syntax highlighting text editor for ImGui

null 1.1k Nov 27, 2022
📝 Performant plain text editor for iOS with syntax highlighting, line numbers, invisible characters and much more.

?? 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 syn

Simon Støvring 2k Dec 1, 2022
cmake-font-lock - Advanced, type aware, highlight support for CMake

cmake-font-lock - Advanced, type aware, highlight support for CMake

Anders Lindgren 39 Oct 2, 2022
A vim plugin for libclang-based highlighting of C, C++, ObjC

color_coded: semantic highlighting with vim color_coded is a vim plugin that provides realtime (fast), tagless code highlighting for C++, C, and Objec

Jeaye Wilkerson 863 Nov 22, 2022
C/C++ language server supporting multi-million line code base, powered by libclang. Emacs, Vim, VSCode, and others with language server protocol support. Cross references, completion, diagnostics, semantic highlighting and more

Archived cquery is no longer under development. clangd and ccls are both good replacements. cquery cquery is a highly-scalable, low-latency language s

Jacob Dufault 2.3k Nov 20, 2022
C/C++ language server supporting multi-million line code base, powered by libclang. Emacs, Vim, VSCode, and others with language server protocol support. Cross references, completion, diagnostics, semantic highlighting and more

Archived cquery is no longer under development. clangd and ccls are both good replacements. cquery cquery is a highly-scalable, low-latency language s

Jacob Dufault 2.3k Nov 20, 2022
C/C++/ObjC language server supporting cross references, hierarchies, completion and semantic highlighting

ccls ccls, which originates from cquery, is a C/C++/Objective-C language server. code completion (with both signature help and snippets) definition/re

Fangrui Song 3.2k Nov 23, 2022
asyncio is a c++20 library to write concurrent code using the async/await syntax.(developing in progress)

asyncio Asyncio is a C++20 coroutine library to write concurrent code using the await syntax, and imitate python asyncio library. Hello world Task<> h

Netcan 496 Nov 21, 2022
Math library using hlsl syntax with SSE/NEON support

HLSL++ Small header-only math library for C++ with the same syntax as the hlsl shading language. It supports any SSE (x86/x64 devices like PC, Mac, PS

null 352 Nov 29, 2022
An open source, OOP language with editable syntax.

Copper An Open source compiled programming language, In development. Goals Copper is an general-purpose OOP language. Coppers main goal is to allow ea

null 13 Nov 18, 2022
Tau is a fast syntax highlighter capable of emitting HTML.

tau - a reasonably fast (wip) syntax highlighter. Tau is a fast syntax highlighter capable of emitting HTML. It highlights the following languages: py

Palaiologos 12 Nov 16, 2022