GoToDefinition/Declaration commands for C-family
These are accessible through the :YcmCompleter command. The docs have more information.
This commit is contained in:
parent
99a699cb03
commit
1f094e50d0
78
README.md
78
README.md
@ -87,7 +87,7 @@ Command Line Tools (that you install from within Xcode).
|
|||||||
Install CMake. Preferably with [Homebrew][brew], but here's the [stand-alone
|
Install CMake. Preferably with [Homebrew][brew], but here's the [stand-alone
|
||||||
CMake installer][cmake-download].
|
CMake installer][cmake-download].
|
||||||
|
|
||||||
_If_ you have installed a Homebrew Python and/or Homebrew MacVim, see the FAQ
|
_If_ you have installed a Homebrew Python and/or Homebrew MacVim, see the _FAQ_
|
||||||
for details.
|
for details.
|
||||||
|
|
||||||
Compiling YCM **with** semantic support for C-family languages:
|
Compiling YCM **with** semantic support for C-family languages:
|
||||||
@ -100,10 +100,10 @@ Compiling YCM **without** semantic support for C-family languages:
|
|||||||
cd ~/.vim/bundle/YouCompleteMe
|
cd ~/.vim/bundle/YouCompleteMe
|
||||||
./install.sh
|
./install.sh
|
||||||
|
|
||||||
That's it. You're done. Refer to the User Guide section on how to use YCM. Don't
|
That's it. You're done. Refer to the _User Guide_ section on how to use YCM.
|
||||||
forget that if you want the C-family semantic completion engine to work, you
|
Don't forget that if you want the C-family semantic completion engine to work,
|
||||||
will need to provide the compilation flags for your project to YCM. It's all in
|
you will need to provide the compilation flags for your project to YCM. It's all
|
||||||
the User Guide.
|
in the User Guide.
|
||||||
|
|
||||||
YCM comes with sane defaults for its options, but you still may want to take a
|
YCM comes with sane defaults for its options, but you still may want to take a
|
||||||
look at what's available for configuration. There are a few interesting options
|
look at what's available for configuration. There are a few interesting options
|
||||||
@ -139,10 +139,10 @@ Compiling YCM **without** semantic support for C-family languages:
|
|||||||
cd ~/.vim/bundle/YouCompleteMe
|
cd ~/.vim/bundle/YouCompleteMe
|
||||||
./install.sh
|
./install.sh
|
||||||
|
|
||||||
That's it. You're done. Refer to the User Guide section on how to use YCM. Don't
|
That's it. You're done. Refer to the _User Guide_ section on how to use YCM.
|
||||||
forget that if you want the C-family semantic completion engine to work, you
|
Don't forget that if you want the C-family semantic completion engine to work,
|
||||||
will need to provide the compilation flags for your project to YCM. It's all in
|
you will need to provide the compilation flags for your project to YCM. It's all
|
||||||
the User Guide.
|
in the User Guide.
|
||||||
|
|
||||||
YCM comes with sane defaults for its options, but you still may want to take a
|
YCM comes with sane defaults for its options, but you still may want to take a
|
||||||
look at what's available for configuration. There are a few interesting options
|
look at what's available for configuration. There are a few interesting options
|
||||||
@ -164,7 +164,7 @@ code is platform agnostic, so if everything is configured correctly, YCM
|
|||||||
_should_ work on Windows without issues (but as of writing, it's untested on
|
_should_ work on Windows without issues (but as of writing, it's untested on
|
||||||
that platform).
|
that platform).
|
||||||
|
|
||||||
See the FAQ if you have any issues.
|
See the _FAQ_ if you have any issues.
|
||||||
|
|
||||||
**Remember:** YCM is a plugin with a compiled component. If you **update** YCM
|
**Remember:** YCM is a plugin with a compiled component. If you **update** YCM
|
||||||
using Vundle and the ycm_core library API has changed (happens rarely), YCM will
|
using Vundle and the ycm_core library API has changed (happens rarely), YCM will
|
||||||
@ -270,10 +270,10 @@ notify you to recompile it. You should then rerun the install process.
|
|||||||
version 3.2 into the `YouCompleteMe/python` folder then YCM _will not work_
|
version 3.2 into the `YouCompleteMe/python` folder then YCM _will not work_
|
||||||
if you selected C-family support during YCM compilation.
|
if you selected C-family support during YCM compilation.
|
||||||
|
|
||||||
That's it. You're done. Refer to the User Guide section on how to use YCM. Don't
|
That's it. You're done. Refer to the _User Guide_ section on how to use YCM.
|
||||||
forget that if you want the C-family semantic completion engine to work, you
|
Don't forget that if you want the C-family semantic completion engine to work,
|
||||||
will need to provide the compilation flags for your project to YCM. It's all in
|
you will need to provide the compilation flags for your project to YCM. It's all
|
||||||
the User Guide.
|
in the User Guide.
|
||||||
|
|
||||||
YCM comes with sane defaults for its options, but you still may want to take a
|
YCM comes with sane defaults for its options, but you still may want to take a
|
||||||
look at what's available for configuration. There are a few interesting options
|
look at what's available for configuration. There are a few interesting options
|
||||||
@ -294,7 +294,7 @@ User Guide
|
|||||||
through the completions. Use Shift-TAB to cycle backwards. Note that if you're
|
through the completions. Use Shift-TAB to cycle backwards. Note that if you're
|
||||||
using console Vim (that is, not Gvim or MacVim) then it's likely that the
|
using console Vim (that is, not Gvim or MacVim) then it's likely that the
|
||||||
Shift-TAB binding will not work because the console will not pass it to Vim.
|
Shift-TAB binding will not work because the console will not pass it to Vim.
|
||||||
You can remap the keys; see the options section below.
|
You can remap the keys; see the _Options_ section below.
|
||||||
|
|
||||||
### Completion string ranking
|
### Completion string ranking
|
||||||
|
|
||||||
@ -328,7 +328,7 @@ compile the current file. You can also provide a path to a global
|
|||||||
`.ycm_extra_conf.py` file, which will be used as a fallback. To prevent the
|
`.ycm_extra_conf.py` file, which will be used as a fallback. To prevent the
|
||||||
execution of malicious code from a file you didn't write YCM will ask you once
|
execution of malicious code from a file you didn't write YCM will ask you once
|
||||||
per `.ycm_extra_conf.py` if it is safe to load. This can be disabled and you can
|
per `.ycm_extra_conf.py` if it is safe to load. This can be disabled and you can
|
||||||
white-/blacklist files. See the Options section for more details.
|
white-/blacklist files. See the _Options_ section for more details.
|
||||||
|
|
||||||
This system was designed this way so that the user can perform any arbitrary
|
This system was designed this way so that the user can perform any arbitrary
|
||||||
sequence of operations to produce a list of compilation flags YCM should hand
|
sequence of operations to produce a list of compilation flags YCM should hand
|
||||||
@ -492,6 +492,50 @@ used.
|
|||||||
Call `YcmCompleter` without further arguments for information about the
|
Call `YcmCompleter` without further arguments for information about the
|
||||||
commands you can call for the selected completer.
|
commands you can call for the selected completer.
|
||||||
|
|
||||||
|
See the _YcmCompleter subcommands_ section for more information on the available
|
||||||
|
subcommands.
|
||||||
|
|
||||||
|
YcmCompleter subcommands
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
[See the docs for the `YcmCompleter` command before tackling this section.]
|
||||||
|
|
||||||
|
The invoked subcommand is automatically routed to the currently active semantic
|
||||||
|
completer, so `:YcmCommand GoToDefinition` will invoke the `GoToDefinition`
|
||||||
|
subcommand on the Python semantic completer if the currently active file is a
|
||||||
|
Python one and on the Clang completer if the currently active file is a
|
||||||
|
C/C++/Objective-C one.
|
||||||
|
|
||||||
|
You may also want to map the subcommands to something less verbose; for
|
||||||
|
instance, `nnoremap <leader>jd :YcmCommand GoToDefinitionElseDeclaration<CR>`
|
||||||
|
maps the `<leader>jd` sequence to the longer subcommand invocation.
|
||||||
|
|
||||||
|
### The `GoToDeclaration` subcommand
|
||||||
|
|
||||||
|
Looks up the symbol under the cursor and jumps to its declaration.
|
||||||
|
|
||||||
|
Supported in filetypes: `c, cpp, objc, objcpp`
|
||||||
|
|
||||||
|
### The `GoToDefinition` subcommand
|
||||||
|
|
||||||
|
Looks up the symbol under the cursor and jumps to its definition.
|
||||||
|
|
||||||
|
NOTE: For C-family languages **this only works in certain situations**, namely when
|
||||||
|
the definition of the symbol is in the current translation unit. A translation
|
||||||
|
unit consists of the file you are editing and all the files you are including
|
||||||
|
with `#include` directives (directly or indirectly) in that file.
|
||||||
|
|
||||||
|
Supported in filetypes: `c, cpp, objc, objcpp`
|
||||||
|
|
||||||
|
### The `GoToDefinitionElseDeclaration` subcommand
|
||||||
|
|
||||||
|
Looks up the symbol under the cursor and jumps to its definition if possible; if
|
||||||
|
the definition is not accessible from the current translation unit, jumps to the
|
||||||
|
symbol's declaration.
|
||||||
|
|
||||||
|
Supported in filetypes: `c, cpp, objc, objcpp`
|
||||||
|
|
||||||
|
|
||||||
Options
|
Options
|
||||||
-------
|
-------
|
||||||
|
|
||||||
@ -904,7 +948,7 @@ This means that YCM tried to set up a key mapping but failed because you already
|
|||||||
had something mapped to that key combination. The `<blah>` part of the message
|
had something mapped to that key combination. The `<blah>` part of the message
|
||||||
will tell you what was the key combination that failed.
|
will tell you what was the key combination that failed.
|
||||||
|
|
||||||
Look in the options section and see if which of the default mappings conflict
|
Look in the _Options_ section and see if which of the default mappings conflict
|
||||||
with your own. Then change that option value to something else so that the
|
with your own. Then change that option value to something else so that the
|
||||||
conflict goes away.
|
conflict goes away.
|
||||||
|
|
||||||
|
@ -262,6 +262,34 @@ ClangCompleter::CandidatesForQueryAndLocationInFileAsync(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Location ClangCompleter::GetDeclarationLocation(
|
||||||
|
const std::string &filename,
|
||||||
|
int line,
|
||||||
|
int column,
|
||||||
|
const std::vector< UnsavedFile > &unsaved_files,
|
||||||
|
const std::vector< std::string > &flags ) {
|
||||||
|
shared_ptr< TranslationUnit > unit = GetTranslationUnitForFile(
|
||||||
|
filename,
|
||||||
|
unsaved_files,
|
||||||
|
flags );
|
||||||
|
return unit->GetDeclarationLocation( line, column, unsaved_files );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Location ClangCompleter::GetDefinitionLocation(
|
||||||
|
const std::string &filename,
|
||||||
|
int line,
|
||||||
|
int column,
|
||||||
|
const std::vector< UnsavedFile > &unsaved_files,
|
||||||
|
const std::vector< std::string > &flags ) {
|
||||||
|
shared_ptr< TranslationUnit > unit = GetTranslationUnitForFile(
|
||||||
|
filename,
|
||||||
|
unsaved_files,
|
||||||
|
flags );
|
||||||
|
return unit->GetDefinitionLocation( line, column, unsaved_files );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void ClangCompleter::DeleteCachesForFileAsync( const std::string &filename ) {
|
void ClangCompleter::DeleteCachesForFileAsync( const std::string &filename ) {
|
||||||
file_cache_delete_stack_.Push( filename );
|
file_cache_delete_stack_.Push( filename );
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@ namespace YouCompleteMe {
|
|||||||
class CandidateRepository;
|
class CandidateRepository;
|
||||||
class TranslationUnit;
|
class TranslationUnit;
|
||||||
struct CompletionData;
|
struct CompletionData;
|
||||||
|
struct Location;
|
||||||
|
|
||||||
typedef std::vector< CompletionData > CompletionDatas;
|
typedef std::vector< CompletionData > CompletionDatas;
|
||||||
|
|
||||||
@ -94,6 +95,20 @@ public:
|
|||||||
const std::vector< UnsavedFile > &unsaved_files,
|
const std::vector< UnsavedFile > &unsaved_files,
|
||||||
const std::vector< std::string > &flags );
|
const std::vector< std::string > &flags );
|
||||||
|
|
||||||
|
Location GetDeclarationLocation(
|
||||||
|
const std::string &filename,
|
||||||
|
int line,
|
||||||
|
int column,
|
||||||
|
const std::vector< UnsavedFile > &unsaved_files,
|
||||||
|
const std::vector< std::string > &flags );
|
||||||
|
|
||||||
|
Location GetDefinitionLocation(
|
||||||
|
const std::string &filename,
|
||||||
|
int line,
|
||||||
|
int column,
|
||||||
|
const std::vector< UnsavedFile > &unsaved_files,
|
||||||
|
const std::vector< std::string > &flags );
|
||||||
|
|
||||||
void DeleteCachesForFileAsync( const std::string &filename );
|
void DeleteCachesForFileAsync( const std::string &filename );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -188,7 +188,7 @@ Diagnostic DiagnosticWrapToDiagnostic( DiagnosticWrap diagnostic_wrap ) {
|
|||||||
&diagnostic.column_number_,
|
&diagnostic.column_number_,
|
||||||
&unused_offset );
|
&unused_offset );
|
||||||
|
|
||||||
diagnostic.filename_ = CXStringToString( clang_getFileName( file ) );
|
diagnostic.filename_ = CXFileToFilepath( file );
|
||||||
diagnostic.text_ = CXStringToString(
|
diagnostic.text_ = CXStringToString(
|
||||||
clang_getDiagnosticSpelling( diagnostic_wrap.get() ) );
|
clang_getDiagnosticSpelling( diagnostic_wrap.get() ) );
|
||||||
diagnostic.long_formatted_text_ = FullDiagnosticText( diagnostic_wrap.get() );
|
diagnostic.long_formatted_text_ = FullDiagnosticText( diagnostic_wrap.get() );
|
||||||
@ -196,6 +196,23 @@ Diagnostic DiagnosticWrapToDiagnostic( DiagnosticWrap diagnostic_wrap ) {
|
|||||||
return diagnostic;
|
return diagnostic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CursorIsValid( CXCursor cursor ) {
|
||||||
|
return !clang_Cursor_isNull( cursor ) &&
|
||||||
|
!clang_isInvalid( clang_getCursorKind( cursor ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CursorIsReference( CXCursor cursor ) {
|
||||||
|
return clang_isReference( clang_getCursorKind( cursor ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CursorIsDeclaration( CXCursor cursor ) {
|
||||||
|
return clang_isDeclaration( clang_getCursorKind( cursor ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CXFileToFilepath( CXFile file ) {
|
||||||
|
return CXStringToString( clang_getFileName( file ) );
|
||||||
|
}
|
||||||
|
|
||||||
std::string ClangVersion() {
|
std::string ClangVersion() {
|
||||||
return CXStringToString( clang_getClangVersion() );
|
return CXStringToString( clang_getClangVersion() );
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,14 @@ std::vector< CXUnsavedFile > ToCXUnsavedFiles(
|
|||||||
|
|
||||||
Diagnostic DiagnosticWrapToDiagnostic( DiagnosticWrap diagnostic_wrap );
|
Diagnostic DiagnosticWrapToDiagnostic( DiagnosticWrap diagnostic_wrap );
|
||||||
|
|
||||||
|
bool CursorIsValid( CXCursor cursor );
|
||||||
|
|
||||||
|
bool CursorIsReference( CXCursor cursor );
|
||||||
|
|
||||||
|
bool CursorIsDeclaration( CXCursor cursor );
|
||||||
|
|
||||||
|
std::string CXFileToFilepath( CXFile file );
|
||||||
|
|
||||||
std::string ClangVersion();
|
std::string ClangVersion();
|
||||||
|
|
||||||
} // namespace YouCompleteMe
|
} // namespace YouCompleteMe
|
||||||
|
59
cpp/ycm/ClangCompleter/Location.h
Normal file
59
cpp/ycm/ClangCompleter/Location.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
// Copyright (C) 2011, 2012 Strahinja Val Markovic <val@markovic.io>
|
||||||
|
//
|
||||||
|
// 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#ifndef LOCATION_H_6TLFQH4I
|
||||||
|
#define LOCATION_H_6TLFQH4I
|
||||||
|
|
||||||
|
#include "standard.h"
|
||||||
|
#include <string>
|
||||||
|
#include <clang-c/Index.h>
|
||||||
|
|
||||||
|
namespace YouCompleteMe {
|
||||||
|
|
||||||
|
struct Location {
|
||||||
|
// Creates an invalid location
|
||||||
|
Location()
|
||||||
|
: line_number_( 0 ),
|
||||||
|
column_number_( 0 ),
|
||||||
|
filename_( "" ) {}
|
||||||
|
|
||||||
|
Location( const std::string &filename, uint line, uint column )
|
||||||
|
: line_number_( line ),
|
||||||
|
column_number_( column ),
|
||||||
|
filename_( filename ) {}
|
||||||
|
|
||||||
|
bool operator== ( const Location &other ) const {
|
||||||
|
return
|
||||||
|
line_number_ == other.line_number_ &&
|
||||||
|
column_number_ == other.column_number_ &&
|
||||||
|
filename_ == other.filename_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsValid() {
|
||||||
|
return !filename_.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint line_number_;
|
||||||
|
uint column_number_;
|
||||||
|
|
||||||
|
// The full, absolute path
|
||||||
|
std::string filename_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace YouCompleteMe
|
||||||
|
|
||||||
|
#endif /* end of include guard: LOCATION_H_6TLFQH4I */
|
@ -21,7 +21,6 @@
|
|||||||
#include "exceptions.h"
|
#include "exceptions.h"
|
||||||
#include "ClangUtils.h"
|
#include "ClangUtils.h"
|
||||||
|
|
||||||
#include <clang-c/Index.h>
|
|
||||||
#include <boost/shared_ptr.hpp>
|
#include <boost/shared_ptr.hpp>
|
||||||
#include <boost/type_traits/remove_pointer.hpp>
|
#include <boost/type_traits/remove_pointer.hpp>
|
||||||
|
|
||||||
@ -133,6 +132,17 @@ void TranslationUnit::Reparse(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void TranslationUnit::ReparseForIndexing(
|
||||||
|
const std::vector< UnsavedFile > &unsaved_files ) {
|
||||||
|
std::vector< CXUnsavedFile > cxunsaved_files =
|
||||||
|
ToCXUnsavedFiles( unsaved_files );
|
||||||
|
|
||||||
|
Reparse( cxunsaved_files,
|
||||||
|
CXTranslationUnit_PrecompiledPreamble |
|
||||||
|
CXTranslationUnit_SkipFunctionBodies );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
std::vector< CompletionData > TranslationUnit::CandidatesForLocation(
|
std::vector< CompletionData > TranslationUnit::CandidatesForLocation(
|
||||||
int line,
|
int line,
|
||||||
int column,
|
int column,
|
||||||
@ -170,22 +180,74 @@ std::vector< CompletionData > TranslationUnit::CandidatesForLocation(
|
|||||||
return candidates;
|
return candidates;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Location TranslationUnit::GetDeclarationLocation(
|
||||||
|
int line,
|
||||||
|
int column,
|
||||||
|
const std::vector< UnsavedFile > &unsaved_files ) {
|
||||||
|
ReparseForIndexing( unsaved_files );
|
||||||
|
unique_lock< mutex > lock( clang_access_mutex_ );
|
||||||
|
|
||||||
|
if ( !clang_translation_unit_ )
|
||||||
|
return Location();
|
||||||
|
|
||||||
|
CXCursor cursor = GetCursor( line, column );
|
||||||
|
if ( !CursorIsValid( cursor ) )
|
||||||
|
return Location();
|
||||||
|
|
||||||
|
CXCursor referenced_cursor = clang_getCursorReferenced( cursor );
|
||||||
|
if ( !CursorIsValid( referenced_cursor ) )
|
||||||
|
return Location();
|
||||||
|
|
||||||
|
return LocationFromSourceLocation(
|
||||||
|
clang_getCursorLocation( referenced_cursor ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
Location TranslationUnit::GetDefinitionLocation(
|
||||||
|
int line,
|
||||||
|
int column,
|
||||||
|
const std::vector< UnsavedFile > &unsaved_files ) {
|
||||||
|
ReparseForIndexing( unsaved_files );
|
||||||
|
unique_lock< mutex > lock( clang_access_mutex_ );
|
||||||
|
|
||||||
|
if ( !clang_translation_unit_ )
|
||||||
|
return Location();
|
||||||
|
|
||||||
|
CXCursor cursor = GetCursor( line, column );
|
||||||
|
if ( !CursorIsValid( cursor ) )
|
||||||
|
return Location();
|
||||||
|
|
||||||
|
CXCursor definition_cursor = clang_getCursorDefinition( cursor );
|
||||||
|
if ( !CursorIsValid( definition_cursor ) )
|
||||||
|
return Location();
|
||||||
|
|
||||||
|
return LocationFromSourceLocation(
|
||||||
|
clang_getCursorLocation( definition_cursor ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Argument taken as non-const ref because we need to be able to pass a
|
// Argument taken as non-const ref because we need to be able to pass a
|
||||||
// non-const pointer to clang. This function (and clang too) will not modify the
|
// non-const pointer to clang. This function (and clang too) will not modify the
|
||||||
// param though.
|
// param though.
|
||||||
void TranslationUnit::Reparse(
|
void TranslationUnit::Reparse(
|
||||||
std::vector< CXUnsavedFile > &unsaved_files ) {
|
std::vector< CXUnsavedFile > &unsaved_files ) {
|
||||||
|
Reparse( unsaved_files, clang_defaultEditingTranslationUnitOptions() );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Argument taken as non-const ref because we need to be able to pass a
|
||||||
|
// non-const pointer to clang. This function (and clang too) will not modify the
|
||||||
|
// param though.
|
||||||
|
void TranslationUnit::Reparse( std::vector< CXUnsavedFile > &unsaved_files,
|
||||||
|
uint parse_options ) {
|
||||||
unique_lock< mutex > lock( clang_access_mutex_ );
|
unique_lock< mutex > lock( clang_access_mutex_ );
|
||||||
|
|
||||||
if ( !clang_translation_unit_ )
|
if ( !clang_translation_unit_ )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int failure = clang_reparseTranslationUnit(
|
int failure = clang_reparseTranslationUnit( clang_translation_unit_,
|
||||||
clang_translation_unit_,
|
unsaved_files.size(),
|
||||||
unsaved_files.size(),
|
&unsaved_files[ 0 ],
|
||||||
&unsaved_files[ 0 ],
|
parse_options );
|
||||||
clang_defaultEditingTranslationUnitOptions() );
|
|
||||||
|
|
||||||
if ( failure ) {
|
if ( failure ) {
|
||||||
Destroy();
|
Destroy();
|
||||||
@ -215,4 +277,33 @@ void TranslationUnit::UpdateLatestDiagnostics() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CXCursor TranslationUnit::GetCursor( int line, int column ) {
|
||||||
|
// ASSUMES A LOCK IS ALREADY HELD ON clang_access_mutex_!
|
||||||
|
if ( !clang_translation_unit_ )
|
||||||
|
return clang_getNullCursor();
|
||||||
|
|
||||||
|
CXFile file = clang_getFile( clang_translation_unit_, filename_.c_str() );
|
||||||
|
CXSourceLocation source_location = clang_getLocation(
|
||||||
|
clang_translation_unit_,
|
||||||
|
file,
|
||||||
|
line,
|
||||||
|
column );
|
||||||
|
|
||||||
|
return clang_getCursor( clang_translation_unit_, source_location );
|
||||||
|
}
|
||||||
|
|
||||||
|
Location TranslationUnit::LocationFromSourceLocation(
|
||||||
|
CXSourceLocation source_location ) {
|
||||||
|
// ASSUMES A LOCK IS ALREADY HELD ON clang_access_mutex_!
|
||||||
|
if ( !clang_translation_unit_ )
|
||||||
|
return Location();
|
||||||
|
|
||||||
|
CXFile file;
|
||||||
|
uint line;
|
||||||
|
uint column;
|
||||||
|
uint offset;
|
||||||
|
clang_getExpansionLocation( source_location, &file, &line, &column, &offset );
|
||||||
|
return Location( CXFileToFilepath( file ), line, column );
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace YouCompleteMe
|
} // namespace YouCompleteMe
|
||||||
|
@ -22,17 +22,15 @@
|
|||||||
#include "Future.h"
|
#include "Future.h"
|
||||||
#include "UnsavedFile.h"
|
#include "UnsavedFile.h"
|
||||||
#include "Diagnostic.h"
|
#include "Diagnostic.h"
|
||||||
|
#include "Location.h"
|
||||||
|
|
||||||
|
#include <clang-c/Index.h>
|
||||||
#include <boost/utility.hpp>
|
#include <boost/utility.hpp>
|
||||||
#include <boost/thread/mutex.hpp>
|
#include <boost/thread/mutex.hpp>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
typedef void *CXIndex;
|
|
||||||
typedef struct CXTranslationUnitImpl *CXTranslationUnit;
|
|
||||||
struct CXUnsavedFile;
|
|
||||||
|
|
||||||
namespace YouCompleteMe {
|
namespace YouCompleteMe {
|
||||||
|
|
||||||
struct CompletionData;
|
struct CompletionData;
|
||||||
@ -62,17 +60,35 @@ public:
|
|||||||
|
|
||||||
void Reparse( const std::vector< UnsavedFile > &unsaved_files );
|
void Reparse( const std::vector< UnsavedFile > &unsaved_files );
|
||||||
|
|
||||||
|
void ReparseForIndexing( const std::vector< UnsavedFile > &unsaved_files );
|
||||||
|
|
||||||
std::vector< CompletionData > CandidatesForLocation(
|
std::vector< CompletionData > CandidatesForLocation(
|
||||||
int line,
|
int line,
|
||||||
int column,
|
int column,
|
||||||
const std::vector< UnsavedFile > &unsaved_files );
|
const std::vector< UnsavedFile > &unsaved_files );
|
||||||
|
|
||||||
private:
|
Location GetDeclarationLocation(
|
||||||
|
int line,
|
||||||
|
int column,
|
||||||
|
const std::vector< UnsavedFile > &unsaved_files );
|
||||||
|
|
||||||
|
Location GetDefinitionLocation(
|
||||||
|
int line,
|
||||||
|
int column,
|
||||||
|
const std::vector< UnsavedFile > &unsaved_files );
|
||||||
|
|
||||||
|
private:
|
||||||
void Reparse( std::vector< CXUnsavedFile > &unsaved_files );
|
void Reparse( std::vector< CXUnsavedFile > &unsaved_files );
|
||||||
|
|
||||||
|
void Reparse( std::vector< CXUnsavedFile > &unsaved_files,
|
||||||
|
uint parse_options );
|
||||||
|
|
||||||
void UpdateLatestDiagnostics();
|
void UpdateLatestDiagnostics();
|
||||||
|
|
||||||
|
CXCursor GetCursor( int line, int column );
|
||||||
|
|
||||||
|
Location LocationFromSourceLocation( CXSourceLocation source_location );
|
||||||
|
|
||||||
/////////////////////////////
|
/////////////////////////////
|
||||||
// PRIVATE MEMBER VARIABLES
|
// PRIVATE MEMBER VARIABLES
|
||||||
/////////////////////////////
|
/////////////////////////////
|
||||||
|
@ -21,6 +21,8 @@
|
|||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include <gmock/gmock.h>
|
#include <gmock/gmock.h>
|
||||||
|
|
||||||
|
#include <clang-c/Index.h>
|
||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
namespace fs = boost::filesystem;
|
namespace fs = boost::filesystem;
|
||||||
|
|
||||||
@ -29,7 +31,21 @@ using ::testing::WhenSorted;
|
|||||||
|
|
||||||
namespace YouCompleteMe {
|
namespace YouCompleteMe {
|
||||||
|
|
||||||
TEST( TranslationUnitTest, ExceptionThrownOnParseFailure ) {
|
class TranslationUnitTest : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
virtual void SetUp() {
|
||||||
|
clang_index_ = clang_createIndex( 0, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void TearDown() {
|
||||||
|
clang_disposeIndex( clang_index_ );
|
||||||
|
}
|
||||||
|
|
||||||
|
CXIndex clang_index_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F( TranslationUnitTest, ExceptionThrownOnParseFailure ) {
|
||||||
fs::path test_file = fs::temp_directory_path() / fs::unique_path();
|
fs::path test_file = fs::temp_directory_path() / fs::unique_path();
|
||||||
std::string junk = "#&9112(^(^#>@(^@!@(&#@a}}}}{nthoeu\n&&^^&^&!#%%@@!aeu";
|
std::string junk = "#&9112(^(^#>@(^@!@(&#@a}}}}{nthoeu\n&&^^&^&!#%%@@!aeu";
|
||||||
WriteUtf8File( test_file, junk );
|
WriteUtf8File( test_file, junk );
|
||||||
@ -44,5 +60,59 @@ TEST( TranslationUnitTest, ExceptionThrownOnParseFailure ) {
|
|||||||
ClangParseError );
|
ClangParseError );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F( TranslationUnitTest, GoToDefinitionWorks ) {
|
||||||
|
fs::path path_to_testdata = fs::current_path() / fs::path( "testdata" );
|
||||||
|
fs::path test_file = path_to_testdata / fs::path( "goto.cpp" );
|
||||||
|
|
||||||
|
TranslationUnit unit( test_file.string(),
|
||||||
|
std::vector< UnsavedFile >(),
|
||||||
|
std::vector< std::string >(),
|
||||||
|
clang_index_ );
|
||||||
|
|
||||||
|
Location location = unit.GetDefinitionLocation(
|
||||||
|
15,
|
||||||
|
3,
|
||||||
|
std::vector< UnsavedFile >() );
|
||||||
|
|
||||||
|
EXPECT_EQ( 1, location.line_number_ );
|
||||||
|
EXPECT_EQ( 8, location.column_number_ );
|
||||||
|
EXPECT_TRUE( !location.filename_.empty() );
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F( TranslationUnitTest, GoToDefinitionFails ) {
|
||||||
|
fs::path path_to_testdata = fs::current_path() / fs::path( "testdata" );
|
||||||
|
fs::path test_file = path_to_testdata / fs::path( "goto.cpp" );
|
||||||
|
|
||||||
|
TranslationUnit unit( test_file.string(),
|
||||||
|
std::vector< UnsavedFile >(),
|
||||||
|
std::vector< std::string >(),
|
||||||
|
clang_index_ );
|
||||||
|
|
||||||
|
Location location = unit.GetDefinitionLocation(
|
||||||
|
17,
|
||||||
|
3,
|
||||||
|
std::vector< UnsavedFile >() );
|
||||||
|
|
||||||
|
EXPECT_FALSE( location.IsValid() );
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F( TranslationUnitTest, GoToDeclarationWorks ) {
|
||||||
|
fs::path path_to_testdata = fs::current_path() / fs::path( "testdata" );
|
||||||
|
fs::path test_file = path_to_testdata / fs::path( "goto.cpp" );
|
||||||
|
|
||||||
|
TranslationUnit unit( test_file.string(),
|
||||||
|
std::vector< UnsavedFile >(),
|
||||||
|
std::vector< std::string >(),
|
||||||
|
clang_index_ );
|
||||||
|
|
||||||
|
Location location = unit.GetDeclarationLocation(
|
||||||
|
17,
|
||||||
|
3,
|
||||||
|
std::vector< UnsavedFile >() );
|
||||||
|
|
||||||
|
EXPECT_EQ( 12, location.line_number_ );
|
||||||
|
EXPECT_EQ( 8, location.column_number_ );
|
||||||
|
EXPECT_TRUE( !location.filename_.empty() );
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace YouCompleteMe
|
} // namespace YouCompleteMe
|
||||||
|
18
cpp/ycm/tests/testdata/goto.cpp
vendored
Normal file
18
cpp/ycm/tests/testdata/goto.cpp
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
struct Foo {
|
||||||
|
int bar;
|
||||||
|
int zoo;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Bar {
|
||||||
|
int foo;
|
||||||
|
int zoo;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Foo;
|
||||||
|
struct Zoo;
|
||||||
|
|
||||||
|
void func() {
|
||||||
|
Foo foo;
|
||||||
|
foo.bar = 5;
|
||||||
|
Zoo *zoo = 0;
|
||||||
|
}
|
@ -24,6 +24,7 @@
|
|||||||
# include "ClangUtils.h"
|
# include "ClangUtils.h"
|
||||||
# include "CompletionData.h"
|
# include "CompletionData.h"
|
||||||
# include "Diagnostic.h"
|
# include "Diagnostic.h"
|
||||||
|
# include "Location.h"
|
||||||
# include "UnsavedFile.h"
|
# include "UnsavedFile.h"
|
||||||
# include "CompilationDatabase.h"
|
# include "CompilationDatabase.h"
|
||||||
#endif // USE_CLANG_COMPLETER
|
#endif // USE_CLANG_COMPLETER
|
||||||
@ -45,7 +46,7 @@ int YcmCoreVersion()
|
|||||||
{
|
{
|
||||||
// We increment this every time when we want to force users to recompile
|
// We increment this every time when we want to force users to recompile
|
||||||
// ycm_core.
|
// ycm_core.
|
||||||
return 2;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -115,6 +116,8 @@ BOOST_PYTHON_MODULE(ycm_core)
|
|||||||
class_< ClangCompleter, boost::noncopyable >( "ClangCompleter" )
|
class_< ClangCompleter, boost::noncopyable >( "ClangCompleter" )
|
||||||
.def( "EnableThreading", &ClangCompleter::EnableThreading )
|
.def( "EnableThreading", &ClangCompleter::EnableThreading )
|
||||||
.def( "DiagnosticsForFile", &ClangCompleter::DiagnosticsForFile )
|
.def( "DiagnosticsForFile", &ClangCompleter::DiagnosticsForFile )
|
||||||
|
.def( "GetDeclarationLocation", &ClangCompleter::GetDeclarationLocation )
|
||||||
|
.def( "GetDefinitionLocation", &ClangCompleter::GetDefinitionLocation )
|
||||||
.def( "DeleteCachesForFileAsync",
|
.def( "DeleteCachesForFileAsync",
|
||||||
&ClangCompleter::DeleteCachesForFileAsync )
|
&ClangCompleter::DeleteCachesForFileAsync )
|
||||||
.def( "UpdatingTranslationUnit", &ClangCompleter::UpdatingTranslationUnit )
|
.def( "UpdatingTranslationUnit", &ClangCompleter::UpdatingTranslationUnit )
|
||||||
@ -144,6 +147,12 @@ BOOST_PYTHON_MODULE(ycm_core)
|
|||||||
.def_readonly( "text_", &Diagnostic::text_ )
|
.def_readonly( "text_", &Diagnostic::text_ )
|
||||||
.def_readonly( "long_formatted_text_", &Diagnostic::long_formatted_text_ );
|
.def_readonly( "long_formatted_text_", &Diagnostic::long_formatted_text_ );
|
||||||
|
|
||||||
|
class_< Location >( "Location" )
|
||||||
|
.def_readonly( "line_number_", &Location::line_number_ )
|
||||||
|
.def_readonly( "column_number_", &Location::column_number_ )
|
||||||
|
.def_readonly( "filename_", &Location::filename_ )
|
||||||
|
.def( "IsValid", &Location::IsValid );
|
||||||
|
|
||||||
class_< std::vector< Diagnostic > >( "DiagnosticVec" )
|
class_< std::vector< Diagnostic > >( "DiagnosticVec" )
|
||||||
.def( vector_indexing_suite< std::vector< Diagnostic > >() );
|
.def( vector_indexing_suite< std::vector< Diagnostic > >() );
|
||||||
|
|
||||||
|
@ -27,6 +27,12 @@ from flags import Flags
|
|||||||
CLANG_FILETYPES = set( [ 'c', 'cpp', 'objc', 'objcpp' ] )
|
CLANG_FILETYPES = set( [ 'c', 'cpp', 'objc', 'objcpp' ] )
|
||||||
MAX_DIAGNOSTICS_TO_DISPLAY = int( vimsupport.GetVariableValue(
|
MAX_DIAGNOSTICS_TO_DISPLAY = int( vimsupport.GetVariableValue(
|
||||||
"g:ycm_max_diagnostics_to_display" ) )
|
"g:ycm_max_diagnostics_to_display" ) )
|
||||||
|
USER_COMMANDS_HELP_MESSAGE = """
|
||||||
|
Supported commands are:
|
||||||
|
GoToDefinition
|
||||||
|
GoToDeclaration
|
||||||
|
GoToDefinitionElseDeclaration
|
||||||
|
See the docs for information on what they do."""
|
||||||
|
|
||||||
|
|
||||||
class ClangCompleter( Completer ):
|
class ClangCompleter( Completer ):
|
||||||
@ -124,6 +130,78 @@ class ClangCompleter( Completer ):
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def OnUserCommand( self, arguments ):
|
||||||
|
if not arguments:
|
||||||
|
vimsupport.EchoText( USER_COMMANDS_HELP_MESSAGE )
|
||||||
|
return
|
||||||
|
|
||||||
|
command = arguments[ 0 ]
|
||||||
|
if command == 'GoToDefinition':
|
||||||
|
self._GoToDefinition()
|
||||||
|
elif command == 'GoToDeclaration':
|
||||||
|
self._GoToDeclaration()
|
||||||
|
elif command == 'GoToDefinitionElseDeclaration':
|
||||||
|
self._GoToDefinitionElseDeclaration()
|
||||||
|
|
||||||
|
|
||||||
|
def _LocationForGoTo( self, goto_function ):
|
||||||
|
filename = vim.current.buffer.name
|
||||||
|
if not filename:
|
||||||
|
return None
|
||||||
|
|
||||||
|
flags = self.flags.FlagsForFile( filename )
|
||||||
|
if not flags:
|
||||||
|
vimsupport.PostVimMessage( 'Still no compile flags, can\'t compile.' )
|
||||||
|
return None
|
||||||
|
|
||||||
|
files = self.GetUnsavedFilesVector()
|
||||||
|
line, column = vimsupport.CurrentLineAndColumn()
|
||||||
|
# Making the line & column 1-based instead of 0-based
|
||||||
|
line += 1
|
||||||
|
column += 1
|
||||||
|
return getattr( self.completer, goto_function )(
|
||||||
|
filename,
|
||||||
|
line,
|
||||||
|
column,
|
||||||
|
files,
|
||||||
|
flags )
|
||||||
|
|
||||||
|
|
||||||
|
def _GoToDefinition( self ):
|
||||||
|
location = self._LocationForGoTo( 'GetDefinitionLocation' )
|
||||||
|
if not location.IsValid():
|
||||||
|
vimsupport.PostVimMessage( 'Can\'t jump to definition.' )
|
||||||
|
return
|
||||||
|
|
||||||
|
vimsupport.JumpToLocation( location.filename_,
|
||||||
|
location.line_number_,
|
||||||
|
location.column_number_ )
|
||||||
|
|
||||||
|
|
||||||
|
def _GoToDeclaration( self ):
|
||||||
|
location = self._LocationForGoTo( 'GetDeclarationLocation' )
|
||||||
|
if not location.IsValid():
|
||||||
|
vimsupport.PostVimMessage( 'Can\'t jump to declaration.' )
|
||||||
|
return
|
||||||
|
|
||||||
|
vimsupport.JumpToLocation( location.filename_,
|
||||||
|
location.line_number_,
|
||||||
|
location.column_number_ )
|
||||||
|
|
||||||
|
|
||||||
|
def _GoToDefinitionElseDeclaration( self ):
|
||||||
|
location = self._LocationForGoTo( 'GetDefinitionLocation' )
|
||||||
|
if not location.IsValid():
|
||||||
|
location = self._LocationForGoTo( 'GetDeclarationLocation' )
|
||||||
|
if not location.IsValid():
|
||||||
|
vimsupport.PostVimMessage( 'Can\'t jump to definition or declaration.' )
|
||||||
|
return
|
||||||
|
|
||||||
|
vimsupport.JumpToLocation( location.filename_,
|
||||||
|
location.line_number_,
|
||||||
|
location.column_number_ )
|
||||||
|
|
||||||
|
|
||||||
def OnFileReadyToParse( self ):
|
def OnFileReadyToParse( self ):
|
||||||
if vimsupport.NumLinesInBuffer( vim.current.buffer ) < 5:
|
if vimsupport.NumLinesInBuffer( vim.current.buffer ) < 5:
|
||||||
self.parse_future = None
|
self.parse_future = None
|
||||||
|
@ -49,6 +49,25 @@ def GetUnsavedBuffers():
|
|||||||
return ( x for x in vim.buffers if BufferModified( x.number ) )
|
return ( x for x in vim.buffers if BufferModified( x.number ) )
|
||||||
|
|
||||||
|
|
||||||
|
# Both |line| and |column| need to be 1-based
|
||||||
|
def JumpToLocation( filename, line, column ):
|
||||||
|
# Add an entry to the jumplist
|
||||||
|
vim.command( "normal m'" )
|
||||||
|
|
||||||
|
if filename != vim.current.buffer.name:
|
||||||
|
# We prefix the command with 'keepjumps' so that opening the file is not
|
||||||
|
# recorded in the jumplist. So when we open the file and move the cursor to
|
||||||
|
# a location in it, the user can use CTRL-O to jump back to the original
|
||||||
|
# location, not to the start of the newly opened file.
|
||||||
|
# Sadly this fails on random occasions and the undesired jump remains in the
|
||||||
|
# jumplist.
|
||||||
|
vim.command( 'keepjumps edit {0}'.format( filename ) )
|
||||||
|
vim.current.window.cursor = ( line, column - 1 )
|
||||||
|
|
||||||
|
# Center the screen on the jumped-to location
|
||||||
|
vim.command( 'normal! zz' )
|
||||||
|
|
||||||
|
|
||||||
def NumLinesInBuffer( buffer ):
|
def NumLinesInBuffer( buffer ):
|
||||||
# This is actually less than obvious, that's why it's wrapped in a function
|
# This is actually less than obvious, that's why it's wrapped in a function
|
||||||
return len( buffer )
|
return len( buffer )
|
||||||
|
@ -257,7 +257,7 @@ def CurrentIdentifierFinished():
|
|||||||
return line[ : current_column ].isspace()
|
return line[ : current_column ].isspace()
|
||||||
|
|
||||||
|
|
||||||
COMPATIBLE_WITH_CORE_VERSION = 2
|
COMPATIBLE_WITH_CORE_VERSION = 3
|
||||||
|
|
||||||
def CompatibleWithYcmCore():
|
def CompatibleWithYcmCore():
|
||||||
try:
|
try:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user