Skip to main content

A curses-based text editor with syntax highlighting

Project description

Editor Documentation

 ,gggggggggggg,
dP"""88""""""Y8b,
Yb,  88       `8b,
 `"  88        `8b
     88         Y8
     88         d8  ,ggg,    ,ggg,,ggg,     ,gggg,gg   ,gggggg,  gg     gg
     88        ,8P i8" "8i  ,8" "8P" "8,   dP"  "Y8I   dP""""8I  I8     8I
     88       ,8P' I8, ,8I  I8   8I   8I  i8'    ,8I  ,8'    8I  I8,   ,8I
     88______,dP'  `YbadP' ,dP   8I   Yb,,d8,   ,d8b,,dP     Y8,,d8b, ,d8I
    888888888P"   888P"Y8888P'   8I   `Y8P"Y8888P"`Y88P      `Y8P""Y88P"888
                                                                      ,d8I'
                                                                    ,dP'8I
                                                                   ,8"  8I
                                                                   I8   8I
                                                                   `8, ,8I
                                                                    `Y8P"

A modal terminal editor built on curses with a command-driven, event-based architecture and chunked text storage.

Includes syntax highlighting, multi-buffer management, registers and markers, undo/redo history, Git integration, async LSP support, subprocess execution, and HTTP requests.

Installation

Install via pip (Recommended)

If your package is published:

pip install denary

Then run:

denary

Or open a file directly:

denary main.py

Install from Source

Clone the repository:

git clone https://gitlab.com/jordaly/editor.git denary
cd denary

Using Poetry:

poetry install
poetry run python -m denary

Or using plain pip:

pip install -e .
denary

Recommended (Better): Alias That Runs Denary Directly

Instead of activating the venv every time, just point the alias to the venv’s binary.

Assume your venv is here:

~/venvs/denary_venv

Edit your ~/.bashrc:

nano ~/.bashrc

Add this line:

alias denary="~/venvs/denary_venv/bin/denary"

Then reload:

source ~/.bashrc

Now you can run:

denary
denary myfile.py

Quick Start

Open a File

denary myfile.py

If the file does not exist, it will be created.

Key Bindings

Arrow & Basic Movement

Shortcut Action
Right Move cursor right
Left Move cursor left
Up Move cursor up
Down Move cursor down
PageUp Page up
PageDown Page down
Ctrl+Right Jump right (word/semantic move)
Ctrl+Left Jump left (word/semantic move)
Ctrl+Up Scroll / move up (mode dependent)
Ctrl+Down Scroll / move down (mode dependent)

Vim-Style Normal Mode Navigation

Shortcut Action
h Move left
j Move down
k Move up
l Move right
0 Jump to start of line
$ Jump to end of line
w / W Move forward by word
b / B Move backward by word
e / E Move to end of word
f / F Find character forward/backward
g Go command prefix
G Go to bottom
H Jump to top of visible window
L Jump to bottom of visible window
% Jump to matching bracket

Insert & Editing (Normal Mode)

Shortcut Action
i Enter insert mode
a Append after cursor
A Append at end of line
o Insert new line below
O Insert new line above
x Delete character
d Delete (operator)
c Change (operator)
y Yank (copy)
p Paste after
P Paste before
u Undo
U Undo line / extended undo
J Join lines
> Indent right
< Indent left

Search & Repeat

Shortcut Action
/ Search forward
n Next match
N Previous match
***** Search word under cursor

Marks & Registers

Shortcut Action
m Set marker
' Jump to marker (linewise)
` Jump to marker (exact position)
@ Execute macro
q Start/stop macro recording

LSP / Diagnostics (Normal Mode)

Shortcut Action
K Show LSP hover / definition
; LSP-related navigation
Ctrl+D Diagnostics-related action

(Exact behavior depends on your handler implementations.)


Alt-Based Commands

Shortcut Action
Alt+; Toggle selection mode
Alt+Right / Alt+. Indent right
Alt+Left / Alt+, Indent left
Alt+/ Alternative slash action
Alt+h / Alt+l Alternative horizontal move
Alt+j / Alt+k Move lines or cursor
Alt+Up / Alt+Down Scroll view
Alt+9 / Alt+0 Jump line start/end
Alt+w File search
Alt+r Custom action
Alt+i / Alt+o Punctuation jump
Alt+[ Bracket-related action
Alt+' Quote-related action

Shift Selection

Shortcut Action
Shift+Right Expand selection right
Shift+Left Expand selection left
Shift+Up Expand selection up
Shift+Down Expand selection down
Ctrl+Shift+Right Expand selection by word
Ctrl+Shift+Left Expand selection by word

Misc

Shortcut Action
Ctrl+A Select all
Ctrl+P Previous buffer
Ctrl+X Cut / special action
Ctrl+O Open file
Ctrl+I Jump forward in jump list
Q Quit variant
PageUp / PageDown Scroll

Commands

Buffer Actions

Command Description
h / help Show help
q Exit editor
w Save current buffer
ls List open buffers
open Open a file
bufnew Create new buffer
nbuf / nextbuf / next / n Next buffer
pbuf / prevbuf / prev / p Previous buffer
delbuf / d Delete buffer
ff File search
ffr New file search
fb Buffer picker
ex File explorer

UI & Behavior

Command Description
themes Change editor theme
ln Toggle line numbers
gch Toggle Git highlight
scrolloff <n> Set scroll offset

Markers

Command Description
m / marker Add local/global marker
pm Pick marker
dm Delete markers in this buffer
dgm Delete global markers
dpm Delete specific marker
dam Delete all markers

Text Utilities

Command Description
clear_registers Reset registers
reset_h_matches Reset highlighting
replace Replace text
comment Toggle comment
upper / lower / capitalize Change case
filetype <type> Set syntax mode

Utilities

Command Description
align Align text by a delimiter (e.g. =, :, ,, `
column_edit Insert, replace, or delete text within column ranges per line
date Copy date
datetime Copy current date & time
file_path Copy current file path
json_format Format JSON in selection or entire file
number_lines Add line numbers to each line
rejoin Split and rejoin selection or entire file using a delimiter
remove_empty_lines Remove blank or whitespace-only lines
reverse_lines Reverse the order of lines
shuffle_lines Randomly shuffle lines
slugify Convert text to URL-friendly slug format
sort / sort_r Sort selection or entire file by lines (normal or reverse)
trim / trim_l / trim_r Trim whitespace (both, left, or right) per line
unique_lines / dedup Remove duplicate lines (keep first occurrence)
uuid Copy random UUID
wrap Wrap text to a specified width
json_format formats json with indentation

Git Commands

Command Description
git_status Show Git status
git_diff Show diff
git_commit_all Commit all
git_blame Show blame
git_log Show history
git_refresh Refresh state
git_hunk Show previous version of changed code

LSP Commands

Command Description
lsp_diagnostics Show buffer diagnostics
lsp_hover Show hover info
lsp_definition Go to definition
lsp_refresh Restart LSP for buffer
lsp_references References picker
lsp_line_diagnostics Show diagnostics for current line
lsp_clear_diagnostics Clear diagnostics
lsp_clear_line_diag / clrld Clear diagnostics for current line

AI Commands

Command Description
ai Send a custom instruction to the AI
ai_gen Generate code at the cursor
ai_review Review Git diff for issues
ai_commit_message Generate a commit message from Git diff
ai_get Retrieve AI result at the cursor
ai_show Show AI completion picker
ai_remove Cancel/remove AI completion at the cursor
change_ai_model Change the active AI model

HTTP Request Execution

Select a block and run:

Command Description
req Make http request

Request Format

<HTTP_METHOD> <URL>
<Header>: <Value>
<Header>: <Value>

<Optional Body>

Examples

GET:

GET https://jsonplaceholder.typicode.com/todos/1
Accept: application/json

POST:

POST https://jsonplaceholder.typicode.com/posts
Content-Type: application/json

{
    "title": "Hello world",
    "body": "Testing request from editor",
    "userId": 1
}

Regex Replacement

Perform regex-based find and replace in the current buffer.

Command Description
replace Run a regex search and replace

Capture Groups

Parentheses create capture groups that can be reused in the replacement.

Syntax Meaning
\0 Entire match
\1 First group
\2 Second group
\3 Third group

Example

Find

(\w+)\s+(\w+)

Replace

\2 \1

Result

hello world → world hello

Named Capture Groups

Groups can also be named.

(?P<name>pattern)

Named groups are referenced in the replacement using:

\g<name>

Example

Find

(?P<first>\w+)\s+(?P<last>\w+)

Replace

\g<last> \g<first>

Result

john doe → doe john

Case Transformations

The replacement string supports case modifiers.

Syntax Effect
\U Uppercase until \E
\L Lowercase until \E
\u Uppercase next character
\l Lowercase next character
\E End transformation

Example

Find

select

Replace

\U\0\E

Result

select → SELECT

Word Boundaries

\b matches the boundary between a word and a non-word character.

Example

Find

\bselect\b

Matches

select

Does not match

selected

Example: SQL Keyword Formatting

Find

\b(select|from|where|join|group\s+by|order\s+by)\b

Replace

\U\0\E

Result

select name from users where id = 1
↓
SELECT name FROM users WHERE id = 1

Configuration

Denary loads its configuration from a platform-specific directory.

Config Location

Linux

~/.config/denary/init.py

macOS

~/Library/Application Support/denary/init.py

Windows (WSL recommended)

~/.config/denary/init.py

If the file does not exist, create it manually.

Example:

mkdir -p ~/.config/denary
nano ~/.config/denary/init.py

LSP Configuration

The init.py file allows you to:

  • Configure Language Servers (LSPs)
  • Map file extensions to language identifiers
  • Provide custom initialization settings

Denary expects two dictionaries:

LSP_CONFIG = {}
EXT_TO_LANG = {}

Example Configuration

LSP_CONFIG

LSP_CONFIG = {
    "python": {
        "cmd": [
            "/home/user/.config/denary/pylsp_venv/bin/python",
            "-m",
            "pylsp",
        ],
        "settings": {
            "pylsp": {
                "plugins": {
                    "flake8": {"enabled": True, "maxLineLength": 100},
                    "pyflakes": {"enabled": False},
                    "pycodestyle": {"enabled": False},
                }
            },
        },
    },

    "javascript": {
        "cmd": [
            "/home/user/.config/denary/ts_server/node_modules/.bin/typescript-language-server",
            "--stdio",
        ],
    },

    "c": {
        "cmd": [
            "/home/user/.config/denary/clangd_venv/bin/clangd",
            "--all-scopes-completion",
            "--clang-tidy",
            "--offset-encoding=utf-8",
        ],
    },

    "csharp": {
        "cmd": [
            "mono",
            "/home/user/.config/denary/omnisharp/OmniSharp.exe",
            "--languageserver",
        ],
    },

    "sql": {
        "cmd": [
            "/home/user/go/bin/sqls",
        ],
    },
}

EXT_TO_LANG

This maps file extensions to language keys defined in LSP_CONFIG.

EXT_TO_LANG = {
    ".py": "python",
    ".c": "c",
    ".h": "c",
    ".cpp": "c",
    ".js": "javascript",
    ".ts": "javascript",
    ".jsx": "javascript",
    ".tsx": "javascript",
    ".cs": "csharp",
    ".sql": "sql",
}

How It Works

  1. You open a file:

    denary main.py
    
  2. Denary checks the file extension (.py)

  3. It maps it using EXT_TO_LANG

  4. It loads the corresponding LSP from LSP_CONFIG

  5. The language server is started using the cmd array


Recommended Structure for LSP Tools

It is recommended to keep all LSP-related tools inside:

~/.config/denary/

Example layout:

~/.config/denary/
├── init.py
├── pylsp_venv/
├── clangd_venv/
├── ts_server/
└── omnisharp/

This keeps your setup portable and isolated.


Important Notes

  • cmd must be a list (not a string)
  • Paths must be absolute
  • The language key in EXT_TO_LANG must exist in LSP_CONFIG
  • If an extension is not mapped, no LSP will be started

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

denary-0.0.10.tar.gz (137.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

denary-0.0.10-py3-none-any.whl (144.4 kB view details)

Uploaded Python 3

File details

Details for the file denary-0.0.10.tar.gz.

File metadata

  • Download URL: denary-0.0.10.tar.gz
  • Upload date:
  • Size: 137.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.3.3 CPython/3.13.0 Linux/5.15.167.4-microsoft-standard-WSL2

File hashes

Hashes for denary-0.0.10.tar.gz
Algorithm Hash digest
SHA256 dd3624e85adbac2c075f6a0bddec0e6488891b958e9220f5d00c223cfad7e2f2
MD5 69a01122899908fc10e2e0d96b36b648
BLAKE2b-256 0016a452b861179c6646e61e7502b69aff8221cbd1fe8b939a8afc6c2d6306f6

See more details on using hashes here.

File details

Details for the file denary-0.0.10-py3-none-any.whl.

File metadata

  • Download URL: denary-0.0.10-py3-none-any.whl
  • Upload date:
  • Size: 144.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.3.3 CPython/3.13.0 Linux/5.15.167.4-microsoft-standard-WSL2

File hashes

Hashes for denary-0.0.10-py3-none-any.whl
Algorithm Hash digest
SHA256 2ab89f72d40eec206a0f8f6a971b3e6b089d09ae06c569abdb20baff2ee7e2a9
MD5 774ffcc1042a79658ad74edb3af61edb
BLAKE2b-256 f67c588022b8b66cd6d52a7373632490d3ad7ebaa562ae208a27dbb66e9afc00

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page