CodeEdit Documentation

CodeEdit App for macOS – Elevate your code editing experience. Open source, free forever.

23,078 stars Swift Swift 1 file · ~30,003 tokens #app#appkit#apple#code#code-edit#code-editor#editor#ide
RAW Doc

File: README.md

CodeEdit is a code editor built by the community, for the community, written entirely and unapologetically for macOS. Features include syntax highlighting, code completion, project find and replace, snippets, terminal, task running, debugging, git integration, code review, extensions, and more.






[!IMPORTANT]
CodeEdit is currently in development and not yet recommended for production use, however you can take part in shaping it's future by test-driving pre-release versions and submitting an issue to let us know what you think.

Table of Contents

Motivation

Most editors in use today rely on Electron or other cross-platform frameworks, limiting their ability to fully utilize system resources. While Xcode provides a native experience, it is specifically designed for projects targeting Apple platforms.

We think developers working on projects not written for Apple platforms deserve that same macOS-native experience we get with Xcode while unlocking the full potential of the Mac.

This raised the question "what if such an editor existed?", a question that led to the creation of this concept, which our project aims to make a reality.

Mission

It is our commitment to keep CodeEdit open source and free forever, supported by the community.

Our goal is to maintain a lightweight experience, similar to TextEdit, while being able to scale up to a more feature-rich experience, comparable to Xcode, as necessary.

We strive to remain true to Apple's human interface guidelines and development patterns, ensuring CodeEdit looks and feels like an application developed by Apple themselves, which includes a meticulous attention to detail.

Community

Join our growing community on Discord and GitHub Discussions where we discuss and collaborate on all things CodeEdit. Don't be shy, jump right in and be part of the discussion!

[!NOTE]
We hold a weekly meetup on Discord every Saturday at 3pm UTC where we discuss latests development, feature requests, goals, and priorities.

Join us

Activity

Contributing

Be part of the next revolution in code editing by contributing to the project. This is a community-led effort, so we welcome as many contributors who can help. Read the Contribution Guide for more information.

This project spans multiple repositories so instead of browsing issues in the issues tab, it may be helpful to find an issue to get started on in our project board.

For issues we want to focus on that are most relevant at any given time, please see the issues scoped to our current iteration here.

Contributors

Austin Condiff🎨 💻 Lukas Pistrol🚇 ⚠️ 💻 Khan Winter💻 🐛 Matthijs Eikelenboom💻 🐛 Wouter Hennen💻 Wesley De Groot🚇 ⚠️ 💻 KaiTheRedNinja💻
Pavel Kasila🚇 ⚠️ 💻 Marco Carnevali🚇 ⚠️ 💻 Nanashi Li💻 ninjiacoder💻 Cihat Gündüz💻 Rehatbir Singh💻 Angelk90💻
Stef Kors💻 Chris Akring💻 highjeans💻 Jason Platts🚇 🔌 Rob Hughes💻 Lingxi Li💻 🐛 HZ.Liu💻 🐛
Richard Topchii💻 Pythonen💻 Javier Solorzano💻 🐛 Cosmin Anghel💻 Shivesh💻 Andrey Plotnikov💻 POPOBE97💻
nrudnyk💻 Ben Koska💻 evolify🐛 Shibo Tong💻 Ethan Wong💻 Gabriel Moreno🐛 Sizhe Zhao🐛
Muhammed Mahmood💻 🚧 Muescha💻 Alex Sinelnikov💻 Heewon Cho🐛 Matt Kiazyk💻 DingoBits💻 Shoto Kobayashi🐛 💻
Aaryan Kothari🐛 Kyle💻 Nakaoka Rei💻 🐛 Alex Deem🚧 deni zakya🐛 Ahmad Yasser🐛 ezraberch💻
Elias Wahl🐛 bombardier200💻 Alex Yapryntsev💻 Code-DJ💻 🐛 Neilzon Viloria🐛 Cubik🐛 💻 Renan Greca💻
maxkel🐛 💻 Scrap💻 iggy890💻 Sara Tavares🐛 💻 luah5💻 Evan Wang💻 Dscyre Scotti💻
Tomáš Boďa🐛 Ahmed Attalla🐛 💻 Esteban Borai💻 avinizhanov🐛 💻 kmohsin11🐛 Axel Martinez🐛 💻 Federico Zivolo💻
Elvis Wong🐛 İbrahim Çetin🐛 phlpsong🐛 Ahnaf Mahmud💻 Dan K💻 Roscoe Rubin-Rottenberg💻 Paul Ebose🐛
Daniel Zhu🐛 Simon Whitaker🐛 Leonardo💻 Levente Anda💻 Nobel💻 Savely💻 Kihron🐛
Filipp Kuznetsov💻 rustemd02🐛 💻 Simon Kudsk💻 Surya💻 George Tchelidze💻 Chris Pineda💻

Sponsors

Support CodeEdit's development by becoming a sponsor.







Backers

Support CodeEdit's development by becoming a backer.








Thanks to all of our other backers

@ivanvorobei
@albertorestifo
@rkusa
@cadenkriese
@petrjahoda
@allejo
@frousselet
@wkillerud

License

Licensed under the MIT license.

    CodeEditKit     CodeEditTextView CodeEditSourceEditor CodeEditLanguages     CodeEditCLI    

File: CODE_OF_CONDUCT.md

Contributor Covenant Code of Conduct

Our Pledge

We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.

We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.

Our Standards

Examples of behavior that contributes to a positive environment for our
community include:

  • Demonstrating empathy and kindness toward other people
  • Being respectful of differing opinions, viewpoints, and experiences
  • Giving and gracefully accepting constructive feedback
  • Accepting responsibility and apologizing to those affected by our mistakes,
    and learning from the experience
  • Focusing on what is best not just for us as individuals, but for the
    overall community

Examples of unacceptable behavior include:

  • The use of sexualized language or imagery, and sexual attention or
    advances of any kind
  • Trolling, insulting or derogatory comments, and personal or political attacks
  • Public or private harassment
  • Publishing others' private information, such as a physical or email
    address, without their explicit permission
  • Repeated low-quality pull requests or pull requests not in accordance to project goals especially after warning from a project maintainer or administrator
  • Other conduct which could reasonably be considered inappropriate in a
    professional setting

Enforcement Responsibilities

Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.

Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.

Scope

This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.

Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
Github Issues.
All complaints will be reviewed and investigated promptly and fairly.

All community leaders are obligated to respect the privacy and security of the
reporter of any incident.

Enforcement Guidelines

Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:

1. Correction

Community Impact: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.

Consequence: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.

2. Warning

Community Impact: A violation through a single incident or series
of actions.

Consequence: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.

3. Temporary Ban

Community Impact: A serious violation of community standards, including
sustained inappropriate behavior.

Consequence: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.

4. Permanent Ban

Community Impact: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.

Consequence: A permanent ban from any sort of public interaction within
the community.

Attribution

This Code of Conduct is adapted from the Contributor Covenant,
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.

Community Impact Guidelines were inspired by Mozilla's code of conduct
enforcement ladder
.

For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.


File: CONTRIBUTING.md

Contribute to CodeEdit

Feel free to join and collaborate on our Discord Server.

[!WARNING]
Please do not submit localization related pull requests at this time.
Once we are ready to support more languages we will let you know with a guide on how to contribute here and on our Discord Server.

Fork & Clone CodeEdit

Tap the "Fork" button on the top of the site. After forking clone the forked repository to your Mac.

Explore Issues

Find issues from the Issues tab or from the To Do column in our project. If you find an issue you want to work on, please indicate it in the issue and/or attach a draft PR once available. An admin or maintainer will then assign the Issue and/or PR to you.

[!IMPORTANT]
Please make sure to first comment under an issue or ask a maintainer to assign you to the issue before working on it. This helps prevent multiple people from working on the same
thing, which could result in your work not being merged. Additionally, some issues might be reserved for those with more in-depth knowledge of the codebase.

Getting Started

Please read the Getting Started guide in our wiki.

We also have a troubleshooting guide that provides common resolutions.

Code Style

Please read our guide on Code Style in our wiki.

Pull Request

Once you are happy with your changes, submit a Pull Request.

The pull request opens with a template loaded. Fill out all fields that are relevant.

The PR should include following information:

  • A descriptive title on what changed.
  • A detailed description of changes.
  • If you made changes to the UI please add a screenshot or video as well.
  • If there is a related issue please add a reference to the issue. If not, create one beforehand and link it.
  • If your PR is still in progress mark it as Draft.

Checks, Tests & Documentation

Request a review from one of our admins @austincondiff, @lukepistrol, @MarcoCarnevali, @jasonplatts, @pkasila or maintainers @cstef, @linusS1, @RayZhao1998, @wdg.

[!TIP]
If it is your first PR, an admin will need to request a review for you.

[!IMPORTANT]
Please resolve all Violation errors in Xcode (except: TODO: warnings). Otherwise the swiftlint check on GitHub will fail.

Once you submit the PR GitHub will run a couple of actions which run tests and SwiftLint (this can take a couple of minutes). Should a test fail, it cannot be merged until tests succeed.

Make sure to resolve all merge-conflicts otherwise the PR cannot be merged.

[!IMPORTANT]
Make sure your code is well documented so others can interact with your code easily!


File: LICENSE.md

MIT License

Copyright (c) 2022 CodeEdit

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


File: .swiftlint.yml

disabled_rules:

  • todo
  • trailing_comma
  • nesting

type_name:
excluded:
- ID

identifier_name:
allowed_symbols: ["$", "_"]
excluded:
- id
- vc

paths to ignore during linting.

excluded:

  • CodeEditModules/.build # Where Swift Package Manager checks out dependency sources
  • DerivedData

opt_in_rules:

  • attributes
  • empty_count
  • closure_spacing
  • contains_over_first_not_nil
  • missing_docs

- implicit_return

  • modifier_order
  • convenience_type
  • pattern_matching_keywords
  • multiline_parameters_brackets
  • multiline_arguments_brackets

custom_rules:
spaces_over_tabs:
included: ".*\.swift"
name: "Spaces over Tabs"
regex: "\t"
message: "Prefer spaces for indents over tabs. See Xcode setting: 'Text Editing' -> 'Indentation'"
severity: warning


File: AppCast/_config.yml

Welcome to Jekyll!

This config file is meant for settings that affect your whole blog, values

which you are expected to set up once and rarely edit after that. If you find

yourself editing this file very often, consider using Jekyll's data files

feature for the data you need to update frequently.

For technical reasons, this file is *NOT* reloaded automatically when you use

'bundle exec jekyll serve'. If you change this file, please restart the server process.

Site settings

These are used to personalize your new site. If you look in the HTML files,

you will see them accessed via {{ site.title }}, {{ site.email }}, and so on.

You can create any custom variable you would like, and they will be accessible

in the templates via {{ site.myvariable }}.

title: CodeEdit.app
baseurl: "" # the subpath of your site, e.g. /blog
url: "" # the base hostname & protocol for your site, e.g. http://example.com

Build settings

markdown: kramdown
plugins:

  • "jekyll-github-metadata"

Exclude from processing.

The following items will not be processed, by default. Create a custom list

to override the default setting.

exclude:

- Gemfile

- Gemfile.lock

- node_modules

- vendor/bundle/

- vendor/cache/

- vendor/gems/

- vendor/ruby/


File: CodeEdit/Assets.xcassets/Contents.json

{
"info" : {
"author" : "xcode",
"version" : 1
}
}


File: CodeEdit/Assets.xcassets/AccentColor.colorset/Contents.json

{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}


File: CodeEdit/Assets.xcassets/Custom Colors/Contents.json

{
"info" : {
"author" : "xcode",
"version" : 1
}
}


File: CodeEdit/Assets.xcassets/Custom Colors/Amber.colorset/Contents.json

{
"colors" : [
{
"color" : {
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0.133",
"green" : "0.635",
"red" : "0.784"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0.302",
"green" : "0.812",
"red" : "0.961"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}


File: CodeEdit/Assets.xcassets/Custom Colors/CoolGray.colorset/Contents.json

{
"colors" : [
{
"color" : {
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0.553",
"green" : "0.541",
"red" : "0.518"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0.549",
"green" : "0.537",
"red" : "0.525"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}


File: CodeEdit/Assets.xcassets/Custom Colors/FolderBlue.colorset/Contents.json

{
"colors" : [
{
"color" : {
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0.973",
"green" : "0.698",
"red" : "0.125"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0.859",
"green" : "0.698",
"red" : "0.365"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}


File: CodeEdit/Assets.xcassets/Custom Colors/InspectorBackgroundColor.colorset/Contents.json

{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xF7",
"green" : "0xF5",
"red" : "0xF4"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x25",
"green" : "0x1F",
"red" : "0x1C"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}


File: CodeEdit/Assets.xcassets/Custom Colors/Scarlet.colorset/Contents.json

{
"colors" : [
{
"color" : {
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0.176",
"green" : "0.303",
"red" : "0.956"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0.376",
"green" : "0.475",
"red" : "0.960"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}


File: CodeEdit/Assets.xcassets/Custom Colors/Steel.colorset/Contents.json

{
"colors" : [
{
"color" : {
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0.522",
"green" : "0.463",
"red" : "0.373"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0.749",
"green" : "0.690",
"red" : "0.585"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}


File: CodeEdit/Assets.xcassets/Icons/Contents.json

{
"info" : {
"author" : "xcode",
"version" : 1
}
}


File: CodeEdit/Assets.xcassets/Icons/BitBucketIcon.imageset/Contents.json

{
"images" : [
{
"filename" : "BitBucketIcon.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}


File: CodeEdit/Assets.xcassets/Icons/GitHubIcon.imageset/Contents.json

{
"images" : [
{
"filename" : "GitHubIcon.png",
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "GitHubIcon-Dark.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}


File: CodeEdit/Assets.xcassets/Icons/GitLabIcon.imageset/Contents.json

{
"images" : [
{
"filename" : "GitLabIcon.png",
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "GitLabIcon-Dark.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}


File: CodeEdit/Assets.xcassets/line.3.horizontal.decrease.chevron.filled.imageset/Contents.json

{
"images" : [
{
"filename" : "line.3.horizontal.decrease.chevron.filled.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true,
"template-rendering-intent" : "template"
}
}


File: CodeEdit/Assets.xcassets/line.3.horizontal.decrease.chevron.imageset/Contents.json

{
"images" : [
{
"filename" : "line.3.horizontal.decrease.chevron.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true,
"template-rendering-intent" : "template"
}
}


File: CodeEdit/Features/Keybindings/default_keybindings.json

[
{
"id": "copy", "name": "copy", "description": "Copy command", "context": "Global", "modifier": "command", "keybinding": "c"
},
{
"id": "paste", "name": "paste", "description": "Copy command", "context": "Global", "modifier": "command", "keybinding": "c"
}

]


File: CodeEdit/Preview Content/Preview Assets.xcassets/Contents.json

{
"info" : {
"author" : "xcode",
"version" : 1
}
}


File: CodeEditUITests/UI TESTING.md

UI Testing in CodeEdit

CodeEdit uses XCUITests for automating tests that require user interaction. Ideally, we have UI tests for every UI
component in CodeEdit, but right now (as of Jan, 2025) we have fewer tests than we'd like.

Test Application Setup

To test workspaces with real files, launch the application with the App enum. To create a temporary test directory,
use the App.launchWithTempDir() method. This will create a random directory in the temporary directory and return
the created path. In tests you can add files to that directory and it will be cleaned up when the tests finish.

There is a App.launchWithCodeEditWorkspace method, but please try not to use it. It exists for compatibility with a
few existing tests and would be a pain to replace. It's more likely to be flaky, and can't test things like file
modification, creation, or anything besides clicking around the workspace or you risk modifying the very project
that's being tested!

Query Extensions

For common, long, queries, add static methods to the Query enum. This enum should be used to help clarify tests
without having to read long XCUI queries. For instance

swift
let window = application.windows.element(matching: .window, identifier: "workspace")
let navigator = window.descendants(matching: .any).matching(identifier: "ProjectNavigator").element
let newFileCell = navigator
                .descendants(matching: .outlineRow)
                .containing(.textField, identifier: "ProjectNavigatorTableViewCell-FileName")
                .element(boundBy: 0)

Should be shortened to the following, which should be easier to read and intuit what the test is doing.

swift
let window = Query.getWindow(app)
let navigator = Query.Window.getProjectNavigator(window)
let newFileCell = Query.Navigator.getProjectNavigatorRow(fileTitle: "FileName", navigator)

This isn't necessary for all tests, but useful for querying common regions like the project navigator, window, or
utility area.


File: Documentation.docc/Documentation.md

``CodeEdit``

Topics

About Window

Settings

  • Settings

App Window

CodeEditExtension

  • ExtensionManager
  • FolderMonitor

CodeEditUI

CodeFile

  • CodeFileDocument
  • CodeFileError

CommandPalette

  • CommandPaletteView
  • CommandPaletteViewModel

Documents

  • doc:FileManagement
  • WorkspaceDocument
  • CEWorkspaceFile
  • CEWorkspaceFileManager
  • CodeFileDocument
  • CodeEditDocumentController

Feedback

  • FeedbackView
  • FeedbackWindowController
  • FeedbackToolbar
  • FeedbackModel
  • FeedbackType
  • FeedbackIssueArea

Git

Keybindings

  • KeybindingManager
  • CommandManager
  • Command
  • CommandClosureWrapper
  • KeyboardShortcutWrapper

LanguageServerProtocol

  • LSPClient

OpenQuickly

  • OpenQuicklyView
  • OpenQuicklyListItemView
  • OpenQuicklyViewModel
  • OpenQuicklyPreviewView
  • SearchModeModel
  • SearchResultModel
  • SearchResultMatchModel
  • SearchResultLabel

Utils

  • CodeEditKeychain
  • ShellClient
  • FileIcon

Welcome


File: Documentation.docc/About/About Window.md

About Window

The About Window displays general information about the app and acknowledgements to external dependencies.

Topics

About View

  • AboutView
  • AboutWindow

Acknowledgements

  • AcknowledgementsView

  • AcknowledgementsViewModel

  • AcknowledgementsViewWindowController

  • AcknowledgementRowView

  • AcknowledgementObject

  • AcknowledgementDependency

  • AcknowledgementPackageState

  • AcknowledgementPin


File: Documentation.docc/App Window/Adding New Tab Type.md

Adding a New Tab Type

This article is about how to add a new tab type to TabBar

Overview

First of all, each data type to be represented as tab in the UI should conform to
EditorTabRepresentable protocol. For example, this is how it is done for
FileItem:

swift
final class FileItem: Identifiable, Codable, EditorTabRepresentable {
    public var tabID: EditorTabID {
        .codeEditor(id)
    }

    public var title: String {
        self.url.lastPathComponent
    }

    public var icon: Image {
        Image(systemName: self.systemImage)
    }

    public var iconColor: Color {
        ...
    }

    ...
}

Add new item identifier case

Each new tab type must have new identifier case, for example:

swift
public enum EditorTabID: Codable, Identifiable, Hashable {
    public var id: String {
        switch self {
        ...
        case .gitHistory(let repo):
            return "gitHistory_\(repo)"
        }
    }

    /// Represents Git history
    case gitHistory(String)
}

Opening and closing new tab types

Tabs are opened using WorkspaceDocument/openTab(item:) method. It does a set of common
things for all tabs. But also it calls a private method based on the EditorTabID of the
item. The private method for your EditorTabRepresentable MUST persist this item
somewhere (I recommend persisting them in WorkspaceSelectionState).

The same is for closing tabs using WorkspaceDocument/closeTab(item:) method.
Closing multiple tabs at once is handled by common functions, so there are no changes
required for them.

WorkspaceDocument/close() calls WorkspaceDocument.saveSelectionState() to persist Workspace Selection State to UserDefaults.

WorkspaceDocument/read(from:ofType:) calls WorkspaceDocument.readSelectionState() to retrieve Workspace Selection State from UserDefaults.

Also, because previously opened tabs are persisted in UserDefaults,
they should be recovered some way later. To recover new tab types you need to add
a case for WorkspaceDocument/read(from:ofType:) to let it know how to recover your new tab type.

If you need to persist something as code editor tabs do for files, then you need to add
functionality to persist changes to WorkspaceDocument/close().

Also, you need to add a case for new tab type to
WorkspaceSelectionState/getItemByTab(id:). It will allow to use
WorkspaceSelectionState/selected property and other features.

Adding a view for the new tab type

To add a view for new tab type, you need to add a case for your tab type to
WorkspaceView/tabContent:

swift
@ViewBuilder var tabContent: some View {
    if let tabID = workspace.selectionState.selectedId {
        switch tabID {
            ...
        case .gitHistory:
            GitHistoryView(windowController: windowController, workspace: workspace)
        }
    } else {
        noEditor
    }
}

File: Documentation.docc/App Window/App Window.md

App Window

A collection of all the views that make up the main app window.

Topics

Window Controller

  • CodeEditWindowController
  • WorkspaceView

Main Content

  • EditorAreaView
  • EditorAreaFileView
  • CodeFileView
  • NonTextFileView
  • AnyFileView
  • LoadingFileView
  • ImageFileView
  • PDFFileView

JumpBar

  • JumpBarView
  • JumpBarComponent
  • JumpBarMenu
  • JumpBarMenuItem
  • NavigatorSidebarView

Inspector Sidebar

  • InspectorAreaView

Status Bar

  • StatusBarView

Tab Bar

  • TabBarView

Terminal Emulator

  • TerminalEmulatorView

File: Documentation.docc/App Window/InspectorSidebarView.md

``CodeEdit/InspectorAreaView``

Topics

Toolbars

  • InspectorAreaToolbarTop

File Inspector

  • FileInspectorView
  • FileInspectorModel
  • FileLocation
  • IndentUsing
  • LanguageType
  • LineEndings
  • TextEncoding

History Inspector

  • HistoryInspectorView
  • HistoryInspectorModel
  • HistoryInspectorItemView
  • HistoryInspectorNoHistoryView
  • HistoryPopoverView

Quick Help Inspector

  • QuickHelpInspectorView

No Selection

  • NoSelectionInspectorView

File List

  • FileTypeList

File: Documentation.docc/App Window/NavigatorSidebarView.md

``CodeEdit/NavigatorSidebarView``

Topics

Toolbars

  • NavigatorSidebarToolbarTop
  • NavigatorSidebarToolbarBottom

Project Navigator

  • ProjectNavigatorView
  • OutlineView
  • OutlineViewController
  • OutlineMenu
  • OutlineTableViewCell
  • OutlineTableViewCellDelegate

Source Control Navigator

  • SourceControlNavigatorView
  • SourceControlModel
  • SourceControlSearchToolbar
  • SourceControlToolbarBottom
  • SourceControlNavigatorRepositoriesView
  • SourceControlNavigatorChangesView
  • SourceControlNavigatorChangedFileView

Find Navigator

  • FindNavigatorView
  • FindNavigatorSearchBar
  • FindNavigatorModeSelector
  • FindNavigatorResultList
  • FindNavigatorListViewController
  • FindNavigatorListMatchCell

Extension Navigator

  • ExtensionNavigatorView
  • ExtensionNavigatorItemView
  • ExtensionNavigatorData
  • ExtensionInstallationView
  • ExtensionInstallationViewModel

File: Documentation.docc/App Window/StatusBarView.md

``CodeEdit/StatusBarView``

Topics

Model

  • ImageDimensions

View Model

  • StatusBarViewModel

View Modifiers

  • UpdateStatusBarInfo

Items

  • StatusBarMenuStyle
  • StatusBarBreakpointButton
  • StatusBarIndentSelector
  • StatusBarEncodingSelector
  • StatusBarLineEndSelector
  • StatusBarToggleUtilityAreaButton
  • StatusBarCursorPositionLabel

File: Documentation.docc/App Window/TabBarView.md

``CodeEdit/TabBarView``

Topics

Articles

Model

  • EditorTabID
  • EditorTabRepresentable

Components

  • EditorTabView
  • TabBarContextMenu
  • EditorTabButtonStyle
  • TabDivider
  • TabBarTopDivider
  • TabBarBottomDivider
  • TabBarAccessoryIcon
  • TabBarXcodeBackground

File: Documentation.docc/App Window/UtilityAreaView.md

``CodeEdit/UtilityAreaView``

Topics

Model

  • UtilityAreaTab

View Model

  • UtilityAreaViewModel
  • UtilityAreaTabViewModel

Utility

  • UtilityAreaTerminal
  • UtilityAreaTerminalTab
  • UtilityAreaDebugView
  • UtilityAreaOutputView

Toolbar

  • UtilityAreaView
  • UtilityAreaSplitTerminalButton
  • UtilityAreaMaximizeButton
  • UtilityAreaClearButton
  • UtilityAreaFilterTextField

File: Documentation.docc/AppPreferences/AppPreferences.md

``CodeEdit/Settings``

Topics

Getting Started

Settings Model

  • SettingsModel
  • SoftwareUpdater

Settings Section Views

  • GeneralSettingsView
  • ThemeSettingsView
  • TextEditingSettingsView
  • TerminalSettingsView
  • LocationsSettingsView
  • KeybindingsSettingsView
  • AccountSettingsView
  • SourceControlSettingsView
  • SettingsPlaceholderView

Helper Views

  • SettingsContent
  • SettingsSection
  • SettingsColorPicker
  • SettingsToolbar

Theme Settings Model

  • Theme
  • ThemeModel

File: Documentation.docc/AppPreferences/Create a View.md

Create a View

Now that you followed the doc:Getting-Started guide it's time to create a view.

Add setting to existing Section

In our example we added ourNewOption in Settings/GeneralSettings.

Now let's take a look at the GeneralSettingsView.

swift
import SwiftUI

struct GeneralSettingsView: View {
    @AppSettings(\.general)
    var settings

    var body: some View {
        SettingsForm {
            Section {
                appearance
                fileIconStyle
                navigatorTabBarPosition
                inspectorTabBarPosition
                ...
            }
        }
    }
}

As you can see SettingsModel is already setup and ready to use.

To add your option toggle below the other options just add something like this:

swift
private extension GeneralSettingsView {
    // MARK: - Settings View
    
    private var yourOption: some View {
        Toggle("Your text", isOn: $general.yourNewOption)
    }
}

Then add it to var body: some View

swift
struct GeneralSettingsView: View {
    var body: some View {
        SettingsForm {
            Section {
                appearanceSection
                showIssuesSection
                fileExtensionsSection
                // REMOVEME: et cetera
                yourOptionSection
            }
        }
    }
}

And now you're done!

Implement a new section

Tip: Rename YourSection to the section name that you want

To implement a new section first create a new folder inside the Pages folder and name it accordingly.

Inside the folder create a new SwiftUI view and name it "YourSectionSettingsView.swift".

Then create a new folder inside called Models and inside of it create a file named "YourSectionSettings.swift"

Tip: The order that pages are arranged in the array is the same as in the settings window, the first array member will be the top item

text
Then find the file `SettingsPage.swift` and add `YourSection` to the `enum Name` like this:

```swift
enum Name: String {
    case general = "General"
    case advanced = "Advanced"
    // et cetera
    case yourSection = "YourSection"
}

Back in YourSectionView.swift implement your option like this:

swift
import SwiftUI

struct YourSectionSettingsView: View {
    @AppSettings(\.yourSection)
    var yourSection

    var body: some View {
        SettingsForm {
            Section {
                yourToggleSection
            }
        }
    }
}

private extension YourSectionSettingsView {
    // MARK: - Settings Views

    private var yourToggle: some View {
        Toggle("Your option", isOn: $yourSection.yourNewOption)
    }
}

There are 3 more steps, almost done.

Open ModelNameToSettingName.swift and add your translated search result:

swift
let translator: [String: String] = [
    // MARK: - General Settings
    "appAppearance": NSLocalizedString("Appearance", comment: ""),
    "fileIconStyle": NSLocalizedString("File Icon Style", comment: ""),
    // etc
    // MARK: - Your Section
    "yourOption": NSLocalizedString("Your Option", comment: "Your translation comment")
]

Now, open SettingsView.swift and add your section to the populatePages() method:

swift
/// Creates all the neccessary pages
private func populatePages() -> [SettingsPage] {
    var pages = [SettingsPage]()
    let settingsData = SettingsData()

    let generalSettings = SettingsPage(.general, baseColor: .gray, icon: .system("gear"))
    pages = createPageAndSettings(settingsData.general, parent: generalSettings, prePages: pages)

    let accountsSettings = SettingsPage(.accounts, baseColor: .blue, icon: .system("at"))
    pages = createPageAndSettings(settingsData.accounts, parent: accountsSettings, prePages: pages)

    // etc
    let yourSectionSettings = SettingsPage(.yourSection, baseColor: /* add color here */, icon: /* add icon */)
    pages = createPageAndSettings(settingsData.yourSection, parent: yourSectionSettings, prePages: pages)

    return pages
}

When you are done, add YourSectionSettingsView to SettingsView.swift:

swift
Group {
    switch selectedPage {
    case .general:
        GeneralSettingsView().environmentObject(updater)
    case .yourSection:
        YourSectionSettingsView()
    default:
        Text("Implementation Needed").frame(alignment: .center)
    }
}

File: Documentation.docc/AppPreferences/Getting Started.md

Getting Started

There are a few things to consider when using the Settings.

Reading/Writing Values

The Settings can be accessed from everywhere in the app like this:

swift
@AppSettings(\.settingName)
var setting
swift
Toggle("Enable some Feature", value: $setting)

Creating a New Preference

When implementing a new feature, we might have some options in regards to this new feature we want to show the user in the apps Settings Window.

Find a Section

The settings window is structured in different sections. Figure out in which section your new option should appear in.

If the section is already populated with other options (e.g. Settings/GeneralSettings), just add your new option like this:

swift
struct GeneralSettings: Codable, Hashable {

  // ...

  // This will be your new option. Be sure to provide a default value
  public var yourNewOption: Bool = true

  public init() {}

  public init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    // ...

    // Try to decode the value from json.
    self.yourNewOption = try container.decodeIfPresent(
      Bool.self, 
      forKey: .yourNewOption
    ) ?? true // If the key is not present in the json, set the default value
  }
}

Create a Section

In some cases in early development the section you decided on where to put your option in might not yet have been implemented. In this
case you can create a new struct inside Settings like this:

swift
public extension YourNewSection: Codable {

  // This will be your new option. Be sure to provide a default value
  public var yourNewOption: Bool = true

  public init() {}

  public init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)

    // Try to decode the value from json.
    self.yourNewOption = try container.decodeIfPresent(
      Bool.self, 
      forKey: .yourNewOption
    ) ?? true // If the key is not present in the json, set the default value
  }
}

Now let's add the new section to Settings like this:

swift
public struct Settings: Codable {
  // ...

  // Add your new section above the `public init() {}`
  public var yourNewSection: YourNewSection = .init()

  // ...
}

Topics

Up Next

Main Components

  • Settings
  • SettingsModel

File: Documentation.docc/AppPreferences/Themes.md

``CodeEdit/Theme``

Overview

A Theme is stored in a theme_name.json file in the ~/Library/Application Support/CodeEdit/themes/ directory. There are a
couple of bundled themes that will automatically be put there once the app starts.

Once a JSON file is loaded, the Theme gets added to ThemeModel/themes.

They can either be Theme/ThemeType/dark or Theme/ThemeType/light.

JSON Structure

json
{
  "author" : "CodeEdit",
  "name" : "codeedit-xcode-dark",
  "displayName" : "Xcode Dark",
  "description" : "Xcode dark theme.",
  "version" : "0.0.1",
  "license" : "MIT",
  "type" : "dark",
  "distributionURL" : "https:\/\/github.com\/CodeEditApp\/CodeEdit",
  "editor" : { ... },
  "terminal" : { ... }
}
Key Description
author Your Name
name A unique string representing the theme. It's good practice to start it with your name or domain to make sure it is unique.
displayName The name that will appear in the UI
description / metadataDescription A short description that will appear when hovering over the theme thumbnail
version A version number
license Which license your theme is published under
type / appearance The type of the theme [dark, light]
distributionURL A URL to your web presentation
editor A collection of colors for the editor
terminal A collection of colors for the terminal

Editor

json
{
  "invisibles" : {
    "color" : "#424D5B"
  },
  "comments" : {
    "color" : "#73A74E"
  },
  "numbers" : {
    "color" : "#D0BF69"
  },
  "commands" : {
    "color" : "#67B7A4"
  },
  "lineHighlight" : {
    "color" : "#23252B"
  },
  "values" : {
    "color" : "#A167E6"
  },
  "background" : {
    "color" : "#1F1F24"
  },
  "keywords" : {
    "color" : "#FF7AB3"
  },
  "text" : {
    "color" : "#D9D9D9"
  },
  "insertionPoint" : {
    "color" : "#D9D9D9"
  },
  "strings" : {
    "color" : "#FC6A5D"
  },
  "selection" : {
    "color" : "#515B70"
  },
  "types" : {
    "color" : "#5DD8FF"
  },
  "variables" : {
    "color" : "#41A1C0"
  },
  "attributes" : {
    "color" : "#D0A8FF"
  },
  "characters" : {
    "color" : "#D0BF69"
  }
}

Terminal

json
{
  "white" : {
    "color" : "#d9d9d9"
  },
  "brightMagenta" : {
    "color" : "#af52de"
  },
  "brightRed" : {
    "color" : "#ff3b30"
  },
  "blue" : {
    "color" : "#007aff"
  },
  "red" : {
    "color" : "#ff3b30"
  },
  "green" : {
    "color" : "#28cd41"
  },
  "boldText" : {
    "color" : "#d9d9d9"
  },
  "brightGreen" : {
    "color" : "#28cd41"
  },
  "background" : {
    "color" : "#1f2024"
  },
  "cursor" : {
    "color" : "#d9d9d9"
  },
  "selection" : {
    "color" : "#515b70"
  },
  "magenta" : {
    "color" : "#af52de"
  },
  "black" : {
    "color" : "#1f2024"
  },
  "text" : {
    "color" : "#d9d9d9"
  },
  "brightWhite" : {
    "color" : "#ffffff"
  },
  "brightBlue" : {
    "color" : "#007aff"
  },
  "brightYellow" : {
    "color" : "#ffff00"
  },
  "cyan" : {
    "color" : "#59adc4"
  },
  "yellow" : {
    "color" : "#ffcc00"
  },
  "brightCyan" : {
    "color" : "#55bef0"
  },
   "brightBlack" : {
    "color" : "#8e8e93"
  }
}

Topics

General Info

  • author
  • name
  • displayName
  • metadataDescription
  • version
  • license
  • appearance
  • distributionURL
  • ThemeType

Editor

  • Theme/EditorColors
  • editor
  • Attributes

Terminal

  • Theme/TerminalColors
  • terminal
  • Attributes

File: Documentation.docc/AppPreferences/Sections/AccountPreferencesView.md

``CodeEdit/AccountSettingsView``

Topics

Model

  • Settings/AccountsSettings
  • SourceControlAccounts
  • SourceControlProvider

Views

  • AccountListItemView
  • AccountSelectionDialog
  • GitAccountItem
  • GitAccountItemView

Login Views

  • GitLabHostedLoginView
  • GitLabLoginView
  • GitHubLoginView
  • GitHubEnterpriseLoginView

File: Documentation.docc/AppPreferences/Sections/GeneralPreferencesView.md

``CodeEdit/GeneralSettingsView``

Topics

Model

  • Settings/GeneralSettings

File: Documentation.docc/AppPreferences/Sections/KeybindingsPreferencesView.md

``CodeEdit/KeybindingsSettingsView``

Topics

Model

  • Settings/KeybindingsSettings

File: Documentation.docc/AppPreferences/Sections/SourceControlPreferencesView.md

``CodeEdit/SourceControlSettingsView``

Topics

Model

  • Settings/SourceControlSettings
  • IgnoredFiles

Views

  • SourceControlGeneralView
  • SourceControlGitView
  • IgnoredFileView

File: Documentation.docc/AppPreferences/Sections/TerminalPreferencesView.md

``CodeEdit/TerminalSettingsView``

Topics

Model

  • Settings/TerminalSettings

File: Documentation.docc/AppPreferences/Sections/TextEditingPreferencesView.md

``CodeEdit/TextEditingSettingsView``

Topics

Model

  • Settings/TextEditingSettings

File: Documentation.docc/AppPreferences/Sections/ThemePreferencesView.md

``CodeEdit/ThemeSettingsView``

Topics

Model

  • Settings/ThemeSettings
  • ThemeModel

Views

  • PreviewThemeView
  • TerminalThemeView
  • EditorThemeView
  • ThemePreviewIcon

File: Documentation.docc/CodeEditUI/CodeEditUI.md

CodeEditUI

A collection of reusable UI elements for CodeEdit.

Overview

This module contains UI elements that can be reused throughout the CodeEdit app.

Topics

Controls

  • ToolbarBranchPicker
  • HelpButton
  • SegmentedControl
  • SettingsTextEditor

Other

  • EffectView
  • OverlayPanel
  • PressActions
  • PanelDivider

File: Documentation.docc/CodeEditUI/HelpButton.md

``CodeEdit/HelpButton``

Usage

swift
HelpButton {
    // an action to perform on click
}

Preview


File: Documentation.docc/CodeEditUI/SegmentedControl.md

``CodeEdit/SegmentedControl``

Usage

swift
@State var selected: Int = 0
var items: [String] = ["Tab 1", "Tab 2"]

SegmentedControl($selected, options: items)

Preview

Topics

Item

  • SegmentedControlItem

File: Documentation.docc/CodeEditUI/ToolbarBranchPicker.md

``CodeEdit/ToolbarBranchPicker``

Overview

When the current project is a git repository, this will show the currently
checked-out branch as a subtitle. Once a tap is registered, a popup will
appear displaying the currently checked-out branch and all other local branches.

This view should be set to the view property in a NSToolbarItem.

Usage

First make sure a WorkspaceDocument is accessible in the context.

swift
var workspace: WorkspaceDocument?

Then in
toolbar(_:itemForItemIdentifier:willBeInsertedIntoToolbar:),
create a new NSToolbarItem:

swift
let toolbarItem = NSToolbarItem(itemIdentifier: /* Identifier */)

// create a NSHostingView
let view = BranchPickerToolbarItem(workspace?.workspaceClient)
let hostingView = NSHostingView(rootView: view)

// set the view property of the toolbar item
toolbarItem.view = hostingView

// return the toolbar item
return toolbarItem

Preview


File: Documentation.docc/FileManagement/FileManagement.md

File Management

Working with files and directories in CodeEdit.

Overview

CodeEdit manages files using three classes:

  • CEWorkspaceFile for representing files and other file system objects.
  • CEWorkspaceFileManager for loading, modifying, and listening to the file system.
  • CodeFileDocument for loading contents of files for editing.

File: Documentation.docc/Git/Git.md

Git

Topics

Client

  • GitClient

Protocols

  • GitRouter
  • GitJSONPostRouter
  • GitRouterConfiguration
  • GitURLSession
  • GitURLSessionDataTaskProtocol

Structs

  • GitCommit
  • GitAccountItem
  • GitChangedFile
  • GitHTTPHeader

Enums

  • GitHTTPEncoding
  • GitHTTPMethod
  • GitType
  • GitTime
  • GitURL
  • GitSortDirection
  • GitSortType

Views

  • GitCheckoutBranchView
  • GitCloneView
  • GitHubEnterpriseLoginView
  • GitHubLoginView
  • GitLabHostedLoginView
  • GitLabLoginView

GitHub

  • GitHubFile
  • GitHubGist
  • GitHubIssue
  • GitHubPullRequest
  • GitHubRepositories
  • GitHubUser
  • GitHubAccount
  • GitHubComment
  • GitHubReview
  • GitHubTokenConfiguration
  • GitHubRouter
  • GitHubUserRouter
  • GitHubGistRouter
  • GitHubIssueRouter
  • GitHubOAuthRouter
  • GitHubOAuthConfiguration
  • GitHubOpenness
  • GitHubPreviewHeader
  • GitHubPublicKeyRouter
  • GitHubPullRequestRouter
  • GitHubRepositoryRouter
  • GitHubReviewsRouter

GitLab

  • GitLabAvatarURL
  • GitLabCommit
  • GitLabCommitComment
  • GitLabCommitDiff
  • GitLabCommitStats
  • GitLabCommitStatus
  • GitLabEvent
  • GitLabEventData
  • GitLabGroupAccess
  • GitLabPermissions
  • GitLabProject
  • GitLabProjectAccess
  • GitLabProjectHook
  • GitLabUser
  • GitLabNamespace
  • GitLabEventNote
  • GitLabAccount
  • GitLabTokenConfiguration
  • GitLabOAuthConfiguration
  • GitLabPrivateTokenConfiguration
  • GitLabSort
  • GitLabOrderBy
  • GitLabVisibility
  • GitLabVisibilityLevel
  • GitLabUserRouter
  • GitLabCommitRouter
  • GitLabProjectRouter
  • GitLabOAuthRouter

BitBucket

  • BitBucketEmail
  • BitBucketRepositories
  • BitBucketUser
  • BitBucketAccount
  • BitBucketOAuthConfiguration
  • BitBucketTokenConfiguration
  • BitBucketOAuthRouter
  • BitBucketRepositoryRouter
  • BitBucketTokenRouter
  • BitBucketUserRouter
  • BitbucketPaginatedResponse

File: Documentation.docc/Keybindings/KeybindingManager.md

``CodeEdit/KeybindingManager``

This module created in order to put all keybindings into single place in code, so it'd be easy to interact, reuse and change keybindings without going through every class and changing code to use other shortcut. It uses default_keybindings.json file to store initial set of keybindings. After app launched all keybindings loaded into memory and can be referenced via KeybindingManager/named(with:) function.

Initial setup

In order to get it working you just need to add Keybindings as dependency to your module just like

text
.target(
    name: "WelcomeModule",
    dependencies: [
        ...other dependencies
        "Keybindings",
    ])

Keybinding module exists as singleton, so you always can reference Keybindings using KeybindingManager.shared

Topics

Fetching shortcut

In order to fetch keybinding you need to call following function with string name KeybindingManager/named(with:) returning you KeyboardShortcutWrapper which contains KeyboardShortcutWrapper/keyboardShortcut which can be passed directly to keyboardShortcut. So the end code would look like .keyboardShortcut(KeyboardShortcutWrapper.shared.named(with: "copy").keyboardShortcut

If shortcut wasn't found by name, it will return fallback shortcut which has following keybinding Shift + ?

Adding new shortcut

To add new shortcut you need first to add new row to default_keybindings.json file. Make sure you follow other keybindings format. Also check that there's no other keybindings with same ID,
because we use it to identify keybindings later. Once added - you can refer to Fetching Shortcut section. It is possible to add new shortcut in runtime via KeybindingManager/addNewShortcut(shortcut:name:)


File: Documentation.docc/KeyChain/CodeEditKeychain.md

``CodeEdit/CodeEditKeychain``

Topics

Articles

Enumerations

  • CodeEditKeychainAccessOptions
  • CodeEditKeychainConstants

File: Documentation.docc/KeyChain/What is Keychain.md

What is Keychain?

Keychain is the password management system in macOS, developed by Apple. It was introduced with Mac OS 8.6, and has been included in all subsequent versions of the operating system, now known as macOS. A Keychain can contain various types of data: passwords, private keys, certificates, and secure notes.

Notice:

This build of CodeEditKeychain could change at anytime if bugs or breaking changes are found in the module.

Usage

String

swift
let keychain = CodeEditKeychain()
keychain.set("hello world", forKey: "my key")
keychain.get("my key")

Boolean

swift
let keychain = CodeEditKeychain()
keychain.set(true, forKey: "my key")
keychain.getBool("my key")

Data

swift
let keychain = CodeEditKeychain()
keychain.set(dataObject, forKey: "my key")
keychain.getData("my key")

Removing Keys

swift
let keychain = CodeEditKeychain()
keychain.delete("my key")

Return All Keys

swift
let keychain = CodeEditKeychain()
keychain.allKeys // Returns the names of all keys

Check if operation was successful

One can verify if set, delete and clear methods finished successfully by checking their return values. Those methods return true on success and false on error.

swift
if keychain.set("hello world", forKey: "my key") {
  // Keychain item is saved successfully
} else {
  // Report error
}

Setting key prefix

One can pass a keyPrefix argument when initializing a CodeEditKeychain object. The string passed in keyPrefix argument will be used as a prefix to all the keys used in set, get, getData and delete methods. Adding a prefix to the keychain keys can be useful in unit tests. This prevents the tests from changing the Keychain keys that are used when the app is launched manually.

swift
let keychain = CodeEditKeychain(keyPrefix: "myTestKey_")
keychain.set("hello world", forKey: "hello") // Value will be stored under "myTestKey_hello" key

File: Documentation.docc/Welcome/Welcome Window.md

Welcome Window

Topics

Views

  • WelcomeWindowView

  • WelcomeView

  • WelcomeActionView

  • RecentProjectsView

  • RecentProjectItem


File: OpenWithCodeEdit/Media.xcassets/Contents.json

{
"info" : {
"author" : "xcode",
"version" : 1
}
}


File: ProductIcons/CodeEditAlphaIcon.icon/icon.json

{
"color-space-for-untagged-svg-colors" : "display-p3",
"fill" : {
"linear-gradient" : [
"display-p3:0.36667,0.77129,1.00000,1.00000",
"srgb:0.00000,0.42405,1.00000,1.00000"
]
},
"groups" : [
{
"blend-mode" : "normal",
"blur-material" : 0.05,
"hidden" : false,
"layers" : [
{
"glass" : true,
"hidden-specializations" : [
{
"value" : true
},
{
"appearance" : "dark",
"value" : false
},
{
"appearance" : "tinted",
"value" : true
}
],
"image-name" : "label-alpha-light.svg",
"name" : "label-alpha-light",
"opacity-specializations" : [
{
"value" : 1
},
{
"appearance" : "dark",
"value" : 1
},
{
"appearance" : "tinted",
"value" : 0
}
],
"position" : {
"scale" : 1,
"translation-in-points" : [
33.1290445202104,
205.5
]
}
},
{
"hidden-specializations" : [
{
"value" : false
},
{
"appearance" : "dark",
"value" : true
},
{
"appearance" : "tinted",
"value" : false
}
],
"image-name" : "label-alpha-dark.svg",
"name" : "label-alpha-dark",
"opacity-specializations" : [
{
"value" : 1
},
{
"appearance" : "dark",
"value" : 0
}
],
"position" : {
"scale" : 1,
"translation-in-points" : [
33.1290445202104,
205.5
]
}
}
],
"lighting" : "combined",
"name" : "Label",
"position" : {
"scale" : 1.25,
"translation-in-points" : [
0,
0
]
},
"shadow" : {
"kind" : "neutral",
"opacity" : 0.5
},
"specular" : false,
"translucency" : {
"enabled" : true,
"value" : 0.5
}
},
{
"blur-material" : 0.5,
"hidden" : false,
"layers" : [
{
"hidden" : false,
"image-name" : "pen-dark-color.png",
"name" : "pen-dark-color",
"opacity-specializations" : [
{
"value" : 0
},
{
"appearance" : "dark",
"value" : 0
},
{
"appearance" : "tinted",
"value" : 0
}
]
},
{
"hidden" : false,
"image-name" : "pen-light-color.png",
"name" : "pen-light-color",
"opacity-specializations" : [
{
"value" : 0
},
{
"appearance" : "dark",
"value" : 1
},
{
"appearance" : "tinted",
"value" : 0
}
]
},
{
"hidden" : false,
"image-name" : "pen-light.png",
"name" : "pen-light",
"opacity-specializations" : [
{
"value" : 0
},
{
"appearance" : "dark",
"value" : 0
},
{
"appearance" : "tinted",
"value" : 1
}
]
},
{
"glass" : true,
"hidden" : false,
"image-name" : "CodeEdit pen.png",
"name" : "CodeEdit pen",
"opacity-specializations" : [
{
"appearance" : "dark",
"value" : 0
},
{
"appearance" : "tinted",
"value" : 0
}
],
"position" : {
"scale" : 1,
"translation-in-points" : [
0,
0
]
}
}
],
"name" : "Pen",
"position" : {
"scale" : 0.88,
"translation-in-points" : [
0,
0
]
},
"shadow" : {
"kind" : "layer-color",
"opacity" : 0.75
},
"specular" : false,
"translucency" : {
"enabled" : true,
"value" : 0.25
}
},
{
"blend-mode" : "normal",
"blur-material" : null,
"hidden" : false,
"layers" : [
{
"fill-specializations" : [
{
"value" : {
"automatic-gradient" : "display-p3:1.00000,1.00000,1.00000,1.00000"
}
},
{
"appearance" : "dark",
"value" : "automatic"
},
{
"appearance" : "tinted",
"value" : {
"automatic-gradient" : "display-p3:1.00000,1.00000,1.00000,0.86284"
}
}
],
"glass" : true,
"hidden" : false,
"image-name" : "Slice 1 (9).svg",
"name" : "CodeLines",
"opacity-specializations" : [
{
"value" : 0.75
},
{
"appearance" : "dark",
"value" : 0.5
},
{
"appearance" : "tinted",
"value" : 0.33
}
],
"position" : {
"scale" : 1,
"translation-in-points" : [
0,
0
]
}
}
],
"name" : "Code Lines",
"position" : {
"scale" : 1.24,
"translation-in-points" : [
-2.4609375,
0
]
},
"shadow" : {
"kind" : "layer-color",
"opacity" : 0.5
},
"translucency" : {
"enabled" : true,
"value" : 0.5
}
}
],
"supported-platforms" : {
"circles" : [
"watchOS"
],
"squares" : "shared"
}
}


File: ProductIcons/CodeEditBetaIcon.icon/icon.json

{
"color-space-for-untagged-svg-colors" : "display-p3",
"fill" : {
"linear-gradient" : [
"display-p3:0.36667,0.77129,1.00000,1.00000",
"srgb:0.00000,0.42405,1.00000,1.00000"
]
},
"groups" : [
{
"blend-mode" : "normal",
"blur-material" : 0.05,
"hidden" : false,
"layers" : [
{
"hidden-specializations" : [
{
"value" : true
},
{
"appearance" : "dark",
"value" : false
},
{
"appearance" : "tinted",
"value" : true
}
],
"image-name" : "label-beta-light.svg",
"name" : "label-beta-light",
"position" : {
"scale" : 1,
"translation-in-points" : [
91.12904452021039,
205.5
]
}
},
{
"hidden-specializations" : [
{
"value" : false
},
{
"appearance" : "dark",
"value" : true
},
{
"appearance" : "tinted",
"value" : false
}
],
"image-name" : "label-beta-dark.svg",
"name" : "label-beta-dark",
"position" : {
"scale" : 1,
"translation-in-points" : [
91.12904452021039,
205.5
]
}
}
],
"lighting" : "combined",
"name" : "Label",
"position" : {
"scale" : 1.25,
"translation-in-points" : [
0,
0
]
},
"shadow" : {
"kind" : "neutral",
"opacity" : 0.5
},
"specular" : false,
"translucency" : {
"enabled" : true,
"value" : 0.5
}
},
{
"blur-material" : 0.5,
"hidden" : false,
"layers" : [
{
"hidden" : false,
"image-name" : "pen-dark-color.png",
"name" : "pen-dark-color",
"opacity-specializations" : [
{
"value" : 0
},
{
"appearance" : "dark",
"value" : 0
},
{
"appearance" : "tinted",
"value" : 0
}
]
},
{
"hidden" : false,
"image-name" : "pen-light-color.png",
"name" : "pen-light-color",
"opacity-specializations" : [
{
"value" : 0
},
{
"appearance" : "dark",
"value" : 1
},
{
"appearance" : "tinted",
"value" : 0
}
]
},
{
"hidden" : false,
"image-name" : "pen-light.png",
"name" : "pen-light",
"opacity-specializations" : [
{
"value" : 0
},
{
"appearance" : "dark",
"value" : 0
},
{
"appearance" : "tinted",
"value" : 1
}
]
},
{
"glass" : true,
"hidden" : false,
"image-name" : "CodeEdit pen.png",
"name" : "CodeEdit pen",
"opacity-specializations" : [
{
"appearance" : "dark",
"value" : 0
},
{
"appearance" : "tinted",
"value" : 0
}
],
"position" : {
"scale" : 1,
"translation-in-points" : [
0,
0
]
}
}
],
"name" : "Pen",
"position" : {
"scale" : 0.88,
"translation-in-points" : [
0,
0
]
},
"shadow" : {
"kind" : "layer-color",
"opacity" : 0.75
},
"specular" : false,
"translucency" : {
"enabled" : true,
"value" : 0.25
}
},
{
"blend-mode" : "normal",
"blur-material" : null,
"hidden" : false,
"layers" : [
{
"fill-specializations" : [
{
"value" : {
"automatic-gradient" : "display-p3:1.00000,1.00000,1.00000,1.00000"
}
},
{
"appearance" : "dark",
"value" : "automatic"
},
{
"appearance" : "tinted",
"value" : {
"automatic-gradient" : "display-p3:1.00000,1.00000,1.00000,0.86284"
}
}
],
"glass" : true,
"hidden" : false,
"image-name" : "Slice 1 (9).svg",
"name" : "CodeLines",
"opacity-specializations" : [
{
"value" : 0.75
},
{
"appearance" : "dark",
"value" : 0.5
},
{
"appearance" : "tinted",
"value" : 0.33
}
],
"position" : {
"scale" : 1,
"translation-in-points" : [
0,
0
]
}
}
],
"name" : "Code Lines",
"position" : {
"scale" : 1.24,
"translation-in-points" : [
-2.4609375,
0
]
},
"shadow" : {
"kind" : "layer-color",
"opacity" : 0.5
},
"translucency" : {
"enabled" : true,
"value" : 0.5
}
}
],
"supported-platforms" : {
"circles" : [
"watchOS"
],
"squares" : "shared"
}
}


File: ProductIcons/CodeEditDevIcon.icon/icon.json

{
"color-space-for-untagged-svg-colors" : "display-p3",
"fill" : {
"linear-gradient" : [
"display-p3:0.36667,0.77129,1.00000,1.00000",
"srgb:0.00000,0.42405,1.00000,1.00000"
]
},
"groups" : [
{
"blend-mode" : "normal",
"blur-material" : 0.05,
"hidden" : false,
"layers" : [
{
"blend-mode" : "normal",
"hidden-specializations" : [
{
"value" : true
},
{
"appearance" : "dark",
"value" : false
},
{
"appearance" : "tinted",
"value" : true
}
],
"image-name" : "label-dev-light.svg",
"name" : "label-dev-light",
"opacity" : 1,
"position" : {
"scale" : 1,
"translation-in-points" : [
122.12904452021026,
205.5
]
}
},
{
"hidden-specializations" : [
{
"value" : false
},
{
"appearance" : "dark",
"value" : true
},
{
"appearance" : "tinted",
"value" : false
}
],
"image-name" : "label-dev-dark.svg",
"name" : "label-dev-dark",
"position" : {
"scale" : 1,
"translation-in-points" : [
122.12904452021037,
205.5
]
}
}
],
"lighting" : "combined",
"name" : "Label",
"position" : {
"scale" : 1.25,
"translation-in-points" : [
0,
0
]
},
"shadow" : {
"kind" : "neutral",
"opacity" : 0.5
},
"specular" : false,
"translucency" : {
"enabled" : true,
"value" : 0.5
}
},
{
"blur-material" : 0.5,
"hidden" : false,
"layers" : [
{
"hidden" : false,
"image-name" : "pen-dark-color.png",
"name" : "pen-dark-color",
"opacity-specializations" : [
{
"value" : 0
},
{
"appearance" : "dark",
"value" : 0
},
{
"appearance" : "tinted",
"value" : 0
}
]
},
{
"hidden" : false,
"image-name" : "pen-light-color.png",
"name" : "pen-light-color",
"opacity-specializations" : [
{
"value" : 0
},
{
"appearance" : "dark",
"value" : 1
},
{
"appearance" : "tinted",
"value" : 0
}
]
},
{
"hidden" : false,
"image-name" : "pen-light.png",
"name" : "pen-light",
"opacity-specializations" : [
{
"value" : 0
},
{
"appearance" : "dark",
"value" : 0
},
{
"appearance" : "tinted",
"value" : 1
}
]
},
{
"glass" : true,
"hidden" : false,
"image-name" : "CodeEdit pen.png",
"name" : "CodeEdit pen",
"opacity-specializations" : [
{
"appearance" : "dark",
"value" : 0
},
{
"appearance" : "tinted",
"value" : 0
}
],
"position" : {
"scale" : 1,
"translation-in-points" : [
0,
0
]
}
}
],
"name" : "Pen",
"position" : {
"scale" : 0.88,
"translation-in-points" : [
0,
0
]
},
"shadow" : {
"kind" : "layer-color",
"opacity" : 0.75
},
"specular" : false,
"translucency" : {
"enabled" : true,
"value" : 0.25
}
},
{
"blend-mode" : "normal",
"blur-material" : null,
"hidden" : false,
"layers" : [
{
"fill-specializations" : [
{
"value" : {
"automatic-gradient" : "display-p3:1.00000,1.00000,1.00000,1.00000"
}
},
{
"appearance" : "dark",
"value" : "automatic"
},
{
"appearance" : "tinted",
"value" : {
"automatic-gradient" : "display-p3:1.00000,1.00000,1.00000,0.86284"
}
}
],
"glass" : true,
"hidden" : false,
"image-name" : "Slice 1 (9).svg",
"name" : "CodeLines",
"opacity-specializations" : [
{
"value" : 0.75
},
{
"appearance" : "dark",
"value" : 0.5
},
{
"appearance" : "tinted",
"value" : 0.33
}
],
"position" : {
"scale" : 1,
"translation-in-points" : [
0,
0
]
}
}
],
"name" : "Code Lines",
"position" : {
"scale" : 1.24,
"translation-in-points" : [
-2.4609375,
0
]
},
"shadow" : {
"kind" : "layer-color",
"opacity" : 0.5
},
"translucency" : {
"enabled" : true,
"value" : 0.5
}
}
],
"supported-platforms" : {
"circles" : [
"watchOS"
],
"squares" : "shared"
}
}


File: ProductIcons/CodeEditIcon.icon/icon.json

{
"color-space-for-untagged-svg-colors" : "display-p3",
"fill" : {
"linear-gradient" : [
"display-p3:0.36667,0.77129,1.00000,1.00000",
"srgb:0.00000,0.42405,1.00000,1.00000"
]
},
"groups" : [
{
"blur-material" : 0.5,
"hidden" : false,
"layers" : [
{
"hidden" : false,
"image-name" : "pen-dark-color.png",
"name" : "pen-dark-color",
"opacity-specializations" : [
{
"value" : 0
},
{
"appearance" : "dark",
"value" : 0
},
{
"appearance" : "tinted",
"value" : 0
}
]
},
{
"hidden" : false,
"image-name" : "pen-light-color.png",
"name" : "pen-light-color",
"opacity-specializations" : [
{
"value" : 0
},
{
"appearance" : "dark",
"value" : 1
},
{
"appearance" : "tinted",
"value" : 0
}
]
},
{
"hidden" : false,
"image-name" : "pen-light.png",
"name" : "pen-light",
"opacity-specializations" : [
{
"value" : 0
},
{
"appearance" : "dark",
"value" : 0
},
{
"appearance" : "tinted",
"value" : 1
}
]
},
{
"glass" : true,
"hidden" : false,
"image-name" : "CodeEdit pen.png",
"name" : "CodeEdit pen",
"opacity-specializations" : [
{
"appearance" : "dark",
"value" : 0
},
{
"appearance" : "tinted",
"value" : 0
}
],
"position" : {
"scale" : 1,
"translation-in-points" : [
0,
0
]
}
}
],
"name" : "Pen",
"position" : {
"scale" : 0.88,
"translation-in-points" : [
0,
0
]
},
"shadow" : {
"kind" : "layer-color",
"opacity" : 0.75
},
"specular" : false,
"translucency" : {
"enabled" : true,
"value" : 0.25
}
},
{
"blend-mode" : "normal",
"blur-material" : null,
"hidden" : false,
"layers" : [
{
"fill-specializations" : [
{
"value" : {
"automatic-gradient" : "display-p3:1.00000,1.00000,1.00000,1.00000"
}
},
{
"appearance" : "dark",
"value" : "automatic"
},
{
"appearance" : "tinted",
"value" : {
"automatic-gradient" : "display-p3:1.00000,1.00000,1.00000,0.86284"
}
}
],
"glass" : true,
"hidden" : false,
"image-name" : "Slice 1 (9).svg",
"name" : "CodeLines",
"opacity-specializations" : [
{
"value" : 0.75
},
{
"appearance" : "dark",
"value" : 0.5
},
{
"appearance" : "tinted",
"value" : 0.33
}
],
"position" : {
"scale" : 1,
"translation-in-points" : [
0,
0
]
}
}
],
"name" : "Code Lines",
"position" : {
"scale" : 1.24,
"translation-in-points" : [
-2.4609375,
0
]
},
"shadow" : {
"kind" : "layer-color",
"opacity" : 0.5
},
"translucency" : {
"enabled" : true,
"value" : 0.5
}
}
],
"supported-platforms" : {
"circles" : [
"watchOS"
],
"squares" : "shared"
}
}


File: ProductIcons/CodeEditPreIcon.icon/icon.json

{
"color-space-for-untagged-svg-colors" : "display-p3",
"fill" : {
"linear-gradient" : [
"display-p3:0.36667,0.77129,1.00000,1.00000",
"srgb:0.00000,0.42405,1.00000,1.00000"
]
},
"groups" : [
{
"blend-mode" : "normal",
"blur-material" : 0.05,
"hidden" : false,
"layers" : [
{
"hidden-specializations" : [
{
"value" : true
},
{
"appearance" : "dark",
"value" : false
},
{
"appearance" : "tinted",
"value" : true
}
],
"image-name" : "label-pre-light.svg",
"name" : "label-pre-light",
"position" : {
"scale" : 1,
"translation-in-points" : [
129.62904452021024,
205.5
]
}
},
{
"blend-mode" : "screen",
"glass" : true,
"hidden-specializations" : [
{
"value" : false
},
{
"appearance" : "dark",
"value" : true
},
{
"appearance" : "tinted",
"value" : false
}
],
"image-name" : "label-pre-dark.svg",
"name" : "label-pre-dark",
"opacity" : 1,
"position" : {
"scale" : 1,
"translation-in-points" : [
129.62904452021024,
205.5
]
}
}
],
"lighting" : "combined",
"name" : "Label",
"position" : {
"scale" : 1.25,
"translation-in-points" : [
0,
0
]
},
"shadow" : {
"kind" : "neutral",
"opacity" : 0.5
},
"specular" : false,
"translucency" : {
"enabled" : true,
"value" : 0.5
}
},
{
"blur-material" : 0.5,
"hidden" : false,
"layers" : [
{
"hidden" : false,
"image-name" : "pen-dark-color.png",
"name" : "pen-dark-color",
"opacity-specializations" : [
{
"value" : 0
},
{
"appearance" : "dark",
"value" : 0
},
{
"appearance" : "tinted",
"value" : 0
}
]
},
{
"hidden" : false,
"image-name" : "pen-light-color.png",
"name" : "pen-light-color",
"opacity-specializations" : [
{
"value" : 0
},
{
"appearance" : "dark",
"value" : 1
},
{
"appearance" : "tinted",
"value" : 0
}
]
},
{
"hidden" : false,
"image-name" : "pen-light.png",
"name" : "pen-light",
"opacity-specializations" : [
{
"value" : 0
},
{
"appearance" : "dark",
"value" : 0
},
{
"appearance" : "tinted",
"value" : 1
}
]
},
{
"glass" : true,
"hidden" : false,
"image-name" : "CodeEdit pen.png",
"name" : "CodeEdit pen",
"opacity-specializations" : [
{
"appearance" : "dark",
"value" : 0
},
{
"appearance" : "tinted",
"value" : 0
}
],
"position" : {
"scale" : 1,
"translation-in-points" : [
0,
0
]
}
}
],
"name" : "Pen",
"position" : {
"scale" : 0.88,
"translation-in-points" : [
0,
0
]
},
"shadow" : {
"kind" : "layer-color",
"opacity" : 0.75
},
"specular" : false,
"translucency" : {
"enabled" : true,
"value" : 0.25
}
},
{
"blend-mode" : "normal",
"blur-material" : null,
"hidden" : false,
"layers" : [
{
"fill-specializations" : [
{
"value" : {
"automatic-gradient" : "display-p3:1.00000,1.00000,1.00000,1.00000"
}
},
{
"appearance" : "dark",
"value" : "automatic"
},
{
"appearance" : "tinted",
"value" : {
"automatic-gradient" : "display-p3:1.00000,1.00000,1.00000,0.86284"
}
}
],
"glass" : true,
"hidden" : false,
"image-name" : "Slice 1 (9).svg",
"name" : "CodeLines",
"opacity-specializations" : [
{
"value" : 0.75
},
{
"appearance" : "dark",
"value" : 0.5
},
{
"appearance" : "tinted",
"value" : 0.33
}
],
"position" : {
"scale" : 1,
"translation-in-points" : [
0,
0
]
}
}
],
"name" : "Code Lines",
"position" : {
"scale" : 1.24,
"translation-in-points" : [
-2.4609375,
0
]
},
"shadow" : {
"kind" : "layer-color",
"opacity" : 0.5
},
"translucency" : {
"enabled" : true,
"value" : 0.5
}
}
],
"supported-platforms" : {
"circles" : [
"watchOS"
],
"squares" : "shared"
}
}


File: .github/FUNDING.yml

These are supported funding model platforms

github: [CodeEditApp, austincondiff]
patreon: # Replace with a single Patreon username
open_collective: codeeditapp
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']


File: .github/pull_request_template.md

Description

  • #ISSUE_NUMBER

Checklist

  • I read and understood the contributing guide as well as the code of conduct
  • The issues this PR addresses are related to each other
  • My changes generate no new warnings
  • My code builds and runs on my machine
  • My changes are all related to the related issue above
  • I documented my code

Screenshots


File: .github/release-drafter.yml

categories:

  • title: '✨ Enhancements'
    labels:
    • 'enhancement'
    • 'feature'
  • title: '🐞 Bug Fixes'
    labels:
    • 'bugfix'
    • 'bug'
  • title: '🧰 Maintenance'
    label:
    • 'chore'
    • 'documentation'
    • 'dependencies'
      template: |

Changes

$CHANGES


File: .github/stale.yml

Number of days of inactivity before an issue becomes stale

daysUntilStale: 60

Number of days of inactivity before a stale issue is closed

daysUntilClose: 7

Issues with these labels will never be considered stale

exemptLabels:

  • pinned
  • security

Label to use when marking an issue as stale

staleLabel: wontfix

Comment to post when marking an issue as stale. Set to `false` to disable

markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.

Comment to post when closing a stale issue. Set to `false` to disable

closeComment: false


File: .github/ISSUE_TEMPLATE/bug_report.yml

name: 🐞 Bug report
title: 🐞
labels: bug

body:

  • type: textarea
    attributes:
    label: Description
    placeholder: >-
    A clear and concise description of what the bug is...

    validations:
    required: true

  • type: textarea
    attributes:
    label: To Reproduce
    Steps to reliably reproduce the behavior.

    placeholder: |
    1. Go to '...'
    2. Click on '....'
    3. Scroll down to '....'
    4. See error

    validations:
    required: true

  • type: textarea
    attributes:
    label: Expected Behavior
    placeholder: >-
    A clear and concise description of what you expected to happen...

    validations:
    required: true

  • type: textarea
    attributes:
    label: Version Information
    click on the version number on the welcome screen

    value: |
    CodeEdit: [e.g. 0.0.x-alpha.y]
    macOS: [e.g. 13.2.1]
    Xcode: [e.g. 14.2]

  • type: textarea
    attributes:
    label: Additional Context
    placeholder: >-
    Any other context or considerations about the bug...

  • type: textarea
    attributes:
    label: Screenshots
    placeholder: >-
    If applicable, please provide relevant screenshots or screen recordings...


File: .github/ISSUE_TEMPLATE/feature_request.yml

name: ✨ Feature request
title: ✨
labels: enhancement

body:

  • type: textarea
    attributes:
    label: Description
    placeholder: >-
    A clear and concise description of what you would like to happen...

    validations:
    required: true

  • type: textarea
    attributes:
    label: Alternatives Considered
    placeholder: >-
    Any alternative solutions or features you've considered...

  • type: textarea
    attributes:
    label: Additional Context
    placeholder: >-
    Any other context or considerations about the feature request...

  • type: textarea
    attributes:
    label: Screenshots
    placeholder: >-
    If applicable, please provide relevant screenshots or screen recordings...


File: .github/workflows/add-to-project.yml

name: Add new issues to project

on:
issues:
types:
- opened

jobs:
add-to-project:
name: Add new issues labeled with enhancement or bug to project
runs-on: ubuntu-latest
steps:
- uses: actions/[email protected]
with:
# You can target a repository in a different organization
# to the issue
project-url: https://github.com/orgs/CodeEditApp/projects/3
github-token: ${{ secrets.ADD_TO_PROJECT_PAT }}
labeled: enhancement, bug
label-operator: OR


File: .github/workflows/appcast.yml

name: Build and publish a new appcast file

on:
workflow_dispatch:

jobs:
jekyll:
runs-on: ubuntu-latest
steps:
- name: Checkout 🛎
uses: actions/checkout@v3
with:
# If you're using actions/checkout@v3 you must set persist-credentials to false in most cases for the deployment to work correctly.
persist-credentials: false

text
- name: Cache 📦
  uses: actions/[email protected]
  with:
    path: AppCast/vendor/bundle
    key: ${{ runner.os }}-gems-v1.0-${{ hashFiles('AppCast/Gemfile') }}
    restore-keys: |
      ${{ runner.os }}-gems-
      
- name: Ruby ♦️
  uses: actions/[email protected]
  with:
    ruby-version: '2.7'

- name: Bundler 💎
  working-directory: AppCast
  env:
    BUNDLE_PATH: vendor/bundle
  run: |
    gem install bundler
    bundle install

- name: Build 🛠
  working-directory: AppCast
  env:
    BUNDLE_PATH: vendor/bundle
    JEKYLL_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  run: bundle exec jekyll build
  
- name: Publish 🚀
  uses: JamesIves/github-pages-deploy-action@releases/v3
  with:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    BRANCH: gh-pages
    FOLDER: AppCast/_site

File: .github/workflows/CI-bump-build-number.yml

name: CI - Update Build Number
on:
workflow_dispatch:
jobs:
upadate-build-number:
name: Update Build Number
runs-on: [self-hosted, macOS]
steps:
- name: Checkout repository
uses: actions/checkout@v3

text
- name: Bump Build Number
    run: |
      xcrun agvtool next-version -all
      APP_BUILD=$(xcrun agvtool vers -terse)
      echo "APP_BUILD=$APP_BUILD" >> $GITHUB_ENV

  - name: Commit Changes
    env:
      APP_BUILD: ${{ env.APP_BUILD }}
    run: |
      git add .
      git config --local user.email "[email protected]"
      git config --local user.name "GitHub Action"
      git commit -m "bump build number to $APP_BUILD"
  - name: Push Changes
    uses: ad-m/[email protected]
    with:
      github_token: ${{ secrets.GITHUB_TOKEN }}
      branch: temp/bump-build-number
      force: true
  - name: Create Pull Request
    uses: peter-evans/create-pull-request@v4
    env:
      APP_BUILD: ${{ env.APP_BUILD }}
    with:
      branch: temp/bump-build-number
      delete-branch: true
      base: main
      title: Bump Build Number to ${{ env.APP_BUILD }}
      body: Automatically bump build number of all targets to ${{ env.APP_BUILD }}

File: .github/workflows/CI-pre-release.yml

name: CI - Pre Release
on:
workflow_dispatch:
jobs:
swiftlint:
name: SwiftLint
uses: ./.github/workflows/lint.yml
secrets: inherit
test:
name: Testing CodeEdit
needs: swiftlint
uses: ./.github/workflows/tests.yml
secrets: inherit
deploy:
if: github.repository_owner == 'CodeEditApp'
name: Deploying CodeEdit [Prerelease]
needs: [swiftlint, test]
uses: ./.github/workflows/pre-release.yml
secrets: inherit
ReleaseDrafter:
name: Release Drafter
needs: [swiftlint, test, deploy]
uses: ./.github/workflows/release-drafter.yml


File: .github/workflows/CI-pull-request.yml

name: CI - Pull Request
on:
pull_request:
branches:
- 'main'
workflow_dispatch:
jobs:
swiftlint:
name: SwiftLint
uses: ./.github/workflows/lint.yml
secrets: inherit
test:
name: Testing CodeEdit
needs: swiftlint
uses: ./.github/workflows/tests.yml
secrets: inherit


File: .github/workflows/CI-release-notes.yml

name: Deploy Website on Release Note Changes

on:
workflow_dispatch:
release:
types: [created, edited, deleted]

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Send deploy request to Vercel
run: curl -X POST ${{ secrets.VERCEL_DEPLOY_URL }}


File: .github/workflows/issue.yml

name: Labeling new issue
on:
issues:
types: ['opened']
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: Renato66/auto-label@v2
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
ignore-comments: true
labels-synonyms: '{"bug":["error","need fix","not working"],"enhancement":["upgrade"],"question":["help"]}'
labels-not-allowed: '["good first issue", "priority"]'
default-labels: '["triage needed"]'


File: .github/workflows/lint.yml

name: SwiftLint

on:
workflow_dispatch:
workflow_call:

jobs:
SwiftLint:
runs-on: [self-hosted, macOS] # ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: GitHub Action for SwiftLint
run: swiftlint --reporter github-actions-logging --strict


File: .github/workflows/pre-release.yml

name: Build and Publish Pre-release

on:
workflow_dispatch:
workflow_call:

jobs:
pre-release:
name: Build and Publish Pre-release
runs-on: [self-hosted, macOS]
steps:
- name: Checkout repository
uses: actions/checkout@v3

text
############################
  # Install Certificate
  ############################
  - name: Install codesign certificate
    env:
      # DEV_CERT_B64: Base64-encoded developer certificate as .p12
      # DEV_CERT_PWD: Developer certificate .p12 password
      # PROVISION_PROFILE_B64: Base64-encoded provisioning profile as .provisionprofile
      # KEYCHAIN_TIMEOUT: Lock keychain after timeout interval
      # https://docs.github.com/en/actions/deployment/deploying-xcode-applications/installing-an-apple-certificate-on-macos-runners-for-xcode-development
      DEV_CERT_B64: ${{ secrets.DEV_CERT_B64 }}
      DEV_CERT_PWD: ${{ secrets.DEV_CERT_PWD }}
      MAC_CERT_B64: ${{ secrets.MAC_CERT_B64 }}
      MAC_CERT_PWD: ${{ secrets.MAC_CERT_PWD }}
      # PROVISION_PROFILE_B64: ${{ secrets.PROVISION_PROFILE_B64 }}
      KEYCHAIN_TIMEOUT: 21600
    run: |
      DEV_CERT_P12="$RUNNER_TEMP/dev_cert.p12"
      MAC_CERT_P12="$RUNNER_TEMP/mac_cert.p12"
      KEYCHAIN_DB="$RUNNER_TEMP/keychain.keychain-db"
      KEYCHAIN_PWD=$(openssl rand -base64 24)
      security create-keychain -p "$KEYCHAIN_PWD" "$KEYCHAIN_DB"
      security set-keychain-settings -lut "$KEYCHAIN_TIMEOUT" "$KEYCHAIN_DB"
      security unlock-keychain -p "$KEYCHAIN_PWD" "$KEYCHAIN_DB"
      echo -n "$DEV_CERT_B64" | base64 --decode -o "$DEV_CERT_P12"
      security import "$DEV_CERT_P12" -P "$DEV_CERT_PWD" -A -t cert -f pkcs12 -k "$KEYCHAIN_DB"
      echo -n "$MAC_CERT_B64" | base64 --decode -o "$MAC_CERT_P12"
      security import "$MAC_CERT_P12" -P "$MAC_CERT_PWD" -A -t cert -f pkcs12 -k "$KEYCHAIN_DB"
      security list-keychain -d user -s "$KEYCHAIN_DB"
  
  ############################
  # Build
  ############################
  - name: Build CodeEdit
    env:
      APPLE_TEAM_ID:  ${{ secrets.APPLE_TEAM_ID }}
    run: xcodebuild -scheme CodeEdit -configuration Pre -derivedDataPath "$RUNNER_TEMP/DerivedData" -archivePath "$RUNNER_TEMP/CodeEdit.xcarchive" -skipPackagePluginValidation DEVELOPMENT_TEAM=$APPLE_TEAM_ID archive | xcpretty
  
  ############################
  # Sign
  ############################
  - name: Sign CodeEdit
    env:
      CODESIGN_SIGN: ${{ secrets.CODESIGN_SIGN }}
    run: |
      REV=$(git rev-parse --short HEAD)
      echo "REV=$REV" >> $GITHUB_ENV
      codesign --sign "$CODESIGN_SIGN" -vvv --verbose --strict --options=runtime --prefix app.codeedit.CodeEdit. --force --timestamp "$RUNNER_TEMP/CodeEdit.xcarchive/Products/Applications/CodeEdit.app/Contents/Frameworks/CodeEditKit.framework"
      codesign --sign "$CODESIGN_SIGN" -vvv --verbose --strict --options=runtime --prefix app.codeedit.CodeEdit. --force --timestamp "$RUNNER_TEMP/CodeEdit.xcarchive/Products/Applications/CodeEdit.app/Contents/Frameworks/CodeLanguages_Container.framework"
      codesign --sign "$CODESIGN_SIGN" -vvv --verbose --strict --options=runtime --prefix app.codeedit.CodeEdit. --force --deep --timestamp "$RUNNER_TEMP/CodeEdit.xcarchive/Products/Applications/CodeEdit.app/Contents/Frameworks/Sparkle.framework"
      codesign --sign "$CODESIGN_SIGN" -vvv --verbose --strict --options=runtime --prefix app.codeedit.CodeEdit. --force --timestamp --entitlements "$RUNNER_TEMP/../CodeEdit/CodeEdit/OpenWithCodeEdit/OpenWithCodeEdit.entitlements" "$RUNNER_TEMP/CodeEdit.xcarchive/Products/Applications/CodeEdit.app/Contents/PlugIns/OpenWithCodeEdit.appex"
      codesign --sign "$CODESIGN_SIGN" -vvv --verbose --strict --options=runtime --prefix app.codeedit.CodeEdit. --force --timestamp "$RUNNER_TEMP/CodeEdit.xcarchive/Products/Applications/CodeEdit.app"

  ############################
  # DMG & Notarize
  ############################
  - name: Create DMG & Notarize
    env:
      CODESIGN_SIGN: ${{ secrets.CODESIGN_SIGN }}
      APPLE_ID: ${{ secrets.APPLE_ID }}
      APPLE_ID_PWD: ${{ secrets.APPLE_ID_PWD }}
      APPLE_TEAM_ID:  ${{ secrets.APPLE_TEAM_ID }}
      RUNNER_PW: ${{ secrets.RUNNER_PW }}
    run: |
      REV=$(git rev-parse --short HEAD)
      echo "REV=$REV" >> $GITHUB_ENV
      security unlock-keychain -p "$RUNNER_PW"
      xcrun notarytool store-credentials CodeEdit --apple-id "$APPLE_ID" --team-id "$APPLE_TEAM_ID" --password "$APPLE_ID_PWD"
      cp "$RUNNER_TEMP/../CodeEdit/CodeEdit/Resources/dmgBackground.png" "$RUNNER_TEMP/.background.png"
      epochdate=$(($(date +'%s * 1000 + %-N / 1000000')))
      tcc_service_appleevents="replace into access (service,client,client_type,auth_value,auth_reason,auth_version,indirect_object_identifier_type,indirect_object_identifier,flags,last_modified) values (\"kTCCServiceAppleEvents\",\"/usr/sbin/sshd\",1,2,4,1,0,\"com.apple.finder\",0,$epochdate);"
      echo $RUNNER_PW | sudo -S sqlite3 "/Users/administrator/Library/Application Support/com.apple.TCC/TCC.db" "$tcc_service_appleevents"
      create-dmg \
        --volname "CodeEdit" \
        --window-pos 200 120 \
        --window-size 699 518 \
        --background "$RUNNER_TEMP/.background.png" \
        --icon-size 128 \
        --icon "CodeEdit.app" 170 210 \
        --hide-extension "CodeEdit.app" \
        --app-drop-link 530 210 \
        --codesign "$CODESIGN_SIGN" \
        --notarize "CodeEdit" \
        "$RUNNER_TEMP/CodeEdit.dmg" \
        "$RUNNER_TEMP/CodeEdit.xcarchive/Products/Applications/"
      echo $RUNNER_PW | sudo -S security lock-keychain
      
  ############################
  # Get Version and Build number
  ############################
  - name: Get Version and Build number
    run: |
      APP_VERSION=$(xcrun agvtool mvers -terse1)
      APP_BUILD=$(xcrun agvtool vers -terse)
      echo "APP_VERSION=$APP_VERSION" >> $GITHUB_ENV
      echo "APP_BUILD=$APP_BUILD" >> $GITHUB_ENV

  ############################
  # Upload dSYMs Artifact
  ############################
  - name: Upload dSYMs Artifact
    uses: actions/upload-artifact@v4
    with:
      name: "CodeEdit-${{ env.APP_BUILD }}-dSYMs"
      path: "${{ RUNNER.TEMP }}/CodeEdit.xcarchive/dSYMs"
      if-no-files-found: error
      # overwrite files for the same build number
      overwrite: true
      # these can be big, use maximum compression
      compression-level: 9

  ############################
  # Sparkle Appcast
  ############################
  - name: Update Sparkle appcast
    env:
      # SPARKLE_KEY: Sparkle EdDSA key exported from `generate_keys -x` as plain text
      # SPARKLE_CHANNEL: Seperate dev builds from default channel, to be specified in [SPUUpdaterDelegate allowedChannelsForUpdater:]
      # SPARKLE_DL_PREFIX: Prefix for the URL from where updates will be downloaded
      # SPARKLE_LINK: CodeEdit Website
      #   https://github.com/CodeEditApp/CodeEdit/releases/download/0.0.1-alpha.11/CodeEdit-9113dc5.dmg
      # RELEASE_NOTES_PREFIX: The URL to prefix before an update link:
      #   https://codeedit.app/whats-new/raw/{v0.1.0} -- data in {} is inserted by sparkle
      # RELEASE_NOTES_URL: The URL of the entire release notes page: https://codeedit.app/whats-new
      SPARKLE_KEY: ${{ secrets.SPARKLE_KEY }}
      SPARKLE_CHANNEL: dev
      SPARKLE_DL_PREFIX: "https://github.com/CodeEditApp/CodeEdit/releases/download"
      SPARKLE_LINK: "https://github.com/CodeEditApp/CodeEdit"
      APP_VERSION: ${{ env.APP_VERSION }}
      APP_BUILD: ${{ env.APP_BUILD }}
      RELEASE_NOTES_URL: "https://codeedit.app/whats-new/"
      RELEASE_NOTES_PREFIX: "https://codeedit.app/sparkle/"
    run: |
      SPARKLE_BIN="$RUNNER_TEMP/DerivedData/SourcePackages/artifacts/sparkle/Sparkle/bin"
      SPARKLE_ARCHIVE="$RUNNER_TEMP/Sparkle_Archive"
      echo -n "$SPARKLE_KEY" | tee "$RUNNER_TEMP/sparkle_key"
      mkdir "$SPARKLE_ARCHIVE"
      cp "$RUNNER_TEMP/CodeEdit.dmg" "$SPARKLE_ARCHIVE"
      SPARKLE_SIG=$("$SPARKLE_BIN/sign_update" --ed-key-file "$RUNNER_TEMP/sparkle_key" "$SPARKLE_ARCHIVE/CodeEdit.dmg" | cut -d\" -f2)
      echo "<!DOCTYPE>" > "$SPARKLE_ARCHIVE/CodeEdit.html" # Need a blank html doc with the DOCTYPE tag to trick sparkle into loading our remote release notes.
      "$SPARKLE_BIN/generate_appcast" --ed-key-file "$RUNNER_TEMP/sparkle_key" --download-url-prefix "${{ env.SPARKLE_DL_PREFIX }}/v${{ env.APP_VERSION }}/" --link "$SPARKLE_LINK" --channel "$SPARKLE_CHANNEL" --maximum-deltas 0 "$SPARKLE_ARCHIVE" --release-notes-url-prefix "${{ env.RELEASE_NOTES_PREFIX }}v${{ env.APP_VERSION }}/" --full-release-notes-url "$RELEASE_NOTES_URL"

  ############################
  # Publish Pre Release
  ############################
  - name: Publish Pre-release
    uses: marvinpinto/action-automatic-releases@latest
    env:
      APP_VERSION: ${{ env.APP_VERSION }}
      APP_BUILD: ${{ env.APP_BUILD }}
    with:
      title: "v${{ env.APP_VERSION }}"
      files: |
        ${{ RUNNER.TEMP }}/Sparkle_Archive/CodeEdit.dmg
        ${{ RUNNER.TEMP }}/Sparkle_Archive/appcast.xml
      automatic_release_tag: "v${{ env.APP_VERSION }}"
      prerelease: false
      repo_token: "${{ secrets.GITHUB_TOKEN }}"
      draft: true

  ############################
  # Cleanup Secrets
  ############################
  - name: Clean up keychain and provisioning profile
    if: ${{ always() }}
    run: |
      security delete-keychain "$RUNNER_TEMP/keychain.keychain-db"
      rm -rf "~/Library/MobileDevice/Provisioning Profiles"

File: .github/workflows/release-drafter.yml

name: Release Drafter

on:

Allow running it manually in case we forget to label a PR before merging

workflow_dispatch:
workflow_call:

jobs:
update_release_draft:
runs-on: ubuntu-latest
steps:
- id: release
uses: release-drafter/release-drafter@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}


File: .github/workflows/tests.yml

name: Tests
on:
workflow_dispatch:
workflow_call:
jobs:
test:
name: Testing CodeEdit
runs-on: [self-hosted, macOS]
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Check Version Number exists
run: exec ./.github/scripts/test_version_number.sh
- name: Testing App
run: exec ./.github/scripts/test_app.sh arm