From 9a82319c77e626e8075f6266159419e87bd049a0 Mon Sep 17 00:00:00 2001 From: Strahinja Val Markovic Date: Sun, 8 Jul 2012 11:54:57 -0700 Subject: [PATCH] Initial version of the ClangComplete class --- cpp/ycm/ClangComplete.cpp | 233 ++++++++++++++++++++++++++++++++++++++ cpp/ycm/ClangComplete.h | 89 +++++++++++++++ 2 files changed, 322 insertions(+) create mode 100644 cpp/ycm/ClangComplete.cpp create mode 100644 cpp/ycm/ClangComplete.h diff --git a/cpp/ycm/ClangComplete.cpp b/cpp/ycm/ClangComplete.cpp new file mode 100644 index 00000000..1724ca55 --- /dev/null +++ b/cpp/ycm/ClangComplete.cpp @@ -0,0 +1,233 @@ +// Copyright (C) 2011, 2012 Strahinja Val Markovic +// +// This file is part of YouCompleteMe. +// +// YouCompleteMe is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// YouCompleteMe is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with YouCompleteMe. If not, see . + +#include "ClangComplete.h" +#include "standard.h" +#include + +namespace YouCompleteMe +{ + +namespace +{ + +std::vector< CXUnsavedFile > ToCXUnsavedFiles( + const std::vector< UnsavedFile > &unsaved_files ) +{ + std::vector< CXUnsavedFile > clang_unsaved_files( unsaved_files.size() ); + for ( uint i = 0; i < unsaved_files.size(); ++i ) + { + clang_unsaved_files[ i ].Filename = unsaved_files[ i ].filename_->c_str(); + clang_unsaved_files[ i ].Contents = unsaved_files[ i ].contents_->c_str(); + clang_unsaved_files[ i ].Length = unsaved_files[ i ].contents_->length(); + } + + return clang_unsaved_files; +} + + +std::string CXStringToString( CXString text ) +{ + std::string final_string( clang_getCString( text ) ); + clang_disposeString( text ); + return final_string; +} + + +std::string ChunkToString( CXCompletionString completion_string, int chunk_num ) +{ + return CXStringToString( + clang_getCompletionChunkText( completion_string, chunk_num ) ); +} + + +std::vector< std::string > ToStringVector( CXCodeCompleteResults *results ) +{ + std::vector< std::string > completions; + completions.reserve( results->NumResults ); + + for ( uint i = 0; i < results->NumResults; ++i ) + { + CXCompletionString completion_string = + results->Results[ i ].CompletionString; + + uint num_chunks = clang_getNumCompletionChunks( completion_string ); + for ( uint j = 0; j < num_chunks; ++j ) + { + CXCompletionChunkKind kind = clang_getCompletionChunkKind( + completion_string, j ); + + if ( kind == CXCompletionChunk_TypedText ) + { + completions.push_back( ChunkToString( completion_string, j ) ); + break; + } + } + } + + return completions; +} + + +} // unnamed namespace + + +ClangComplete::ClangComplete() +{ + clang_index_ = clang_createIndex( 0, 0 ); +} + + +ClangComplete::~ClangComplete() +{ + foreach ( const TranslationUnitForFilename::value_type &filename_unit, + filename_to_translation_unit_ ) + { + clang_disposeTranslationUnit( filename_unit.second ); + } + + clang_disposeIndex( clang_index_ ); +} + + +void ClangComplete::SetGlobalCompileFlags( + const std::vector< std::string > &flags ) +{ + global_flags_ = flags; +} + + +void ClangComplete::SetFileCompileFlags( + const std::string &filename, + const std::vector< std::string > &flags ) +{ + flags_for_file_[ filename ] = flags; +} + + +void ClangComplete::UpdateTranslationUnit( + const std::string &filename, + const std::vector< UnsavedFile > &unsaved_files ) +{ + TranslationUnitForFilename::iterator it = + filename_to_translation_unit_.find( filename ); + + if ( it != filename_to_translation_unit_.end() ) + { + std::vector< CXUnsavedFile > cxunsaved_files = ToCXUnsavedFiles( + unsaved_files ); + + clang_reparseTranslationUnit( + it->second, + cxunsaved_files.size(), + &cxunsaved_files[ 0 ], + clang_defaultEditingTranslationUnitOptions() ); + } + + else + { + filename_to_translation_unit_[ filename ] = + CreateTranslationUnit( filename, unsaved_files ); + } +} + + +std::vector< std::string > ClangComplete::CandidatesForLocationInFile( + const std::string &filename, + int line, + int column, + const std::vector< UnsavedFile > &unsaved_files ) +{ + std::vector< CXUnsavedFile > cxunsaved_files = ToCXUnsavedFiles( + unsaved_files ); + + // TODO: figure out does codeCompleteAt reparse the TU if the underlying + // source file has changed on disk since the last time the TU was updated and + // there are no unsaved files. + + CXCodeCompleteResults *results = + clang_codeCompleteAt( GetTranslationUnitForFile( filename, unsaved_files ), + filename.c_str(), + line, + column, + &cxunsaved_files[ 0 ], + cxunsaved_files.size(), + clang_defaultCodeCompleteOptions()); + + std::vector< std::string > completions = ToStringVector( results ); + clang_disposeCodeCompleteResults( results ); + return completions; +} + + +CXTranslationUnit ClangComplete::CreateTranslationUnit( + const std::string &filename, + const std::vector< UnsavedFile > &unsaved_files ) +{ + std::vector< const char* > flags = ClangFlagsForFilename( filename ); + std::vector< CXUnsavedFile > cxunsaved_files = ToCXUnsavedFiles( + unsaved_files ); + + return clang_parseTranslationUnit( + clang_index_, + filename.c_str(), + &flags[ 0 ], + flags.size(), + &cxunsaved_files[ 0 ], + cxunsaved_files.size(), + clang_defaultEditingTranslationUnitOptions()); +} + + +std::vector< const char* > ClangComplete::ClangFlagsForFilename( + const std::string &filename ) +{ + std::vector< const char* > flags; + + std::vector< std::string > file_flags = flags_for_file_[ filename ]; + flags.reserve( file_flags.size() + global_flags_.size() ); + + foreach ( const std::string &flag, global_flags_ ) + { + flags.push_back( flag.c_str() ); + } + + foreach ( const std::string &flag, file_flags ) + { + flags.push_back( flag.c_str() ); + } + + return flags; +} + + +CXTranslationUnit ClangComplete::GetTranslationUnitForFile( + const std::string &filename, + const std::vector< UnsavedFile > &unsaved_files ) +{ + TranslationUnitForFilename::iterator it = + filename_to_translation_unit_.find( filename ); + + if ( it != filename_to_translation_unit_.end() ) + return it->second; + + CXTranslationUnit unit = CreateTranslationUnit( filename, unsaved_files ); + filename_to_translation_unit_[ filename ] = unit; + return unit; +} + +} // namespace YouCompleteMe diff --git a/cpp/ycm/ClangComplete.h b/cpp/ycm/ClangComplete.h new file mode 100644 index 00000000..fec31eee --- /dev/null +++ b/cpp/ycm/ClangComplete.h @@ -0,0 +1,89 @@ +// Copyright (C) 2011, 2012 Strahinja Val Markovic +// +// This file is part of YouCompleteMe. +// +// YouCompleteMe is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// YouCompleteMe is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with YouCompleteMe. If not, see . + +#ifndef CLANGCOMPLETE_H_WLKDU0ZV +#define CLANGCOMPLETE_H_WLKDU0ZV + +#include +#include + +#include + +typedef void *CXIndex; +typedef struct CXTranslationUnitImpl *CXTranslationUnit; + +namespace YouCompleteMe +{ + +struct UnsavedFile +{ + std::string *filename_; + std::string *contents_; +}; + + +typedef boost::unordered_map< std::string, std::vector< std::string > > + FlagsForFile; +typedef boost::unordered_map< std::string, CXTranslationUnit > + TranslationUnitForFilename; + + +class ClangComplete : boost::noncopyable +{ +public: + ClangComplete(); + + ~ClangComplete(); + + void SetGlobalCompileFlags( const std::vector< std::string > &flags ); + + void SetFileCompileFlags( const std::string &filename, + const std::vector< std::string > &flags ); + + void UpdateTranslationUnit( const std::string &filename, + const std::vector< UnsavedFile > &unsaved_files ); + + std::vector< std::string > CandidatesForLocationInFile( + const std::string &filename, + int line, + int column, + const std::vector< UnsavedFile > &unsaved_files ); + +private: + + // caller takes ownership of translation unit + CXTranslationUnit CreateTranslationUnit( + const std::string &filename, + const std::vector< UnsavedFile > &unsaved_files ); + + std::vector< const char* > ClangFlagsForFilename( + const std::string &filename ); + + CXTranslationUnit GetTranslationUnitForFile( + const std::string &filename, + const std::vector< UnsavedFile > &unsaved_files ); + + CXIndex clang_index_; + FlagsForFile flags_for_file_; + TranslationUnitForFilename filename_to_translation_unit_; + std::vector< std::string > global_flags_; + +}; + +} // namespace YouCompleteMe + +#endif /* end of include guard: CLANGCOMPLETE_H_WLKDU0ZV */