/ src / languages / cpp.rs
cpp.rs
  1  use crate::languages::LanguageSupport;
  2  use std::sync::OnceLock;
  3  use tracing::error;
  4  use tree_sitter::{Language, Query};
  5  
  6  pub struct Cpp;
  7  
  8  static REFERENCE_QUERY: OnceLock<Query> = OnceLock::new();
  9  static BINDING_QUERY: OnceLock<Query> = OnceLock::new();
 10  static IMPORT_QUERY: OnceLock<Query> = OnceLock::new();
 11  static COMPLETION_QUERY: OnceLock<Query> = OnceLock::new();
 12  static REASSIGNMENT_QUERY: OnceLock<Query> = OnceLock::new();
 13  static IDENTIFIER_QUERY: OnceLock<Query> = OnceLock::new();
 14  static EXPORT_QUERY: OnceLock<Query> = OnceLock::new();
 15  static ASSIGNMENT_QUERY: OnceLock<Query> = OnceLock::new();
 16  static DESTRUCTURE_QUERY: OnceLock<Query> = OnceLock::new();
 17  static SCOPE_QUERY: OnceLock<Query> = OnceLock::new();
 18  
 19  fn compile_query(grammar: &Language, source: &str, query_name: &str) -> Query {
 20      match Query::new(grammar, source) {
 21          Ok(query) => query,
 22          Err(e) => {
 23              error!(
 24                  language = "cpp",
 25                  query = query_name,
 26                  error = %e,
 27                  "Failed to compile query, failing fast"
 28              );
 29              panic!("Failed to compile query '{}': {}", query_name, e)
 30          }
 31      }
 32  }
 33  
 34  impl LanguageSupport for Cpp {
 35      fn id(&self) -> &'static str {
 36          "cpp"
 37      }
 38  
 39      fn extensions(&self) -> &'static [&'static str] {
 40          &["cpp", "cc", "cxx", "hpp", "hh", "hxx"]
 41      }
 42  
 43      fn language_ids(&self) -> &'static [&'static str] {
 44          &["cpp", "c++"]
 45      }
 46  
 47      fn grammar(&self) -> Language {
 48          tree_sitter_cpp::LANGUAGE.into()
 49      }
 50  
 51      fn reference_query(&self) -> &Query {
 52          REFERENCE_QUERY.get_or_init(|| {
 53              compile_query(
 54                  &self.grammar(),
 55                  include_str!("../../queries/cpp/references.scm"),
 56                  "references",
 57              )
 58          })
 59      }
 60  
 61      fn binding_query(&self) -> Option<&Query> {
 62          Some(BINDING_QUERY.get_or_init(|| {
 63              compile_query(
 64                  &self.grammar(),
 65                  include_str!("../../queries/cpp/bindings.scm"),
 66                  "bindings",
 67              )
 68          }))
 69      }
 70  
 71      fn import_query(&self) -> Option<&Query> {
 72          Some(IMPORT_QUERY.get_or_init(|| {
 73              compile_query(
 74                  &self.grammar(),
 75                  include_str!("../../queries/cpp/imports.scm"),
 76                  "imports",
 77              )
 78          }))
 79      }
 80  
 81      fn completion_query(&self) -> Option<&Query> {
 82          Some(COMPLETION_QUERY.get_or_init(|| {
 83              compile_query(
 84                  &self.grammar(),
 85                  include_str!("../../queries/cpp/completion.scm"),
 86                  "completion",
 87              )
 88          }))
 89      }
 90  
 91      fn reassignment_query(&self) -> Option<&Query> {
 92          Some(REASSIGNMENT_QUERY.get_or_init(|| {
 93              compile_query(
 94                  &self.grammar(),
 95                  include_str!("../../queries/cpp/reassignments.scm"),
 96                  "reassignments",
 97              )
 98          }))
 99      }
100  
101      fn identifier_query(&self) -> Option<&Query> {
102          Some(IDENTIFIER_QUERY.get_or_init(|| {
103              compile_query(
104                  &self.grammar(),
105                  include_str!("../../queries/cpp/identifiers.scm"),
106                  "identifiers",
107              )
108          }))
109      }
110  
111      fn export_query(&self) -> Option<&Query> {
112          Some(EXPORT_QUERY.get_or_init(|| {
113              compile_query(
114                  &self.grammar(),
115                  include_str!("../../queries/cpp/exports.scm"),
116                  "exports",
117              )
118          }))
119      }
120  
121      fn assignment_query(&self) -> Option<&Query> {
122          Some(ASSIGNMENT_QUERY.get_or_init(|| {
123              compile_query(
124                  &self.grammar(),
125                  include_str!("../../queries/cpp/assignments.scm"),
126                  "assignments",
127              )
128          }))
129      }
130  
131      fn destructure_query(&self) -> Option<&Query> {
132          Some(DESTRUCTURE_QUERY.get_or_init(|| {
133              compile_query(
134                  &self.grammar(),
135                  include_str!("../../queries/cpp/destructures.scm"),
136                  "destructures",
137              )
138          }))
139      }
140  
141      fn scope_query(&self) -> Option<&Query> {
142          Some(SCOPE_QUERY.get_or_init(|| {
143              compile_query(
144                  &self.grammar(),
145                  include_str!("../../queries/cpp/scopes.scm"),
146                  "scopes",
147              )
148          }))
149      }
150  
151      fn completion_trigger_characters(&self) -> &'static [&'static str] {
152          &["(\"", "('"]
153      }
154  
155      fn is_standard_env_object(&self, name: &str) -> bool {
156          matches!(name, "getenv" | "secure_getenv" | "std")
157      }
158  
159      fn comment_node_kinds(&self) -> &'static [&'static str] {
160          &["comment"]
161      }
162  
163      fn is_scope_node(&self, node: tree_sitter::Node) -> bool {
164          matches!(
165              node.kind(),
166              "function_definition"
167                  | "compound_statement"
168                  | "for_statement"
169                  | "for_range_loop"
170                  | "if_statement"
171                  | "while_statement"
172                  | "do_statement"
173                  | "switch_statement"
174                  | "class_specifier"
175                  | "namespace_definition"
176                  | "lambda_expression"
177                  | "try_statement"
178          )
179      }
180  }
181  
182  #[cfg(test)]
183  mod tests {
184      use super::*;
185  
186      fn get_cpp() -> Cpp {
187          Cpp
188      }
189  
190      #[test]
191      fn test_id() {
192          assert_eq!(get_cpp().id(), "cpp");
193      }
194  
195      #[test]
196      fn test_extensions() {
197          let exts = get_cpp().extensions();
198          assert!(exts.contains(&"cpp"));
199          assert!(exts.contains(&"hpp"));
200      }
201  
202      #[test]
203      fn test_language_ids() {
204          let ids = get_cpp().language_ids();
205          assert!(ids.contains(&"cpp"));
206      }
207  
208      #[test]
209      fn test_grammar_compiles() {
210          let cpp = get_cpp();
211          let _grammar = cpp.grammar();
212      }
213  
214      #[test]
215      fn test_reference_query_compiles() {
216          let cpp = get_cpp();
217          let _query = cpp.reference_query();
218      }
219  
220      #[test]
221      fn test_binding_query_compiles() {
222          let cpp = get_cpp();
223          assert!(cpp.binding_query().is_some());
224      }
225  
226      #[test]
227      fn test_import_query_compiles() {
228          let cpp = get_cpp();
229          assert!(cpp.import_query().is_some());
230      }
231  
232      #[test]
233      fn test_completion_query_compiles() {
234          let cpp = get_cpp();
235          assert!(cpp.completion_query().is_some());
236      }
237  
238      #[test]
239      fn test_reassignment_query_compiles() {
240          let cpp = get_cpp();
241          assert!(cpp.reassignment_query().is_some());
242      }
243  
244      #[test]
245      fn test_identifier_query_compiles() {
246          let cpp = get_cpp();
247          assert!(cpp.identifier_query().is_some());
248      }
249  
250      #[test]
251      fn test_export_query_compiles() {
252          let cpp = get_cpp();
253          assert!(cpp.export_query().is_some());
254      }
255  
256      #[test]
257      fn test_assignment_query_compiles() {
258          let cpp = get_cpp();
259          assert!(cpp.assignment_query().is_some());
260      }
261  
262      #[test]
263      fn test_scope_query_compiles() {
264          let cpp = get_cpp();
265          assert!(cpp.scope_query().is_some());
266      }
267  
268      #[test]
269      fn test_destructure_query_compiles() {
270          let cpp = get_cpp();
271          assert!(cpp.destructure_query().is_some());
272      }
273  }