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
|
||||
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.
|
||||
|
||||
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
|
||||
./install.sh
|
||||
|
||||
That's it. You're done. Refer to the User Guide section on how to use YCM. Don't
|
||||
forget that if you want the C-family semantic completion engine to work, you
|
||||
will need to provide the compilation flags for your project to YCM. It's all in
|
||||
the User Guide.
|
||||
That's it. You're done. Refer to the _User Guide_ section on how to use YCM.
|
||||
Don't forget that if you want the C-family semantic completion engine to work,
|
||||
you will need to provide the compilation flags for your project to YCM. It's all
|
||||
in the User Guide.
|
||||
|
||||
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
|
||||
@ -139,10 +139,10 @@ Compiling YCM **without** semantic support for C-family languages:
|
||||
cd ~/.vim/bundle/YouCompleteMe
|
||||
./install.sh
|
||||
|
||||
That's it. You're done. Refer to the User Guide section on how to use YCM. Don't
|
||||
forget that if you want the C-family semantic completion engine to work, you
|
||||
will need to provide the compilation flags for your project to YCM. It's all in
|
||||
the User Guide.
|
||||
That's it. You're done. Refer to the _User Guide_ section on how to use YCM.
|
||||
Don't forget that if you want the C-family semantic completion engine to work,
|
||||
you will need to provide the compilation flags for your project to YCM. It's all
|
||||
in the User Guide.
|
||||
|
||||
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
|
||||
@ -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
|
||||
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
|
||||
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_
|
||||
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
|
||||
forget that if you want the C-family semantic completion engine to work, you
|
||||
will need to provide the compilation flags for your project to YCM. It's all in
|
||||
the User Guide.
|
||||
That's it. You're done. Refer to the _User Guide_ section on how to use YCM.
|
||||
Don't forget that if you want the C-family semantic completion engine to work,
|
||||
you will need to provide the compilation flags for your project to YCM. It's all
|
||||
in the User Guide.
|
||||
|
||||
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
|
||||
@ -294,7 +294,7 @@ User Guide
|
||||
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
|
||||
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
|
||||
|
||||
@ -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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
-------
|
||||
|
||||
@ -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
|
||||
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
|
||||
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 ) {
|
||||
file_cache_delete_stack_.Push( filename );
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ namespace YouCompleteMe {
|
||||
class CandidateRepository;
|
||||
class TranslationUnit;
|
||||
struct CompletionData;
|
||||
struct Location;
|
||||
|
||||
typedef std::vector< CompletionData > CompletionDatas;
|
||||
|
||||
@ -94,6 +95,20 @@ public:
|
||||
const std::vector< UnsavedFile > &unsaved_files,
|
||||
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 );
|
||||
|
||||
private:
|
||||
|
@ -188,7 +188,7 @@ Diagnostic DiagnosticWrapToDiagnostic( DiagnosticWrap diagnostic_wrap ) {
|
||||
&diagnostic.column_number_,
|
||||
&unused_offset );
|
||||
|
||||
diagnostic.filename_ = CXStringToString( clang_getFileName( file ) );
|
||||
diagnostic.filename_ = CXFileToFilepath( file );
|
||||
diagnostic.text_ = CXStringToString(
|
||||
clang_getDiagnosticSpelling( diagnostic_wrap.get() ) );
|
||||
diagnostic.long_formatted_text_ = FullDiagnosticText( diagnostic_wrap.get() );
|
||||
@ -196,6 +196,23 @@ Diagnostic DiagnosticWrapToDiagnostic( DiagnosticWrap diagnostic_wrap ) {
|
||||
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() {
|
||||
return CXStringToString( clang_getClangVersion() );
|
||||
}
|
||||
|
@ -42,6 +42,14 @@ std::vector< CXUnsavedFile > ToCXUnsavedFiles(
|
||||
|
||||
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();
|
||||
|
||||
} // 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 "ClangUtils.h"
|
||||
|
||||
#include <clang-c/Index.h>
|
||||
#include <boost/shared_ptr.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(
|
||||
int line,
|
||||
int column,
|
||||
@ -170,22 +180,74 @@ std::vector< CompletionData > TranslationUnit::CandidatesForLocation(
|
||||
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
|
||||
// non-const pointer to clang. This function (and clang too) will not modify the
|
||||
// param though.
|
||||
void TranslationUnit::Reparse(
|
||||
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_ );
|
||||
|
||||
if ( !clang_translation_unit_ )
|
||||
return;
|
||||
|
||||
int failure = clang_reparseTranslationUnit(
|
||||
clang_translation_unit_,
|
||||
int failure = clang_reparseTranslationUnit( clang_translation_unit_,
|
||||
unsaved_files.size(),
|
||||
&unsaved_files[ 0 ],
|
||||
clang_defaultEditingTranslationUnitOptions() );
|
||||
parse_options );
|
||||
|
||||
if ( failure ) {
|
||||
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
|
||||
|
@ -22,17 +22,15 @@
|
||||
#include "Future.h"
|
||||
#include "UnsavedFile.h"
|
||||
#include "Diagnostic.h"
|
||||
#include "Location.h"
|
||||
|
||||
#include <clang-c/Index.h>
|
||||
#include <boost/utility.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
typedef void *CXIndex;
|
||||
typedef struct CXTranslationUnitImpl *CXTranslationUnit;
|
||||
struct CXUnsavedFile;
|
||||
|
||||
namespace YouCompleteMe {
|
||||
|
||||
struct CompletionData;
|
||||
@ -62,17 +60,35 @@ public:
|
||||
|
||||
void Reparse( const std::vector< UnsavedFile > &unsaved_files );
|
||||
|
||||
void ReparseForIndexing( const std::vector< UnsavedFile > &unsaved_files );
|
||||
|
||||
std::vector< CompletionData > CandidatesForLocation(
|
||||
int line,
|
||||
int column,
|
||||
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,
|
||||
uint parse_options );
|
||||
|
||||
void UpdateLatestDiagnostics();
|
||||
|
||||
CXCursor GetCursor( int line, int column );
|
||||
|
||||
Location LocationFromSourceLocation( CXSourceLocation source_location );
|
||||
|
||||
/////////////////////////////
|
||||
// PRIVATE MEMBER VARIABLES
|
||||
/////////////////////////////
|
||||
|
@ -21,6 +21,8 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
#include <clang-c/Index.h>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
@ -29,7 +31,21 @@ using ::testing::WhenSorted;
|
||||
|
||||
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();
|
||||
std::string junk = "#&9112(^(^#>@(^@!@(&#@a}}}}{nthoeu\n&&^^&^&!#%%@@!aeu";
|
||||
WriteUtf8File( test_file, junk );
|
||||
@ -44,5 +60,59 @@ TEST( TranslationUnitTest, ExceptionThrownOnParseFailure ) {
|
||||
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
|
||||
|
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 "CompletionData.h"
|
||||
# include "Diagnostic.h"
|
||||
# include "Location.h"
|
||||
# include "UnsavedFile.h"
|
||||
# include "CompilationDatabase.h"
|
||||
#endif // USE_CLANG_COMPLETER
|
||||
@ -45,7 +46,7 @@ int YcmCoreVersion()
|
||||
{
|
||||
// We increment this every time when we want to force users to recompile
|
||||
// ycm_core.
|
||||
return 2;
|
||||
return 3;
|
||||
}
|
||||
|
||||
|
||||
@ -115,6 +116,8 @@ BOOST_PYTHON_MODULE(ycm_core)
|
||||
class_< ClangCompleter, boost::noncopyable >( "ClangCompleter" )
|
||||
.def( "EnableThreading", &ClangCompleter::EnableThreading )
|
||||
.def( "DiagnosticsForFile", &ClangCompleter::DiagnosticsForFile )
|
||||
.def( "GetDeclarationLocation", &ClangCompleter::GetDeclarationLocation )
|
||||
.def( "GetDefinitionLocation", &ClangCompleter::GetDefinitionLocation )
|
||||
.def( "DeleteCachesForFileAsync",
|
||||
&ClangCompleter::DeleteCachesForFileAsync )
|
||||
.def( "UpdatingTranslationUnit", &ClangCompleter::UpdatingTranslationUnit )
|
||||
@ -144,6 +147,12 @@ BOOST_PYTHON_MODULE(ycm_core)
|
||||
.def_readonly( "text_", &Diagnostic::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" )
|
||||
.def( vector_indexing_suite< std::vector< Diagnostic > >() );
|
||||
|
||||
|
@ -27,6 +27,12 @@ from flags import Flags
|
||||
CLANG_FILETYPES = set( [ 'c', 'cpp', 'objc', 'objcpp' ] )
|
||||
MAX_DIAGNOSTICS_TO_DISPLAY = int( vimsupport.GetVariableValue(
|
||||
"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 ):
|
||||
@ -124,6 +130,78 @@ class ClangCompleter( Completer ):
|
||||
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 ):
|
||||
if vimsupport.NumLinesInBuffer( vim.current.buffer ) < 5:
|
||||
self.parse_future = None
|
||||
|
@ -49,6 +49,25 @@ def GetUnsavedBuffers():
|
||||
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 ):
|
||||
# This is actually less than obvious, that's why it's wrapped in a function
|
||||
return len( buffer )
|
||||
|
@ -257,7 +257,7 @@ def CurrentIdentifierFinished():
|
||||
return line[ : current_column ].isspace()
|
||||
|
||||
|
||||
COMPATIBLE_WITH_CORE_VERSION = 2
|
||||
COMPATIBLE_WITH_CORE_VERSION = 3
|
||||
|
||||
def CompatibleWithYcmCore():
|
||||
try:
|
||||
|
Loading…
x
Reference in New Issue
Block a user