1use lattice_grammar::registry::{CommandRegistry, MotionResult, MotionSpec};
8use lattice_grammar::{MotionId, NavBoundary, NavDir};
9use std::sync::Arc;
10
11#[derive(Debug, Clone, Copy)]
17pub struct SyntaxMotionIds {
18 pub next_function_start: MotionId,
19 pub prev_function_start: MotionId,
20 pub next_function_end: MotionId,
21 pub prev_function_end: MotionId,
22 pub next_class_start: MotionId,
23 pub prev_class_start: MotionId,
24 pub next_class_end: MotionId,
25 pub prev_class_end: MotionId,
26 pub next_parameter_start: MotionId,
27 pub prev_parameter_start: MotionId,
28 pub next_parameter_end: MotionId,
29 pub prev_parameter_end: MotionId,
30 pub next_loop_start: MotionId,
31 pub prev_loop_start: MotionId,
32 pub next_loop_end: MotionId,
33 pub prev_loop_end: MotionId,
34}
35
36pub fn register_syntax_motions(registry: &mut CommandRegistry) -> SyntaxMotionIds {
41 SyntaxMotionIds {
42 next_function_start: reg(
43 registry,
44 "motion:next-function-start",
45 "Go to the start of the next function (`]f`). `@function.outer`.",
46 "function.outer",
47 NavDir::Forward,
48 NavBoundary::Start,
49 ),
50 prev_function_start: reg(
51 registry,
52 "motion:prev-function-start",
53 "Go to the start of the previous (or enclosing) function (`[f`).",
54 "function.outer",
55 NavDir::Backward,
56 NavBoundary::Start,
57 ),
58 next_function_end: reg(
59 registry,
60 "motion:next-function-end",
61 "Go to the end of the current/next function (`]F`).",
62 "function.outer",
63 NavDir::Forward,
64 NavBoundary::End,
65 ),
66 prev_function_end: reg(
67 registry,
68 "motion:prev-function-end",
69 "Go to the end of the previous function (`[F`).",
70 "function.outer",
71 NavDir::Backward,
72 NavBoundary::End,
73 ),
74
75 next_class_start: reg(
76 registry,
77 "motion:next-class-start",
78 "Go to the start of the next class/type (`]c`). `@class.outer`.",
79 "class.outer",
80 NavDir::Forward,
81 NavBoundary::Start,
82 ),
83 prev_class_start: reg(
84 registry,
85 "motion:prev-class-start",
86 "Go to the start of the previous (or enclosing) class (`[c`).",
87 "class.outer",
88 NavDir::Backward,
89 NavBoundary::Start,
90 ),
91 next_class_end: reg(
92 registry,
93 "motion:next-class-end",
94 "Go to the end of the current/next class (`]C`).",
95 "class.outer",
96 NavDir::Forward,
97 NavBoundary::End,
98 ),
99 prev_class_end: reg(
100 registry,
101 "motion:prev-class-end",
102 "Go to the end of the previous class (`[C`).",
103 "class.outer",
104 NavDir::Backward,
105 NavBoundary::End,
106 ),
107
108 next_parameter_start: reg(
109 registry,
110 "motion:next-parameter-start",
111 "Go to the start of the next parameter/argument (`]a`). `@parameter.outer`.",
112 "parameter.outer",
113 NavDir::Forward,
114 NavBoundary::Start,
115 ),
116 prev_parameter_start: reg(
117 registry,
118 "motion:prev-parameter-start",
119 "Go to the start of the previous parameter (`[a`).",
120 "parameter.outer",
121 NavDir::Backward,
122 NavBoundary::Start,
123 ),
124 next_parameter_end: reg(
125 registry,
126 "motion:next-parameter-end",
127 "Go to the end of the current/next parameter (`]A`).",
128 "parameter.outer",
129 NavDir::Forward,
130 NavBoundary::End,
131 ),
132 prev_parameter_end: reg(
133 registry,
134 "motion:prev-parameter-end",
135 "Go to the end of the previous parameter (`[A`).",
136 "parameter.outer",
137 NavDir::Backward,
138 NavBoundary::End,
139 ),
140
141 next_loop_start: reg(
142 registry,
143 "motion:next-loop-start",
144 "Go to the start of the next loop (`]l`). `@loop.outer`.",
145 "loop.outer",
146 NavDir::Forward,
147 NavBoundary::Start,
148 ),
149 prev_loop_start: reg(
150 registry,
151 "motion:prev-loop-start",
152 "Go to the start of the previous (or enclosing) loop (`[l`).",
153 "loop.outer",
154 NavDir::Backward,
155 NavBoundary::Start,
156 ),
157 next_loop_end: reg(
158 registry,
159 "motion:next-loop-end",
160 "Go to the end of the current/next loop (`]L`).",
161 "loop.outer",
162 NavDir::Forward,
163 NavBoundary::End,
164 ),
165 prev_loop_end: reg(
166 registry,
167 "motion:prev-loop-end",
168 "Go to the end of the previous loop (`[L`).",
169 "loop.outer",
170 NavDir::Backward,
171 NavBoundary::End,
172 ),
173 }
174}
175
176fn reg(
180 registry: &mut CommandRegistry,
181 name: &str,
182 doc: &str,
183 suffix: &'static str,
184 dir: NavDir,
185 boundary: NavBoundary,
186) -> MotionId {
187 let exclusive = matches!(boundary, NavBoundary::Start);
188 registry.register_motion(
189 name,
190 doc,
191 MotionSpec {
192 curswant: lattice_grammar::CurswantEffect::default(),
193 jump: true,
194 exclusive,
195 args_schema: Vec::new(),
196 apply: Arc::new(move |ctx| {
197 let count = ctx.count.get().max(1);
198 let target = ctx
199 .scope_resolver
200 .and_then(|r| {
201 r.scope_toward(ctx.from.line, ctx.from.byte, suffix, dir, boundary, count)
202 })
203 .unwrap_or(ctx.from); Ok(MotionResult {
205 target,
206 linewise: false,
207 exclusive: None,
208 notice: None,
209 curswant: None,
210 })
211 }),
212 },
213 )
214}
215
216#[cfg(test)]
217mod tests {
218 use super::*;
219
220 #[test]
221 fn registers_sixteen_distinct_motions() {
222 let mut registry = CommandRegistry::new();
223 let ids = register_syntax_motions(&mut registry);
224 let all = [
225 ids.next_function_start,
226 ids.prev_function_start,
227 ids.next_function_end,
228 ids.prev_function_end,
229 ids.next_class_start,
230 ids.prev_class_start,
231 ids.next_class_end,
232 ids.prev_class_end,
233 ids.next_parameter_start,
234 ids.prev_parameter_start,
235 ids.next_parameter_end,
236 ids.prev_parameter_end,
237 ids.next_loop_start,
238 ids.prev_loop_start,
239 ids.next_loop_end,
240 ids.prev_loop_end,
241 ];
242 let mut seen = std::collections::HashSet::new();
243 for id in all {
244 assert!(seen.insert(id.0), "duplicate motion id");
245 let spec = registry.lookup(id.0).expect("registered");
246 assert_eq!(spec.kind, lattice_grammar::CommandKind::Motion);
247 }
248 assert_eq!(seen.len(), 16);
249 assert!(registry.id_by_name("motion:next-function-start").is_some());
251 assert!(registry.id_by_name("motion:prev-loop-end").is_some());
252 }
253}