Skip to content

Commit d4dfbdd

Browse files
author
santerijps
committed
Add Odin syntax highlighting
1 parent 737450c commit d4dfbdd

4 files changed

Lines changed: 341 additions & 0 deletions

File tree

assets/highlighting-tests/markdown.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,9 @@ export function greet(name) {
8484
def greet(name: str) -> str:
8585
return f"hello {name}"
8686
```
87+
88+
```odin
89+
greet :: proc(name: string) -> string {
90+
return fmt.tprintf("hello %s", name)
91+
}
92+
```
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
// Single-line comment
2+
3+
/*
4+
* Multi-line
5+
* comment
6+
*/
7+
8+
#+feature dynamic-literals
9+
package main
10+
11+
import "core:fmt"
12+
13+
// Package-level constants
14+
MY_CONST :: 42
15+
PI :: 3.14159
16+
NAME :: "Odin"
17+
18+
// Struct type
19+
Vector3 :: struct {
20+
x, y, z: f32,
21+
}
22+
23+
// Union type
24+
Value :: union {
25+
int,
26+
f64,
27+
string,
28+
}
29+
30+
// Enum type
31+
Direction :: enum {
32+
North,
33+
South,
34+
East,
35+
West,
36+
}
37+
38+
// Bit set
39+
Flags :: bit_set[Direction]
40+
41+
// Distinct type
42+
Meters :: distinct f32
43+
44+
// Procedures
45+
add :: proc(a, b: int) -> int {
46+
return a + b
47+
}
48+
49+
variadic :: proc(nums: ..int) -> int {
50+
result := 0
51+
for n in nums {
52+
result += n
53+
}
54+
return result
55+
}
56+
57+
get_value :: proc() -> (int, bool) {
58+
return 42, true
59+
}
60+
61+
@(deprecated = "use new_func instead")
62+
old_func :: proc() {
63+
fmt.println("old")
64+
}
65+
66+
demo_numbers :: proc() {
67+
// Integer and float literals
68+
_ := 42
69+
_ := 3.14
70+
_ := 1_000_000
71+
_ := 0xFF
72+
_ := 0b1010
73+
_ := 0o77
74+
_ := 1.5e-3
75+
_ := 2i // imaginary
76+
77+
// Language constants
78+
_ := true
79+
_ := false
80+
_ = nil
81+
}
82+
83+
demo_strings :: proc() {
84+
// Double-quoted strings with escape sequences
85+
_ := "hello, world"
86+
_ := "escape: \" \n \t \\"
87+
88+
// Raw strings (backtick — no escape processing)
89+
_ := `C:\Windows\notepad.exe`
90+
_ := `no \n escapes here`
91+
92+
// Rune / character literals
93+
_ := 'A'
94+
_ := '\n'
95+
_ := ''
96+
}
97+
98+
demo_control_flow :: proc() {
99+
// If / else if / else
100+
if true {
101+
fmt.println("yes")
102+
} else if false {
103+
fmt.println("no")
104+
} else {
105+
fmt.println("maybe")
106+
}
107+
108+
// C-style for loop
109+
for i := 0; i < 10; i += 1 {
110+
if i == 5 { continue }
111+
if i == 8 { break }
112+
}
113+
114+
// Range-based for loop
115+
for i in 0..<10 {
116+
fmt.println(i)
117+
}
118+
119+
// Switch statement
120+
x := 42
121+
switch x {
122+
case 1:
123+
fmt.println("one")
124+
case 2, 3:
125+
fmt.println("two or three")
126+
case:
127+
fmt.println("other")
128+
}
129+
130+
// Fallthrough
131+
switch 0 {
132+
case 0:
133+
fallthrough
134+
case 1:
135+
fmt.println("zero or one")
136+
}
137+
138+
// When (compile-time conditional)
139+
when ODIN_OS == .Windows {
140+
fmt.println("Windows")
141+
} else {
142+
fmt.println("other OS")
143+
}
144+
145+
// Defer
146+
defer fmt.println("deferred")
147+
148+
// or_else / or_break
149+
val := get_value() or_else 0
150+
_ = val
151+
152+
for {
153+
get_value() or_break
154+
}
155+
}
156+
157+
demo_types :: proc() {
158+
// Struct literal
159+
v := Vector3{x = 1, y = 4, z = 9}
160+
fmt.println(v)
161+
162+
// Union
163+
val: Value = 137
164+
if i, ok := val.(int); ok {
165+
fmt.println(i)
166+
}
167+
168+
// Enum and switch
169+
d := Direction.North
170+
switch d {
171+
case .North:
172+
fmt.println("north")
173+
case .South, .East, .West:
174+
fmt.println("other direction")
175+
}
176+
177+
// Bit set
178+
flags := Flags{.North, .East}
179+
fmt.println(.North in flags)
180+
181+
// Distinct type
182+
dist := Meters(100.0)
183+
fmt.println(dist)
184+
185+
// Cast / transmute / auto_cast
186+
x: int = cast(int)3.14
187+
y := transmute([4]u8)u32(0xDEAD_BEEF)
188+
z := auto_cast x
189+
_, _ = y, z
190+
}
191+
192+
demo_collections :: proc() {
193+
// Dynamic array
194+
arr := make([dynamic]int)
195+
defer delete(arr)
196+
append(&arr, 1, 2, 3)
197+
fmt.println(arr)
198+
199+
// Map
200+
m := make(map[string]int)
201+
defer delete(m)
202+
m["key"] = 99
203+
fmt.println(m["key"])
204+
205+
// Pointer
206+
val := 123
207+
ptr := &val
208+
ptr^ = 456
209+
fmt.println(val)
210+
}
211+
212+
demo_builtins :: proc() {
213+
// typeid / type_of
214+
_ = typeid_of(int)
215+
val := 0
216+
_ = type_of(val)
217+
218+
// size_of / align_of / offset_of
219+
_ = size_of(Vector3)
220+
_ = align_of(f64)
221+
_ = offset_of(Vector3, y)
222+
223+
// context
224+
context.user_index = 1
225+
}
226+
227+
demo_directives :: proc() {
228+
// Compile-time assert
229+
#assert(size_of(u8) == 1)
230+
231+
// SOA layout
232+
v: #soa[4]Vector3
233+
_ = v
234+
235+
// Partial switch
236+
#partial switch d := Direction.North; d {
237+
case .North:
238+
fmt.println("north")
239+
}
240+
241+
// Unroll for
242+
#unroll for elem, idx in [3]int{1, 4, 9} {
243+
fmt.println(elem, idx)
244+
}
245+
}
246+
247+
main :: proc() {
248+
demo_numbers()
249+
demo_strings()
250+
demo_control_flow()
251+
demo_types()
252+
demo_collections()
253+
demo_builtins()
254+
demo_directives()
255+
256+
fmt.println(add(1, 2))
257+
fmt.println(variadic(1, 2, 3, 4, 5))
258+
old_func()
259+
}

crates/lsh/definitions/markdown.lsh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,16 @@ pub fn markdown() {
6060
if /.*/ {}
6161
}
6262
}
63+
} else if /(?i:odin)/ {
64+
loop {
65+
await input;
66+
if /\s*```/ {
67+
return;
68+
} else {
69+
odin();
70+
if /.*/ {}
71+
}
72+
}
6373
} else if /(?i:py)/ {
6474
loop {
6575
await input;

crates/lsh/definitions/odin.lsh

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#[display_name = "Odin"]
2+
#[path = "**/*.odin"]
3+
pub fn odin() {
4+
until /$/ {
5+
yield other;
6+
7+
if /\/\/.*/ {
8+
yield comment;
9+
} else if /\/\*/ {
10+
loop {
11+
yield comment;
12+
if /\*\// { yield comment; break; }
13+
await input;
14+
}
15+
} else if /"/ {
16+
until /$/ {
17+
yield string;
18+
if /\\./ {}
19+
else if /"/ { yield string; break; }
20+
await input;
21+
}
22+
} else if /`/ {
23+
loop {
24+
yield string;
25+
if /`/ { yield string; break; }
26+
await input;
27+
}
28+
} else if /'/ {
29+
until /$/ {
30+
yield string;
31+
if /\\./ {}
32+
else if /'/ { yield string; break; }
33+
await input;
34+
}
35+
} else if /(?:break|case|continue|do|else|fallthrough|for|if|in|not_in|or_break|or_continue|or_else|or_return|return|switch|when|where)\>/ {
36+
yield keyword.control;
37+
} else if /(?:align_of|auto_cast|bit_field|bit_set|cast|context|defer|distinct|dynamic|enum|foreign|import|map|matrix|offset_of|package|proc|size_of|struct|transmute|type_of|typeid|union|using)\>/ {
38+
yield keyword.other;
39+
} else if /(?:any|b8|b16|b32|b64|bool|complex32|complex64|complex128|cstring|f16|f16be|f16le|f32|f32be|f32le|f64|f64be|f64le|i8|i16|i16be|i16le|i32|i32be|i32le|i64|i64be|i64le|i128|i128be|i128le|int|quaternion64|quaternion128|quaternion256|rawptr|rune|string|u8|u16|u16be|u16le|u32|u32be|u32le|u64|u64be|u64le|u128|u128be|u128le|uint|uintptr)\>/ {
40+
yield keyword.other;
41+
} else if /(?:true|false|nil)\>/ {
42+
yield constant.language;
43+
} else if /#\+\w+/ {
44+
yield keyword.other;
45+
if /(?:.+)\>/ {
46+
yield string;
47+
}
48+
} else if /#\w+/ {
49+
yield keyword.other;
50+
} else if /@/ {
51+
yield markup.link;
52+
} else if /(?i:-?(?:0x[\da-fA-F_]+|0b[01_]+|0o[0-7_]+|[\d_]+\.?[\d_]*|\.[\d_]+)(?:e[+-]?[\d_]+)?i?)/ {
53+
if /\w+/ {
54+
// Invalid numeric literal
55+
} else {
56+
yield constant.numeric;
57+
}
58+
} else if /(\w+)\s*\(/ {
59+
yield $1 as method;
60+
} else if /\w+/ {
61+
// Gobble word chars to align the next iteration on a word boundary.
62+
}
63+
64+
yield other;
65+
}
66+
}

0 commit comments

Comments
 (0)