Manual

Everything EEditor does, in the order you're likely to need it.

1. The workspace

A workspace is an ordinary folder. EEditor shows what's in it, edits the Markdown files it finds, and writes nothing else there except one hidden folder, .eeditor/, for your keybindings.

On first launch EEditor picks a default folder and opens it. To use a different one, press open… at the top of the file tree and choose any directory — a git repository, a synced folder, an external disk. The choice survives restarts.

Because the workspace is just a folder, everything you already do with folders keeps working: back it up, version it, sync it, or open the same notes in another editor. EEditor takes no lock and keeps no separate index that could drift from the files.

What EEditor stores outside your notes: the agenda database (items, categories, rules) and any tables you create in EELisp live in an SQLite file in the application's own data directory — not in your workspace. Your notes are never inside it.

2. Files and folders

The file tree is the left column. Click a file to open it. The two buttons at the top create a new file and a new folder at the root; to create inside a folder, right-click it (long-press on a touch screen) and use the menu, which also offers Rename… and Delete.

Renaming and deleting keep open tabs honest: a renamed file follows its tab, and deleting a file or folder closes the tabs underneath it and moves you to a neighbour rather than leaving a dead editor.

Files beginning with a dot are hidden from the tree, which is why your keybindings live in .eeditor/keybindings.eelisp — present, editable, out of the way.

The daily note

⌘D opens today's note, YYYY-MM-DD.md at the root of the workspace, creating it with a date heading if it doesn't exist. It never overwrites an existing one, so pressing it repeatedly through the day just takes you back to the same note.

3. The editor

The editor is CodeMirror 6 with Markdown syntax highlighting. Edits save automatically about a second after you stop typing; ⌘S saves immediately.

Preview

⌘E (or the preview button) swaps the editor for rendered Markdown. Wiki links become clickable there. Press it again to go back.

Themes

The ☀/☾ button toggles between a light theme (Solarized) and a dark one (One Dark). Your choice is remembered.

PDF export

PDF renders the current note and opens the system print dialog, where Save as PDF produces a file. Headings, lists, tables and code blocks all come across.

Running code from a note

Put EELisp in a fenced block and press ⌘⇧↵ with the caret inside it. The result appears in the REPL below — a live notebook without leaving the document.

```eelisp
(items :category "work" :when-before (today))
```

4. Tabs

Opening a second note puts it in a new tab rather than replacing what you had. Click a tab to switch, click its × to close. ⌘⌥→ and ⌘⌥← move between tabs if you bind them — see keybindings for the next-tab, prev-tab and close-tab commands.

A tab marked is a file from outside the workspace; see §7.

5. Finding things

Quick open — ⌘P

Fuzzy matching over every file in the workspace. Type fragments in any order — agmt finds agenda-meeting.md. Arrow keys move, opens, Esc dismisses.

Full-text search — ⌘⇧F

Searches the contents of every note and lists each hit with its line. Choosing one opens the note at that line.

Tags

Any #hashtag in a note is indexed and appears in the tags panel in the sidebar, with a count. Clicking a tag opens full-text search filtered to it, which is the fastest way to see everything you marked #idea or #invoice.

7. Files from outside the workspace

There are three ways a foreign file arrives:

  • Drag it onto the EEditor window.
  • Open With from Finder or your file manager, or double-click if EEditor is the default for that type.
  • The file… button in the sidebar.

However it arrives, EEditor asks once what you want:

  • Copy into the workspace — it becomes an ordinary note, indexed for search, tags and links.
  • Open in place — the tab is marked and saves write back to the original location, wherever that is.

An in-place file is deliberately not part of the workspace: it won't appear in the tree, in search results, in the tag index or in backlinks. If you decide you want it after all, press copy in in the header and it becomes a real note — including whatever unsaved edits are on screen, which also makes this the escape hatch for a read-only original.

EEditor registers itself for .md, .markdown, .txt, .eelisp and .lisp, as an alternative handler rather than stealing those types from your existing editor.

8. The agenda

The agenda is a personal information manager in the Lotus Agenda tradition: you throw text at it, and structure emerges from rules rather than from forms you fill in.

Adding items

The box at the top of the agenda panel takes plain language and parses it — call Bob tomorrow !! becomes an item due tomorrow at high priority. Dates, priorities and people are recognised as you write them.

Editing an item

Click one to expand its editor, which holds:

  • text — the item itself
  • when — a due date
  • priority
  • notes — free text underneath
  • recurrence — daily, weekly or monthly, with an interval
  • categories — one or more, from those you've defined

Categories

Categories are hierarchical: work/clients/acme is a child of work/clients. Assigning a child implies its parents, and an exclusive parent allows only one of its children at a time — so an item can't be both status/open and status/done.

Rules

Rules are the part that makes the agenda feel alive. A rule is a condition and an action; run them and every matching item is filed automatically. Open the setup panel to define categories and rules, then apply them.

(defrule invoices
  :when   (str-matches text "invoice|bill|payment")
  :assign "finance/invoices")

Inside a condition the item is in scope: text, notes, id, categories and props are bound, along with (get "field"), (has-category "work"), (overdue?) and (match n) for the capture groups of the last regular expression. Because rules are stored as EELisp and re-evaluated, anything the language can express, a rule can decide.

Views

A view is a saved filter with optional grouping — everything overdue, grouped by category. Define with defview, list with views, display with show.

The calendar

The calendar button opens a month grid of dated items. Drag an item to another day to reschedule it; the change is written straight back.

Recurrence

A recurring item regenerates when completed. every handles the date arithmetic — daily, weekly and monthly, with intervals like every 2 weeks — without depending on an external calendar.

9. The REPL

⌘J shows and hides the EELisp REPL. Type an expression, press ⌘↵, and the result appears above the prompt.

Results are not text dumps. A query renders as a real table; defform and edit render as a real form with working fields, including computed ones. This is dBASE III's interaction model, rebuilt.

λ (deftable notes (title:string body:string))
λ (insert notes {:title "Kickoff" :body "Ship by Friday"})
λ (browse notes)          ; → a table widget
λ (edit notes 1)          ; → a form widget

Snippets

The snippets button loads the standard EELisp bundle — ten modules and over a hundred and fifty functions covering text, maths, dates, conversion, crypto, reference tables, networking, world clocks and Bitcoin. Once loaded they're callable from the REPL and from your keybindings alike.

The full language is documented at eelisp.app.

10. Keybindings

⌘⇧K opens .eeditor/keybindings.eelisp, writing the documented defaults into it the first time. That file is the shortcut table. Save it and it reloads at once; delete it and the built-in defaults apply again.

Writing a binding

(bind "Mod-i" body…)

Modifiers join with - or +. Mod is on macOS and Ctrl elsewhere, and Cmd, Ctrl, Alt (Option) and Shift can be named directly: "Mod-Shift-f", "Ctrl-Alt-t". Every binding needs at least one modifier — function keys and Esc may go bare. A later binding wins the same key, and unbinding one hands the key back to the editor's own behaviour.

What a binding returns

The body is EELisp, evaluated on the engine when the key fires. It returns editor commands as data, and a binding may list several — they run in order against the live document.

CommandEffect
(ed-cmd "save")run a built-in command (table below)
(ed-insert text)insert at the caret, caret after the text
(ed-insert-at pos text)insert at an absolute offset
(ed-goto pos)caret to an absolute offset
(ed-goto-line n)caret to the start of line n (1-based)
(ed-select from to)select a range
(ed-replace text)replace the selection
(ed-replace-range from to text)replace an explicit range
(ed-set-buffer text)replace the whole document
(ed-open "notes/x.md")open a file in a tab
(ed-new path)
(ed-new path text)
create the note if it doesn't exist, then open it — never overwrites
(ed-message text)show a toast
(ed-nothing)do nothing, useful as a cond branch

What your code can see

These are defined fresh before every run:

VariableIs
*file*path of the current note
*cursor*caret offset
*line* / *col*caret line and column, both 1-based
*line-text*text of the caret's line
*lines*number of lines in the document
*sel-from* / *sel-to*selection bounds
*selection*the selected text
*buffer*the whole document
*date*"YYYY-MM-DD", local time
*time*"HH:MM"
*now*"YYYY-MM-DD HH:MM:SS"

*selection* and *buffer* are only computed when your binding mentions them, so a shortcut stays fast in a large document.

Choosing what opens at launch

(on-start …) runs the same kind of commands once, after the workspace loads. With no on-start form at all, EEditor opens welcome.md, or the first note it finds.

(on-start (ed-open "todo.md"))         ; always this note
(on-start (ed-cmd "daily-note"))       ; today's note, created if needed
(on-start (ed-new (str *date* ".md")))  ; the same, spelled out
(on-start)                              ; nothing — an empty editor

An empty (on-start) is deliberately different from leaving the form out: one says "open nothing", the other says "decide for me".

Built-in commands

Anything here can be named by (ed-cmd "…"):

CommandDoes
savesave the current note
quick-openfuzzy file finder
searchfull-text search
daily-noteopen (or create) today's note
new-file / new-foldercreate at the workspace root, asking for a name
open-filepick any file; asks whether to copy it in or edit in place
copy-into-workspaceturn the current ↗ note into a copy in the workspace
open-keys / reload-keysedit or re-read this config
toggle-repl / focus-replshow/hide the REPL, or jump into it
focus-editorput the caret back in the document
toggle-previeweditor ⇄ rendered Markdown
toggle-themelight ⇄ dark
export-pdfprint the current note to PDF
snippetsload the standard EELisp bundle
calendaropen the month grid
agenda-setupcategories and rules panel
close-tab / next-tab / prev-tabtab management

Worked examples

;; A timestamped heading at the caret, on its own line
(bind "Mod-Shift-i"
  (ed-insert (str (cond (= *col* 1) "" true "\n") "## " *time* "\n\n")))

;; Wrap the selection in bold
(bind "Mod-b" (ed-replace (str "**" *selection* "**")))

;; A task line
(bind "Mod-Shift-t" (ed-insert "- [ ] "))

;; Link back to today's note
(bind "Mod-Shift-b" (ed-insert (str "[[" *date* "]]")))

;; File this note under a dated folder and tell me about it
(bind "Mod-Alt-n"
  (ed-new (str "journal/" *date* ".md") (str "# " *date* "\n\n"))
  (ed-message "journal entry ready"))

Mistakes are reported, not swallowed: a binding that fails prints its error in the REPL and shows a toast naming the key, and problems in the config itself are listed when it reloads.

11. iPhone and iPad

Below 900 pixels the layout becomes a single pane with a tab bar — Files, Editor, REPL — so the same app works on a phone without pretending to be a desktop.

The workspace is the app's Documents folder, exposed in Files → On My iPhone → EEditor. Anything you put there appears in EEditor, and anything EEditor writes is visible to other apps and to whatever backup or sync you already use.

file… imports a single document through the system picker. folder… points EEditor at an external folder — an iCloud Drive directory, for instance — and remembers it across launches.

12. Shortcut reference

The defaults. is Ctrl on Windows and Linux.

KeyDoes
⌘SSave
⌘PQuick open
⌘⇧FFull-text search
⌘DToday's note
⌘INew note stamped with the minute
⌘⇧ITimestamped heading at the caret
⌘⇧NNew file
⌘EPreview ⇄ edit
⌘JShow/hide the REPL
⌘⇧KEdit these keybindings
⌘⇧↵Run the eelisp block at the caret
⌘↵Evaluate in the REPL
-clickFollow a wiki link from the editor

Every one of these is a line in your keybindings file, and every one can be changed.