use std::fmt;
use std::mem;
use std::ptr;
use std::slice;
use std::cmp::{self, Ordering};
use std::marker::{PhantomData};
use std::path::{PathBuf};
use clang_sys::*;
use libc::{c_uint};
use utility;
use super::{Availability, EntityKind, TranslationUnit, Unsaved, Usr};
use super::diagnostic::{Diagnostic};
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum CompletionChunk<'r> {
Colon,
Comma,
Equals,
Semicolon,
LeftAngleBracket,
RightAngleBracket,
LeftBrace,
RightBrace,
LeftParenthesis,
RightParenthesis,
LeftSquareBracket,
RightSquareBracket,
HorizontalSpace(String),
VerticalSpace(String),
CurrentParameter(String),
Informative(String),
Placeholder(String),
ResultType(String),
Text(String),
TypedText(String),
Optional(CompletionString<'r>),
}
impl<'r> CompletionChunk<'r> {
pub fn get_text(&self) -> Option<String> {
match *self {
CompletionChunk::Colon => Some(":".into()),
CompletionChunk::Comma => Some(",".into()),
CompletionChunk::Equals => Some("=".into()),
CompletionChunk::Semicolon => Some(";".into()),
CompletionChunk::LeftAngleBracket => Some("<".into()),
CompletionChunk::RightAngleBracket => Some(">".into()),
CompletionChunk::LeftBrace => Some("{".into()),
CompletionChunk::RightBrace => Some("}".into()),
CompletionChunk::LeftParenthesis => Some("(".into()),
CompletionChunk::RightParenthesis => Some(")".into()),
CompletionChunk::LeftSquareBracket => Some("[".into()),
CompletionChunk::RightSquareBracket => Some("]".into()),
CompletionChunk::CurrentParameter(ref text) |
CompletionChunk::Informative(ref text) |
CompletionChunk::Placeholder(ref text) |
CompletionChunk::ResultType(ref text) |
CompletionChunk::TypedText(ref text) |
CompletionChunk::Text(ref text) |
CompletionChunk::HorizontalSpace(ref text) |
CompletionChunk::VerticalSpace(ref text) => Some(text.clone()),
CompletionChunk::Optional(_) => None,
}
}
pub fn is_optional(&self) -> bool {
match *self {
CompletionChunk::Optional(_) => true,
_ => false,
}
}
}
builder! {
builder Completer: CXCodeComplete_Flags {
tu: &'tu TranslationUnit<'tu>,
file: PathBuf,
line: u32,
column: u32,
unsaved: Vec<Unsaved>;
OPTIONS:
pub macros: CXCodeComplete_IncludeMacros,
pub code_patterns: CXCodeComplete_IncludeCodePatterns,
pub briefs: CXCodeComplete_IncludeBriefComments,
}
}
impl<'tu> Completer<'tu> {
#[doc(hidden)]
pub fn new<F: Into<PathBuf>>(
tu: &'tu TranslationUnit<'tu>, file: F, line: u32, column: u32
) -> Completer<'tu> {
let file = file.into();
let flags = unsafe { clang_defaultCodeCompleteOptions() };
Completer { tu, file, line, column, unsaved: vec![], flags }
}
pub fn unsaved(&mut self, unsaved: &[Unsaved]) -> &mut Completer<'tu> {
self.unsaved = unsaved.into();
self
}
pub fn complete(&self) -> CompletionResults {
unsafe {
let ptr = clang_codeCompleteAt(
self.tu.ptr,
utility::from_path(&self.file).as_ptr(),
self.line as c_uint,
self.column as c_uint,
self.unsaved.as_ptr() as *mut CXUnsavedFile,
self.unsaved.len() as c_uint,
self.flags,
);
CompletionResults::from_ptr(ptr)
}
}
}
options! {
options CompletionContext: CXCompletionContext {
pub all_types: CXCompletionContext_AnyType,
pub all_values: CXCompletionContext_AnyValue,
pub class_type_values: CXCompletionContext_CXXClassTypeValue,
pub dot_members: CXCompletionContext_DotMemberAccess,
pub arrow_members: CXCompletionContext_ArrowMemberAccess,
pub enum_tags: CXCompletionContext_EnumTag,
pub union_tags: CXCompletionContext_UnionTag,
pub struct_tags: CXCompletionContext_StructTag,
pub class_names: CXCompletionContext_ClassTag,
pub namespaces: CXCompletionContext_Namespace,
pub nested_name_specifiers: CXCompletionContext_NestedNameSpecifier,
pub macro_names: CXCompletionContext_MacroName,
pub natural_language: CXCompletionContext_NaturalLanguage,
pub objc_object_values: CXCompletionContext_ObjCObjectValue,
pub objc_selector_values: CXCompletionContext_ObjCSelectorValue,
pub objc_property_members: CXCompletionContext_ObjCPropertyAccess,
pub objc_interfaces: CXCompletionContext_ObjCInterface,
pub objc_protocols: CXCompletionContext_ObjCProtocol,
pub objc_categories: CXCompletionContext_ObjCCategory,
pub objc_instance_messages: CXCompletionContext_ObjCInstanceMessage,
pub objc_class_messages: CXCompletionContext_ObjCClassMessage,
pub objc_selector_names: CXCompletionContext_ObjCSelectorName,
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct CompletionResult<'r> {
pub kind: EntityKind,
pub string: CompletionString<'r>,
}
impl<'r> CompletionResult<'r> {
fn from_raw(raw: CXCompletionResult) -> CompletionResult<'r> {
let kind = unsafe { mem::transmute(raw.CursorKind) };
CompletionResult { kind, string: CompletionString::from_ptr(raw.CompletionString) }
}
}
impl<'r> cmp::PartialOrd for CompletionResult<'r> {
fn partial_cmp(&self, other: &CompletionResult<'r>) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<'r> cmp::Ord for CompletionResult<'r> {
fn cmp(&self, other: &CompletionResult<'r>) -> Ordering {
self.string.cmp(&other.string)
}
}
pub struct CompletionResults {
ptr: *mut CXCodeCompleteResults,
}
impl CompletionResults {
fn from_ptr(ptr: *mut CXCodeCompleteResults) -> CompletionResults {
assert!(!ptr.is_null());
CompletionResults { ptr }
}
pub fn get_diagnostics<'tu>(&self, tu: &'tu TranslationUnit<'tu>) -> Vec<Diagnostic<'tu>> {
iter!(
clang_codeCompleteGetNumDiagnostics(self.ptr),
clang_codeCompleteGetDiagnostic(self.ptr),
).map(|d| Diagnostic::from_ptr(d, tu)).collect()
}
pub fn get_context(&self) -> Option<CompletionContext> {
let contexts = unsafe { clang_codeCompleteGetContexts(self.ptr) as CXCompletionContext };
if contexts != 0 && contexts != CXCompletionContext_Unknown {
Some(CompletionContext::from(contexts))
} else {
None
}
}
pub fn get_container_kind(&self) -> Option<(EntityKind, bool)> {
unsafe {
let mut incomplete = mem::MaybeUninit::uninit();
match clang_codeCompleteGetContainerKind(self.ptr, incomplete.as_mut_ptr()) {
CXCursor_InvalidCode => None,
other => Some((mem::transmute(other), incomplete.assume_init() != 0)),
}
}
}
pub fn get_objc_selector(&self) -> Option<String> {
unsafe { utility::to_string_option(clang_codeCompleteGetObjCSelector(self.ptr)) }
}
pub fn get_usr(&self) -> Option<Usr> {
unsafe { utility::to_string_option(clang_codeCompleteGetContainerUSR(self.ptr)).map(Usr) }
}
pub fn get_results(&self) -> Vec<CompletionResult> {
unsafe {
let raws = slice::from_raw_parts((*self.ptr).Results, (*self.ptr).NumResults as usize);
raws.iter().cloned().map(CompletionResult::from_raw).collect()
}
}
}
impl Drop for CompletionResults {
fn drop(&mut self) {
unsafe { clang_disposeCodeCompleteResults(self.ptr); }
}
}
impl fmt::Debug for CompletionResults {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.debug_struct("CompletionResults")
.field("results", &self.get_results())
.finish()
}
}
#[derive(Copy, Clone)]
pub struct CompletionString<'r> {
ptr: CXCompletionString,
_marker: PhantomData<&'r CompletionResults>
}
impl<'r> CompletionString<'r> {
#[doc(hidden)]
pub fn from_ptr(ptr: CXCompletionString) -> CompletionString<'r> {
assert!(!ptr.is_null());
CompletionString { ptr, _marker: PhantomData }
}
pub fn get_priority(&self) -> usize {
unsafe { clang_getCompletionPriority(self.ptr) as usize }
}
pub fn get_annotations(&self) -> Vec<String> {
iter!(
clang_getCompletionNumAnnotations(self.ptr),
clang_getCompletionAnnotation(self.ptr),
).map(utility::to_string).collect()
}
pub fn get_availability(&self) -> Availability {
unsafe { mem::transmute(clang_getCompletionAvailability(self.ptr)) }
}
pub fn get_comment_brief(&self) -> Option<String> {
unsafe { utility::to_string_option(clang_getCompletionBriefComment(self.ptr)) }
}
pub fn get_parent_name(&self) -> Option<String> {
unsafe { utility::to_string_option(clang_getCompletionParent(self.ptr, ptr::null_mut())) }
}
pub fn get_typed_text(&self) -> Option<String> {
for chunk in self.get_chunks() {
if let CompletionChunk::TypedText(text) = chunk {
return Some(text);
}
}
None
}
pub fn get_chunks(&self) -> Vec<CompletionChunk> {
iter!(
clang_getNumCompletionChunks(self.ptr),
clang_getCompletionChunkKind(self.ptr),
).enumerate().map(|(i, k)| {
macro_rules! text {
($variant:ident) => ({
let text = unsafe { clang_getCompletionChunkText(self.ptr, i as c_uint) };
CompletionChunk::$variant(utility::to_string(text))
});
}
match k {
CXCompletionChunk_Colon => CompletionChunk::Colon,
CXCompletionChunk_Comma => CompletionChunk::Comma,
CXCompletionChunk_Equal => CompletionChunk::Equals,
CXCompletionChunk_SemiColon => CompletionChunk::Semicolon,
CXCompletionChunk_LeftAngle => CompletionChunk::LeftAngleBracket,
CXCompletionChunk_RightAngle => CompletionChunk::RightAngleBracket,
CXCompletionChunk_LeftBrace => CompletionChunk::LeftBrace,
CXCompletionChunk_RightBrace => CompletionChunk::RightBrace,
CXCompletionChunk_LeftParen => CompletionChunk::LeftParenthesis,
CXCompletionChunk_RightParen => CompletionChunk::RightParenthesis,
CXCompletionChunk_LeftBracket => CompletionChunk::LeftSquareBracket,
CXCompletionChunk_RightBracket => CompletionChunk::RightSquareBracket,
CXCompletionChunk_HorizontalSpace => text!(HorizontalSpace),
CXCompletionChunk_VerticalSpace => text!(VerticalSpace),
CXCompletionChunk_CurrentParameter => text!(CurrentParameter),
CXCompletionChunk_TypedText => text!(TypedText),
CXCompletionChunk_Text => text!(Text),
CXCompletionChunk_Placeholder => text!(Placeholder),
CXCompletionChunk_Informative => text!(Informative),
CXCompletionChunk_ResultType => text!(ResultType),
CXCompletionChunk_Optional => {
let i = i as c_uint;
let ptr = unsafe { clang_getCompletionChunkCompletionString(self.ptr, i) };
CompletionChunk::Optional(CompletionString::from_ptr(ptr))
},
_ => panic!("unexpected completion chunk kind: {:?}", k),
}
}).collect()
}
}
impl<'r> fmt::Debug for CompletionString<'r> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.debug_struct("CompletionString")
.field("chunks", &self.get_chunks())
.finish()
}
}
impl<'r> cmp::PartialEq for CompletionString<'r> {
fn eq(&self, other: &CompletionString<'r>) -> bool {
self.get_chunks() == other.get_chunks()
}
}
impl<'r> cmp::Eq for CompletionString<'r> { }
impl<'r> cmp::PartialOrd for CompletionString<'r> {
fn partial_cmp(&self, other: &CompletionString<'r>) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<'r> cmp::Ord for CompletionString<'r> {
fn cmp(&self, other: &CompletionString<'r>) -> Ordering {
match self.get_priority().cmp(&other.get_priority()) {
Ordering::Equal => self.get_typed_text().cmp(&other.get_typed_text()),
other => other,
}
}
}