GoToDefinition/Declaration commands for C-family

These are accessible through the :YcmCompleter command. The docs have more
information.
This commit is contained in:
Strahinja Val Markovic 2013-03-31 20:38:29 -07:00
parent 99a699cb03
commit 1f094e50d0
14 changed files with 504 additions and 32 deletions

View File

@ -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.

View File

@ -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 );
}

View File

@ -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:

View File

@ -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() );
}

View File

@ -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

View 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 */

View File

@ -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

View File

@ -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
/////////////////////////////

View File

@ -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
View 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;
}

View File

@ -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 > >() );

View File

@ -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

View File

@ -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 )

View File

@ -257,7 +257,7 @@ def CurrentIdentifierFinished():
return line[ : current_column ].isspace()
COMPATIBLE_WITH_CORE_VERSION = 2
COMPATIBLE_WITH_CORE_VERSION = 3
def CompatibleWithYcmCore():
try: