Skip to content
This repository was archived by the owner on Oct 12, 2026. It is now read-only.

Commit 978b785

Browse files
committed
feat: add GNU emacs editor plugin
1 parent 882da33 commit 978b785

5 files changed

Lines changed: 152 additions & 4 deletions

File tree

‎README.md‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,13 @@ lbd [options]
4141

4242
---
4343

44-
### TODO
44+
## Editor Plugins
45+
46+
1. [GNU Emacs](./editor-plugins/emacs)
47+
48+
---
49+
50+
## TODO
4551

4652
- TODO: Add build instructions for Windows (MSVC toolchain).
4753
- TODO: Provide better examples (e.g., Advent of Code snippets).

‎editor-plugins/emacs/README.md‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
## Thumbnail
2+
3+
![Thumbnail](./resources/thumbnail.png)
4+
5+
## Getting Started
6+
7+
Right now the only way to use this is to:
8+
9+
1. `C-x C-f lbd-mode.el RET`
10+
2. `M-x eval-buffer RET`
11+
3. `M-x lbd-mode RET`

‎editor-plugins/emacs/lbd-mode.el‎

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
;;; lbd-mode.el --- Major mode for Lambda-Discipline Language -*- lexical-binding: t; -*-
2+
;;
3+
;; Author: NinthCircle
4+
;; Version: 0.1
5+
;; Keywords: languages
6+
;; URL: https://gitlab.com/ninthcircle/lambda-discipline/-/tree/master/editor-plugins/emacs/lbd-mode.el
7+
8+
(defvar lbd-mode-hook nil)
9+
10+
(defvar lbd-mode-map
11+
(let ((map (make-sparse-keymap)))
12+
map)
13+
"Keymap for lbd major mode")
14+
15+
;; Syntax table
16+
(defvar lbd-mode-syntax-table
17+
(let ((st (make-syntax-table)))
18+
;; Comment syntax: “--” starts a comment, newline ends it
19+
(modify-syntax-entry ?- ". 12" st)
20+
(modify-syntax-entry ?\n ">" st)
21+
22+
;; Parentheses
23+
(modify-syntax-entry ?\( "(" st)
24+
(modify-syntax-entry ?\) ")" st)
25+
26+
;; You can tweak these if you want string/char support later
27+
(modify-syntax-entry ?\" "\"" st)
28+
(modify-syntax-entry ?' "w" st)
29+
30+
st)
31+
"Syntax table for lbd-mode.")
32+
33+
;; Comment settings
34+
(setq-local comment-start "-- ")
35+
(setq-local comment-end "")
36+
37+
;; Font lock
38+
(defconst lbd--keywords
39+
'("true" "false" "if_then" "pair" "first" "second" "use")
40+
"Language keywords for lbd-mode.")
41+
42+
(defvar lbd-font-lock-keywords
43+
(let* ((kw (regexp-opt lbd--keywords 'words))
44+
;; types are capitalized identifiers like Any Bool Float
45+
(type-re "\\b\\([A-Z][A-Za-z0-9_]*\\)\\b")
46+
;; arrows
47+
(arrow-re "->")
48+
;; lambda/backslash `\` and the dot that follows the argument list
49+
(lambda-re "\\\\[[:alnum:]_]+:\\s-*\\([A-Za-z0-9_<>]+\\)\\.\\|\\\\")
50+
;; top-level defs: name : type =
51+
(def-re "^\\s-*\\([a-zA-Z_][a-zA-Z0-9_?']*\\)\\s-*:")
52+
)
53+
`(
54+
(,kw . font-lock-keyword-face)
55+
(,type-re 1 font-lock-type-face)
56+
(,arrow-re . font-lock-builtin-face)
57+
("\\\\" . font-lock-builtin-face) ;; the backslash lambda
58+
("\\." . font-lock-constant-face) ;; dots as separators
59+
;; highlight top-level names like `name:` at bol
60+
(,def-re 1 font-lock-function-name-face)
61+
))
62+
"Font lock patterns for lbd-mode.")
63+
64+
;; Indentation
65+
(defun lbd--calculate-indent ()
66+
"Calculate indentation for current line in `lbd-mode'.
67+
Returns number of spaces to indent to.
68+
This implementation uses parenthesis depth as a baseline and adds
69+
4 spaces for continued lines after a dot or after a lambda argument list."
70+
(save-excursion
71+
(beginning-of-line)
72+
(let* ((parse (syntax-ppss))
73+
(paren-depth (nth 0 parse))
74+
indent)
75+
(cond
76+
;; If inside parens, align with the opening paren plus two spaces
77+
((> paren-depth 0)
78+
(goto-char (nth 1 parse))
79+
(setq indent (+ (current-column) 2)))
80+
;; If previous non-blank line ends with '.' or is a lambda start, indent
81+
(t
82+
(save-excursion
83+
(forward-line -1)
84+
(while (and (not (bobp)) (looking-at "^\\s-*$"))
85+
(forward-line -1))
86+
(end-of-line)
87+
(cond
88+
((looking-back "\\\\.*\\.\\s-*" (line-beginning-position))
89+
(setq indent (+ (current-indentation) 4)))
90+
((looking-back "\\\\" (line-beginning-position))
91+
(setq indent (+ (current-indentation) 4)))
92+
((looking-back "\\.\\s-*" (line-beginning-position))
93+
(setq indent (+ (current-indentation) 4)))
94+
(t (setq indent 0))))))
95+
(max indent 0))))
96+
97+
(defun lbd-indent-line ()
98+
"Indent current line as lbd code."
99+
(interactive)
100+
(let ((indent (lbd--calculate-indent))
101+
(offset (- (current-column) (current-indentation))))
102+
(if indent
103+
(progn
104+
(indent-line-to indent)
105+
(when (> offset 0) (forward-char offset)))
106+
(indent-line-to 0))))
107+
108+
;; Imenu support
109+
(defvar lbd-imenu-generic-expression
110+
'(( "Definitions" "^\\s-*\\([a-zA-Z_][a-zA-Z0-9_?']*\\)\\s-*:" 1))
111+
"Imenu expression for lbd-mode to index top-level definitions.")
112+
113+
;;;###autoload
114+
(define-derived-mode lbd-mode prog-mode "LBD"
115+
"Major mode for editing Lambda-Discipline-like files."
116+
:syntax-table lbd-mode-syntax-table
117+
(setq-local font-lock-defaults '(lbd-font-lock-keywords))
118+
;; comment style
119+
(setq-local comment-start "-- ")
120+
(setq-local comment-end "")
121+
;; indentation
122+
(setq-local indent-line-function #'lbd-indent-line)
123+
;; imenu
124+
(setq-local imenu-generic-expression lbd-imenu-generic-expression))
125+
126+
;;;###autoload
127+
(add-to-list 'auto-mode-alist '("\\.lbd\\'" . lbd-mode))
128+
(add-to-list 'auto-mode-alist '("\\.lambda\\'" . lbd-mode))
129+
130+
(provide 'lbd-mode)
131+
;;; lbd-mode.el ends here
79.2 KB
Loading

‎examples/ifthen.lbd‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ true: Bool = \x: Any. \y: Any. x
22
false: Bool = \x: Any. \y: Any. y
33

44
ifthen: Any = \condition: Bool -> Any -> Any -> Any.
5-
\then_clause: Any. \else_clause: Any.
6-
(condition then_clause else_clause)
5+
\then_clause: Any. \else_clause: Any.
6+
(condition then_clause else_clause)
77

88
(print (ifthen true "True" "False"))
9-
(print (ifthen false "True" "False"))
9+
(print (ifthen false "True" "False"))

0 commit comments

Comments
 (0)