clang/
completion.rs

1// Copyright 2016 Kyle Mayes
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Code completion.
16
17use std::fmt;
18use std::mem;
19use std::ptr;
20use std::slice;
21use std::cmp::{self, Ordering};
22use std::marker::{PhantomData};
23use std::path::{PathBuf};
24
25use clang_sys::*;
26
27use libc::{c_uint};
28
29use utility;
30use super::{Availability, EntityKind, TranslationUnit, Unsaved, Usr};
31use super::diagnostic::{Diagnostic};
32
33//================================================
34// Enums
35//================================================
36
37// CompletionChunk _______________________________
38
39/// A piece of a code completion string.
40#[derive(Clone, Debug, PartialEq, Eq)]
41pub enum CompletionChunk<'r> {
42    /// A colon (`':'`).
43    Colon,
44    /// A comma (`','`).
45    Comma,
46    /// An equals sign (`'='`).
47    Equals,
48    /// A semicolon (`';'`).
49    Semicolon,
50    /// A left angle bracket (`'<'`).
51    LeftAngleBracket,
52    /// A right angle bracket (`'>'`).
53    RightAngleBracket,
54    /// A left brace (`'{'`).
55    LeftBrace,
56    /// A right brace (`'}'`).
57    RightBrace,
58    /// A left parentesis (`'('`)).
59    LeftParenthesis,
60    /// A right parenthesis (`')'`).
61    RightParenthesis,
62    /// A left square bracket (`'['`).
63    LeftSquareBracket,
64    /// A right square bracket (`']'`).
65    RightSquareBracket,
66    /// Horizontal space (e.g., `' '`).
67    HorizontalSpace(String),
68    /// Vertical space (e.g., `'\n'`).
69    VerticalSpace(String),
70    /// Text that describes the current parameter when code completion was run on a function call,
71    /// message send, or template specialization.
72    CurrentParameter(String),
73    /// Informative text that should be displayed but not inserted as part of the template.
74    Informative(String),
75    /// Text that should be replaced by the user.
76    Placeholder(String),
77    /// Text that specifies the result type of the containing result.
78    ResultType(String),
79    /// Text that should be inserted.
80    Text(String),
81    /// Text that the user would be expected to type to get the containing code completion result.
82    TypedText(String),
83    /// An optional piece that could be part of the template but is not required.
84    Optional(CompletionString<'r>),
85}
86
87impl<'r> CompletionChunk<'r> {
88    //- Accessors --------------------------------
89
90    /// Returns the text associated with this completion chunk if this chunk is not optional.
91    pub fn get_text(&self) -> Option<String> {
92        match *self {
93            CompletionChunk::Colon => Some(":".into()),
94            CompletionChunk::Comma => Some(",".into()),
95            CompletionChunk::Equals => Some("=".into()),
96            CompletionChunk::Semicolon => Some(";".into()),
97            CompletionChunk::LeftAngleBracket => Some("<".into()),
98            CompletionChunk::RightAngleBracket => Some(">".into()),
99            CompletionChunk::LeftBrace => Some("{".into()),
100            CompletionChunk::RightBrace => Some("}".into()),
101            CompletionChunk::LeftParenthesis => Some("(".into()),
102            CompletionChunk::RightParenthesis => Some(")".into()),
103            CompletionChunk::LeftSquareBracket => Some("[".into()),
104            CompletionChunk::RightSquareBracket => Some("]".into()),
105            CompletionChunk::CurrentParameter(ref text) |
106            CompletionChunk::Informative(ref text) |
107            CompletionChunk::Placeholder(ref text) |
108            CompletionChunk::ResultType(ref text) |
109            CompletionChunk::TypedText(ref text) |
110            CompletionChunk::Text(ref text) |
111            CompletionChunk::HorizontalSpace(ref text) |
112            CompletionChunk::VerticalSpace(ref text) => Some(text.clone()),
113            CompletionChunk::Optional(_) => None,
114        }
115    }
116
117    /// Returns whether this completion chunk is optional.
118    pub fn is_optional(&self) -> bool {
119        matches!(*self, CompletionChunk::Optional(_))
120    }
121}
122
123//================================================
124// Structs
125//================================================
126
127// Completer ____________________________________
128
129builder! {
130    /// Runs code completion.
131    builder Completer: CXCodeComplete_Flags {
132        tu: &'tu TranslationUnit<'tu>,
133        file: PathBuf,
134        line: u32,
135        column: u32,
136        unsaved: Vec<Unsaved>;
137    OPTIONS:
138        /// Sets whether macros will be included in code completion results.
139        pub macros: CXCodeComplete_IncludeMacros,
140        /// Sets whether code patterns (e.g., for loops) will be included in code completion
141        /// results.
142        pub code_patterns: CXCodeComplete_IncludeCodePatterns,
143        /// Sets whether documentation comment briefs will be included in code completion results.
144        pub briefs: CXCodeComplete_IncludeBriefComments,
145    }
146}
147
148impl<'tu> Completer<'tu> {
149    //- Constructors -----------------------------
150
151    #[doc(hidden)]
152    pub fn new<F: Into<PathBuf>>(
153        tu: &'tu TranslationUnit<'tu>, file: F, line: u32, column: u32
154    ) -> Completer<'tu> {
155        let file = file.into();
156        let flags = unsafe { clang_defaultCodeCompleteOptions() };
157        Completer { tu, file, line, column, unsaved: vec![], flags }
158    }
159
160    //- Mutators ---------------------------------
161
162    /// Sets the unsaved files to use.
163    pub fn unsaved(&mut self, unsaved: &[Unsaved]) -> &mut Completer<'tu> {
164        self.unsaved = unsaved.into();
165        self
166    }
167
168    //- Accessors --------------------------------
169
170    /// Runs code completion.
171    pub fn complete(&self) -> CompletionResults {
172        unsafe {
173            let ptr = clang_codeCompleteAt(
174                self.tu.ptr,
175                utility::from_path(&self.file).as_ptr(),
176                self.line as c_uint,
177                self.column as c_uint,
178                self.unsaved.as_ptr() as *mut CXUnsavedFile,
179                self.unsaved.len() as c_uint,
180                self.flags,
181            );
182            CompletionResults::from_ptr(ptr)
183        }
184    }
185}
186
187// CompletionContext _____________________________
188
189options! {
190    /// Indicates which types of results were included in a set of code completion results.
191    options CompletionContext: CXCompletionContext {
192        /// Indicates whether all possible types were included.
193        pub all_types: CXCompletionContext_AnyType,
194        /// Indicates whether all possible values were included.
195        pub all_values: CXCompletionContext_AnyValue,
196        /// Indicates whether values that resolve to C++ class types were included.
197        pub class_type_values: CXCompletionContext_CXXClassTypeValue,
198        /// Indicates whether the members of a record that are accessed with the dot operator were
199        /// included.
200        pub dot_members: CXCompletionContext_DotMemberAccess,
201        /// Indicates whether the members of a record that are accessed with the arrow operator were
202        /// included.
203        pub arrow_members: CXCompletionContext_ArrowMemberAccess,
204        /// Indicates whether enum tags were included.
205        pub enum_tags: CXCompletionContext_EnumTag,
206        /// Indicates whether union tags were included.
207        pub union_tags: CXCompletionContext_UnionTag,
208        /// Indicates whether struct tags were included.
209        pub struct_tags: CXCompletionContext_StructTag,
210        /// Indicates whether C++ class names were included.
211        pub class_names: CXCompletionContext_ClassTag,
212        /// Indicates whether C++ namespaces and namespace aliases were included.
213        pub namespaces: CXCompletionContext_Namespace,
214        /// Indicates whether C++ nested name specifiers were included.
215        pub nested_name_specifiers: CXCompletionContext_NestedNameSpecifier,
216        /// Indicates whether macro names were included.
217        pub macro_names: CXCompletionContext_MacroName,
218        /// Indicates whether natural language results were included.
219        pub natural_language: CXCompletionContext_NaturalLanguage,
220        /// Indicates whether values that resolve to Objective-C objects were included.
221        pub objc_object_values: CXCompletionContext_ObjCObjectValue,
222        /// Indicates whether values that resolve to Objective-C selectors were included.
223        pub objc_selector_values: CXCompletionContext_ObjCSelectorValue,
224        /// Indicates whether the properties of an Objective-C object that are accessed with the dot
225        /// operator were included.
226        pub objc_property_members: CXCompletionContext_ObjCPropertyAccess,
227        /// Indicates whether Objective-C interfaces were included.
228        pub objc_interfaces: CXCompletionContext_ObjCInterface,
229        /// Indicates whether Objective-C protocols were included.
230        pub objc_protocols: CXCompletionContext_ObjCProtocol,
231        /// Indicates whether Objective-C categories were included.
232        pub objc_categories: CXCompletionContext_ObjCCategory,
233        /// Indicates whether Objective-C instance messages were included.
234        pub objc_instance_messages: CXCompletionContext_ObjCInstanceMessage,
235        /// Indicates whether Objective-C class messages were included.
236        pub objc_class_messages: CXCompletionContext_ObjCClassMessage,
237        /// Indicates whether Objective-C selector names were included.
238        pub objc_selector_names: CXCompletionContext_ObjCSelectorName,
239    }
240}
241
242// CompletionResult ______________________________
243
244/// A code completion result.
245#[derive(Copy, Clone, Debug, PartialEq, Eq)]
246pub struct CompletionResult<'r> {
247    /// The categorization of the AST entity this code completion result produces.
248    pub kind: EntityKind,
249    /// The completion string for this code completion result.
250    pub string: CompletionString<'r>,
251}
252
253impl<'r> CompletionResult<'r> {
254    //- Constructors -----------------------------
255
256    fn from_raw(raw: CXCompletionResult) -> CompletionResult<'r> {
257        let kind = unsafe { mem::transmute(raw.CursorKind) };
258        CompletionResult { kind, string: CompletionString::from_ptr(raw.CompletionString) }
259    }
260}
261
262impl<'r> cmp::PartialOrd for CompletionResult<'r> {
263    fn partial_cmp(&self, other: &CompletionResult<'r>) -> Option<Ordering> {
264        Some(self.cmp(other))
265    }
266}
267
268impl<'r> cmp::Ord for CompletionResult<'r> {
269    fn cmp(&self, other: &CompletionResult<'r>) -> Ordering {
270        self.string.cmp(&other.string)
271    }
272}
273
274// CompletionResults _____________________________
275
276/// A set of code completion results.
277pub struct CompletionResults {
278    ptr: *mut CXCodeCompleteResults,
279}
280
281impl CompletionResults {
282    //- Constructors -----------------------------
283
284    fn from_ptr(ptr: *mut CXCodeCompleteResults) -> CompletionResults {
285        assert!(!ptr.is_null());
286        CompletionResults { ptr }
287    }
288
289    //- Accessors --------------------------------
290
291    /// Returns the diagnostics that were produced prior to the code completion context for this set
292    /// of code completion results.
293    pub fn get_diagnostics<'tu>(&self, tu: &'tu TranslationUnit<'tu>) -> Vec<Diagnostic<'tu>> {
294        iter!(
295            clang_codeCompleteGetNumDiagnostics(self.ptr),
296            clang_codeCompleteGetDiagnostic(self.ptr),
297        ).map(|d| Diagnostic::from_ptr(d, tu)).collect()
298    }
299
300    /// Returns the code completion context for this set of code completion results, if any.
301    pub fn get_context(&self) -> Option<CompletionContext> {
302        let contexts = unsafe { clang_codeCompleteGetContexts(self.ptr) as CXCompletionContext };
303        if contexts != 0 && contexts != CXCompletionContext_Unknown {
304            Some(CompletionContext::from(contexts))
305        } else {
306            None
307        }
308    }
309
310    /// Returns the categorization of the entity that contains the code completion context for this
311    /// set of code completion results and whether that entity is incomplete, if applicable.
312    pub fn get_container_kind(&self) -> Option<(EntityKind, bool)> {
313        unsafe {
314            let mut incomplete = mem::MaybeUninit::uninit();
315            match clang_codeCompleteGetContainerKind(self.ptr, incomplete.as_mut_ptr()) {
316                CXCursor_InvalidCode => None,
317                other => Some((mem::transmute(other), incomplete.assume_init() != 0)),
318            }
319        }
320    }
321
322    /// Returns the selector or partial selector that has been entered this far for the Objective-C
323    /// message send context for this set of code completion results.
324    pub fn get_objc_selector(&self) -> Option<String> {
325        unsafe { utility::to_string_option(clang_codeCompleteGetObjCSelector(self.ptr)) }
326    }
327
328    /// Returns the USR for the entity that contains the code completion context for this set of
329    /// code completion results, if applicable.
330    pub fn get_usr(&self) -> Option<Usr> {
331        unsafe { utility::to_string_option(clang_codeCompleteGetContainerUSR(self.ptr)).map(Usr) }
332    }
333
334    /// Returns the code completion results in this set of code completion results.
335    pub fn get_results(&self) -> Vec<CompletionResult> {
336        unsafe {
337            let raws = slice::from_raw_parts((*self.ptr).Results, (*self.ptr).NumResults as usize);
338            raws.iter().cloned().map(CompletionResult::from_raw).collect()
339        }
340    }
341}
342
343impl Drop for CompletionResults {
344    fn drop(&mut self) {
345        unsafe { clang_disposeCodeCompleteResults(self.ptr); }
346    }
347}
348
349impl fmt::Debug for CompletionResults {
350    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
351        formatter.debug_struct("CompletionResults")
352            .field("results", &self.get_results())
353            .finish()
354    }
355}
356
357// CompletionString ______________________________
358
359/// A semantic string that describes a code completion result.
360#[derive(Copy, Clone)]
361pub struct CompletionString<'r> {
362    ptr: CXCompletionString,
363    _marker: PhantomData<&'r CompletionResults>
364}
365
366impl<'r> CompletionString<'r> {
367    //- Constructors -----------------------------
368
369    #[doc(hidden)]
370    pub fn from_ptr(ptr: CXCompletionString) -> CompletionString<'r> {
371        assert!(!ptr.is_null());
372        CompletionString { ptr, _marker: PhantomData }
373    }
374
375    //- Accessors --------------------------------
376
377    /// Returns an integer that represents how likely a user is to select this completion string as
378    /// determined by internal heuristics. Smaller values indicate higher priorities.
379    pub fn get_priority(&self) -> usize {
380        unsafe { clang_getCompletionPriority(self.ptr) as usize }
381    }
382
383    /// Returns the annotations associated with this completion string.
384    pub fn get_annotations(&self) -> Vec<String> {
385        iter!(
386            clang_getCompletionNumAnnotations(self.ptr),
387            clang_getCompletionAnnotation(self.ptr),
388        ).map(utility::to_string).collect()
389    }
390
391    /// Returns the availability of this completion string.
392    pub fn get_availability(&self) -> Availability {
393        unsafe { mem::transmute(clang_getCompletionAvailability(self.ptr)) }
394    }
395
396    /// Returns the documentation comment brief associated with the declaration this completion
397    /// string refers to, if applicable.
398    pub fn get_comment_brief(&self) -> Option<String> {
399        unsafe { utility::to_string_option(clang_getCompletionBriefComment(self.ptr)) }
400    }
401
402    /// Returns the name of the semantic parent of the declaration this completion string refers to,
403    /// if applicable.
404    pub fn get_parent_name(&self) -> Option<String> {
405        unsafe { utility::to_string_option(clang_getCompletionParent(self.ptr, ptr::null_mut())) }
406    }
407
408    /// Returns the text of the typed text chunk for this completion string, if any.
409    pub fn get_typed_text(&self) -> Option<String> {
410        for chunk in self.get_chunks() {
411            if let CompletionChunk::TypedText(text) = chunk {
412                return Some(text);
413            }
414        }
415        None
416    }
417
418    /// Returns the chunks of this completion string.
419    pub fn get_chunks(&self) -> Vec<CompletionChunk> {
420        iter!(
421            clang_getNumCompletionChunks(self.ptr),
422            clang_getCompletionChunkKind(self.ptr),
423        ).enumerate().map(|(i, k)| {
424            macro_rules! text {
425                ($variant:ident) => ({
426                    let text = unsafe { clang_getCompletionChunkText(self.ptr, i as c_uint) };
427                    CompletionChunk::$variant(utility::to_string(text))
428                });
429            }
430
431            match k {
432                CXCompletionChunk_Colon => CompletionChunk::Colon,
433                CXCompletionChunk_Comma => CompletionChunk::Comma,
434                CXCompletionChunk_Equal => CompletionChunk::Equals,
435                CXCompletionChunk_SemiColon => CompletionChunk::Semicolon,
436                CXCompletionChunk_LeftAngle => CompletionChunk::LeftAngleBracket,
437                CXCompletionChunk_RightAngle => CompletionChunk::RightAngleBracket,
438                CXCompletionChunk_LeftBrace => CompletionChunk::LeftBrace,
439                CXCompletionChunk_RightBrace => CompletionChunk::RightBrace,
440                CXCompletionChunk_LeftParen => CompletionChunk::LeftParenthesis,
441                CXCompletionChunk_RightParen => CompletionChunk::RightParenthesis,
442                CXCompletionChunk_LeftBracket => CompletionChunk::LeftSquareBracket,
443                CXCompletionChunk_RightBracket => CompletionChunk::RightSquareBracket,
444                CXCompletionChunk_HorizontalSpace => text!(HorizontalSpace),
445                CXCompletionChunk_VerticalSpace => text!(VerticalSpace),
446                CXCompletionChunk_CurrentParameter => text!(CurrentParameter),
447                CXCompletionChunk_TypedText => text!(TypedText),
448                CXCompletionChunk_Text => text!(Text),
449                CXCompletionChunk_Placeholder => text!(Placeholder),
450                CXCompletionChunk_Informative => text!(Informative),
451                CXCompletionChunk_ResultType => text!(ResultType),
452                CXCompletionChunk_Optional => {
453                    let i = i as c_uint;
454                    let ptr = unsafe { clang_getCompletionChunkCompletionString(self.ptr, i) };
455                    CompletionChunk::Optional(CompletionString::from_ptr(ptr))
456                },
457                _ => panic!("unexpected completion chunk kind: {:?}", k),
458            }
459        }).collect()
460    }
461}
462
463impl<'r> fmt::Debug for CompletionString<'r> {
464    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
465        formatter.debug_struct("CompletionString")
466            .field("chunks", &self.get_chunks())
467            .finish()
468    }
469}
470
471impl<'r> cmp::PartialEq for CompletionString<'r> {
472    fn eq(&self, other: &CompletionString<'r>) -> bool {
473        self.get_chunks() == other.get_chunks()
474    }
475}
476
477impl<'r> cmp::Eq for CompletionString<'r> { }
478
479impl<'r> cmp::PartialOrd for CompletionString<'r> {
480    fn partial_cmp(&self, other: &CompletionString<'r>) -> Option<Ordering> {
481        Some(self.cmp(other))
482    }
483}
484
485impl<'r> cmp::Ord for CompletionString<'r> {
486    fn cmp(&self, other: &CompletionString<'r>) -> Ordering {
487        match self.get_priority().cmp(&other.get_priority()) {
488            Ordering::Equal => self.get_typed_text().cmp(&other.get_typed_text()),
489            other => other,
490        }
491    }
492}