hassle_rs/intellisense/
wrapper.rs

1use com::Interface;
2
3use crate::intellisense::ffi::*;
4use crate::os::{CoTaskMemFree, BSTR, LPSTR};
5use crate::utils::Result;
6use crate::wrapper::Dxc;
7use std::ffi::CString;
8use std::mem::ManuallyDrop;
9
10pub struct DxcIntellisense {
11    inner: IDxcIntelliSense,
12}
13
14impl DxcIntellisense {
15    fn new(inner: IDxcIntelliSense) -> Self {
16        Self { inner }
17    }
18
19    pub fn get_default_editing_tu_options(&self) -> Result<DxcTranslationUnitFlags> {
20        let mut options: DxcTranslationUnitFlags = DxcTranslationUnitFlags::NONE;
21        unsafe { self.inner.get_default_editing_tu_options(&mut options) }
22            .result_with_success(options)
23    }
24
25    pub fn create_index(&self) -> Result<DxcIndex> {
26        let mut index = None;
27        unsafe { self.inner.create_index(&mut index) }.result()?;
28        Ok(DxcIndex::new(index.unwrap()))
29    }
30
31    pub fn create_unsaved_file(&self, file_name: &str, contents: &str) -> Result<DxcUnsavedFile> {
32        let c_file_name = CString::new(file_name).expect("Failed to convert `file_name`");
33        let c_contents = CString::new(contents).expect("Failed to convert `contents`");
34
35        let mut file = None;
36        unsafe {
37            self.inner.create_unsaved_file(
38                c_file_name.as_ptr(),
39                c_contents.as_ptr(),
40                contents.len() as u32,
41                &mut file,
42            )
43        }
44        .result()?;
45        Ok(DxcUnsavedFile::new(file.unwrap()))
46    }
47}
48
49pub struct DxcIndex {
50    inner: IDxcIndex,
51}
52
53impl DxcIndex {
54    fn new(inner: IDxcIndex) -> Self {
55        Self { inner }
56    }
57}
58
59impl DxcIndex {
60    pub fn parse_translation_unit(
61        &self,
62        source_filename: &str,
63        args: &[&str],
64        unsaved_files: &[&DxcUnsavedFile],
65        options: DxcTranslationUnitFlags,
66    ) -> Result<DxcTranslationUnit> {
67        let c_source_filename =
68            CString::new(source_filename).expect("Failed to convert `source_filename`");
69
70        let uf = unsaved_files
71            .iter()
72            .map(|unsaved_file| unsaved_file.inner.clone())
73            .collect::<Vec<_>>();
74
75        let mut c_args: Vec<CString> = vec![];
76        let mut cliargs = vec![];
77
78        for arg in args.iter() {
79            let c_arg = CString::new(*arg).expect("Failed to convert `arg`");
80            cliargs.push(c_arg.as_ptr().cast());
81            c_args.push(c_arg);
82        }
83
84        let mut tu = None;
85
86        unsafe {
87            self.inner.parse_translation_unit(
88                c_source_filename.as_ptr().cast(),
89                cliargs.as_ptr(),
90                cliargs.len() as i32,
91                uf.as_ptr(),
92                uf.len() as u32,
93                options,
94                &mut tu,
95            )
96        }
97        .result()?;
98        Ok(DxcTranslationUnit::new(tu.unwrap()))
99    }
100}
101
102pub struct DxcUnsavedFile {
103    inner: IDxcUnsavedFile,
104}
105
106impl DxcUnsavedFile {
107    pub fn get_length(&self) -> Result<u32> {
108        let mut length: u32 = 0;
109        unsafe { self.inner.get_length(&mut length) }.result_with_success(length)
110    }
111
112    fn new(inner: IDxcUnsavedFile) -> Self {
113        DxcUnsavedFile { inner }
114    }
115}
116
117pub struct DxcTranslationUnit {
118    inner: IDxcTranslationUnit,
119}
120
121impl DxcTranslationUnit {
122    fn new(inner: IDxcTranslationUnit) -> Self {
123        DxcTranslationUnit { inner }
124    }
125
126    pub fn get_file(&self, name: &[u8]) -> Result<DxcFile> {
127        let mut file = None;
128        unsafe { self.inner.get_file(name.as_ptr(), &mut file) }.result()?;
129        Ok(DxcFile::new(file.unwrap()))
130    }
131
132    pub fn get_cursor(&self) -> Result<DxcCursor> {
133        let mut cursor = None;
134        unsafe { self.inner.get_cursor(&mut cursor) }.result()?;
135        Ok(DxcCursor::new(cursor.unwrap()))
136    }
137}
138
139pub struct DxcCursor {
140    inner: IDxcCursor,
141}
142
143impl DxcCursor {
144    fn new(inner: IDxcCursor) -> Self {
145        DxcCursor { inner }
146    }
147
148    pub fn get_children(&self, skip: u32, max_count: u32) -> Result<Vec<DxcCursor>> {
149        let mut result: *mut IDxcCursor = std::ptr::null_mut();
150        let mut result_length: u32 = 0;
151
152        unsafe {
153            self.inner
154                .get_children(skip, max_count, &mut result_length, &mut result)
155        }
156        .result()?;
157
158        // get_children allocates a buffer to pass the result in.
159        // Create a vector so that we get ownership of the `IDxcCursor(s) (received from get_children), instead of
160        // having to clone (copy is intentionally not implemented) them and leaving unowned COM references alive.
161        // It is wrapped in ManuallyDrop to free the underlying pointer by hand using CoTaskMemFree.
162        // TODO: switch to Vec::from_raw_parts_in with custom deallocator when this is stabilized
163        let child_cursors = ManuallyDrop::new(unsafe {
164            Vec::from_raw_parts(result, result_length as usize, result_length as usize)
165        })
166        .drain(..)
167        .map(DxcCursor::new)
168        .collect::<Vec<_>>();
169
170        unsafe { CoTaskMemFree(result.cast()) };
171        Ok(child_cursors)
172    }
173
174    pub fn get_all_children(&self) -> Result<Vec<DxcCursor>> {
175        const MAX_CHILDREN_PER_CHUNK: u32 = 10;
176        let mut children = vec![];
177
178        loop {
179            let res = self.get_children(children.len() as u32, MAX_CHILDREN_PER_CHUNK)?;
180            let res_len = res.len();
181            children.extend(res);
182            if res_len < MAX_CHILDREN_PER_CHUNK as usize {
183                break Ok(children);
184            }
185        }
186    }
187
188    pub fn get_extent(&self) -> Result<DxcSourceRange> {
189        let mut range = None;
190        unsafe { self.inner.get_extent(&mut range) }.result()?;
191        Ok(DxcSourceRange::new(range.unwrap()))
192    }
193
194    pub fn get_location(&self) -> Result<DxcSourceLocation> {
195        let mut location = None;
196        unsafe { self.inner.get_location(&mut location) }.result()?;
197        Ok(DxcSourceLocation::new(location.unwrap()))
198    }
199
200    pub fn get_display_name(&self) -> Result<String> {
201        let mut name: BSTR = std::ptr::null_mut();
202        unsafe { self.inner.get_display_name(&mut name) }.result()?;
203        Ok(crate::utils::from_bstr(name))
204    }
205
206    pub fn get_formatted_name(&self, formatting: DxcCursorFormatting) -> Result<String> {
207        let mut name: BSTR = std::ptr::null_mut();
208        unsafe { self.inner.get_formatted_name(formatting, &mut name) }.result()?;
209        Ok(crate::utils::from_bstr(name))
210    }
211
212    pub fn get_qualified_name(&self, include_template_args: bool) -> Result<String> {
213        let mut name: BSTR = std::ptr::null_mut();
214        unsafe {
215            self.inner
216                .get_qualified_name(include_template_args, &mut name)
217        }
218        .result()?;
219        Ok(crate::utils::from_bstr(name))
220    }
221
222    pub fn get_kind(&self) -> Result<DxcCursorKind> {
223        let mut cursor_kind: DxcCursorKind = DxcCursorKind::UNEXPOSED_DECL;
224        unsafe { self.inner.get_kind(&mut cursor_kind) }.result_with_success(cursor_kind)
225    }
226
227    pub fn get_kind_flags(&self) -> Result<DxcCursorKindFlags> {
228        let mut cursor_kind_flags: DxcCursorKindFlags = DxcCursorKindFlags::NONE;
229        unsafe { self.inner.get_kind_flags(&mut cursor_kind_flags) }
230            .result_with_success(cursor_kind_flags)
231    }
232
233    pub fn get_semantic_parent(&self) -> Result<DxcCursor> {
234        let mut inner = None;
235        unsafe { self.inner.get_semantic_parent(&mut inner) }.result()?;
236        Ok(DxcCursor::new(inner.unwrap()))
237    }
238
239    pub fn get_lexical_parent(&self) -> Result<DxcCursor> {
240        let mut inner = None;
241        unsafe { self.inner.get_lexical_parent(&mut inner) }.result()?;
242        Ok(DxcCursor::new(inner.unwrap()))
243    }
244
245    pub fn get_cursor_type(&self) -> Result<DxcType> {
246        let mut inner = None;
247        unsafe { self.inner.get_cursor_type(&mut inner) }.result()?;
248        Ok(DxcType::new(inner.unwrap()))
249    }
250
251    pub fn get_num_arguments(&self) -> Result<i32> {
252        let mut result: i32 = 0;
253
254        unsafe { self.inner.get_num_arguments(&mut result) }.result_with_success(result)
255    }
256
257    pub fn get_argument_at(&self, index: i32) -> Result<DxcCursor> {
258        let mut inner = None;
259        unsafe { self.inner.get_argument_at(index, &mut inner) }.result()?;
260        Ok(DxcCursor::new(inner.unwrap()))
261    }
262
263    pub fn get_referenced_cursor(&self) -> Result<DxcCursor> {
264        let mut inner = None;
265        unsafe { self.inner.get_referenced_cursor(&mut inner) }.result()?;
266        Ok(DxcCursor::new(inner.unwrap()))
267    }
268
269    pub fn get_definition_cursor(&self) -> Result<DxcCursor> {
270        let mut inner = None;
271        unsafe { self.inner.get_definition_cursor(&mut inner) }.result()?;
272        Ok(DxcCursor::new(inner.unwrap()))
273    }
274
275    pub fn find_references_in_file(
276        &self,
277        file: &DxcFile,
278        skip: u32,
279        top: u32,
280    ) -> Result<Vec<DxcCursor>> {
281        let mut result: *mut IDxcCursor = std::ptr::null_mut();
282        let mut result_length: u32 = 0;
283
284        unsafe {
285            self.inner.find_references_in_file(
286                &file.inner,
287                skip,
288                top,
289                &mut result_length,
290                &mut result,
291            )
292        }
293        .result()?;
294
295        // find_references_in_file allocates a buffer to pass the result in.
296        // Create a vector so that we get ownership of the `IDxcCursor(s) (received from find_references_in_file), instead
297        // of having to clone (copy is intentionally not implemented) them and leaving unowned COM references alive.
298        // It is wrapped in ManuallyDrop to free the underlying pointer by hand using CoTaskMemFree.
299        // TODO: switch to Vec::from_raw_parts_in with custom deallocator when this is stabilized
300        let child_cursors = ManuallyDrop::new(unsafe {
301            Vec::from_raw_parts(result, result_length as usize, result_length as usize)
302        })
303        .drain(..)
304        .map(DxcCursor::new)
305        .collect::<Vec<_>>();
306
307        unsafe { CoTaskMemFree(result.cast()) };
308        Ok(child_cursors)
309    }
310
311    pub fn get_spelling(&self) -> Result<String> {
312        let mut spelling: LPSTR = std::ptr::null_mut();
313        unsafe { self.inner.get_spelling(&mut spelling) }.result()?;
314        Ok(crate::utils::from_lpstr(spelling))
315    }
316
317    pub fn is_equal_to(&self, other: &DxcCursor) -> Result<bool> {
318        let mut result: bool = false;
319        unsafe { self.inner.is_equal_to(&other.inner, &mut result) }.result_with_success(result)
320    }
321
322    pub fn is_null(&mut self) -> Result<bool> {
323        let mut result: bool = false;
324        unsafe { IDxcCursor::is_null(&self.inner, &mut result) }.result_with_success(result)
325    }
326
327    pub fn is_definition(&self) -> Result<bool> {
328        let mut result: bool = false;
329        unsafe { self.inner.is_definition(&mut result) }.result_with_success(result)
330    }
331
332    pub fn get_snapped_child(&self, location: &DxcSourceLocation) -> Result<DxcCursor> {
333        let mut inner = None;
334        unsafe { self.inner.get_snapped_child(&location.inner, &mut inner) }.result()?;
335        Ok(DxcCursor::new(inner.unwrap()))
336    }
337
338    pub fn get_source<'a>(&self, source: &'a str) -> Result<&'a str> {
339        let range = self.get_extent()?;
340
341        let DxcSourceOffsets {
342            start_offset,
343            end_offset,
344        } = range.get_offsets()?;
345
346        let source_range = (start_offset as usize)..(end_offset as usize);
347
348        Ok(&source[source_range])
349    }
350}
351
352pub struct DxcType {
353    inner: IDxcType,
354}
355
356impl DxcType {
357    fn new(inner: IDxcType) -> Self {
358        DxcType { inner }
359    }
360
361    pub fn get_spelling(&self) -> Result<String> {
362        let mut spelling: LPSTR = std::ptr::null_mut();
363        unsafe { self.inner.get_spelling(&mut spelling) }
364            .result_with_success(crate::utils::from_lpstr(spelling))
365    }
366}
367
368pub struct DxcSourceLocation {
369    inner: IDxcSourceLocation,
370}
371
372impl std::fmt::Debug for DxcSourceLocation {
373    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
374        f.debug_struct("DxcSourceLocation")
375            .field("inner", &self.inner)
376            .finish()
377    }
378}
379
380impl DxcSourceLocation {
381    fn new(inner: IDxcSourceLocation) -> Self {
382        DxcSourceLocation { inner }
383    }
384}
385
386#[derive(Debug)]
387pub struct DxcSourceOffsets {
388    pub start_offset: u32,
389    pub end_offset: u32,
390}
391
392pub struct DxcSourceRange {
393    inner: IDxcSourceRange,
394}
395
396impl std::fmt::Debug for DxcSourceRange {
397    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
398        f.debug_struct("DxcSourceRange")
399            .field("inner", &self.inner)
400            .finish()
401    }
402}
403
404impl DxcSourceRange {
405    pub fn get_offsets(&self) -> Result<DxcSourceOffsets> {
406        let mut start_offset: u32 = 0;
407        let mut end_offset: u32 = 0;
408        unsafe { self.inner.get_offsets(&mut start_offset, &mut end_offset) }.result_with_success(
409            DxcSourceOffsets {
410                start_offset,
411                end_offset,
412            },
413        )
414    }
415}
416
417impl DxcSourceRange {
418    fn new(inner: IDxcSourceRange) -> Self {
419        DxcSourceRange { inner }
420    }
421}
422
423pub struct DxcFile {
424    inner: IDxcFile,
425}
426
427impl DxcFile {
428    fn new(inner: IDxcFile) -> Self {
429        DxcFile { inner }
430    }
431}
432
433impl Dxc {
434    pub fn create_intellisense(&self) -> Result<DxcIntellisense> {
435        let mut intellisense = None;
436
437        self.get_dxc_create_instance()?(
438            &CLSID_DxcIntelliSense,
439            &IDxcIntelliSense::IID,
440            &mut intellisense,
441        )
442        .result()?;
443        Ok(DxcIntellisense::new(intellisense.unwrap()))
444    }
445}