ecode is a lightweight multi-platform code editor designed for modern hardware with a focus on responsiveness and performance. It has been developed with the hardware-accelerated eepp GUI, which provides the core technology for the editor. The project comes as the first serious project using the eepp GUI, and it's currently being developed to improve the eepp GUI library as part of one of its main objectives.
For more screenshots checkout running on macOS, running on Windows, running on Haiku, low dpi, code completion, terminal, file locator, file formats, global find, global replace, linter.
.gitignore
support *ecode treats folders as projects, like many other editors. The main difference is that it also tries
to automatically sanitize the project files by filtering out any file that it's filtered in the repository
.gitignore
files. The idea is to use the .gitignore
file as a project setting.
The project files will be the ones used to find files in the project and do global searches.
Usually, this translates into much better results for any project-related search.
There's also a very simple mechanism to allow visibility of filtered files by the .gitignore
, by
adding a file with the allowed filtered patterns in a subfolder over the folder loaded, creating a file
in .ecode/.prjallowed
with the necessary glob patterns allowing the filtered patterns to be "unfiltered".
ecode will only add files that are supported by the editor, the editor won't try to do anything
with files that are not officially supported.
Some points to illustrate the project philosophy:
ecode can be compiled to WASM and run in any modern browser. There are no plans to focus the development on the web version (at least for the moment) since there are plenty of good solutions out there. But you can give it a try:
Currently, the source code is located at the eepp project repository. ecode editor source is located at src/tools/ecode. ecode is being used to actively improve and iterate the eepp GUI library. At some point, it will be migrated to this repository. The ecode repository should be used for issues and documentation. PRs for ecode will be accepted at the eepp repository.
There are scripts for each supported platform ready to build the application.
For Linux and macOS it is trivial to build the project, you'll just need to have GCC/Clang installed
and also the development library from libSDL2. Windows build script is currently a cross-compiling script and it uses mingw64.
But it also can be easily built with Visual Studio and libSDL2 development libraries installed.
For more information on how to build manually a project please follow the eepp build instructions.
The project name is always ecode (so if you are building with make, you'll need to run make ecode
).
build.app.sh
will try to build the AppImage
package.build.app.sh
will create ecode.app
.build.app.sh
will create a zip
file with the zipped application package.ecode is constantly adding more languages support and also supports extending it's language support via configuration files (for every feature: syntax highlighting, LSP, linter and formatter).
Language | Highlight | LSP | Linter | Formatter |
---|---|---|---|---|
.htaccess | ✓ | None | None | None |
.ignore file | ✓ | None | None | None |
[x]it! | ✓ | None | None | None |
adept | ✓ | AdeptLSP | None | None |
angelscript | ✓ | None | None | None |
bat | ✓ | None | None | None |
bend | ✓ | None | None | None |
blueprint | ✓ | None | None | None |
brainfuck | ✓ | None | None | None |
buzz | ✓ | None | None | None |
c | ✓ | clangd | cppcheck | clang-format |
carbon | ✓ | None | None | None |
clojure | ✓ | clojure-lsp | None | None |
cmake | ✓ | cmake-language-server | None | None |
cpp | ✓ | clangd | cppcheck | clang-format |
crystal | ✓ | crystalline | None | None |
csharp | ✓ | OmniSharp | None | None |
css | ✓ | emmet-language-server | None | native |
d | ✓ | serve-d | None | None |
dart | ✓ | dart language-server | None | None |
diff | ✓ | None | None | None |
dockerfile | ✓ | docker-langserver | None | None |
elixir | ✓ | elixir-ls | None | None |
elm | ✓ | elm-language-server | None | None |
environment file | ✓ | None | None | None |
fantom | ✓ | None | None | None |
fortran | ✓ | None | None | None |
fstab | ✓ | None | None | None |
gdscript | ✓ | None | None | None |
glsl | ✓ | glsl_analyzer | None | None |
go | ✓ | gopls | None | gopls |
graphql | ✓ | None | None | None |
groovy | ✓ | None | None | None |
hare | ✓ | None | None | None |
haskell | ✓ | haskell-language-server | hlint | ormolu |
haxe | ✓ | None | None | None |
haxe compiler arguments | ✓ | None | None | None |
hlsl | ✓ | None | None | None |
html | ✓ | emmet-language-server | None | prettier |
ini | ✓ | None | None | None |
jai | ✓ | None | None | None |
java | ✓ | jdtls | None | clang-format |
javascript | ✓ | typescript-language-server | eslint | prettier |
javascriptreact | ✓ | typescript-language-server | None | None |
json | ✓ | None | jq | native |
julia | ✓ | None | None | None |
kotlin | ✓ | kotlin-language-server | ktlint | ktlint |
latex | ✓ | texlab | None | None |
lobster | ✓ | None | None | None |
lua | ✓ | lua-language-server | luacheck | None |
makefile | ✓ | None | None | None |
markdown | ✓ | None | None | None |
meson | ✓ | None | None | None |
moonscript | ✓ | None | None | None |
nelua | ✓ | None | nelua | None |
nim | ✓ | nimlsp | nim | None |
objeck | ✓ | None | None | None |
objective-c | ✓ | clangd | None | clang-format |
odin | ✓ | ols | None | None |
pascal | ✓ | None | None | None |
perl | ✓ | None | None | None |
php | ✓ | phpactor | php | None |
pico-8 | ✓ | None | None | None |
plaintext | ✓ | None | None | None |
po | ✓ | None | None | None |
pony | ✓ | None | None | None |
postgresql | ✓ | None | None | None |
powershell | ✓ | None | None | None |
python | ✓ | pylsp | ruff | black |
r | ✓ | r languageserver | None | None |
ruby | ✓ | solargraph | None | None |
rust | ✓ | rust-analyzer | None | rustfmt |
sass | ✓ | emmet-language-server | None | None |
scala | ✓ | metals | None | None |
shellscript | ✓ | bash-language-server | None | None |
smallbasic | ✓ | None | None | None |
solidity | ✓ | solc | solhint | None |
sql | ✓ | None | None | None |
swift | ✓ | sourcekit-lsp | None | None |
teal | ✓ | None | tl | None |
toml | ✓ | None | None | None |
typescript | ✓ | typescript-language-server | eslint | prettier |
typescriptreact | ✓ | typescript-language-server | None | None |
v | ✓ | v-analyzer | None | v |
vala | ✓ | vala-language-server | None | None |
verilog | ✓ | None | None | None |
visual basic | ✓ | None | None | None |
vue | ✓ | vls | None | None |
wren | ✓ | None | None | None |
x86 assembly | ✓ | None | None | None |
xml | ✓ | emmet-language-server | native | native |
xtend | ✓ | None | None | None |
yaml | ✓ | yaml-language-server | None | None |
zig | ✓ | zls | zig | zig |
Native tag means that the feature is supported natively by ecode and it doesn't need any external tool to function.
ecode brings a tool to display the current language support health. From ecode you can check its health
status from Settings -> Tools -> Check Language Health
, and from CLI you can use the --health
flag: ecode --health
.
Use the health check flag to troubleshoot missing language servers, linters and formatters.
Check the health of all languages with ecode --health
or ask for details about a specific language
with ecode --health-lang=<lang>
.
Plugins extend the base code editor functionality. Currently all plugins are enabled by default, but they are optional and they can be disabled at any time. ecode implements an internal protocol that allow plugins to communicate with each other. The LSP protocol is going to be used as a base to implement the plugin communication. And, for example, the Linter plugin will consume the LSP to improve its diagnostics. Also the Auto Complete module will request assistance from the LSP, if available, to improve the completions and to provide signature help.
Linter support is provided by executing already stablished linters from each language.
ecode provides support for several languages by default and can be extended easily by expanding the
linters.json
configuration. linters.json
default configuration can be obtained from here.
To configure new linters you can create a new linters.json
file in the default configuration path of ecode.
linters.json
formatThe format is a very simple JSON object with a config object and array of objects containing the file formats supported, the Lua pattern to find any error printed by the linter to the stdout, the position of each group of the pattern, and the command to execute. It also supports some optional extra object keys.
JavaScript linter example (using eslint)
{
"config": {
"delay_time": "0.5s"
},
"linters": [
{
"file_patterns": ["%.js$", "%.ts$"],
"warning_pattern": "[^:]:(%d+):(%d+): ([^%[]+)%[([^\n]+)",
"warning_pattern_order": { "line": 1, "col": 2, "message": 3, "type": 4 },
"command": "eslint --no-ignore --format unix $FILENAME"
}
]
}
That's all we need to have a working linter in ecode. Linters executables must be installed manually
by the user, linters will not come with the editor, and they also need to be visible to the executable.
This means that it must be on PATH
environment variable or the path to the binary must be absolute.
Please check the language support table
"disable_lsp_languages": ["lua", "python"]
, disables lua and python.The formatter plugin works exactly like the linter plugin, but it will execute tools that auto-format code.
ecode provides support for several languages by default with can be extended easily by expanding the
formatters.json
configuration. formatters.json
default configuration can be obtained from here.
It also supports some formatters natively, this means that the formatter comes with ecode without requiring any external dependency.
And also supports LSP text document formatting, meaning that if you're running an LSP that supports formatting documents, formatting will be available too.
To configure new formatters you can create a new formatters.json
file in the default configuration path of ecode.
formatters.json
format{
"config": {
"auto_format_on_save": false
},
"keybindings": {
"format-doc": "alt+f"
},
"formatters": [
{
"file_patterns": ["%.js$", "%.ts$"],
"command": "prettier $FILENAME"
}
]
}
Please check the language support table
LSP support is provided by executing already stablished LSP from each language.
ecode provides support for several languages by default and can be extended easily by expanding the
lspclient.json
configuration. lspclient.json
default configuration can be obtained from here.
To configure new LSPs you can create a new lspclient.json
file in the default configuration path of ecode.
Important note: LSP servers can be very resource intensive and might not be always the best option for simple projects.
Implementation details: LSP servers are only loaded when needed, no process will be opened until a supported file is opened in the project.
lspclient.json
formatThe format follows the same pattern that all previous configuration files. Configuration is represented
in a JSON file with three main keys: config
, keybindings
, servers
.
C and C++ LSP server example (using clangd)
{
"config": {
"hover_delay": "0.5s"
},
"servers": [
{
"language": "c",
"name": "clangd",
"url": "https://clangd.llvm.org/",
"command": "clangd -log=error --background-index --limit-results=500 --completion-style=bundled",
"file_patterns": ["%.c$", "%.h$", "%.C$", "%.H$", "%.objc$"]
},
{
"language": "cpp",
"use": "clangd",
"file_patterns": ["%.inl$", "%.cpp$", "%.hpp$", "%.cc$", "%.cxx$", "%.c++$", "%.hh$", "%.hxx$", "%.h++$", "%.objcpp$"]
}
]
}
That's all we need to have a working LSP in ecode. LSPs executables must be installed manually
by the user, LSPs will not come with the editor, and they also need to be visible to the executable.
This means that it must be on PATH
environment variable or the path to the binary must be absolute.
Please check the language support table
lspclient.json
config. It's also possible to specify a different command for each platform, given that it might change in some ocassions per-platform. In that case an object should be used, with each key being a platform, and there's also a wildcard platform "other" to specify any other platform that does not match the platform definition. For example, sourcekit-lsp
uses: "command": {"macos": "xcrun sourcekit-lsp","other": "sourcekit-lsp"}
{"name": "clangd","command_parameters": "--background-index-priority=background --malloc-trim"}
ecode provides some basic Git integration (more features will come in the future). Its main purpose
is to help the user to do the most basics operations with Git. Some of the current features supported:
git status and stats visualization (files states), commit, push, checkout, pull, fetch, fast-forward
merge, creating+renaming+deleting branches, managing stashes. All stats will be automatically
updated/refreshed in real time. There's also some basic configuration available.
The plugin requires the user to have a git
binary installed and available in PATH
environment variable.
git.json
formatThe format follows the same pattern that all previous configuration files. Configuration is represented
in a JSON file with three main keys: config
, keybindings
, servers
.
C and C++ LSP server example (using clangd)
{
"config": {
"silent": false,
"status_recurse_submodules": true,
"statusbar_display_branch": true,
"statusbar_display_modifications": true,
"ui_refresh_frequency": "5s"
},
"keybindings": {
"git-blame": "alt+shift+b"
}
}
.git
directory).ecode respects the standard configuration paths on each OS:
XDG_CONFIG_HOME
, usually translates to ~/.config/ecode/plugins
Application Support
folder in HOME
, usually translates to ~/Library/Application Support/ecode/plugins
APPDATA
, usually translates to C:\Users\{username}\AppData\Roaming\ecode\plugins
All plugin configurations are designed to be overwriteable by the user. This means that the default configuration can be replaced with custom configurations from the user. For example, if the user wants to use a different linter, it just needs to declare a new linter definition in its own linter configuration file. The same applies to formatters and LSPs servers. Plugins will always implement a "config" for plugins customization, and will always implement a "keybindings" key to configure custom keybindings.
Custom editor color schemes can be added in the user color schemes directory found at:
XDG_CONFIG_HOME
, usually translates to ~/.config/ecode/editor/colorschemes
Application Support
folder in HOME
, usually translates to ~/Library/Application Support/ecode/editor/colorschemes
APPDATA
, usually translates to C:\Users\{username}\AppData\Roaming\ecode\editor\colorschemes
Any file written in the directory will be treated as an editor color scheme file. Each file can contain any number of color schemes.
The format of a color scheme can be read from here.
Custom terminal color schemes can be added in the user terminal color schemes directory found at:
XDG_CONFIG_HOME
, usually translates to ~/.config/ecode/terminal/colorschemes
Application Support
folder in HOME
, usually translates to ~/Library/Application Support/ecode/terminal/colorschemes
APPDATA
, usually translates to C:\Users\{username}\AppData\Roaming\ecode\terminal\colorschemes
Any file written in the directory will be treated as a terminal color scheme file. Each file can contain any number of color schemes.
The format of a color scheme can be read from here.
Custom UI schemes can be added in the user UI themes directory found at:
XDG_CONFIG_HOME
, usually translates to ~/.config/ecode/themes
Application Support
folder in HOME
, usually translates to ~/Library/Application Support/ecode/themes
APPDATA
, usually translates to C:\Users\{username}\AppData\Roaming\ecode\themes
A custom UI theme file must have the extension .css
, ecode will look for all the files with .css
extension in the directory, the UI theme name is the file name without the extension. The new theme
will appear in Settings -> Window -> UI Theme
.
Custom UI themes allow customizing the editor at the user's will. Since ecode uses CSS to style all the
elements of the UI, creating new themes is quite easy. It's possible to customize only the color palette
but it's also possible to customize all the UI elements if desired. Customizing the whole UI theme can
be extensive, but customizing the colors is as simple as changing the values of the CSS variables used
to color the UI. For reference, the complete base UI theme used by ecode can be seen here.
The most important selector would be the :root
selector, where all the variables are defined. Color
variables can be easily extracted from that file.
A simple example of a custom UI theme that changes only the tint colors, let's call it Breeze Light Red.css
:
:root {
--inherit-base-theme: true;
--primary: #e93d66;
--scrollbar-button: #a94074;
--item-hover: #502834;
--tab-hover: #5e3347;
}
That effectively would create/add a new UI theme with light red colors.
A very important detail is that if the UI theme must inherit the complete definition of the default theme,
we must add --inherit-base-theme: true
to the :root
element, otherwise the UI theme must be defined
completely, which means, every widget must be styled from scratch (not recommended given its complexity).
It's also possible to override the style of the different widgets redefining their properties with the
usual rules that apply to the well-known CSS specification (A.K.A. using adequate
specificity and probably abusing the
!important flag).
Custom languages support can be added in the languages directory found at:
XDG_CONFIG_HOME
, usually translates to ~/.config/ecode/languages
Application Support
folder in HOME
, usually translates to ~/Library/Application Support/ecode/languages
APPDATA
, usually translates to C:\Users\{username}\AppData\Roaming\ecode\languages
ecode will read each file located at that directory with json
extension. Each file can contain one
or several languages. In order to set several languages the root element of the json file should be
an array, containing one object for each language, otherwise if the root element is an object, it
should contain the language definition. Language definitions can override any currently supported
definition. ecode will prioritize user defined definitions.
{
"name": "language_name",
"files": [ "Array of file extensions supported" ],
"comment": "Sets the comment string used for auto-comment functionality.",
"patterns": [
{ "pattern": "lua_pattern", "type": "type_name" },
{ "pattern": "no_capture(pattern_capture_1)(pattern_capture_2)", "type": { "no_capture_type_name", "capture_1_type_name", "capture_2_type_name" } },
{ "pattern": ["lua_pattern_start", "lua_pattern_end", "escape_character"], "type": "type_name" }
],
"symbols": [
{ "symbol_name": "type_name" }
],
"visible": true, /* sets if the language is visible as a main language in the editor, optional parameter, true by default */
"auto_close_xml_tag": false, /* sets if the language defined supports auto close XML tags, optional parameter, false by default */
"lsp_name": "sets the LSP name assigned for the language, optional parameter, it will use the _name_ in lowercase if not set"
}
ecode uses the same format for language definition as lite and lite-xl editors. This makes much easier to add new languages to ecode. There's also a helper tool that can be download from ecode repository located here that allows to directly export a lite language definition to the JSON file format used in ecode. A minor clarification: ecode does not currently support regex for pattern matching that it's supported by lite-xl (it might be added in the near future).
It's possible to easily extend any language definition by exporting it using the CLI arguments provided:
--export-lang
and --export-lang-path
. A user wanting to extend or improve a language definition can
export it, modify it and install the definition with a .json
extension in the custom languages path.
For example, to extend the language vue
you will need to run:
ecode --export-lang=vue --export-lang-path=./vue.json
, exit the exported file and move it to the
custom languages path.
{
"name": "Elixir",
"files": [ "%.ex$", "%.exs$" ],
"comment": "#",
"patterns": [
{ "pattern": "#.*\n", "type": "comment" },
{ "pattern": [ ":\"", "\"", "\\" ], "type": "number" },
{ "pattern": [ "\"\"\"", "\"\"\"", "\\" ], "type": "string" },
{ "pattern": [ "\"", "\"", "\\" ], "type": "string" },
{ "pattern": [ "'", "'", "\\" ], "type": "string" },
{ "pattern": [ "~%a[/\"|'%(%[%{<]", "[/\"|'%)%]%}>]", "\\" ], "type": "string"},
{ "pattern": "-?0x%x+", "type": "number" },
{ "pattern": "-?%d+[%d%.eE]*f?", "type": "number" },
{ "pattern": "-?%.?%d+f?", "type": "number" },
{ "pattern": ":\"?[%a_][%w_]*\"?", "type": "number" },
{ "pattern": "[%a][%w_!?]*%f[(]", "type": "function" },
{ "pattern": "%u%w+", "type": "normal" },
{ "pattern": "@[%a_][%w_]*", "type": "keyword2" },
{ "pattern": "_%a[%w_]*", "type": "keyword2" },
{ "pattern": "[%+%-=/%*<>!|&]", "type": "operator" },
{ "pattern": "[%a_][%w_]*", "type": "symbol" }
],
"symbols": [
{"def": "keyword"},
{"defp": "keyword"},
{"defguard": "keyword"},
{"defguardp": "keyword"},
{"defmodule": "keyword"},
{"defprotocol": "keyword"},
{"defimpl": "keyword"},
{"defrecord": "keyword"},
{"defrecordp": "keyword"},
{"defmacro": "keyword"},
{"defmacrop": "keyword"},
{"defdelegate": "keyword"},
{"defoverridable": "keyword"},
{"defexception": "keyword"},
{"defcallback": "keyword"},
{"defstruct": "keyword"},
{"for": "keyword"},
{"case": "keyword"},
{"when": "keyword"},
{"with": "keyword"},
{"cond": "keyword"},
{"if": "keyword"},
{"unless": "keyword"},
{"try": "keyword"},
{"receive": "keyword"},
{"after": "keyword"},
{"raise": "keyword"},
{"rescue": "keyword"},
{"catch": "keyword"},
{"else": "keyword"},
{"quote": "keyword"},
{"unquote": "keyword"},
{"super": "keyword"},
{"unquote_splicing": "keyword"},
{"do": "keyword"},
{"end": "keyword"},
{"fn": "keyword"},
{"import": "keyword2"},
{"alias": "keyword2"},
{"use": "keyword2"},
{"require": "keyword2"},
{"and": "operator"},
{"or": "operator"},
{"true": "literal"},
{"false": "literal"},
{"nil": "literal"}
]
}
For more complex syntax definitions please see the definition of all the native languages supported by ecode here.
Listed in no particular order:
The author is more than open to collaborations. Any person interested in the project is invited to participate. Many features are still pending, and the project will grow much more over time. Please, collaborate. =)
Some Unicode characters won't be rendered in the editor out of the box. You'll need to change the default monospace font in favor of a font that supports the characters you want to see that are not being rendered. You could also change the default fallback font in the case you want to use a traditional monospaced font. The default fallback font should cover a wide range of languages but you could need some special font (currently covers CJK languages).
*1 Current eepp feature limitations.
*2 I'm not a fan of sub-pixel hinting. But I'm more than willing to implement it, I'm not very versed in the matter, so any help will be appreciated.
*3 I don't really like ligatures. I'm open to PRs implementing them.
*4 I'm not a VIM user, and I'm not qualified to implement the VIM mode or any modal editing. PRs are welcome to support this.
*5 Better text-shaping support will come with time, but with no rush for the moment. eepp architecture is ready to add HarfBuzz support.
This editor has a deeply rooted inspiration from the lite, lite-xl, QtCreator, and Sublime Text editors. Several features were developed based on the lite/lite-xl implementations. Some features can be ported directly from lite: color-schemes and syntax-highlighting patterns (eepp implementation expands original lite implementation to add many more features).
ecode is being used mostly in Linux and macOS, it's not well tested in Windows. If you find any issues with the editor please report it here.
This is a work in progress, stability is not guaranteed. Please don't use it for critical tasks. I'm using the editor daily and is stable enough for me, but use it at your own risk.
Niels Lohmann for JSON for Modern C++
Neil Henning for subprocess.h
All the authors of the suckless terminal emulator
Fredrik Aleksander for Hexe Terminal
rxi for lite
franko and all the collaborators for lite-xl
Andreas Kling for SerenityOS
And a lot more people!