lattice_syntax/
text_objects.rs1use lattice_grammar::TextObjectId;
23use lattice_grammar::registry::{CommandRegistry, TextObjectSpec};
24use std::sync::Arc;
25
26#[derive(Debug, Clone, Copy)]
32pub struct SyntaxTextObjectIds {
33 pub around_function: TextObjectId,
34 pub inner_function: TextObjectId,
35 pub around_class: TextObjectId,
36 pub inner_class: TextObjectId,
37 pub around_parameter: TextObjectId,
38 pub inner_parameter: TextObjectId,
39 pub around_loop: TextObjectId,
40 pub inner_loop: TextObjectId,
41}
42
43pub fn register_syntax_text_objects(registry: &mut CommandRegistry) -> SyntaxTextObjectIds {
48 SyntaxTextObjectIds {
49 around_function: reg(
50 registry,
51 "text-object:around-function",
52 "A function -- the whole `fn` / `def` / declaration including its \
53 body (vim-grammar `af`). Resolved from the tree-sitter \
54 `@function.outer` capture.",
55 "function.outer",
56 ),
57 inner_function: reg(
58 registry,
59 "text-object:inner-function",
60 "Inner function -- the body block / suite (`if`). \
61 `@function.inner`.",
62 "function.inner",
63 ),
64 around_class: reg(
65 registry,
66 "text-object:around-class",
67 "A class / type -- struct / enum / union / trait / impl / class \
68 including its body (`ac`). `@class.outer`.",
69 "class.outer",
70 ),
71 inner_class: reg(
72 registry,
73 "text-object:inner-class",
74 "Inner class -- the body (field / declaration list) (`ic`). \
75 `@class.inner`.",
76 "class.inner",
77 ),
78 around_parameter: reg(
79 registry,
80 "text-object:around-parameter",
81 "A parameter / argument (`aa`). `@parameter.outer`.",
82 "parameter.outer",
83 ),
84 inner_parameter: reg(
85 registry,
86 "text-object:inner-parameter",
87 "Inner parameter / argument (`ia`). `@parameter.inner`.",
88 "parameter.inner",
89 ),
90 around_loop: reg(
91 registry,
92 "text-object:around-loop",
93 "A loop -- for / while / loop including its body (`al`). \
94 `@loop.outer`.",
95 "loop.outer",
96 ),
97 inner_loop: reg(
98 registry,
99 "text-object:inner-loop",
100 "Inner loop -- the body block / suite (`il`). `@loop.inner`.",
101 "loop.inner",
102 ),
103 }
104}
105
106fn reg(
110 registry: &mut CommandRegistry,
111 name: &str,
112 doc: &str,
113 capture_suffix: &'static str,
114) -> TextObjectId {
115 registry.register_text_object(
116 name,
117 doc,
118 TextObjectSpec {
119 apply: Arc::new(move |ctx| {
120 Ok(ctx
121 .scope_resolver
122 .and_then(|r| r.scope_at(ctx.at.line, ctx.at.byte, capture_suffix))
123 .unwrap_or_else(|| lattice_protocol::position::Range::empty(ctx.at)))
124 }),
125 args_schema: Vec::new(),
126 },
127 )
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133
134 #[test]
135 fn registers_eight_distinct_text_objects() {
136 let mut registry = CommandRegistry::new();
137 let ids = register_syntax_text_objects(&mut registry);
138 let all = [
139 ids.around_function,
140 ids.inner_function,
141 ids.around_class,
142 ids.inner_class,
143 ids.around_parameter,
144 ids.inner_parameter,
145 ids.around_loop,
146 ids.inner_loop,
147 ];
148 let mut seen = std::collections::HashSet::new();
149 for id in all {
150 assert!(seen.insert(id.0), "duplicate text-object id");
151 let spec = registry.lookup(id.0).expect("registered");
152 assert_eq!(spec.kind, lattice_grammar::CommandKind::TextObject);
153 }
154 assert_eq!(seen.len(), 8, "expected 8 distinct structural objects");
155 assert!(registry.id_by_name("text-object:around-function").is_some());
157 assert!(registry.id_by_name("text-object:inner-loop").is_some());
158 }
159}