Initial, rough version of completer separation
We are trying to build a nice plugin-oriented architechure for loading varios completer objects.
This commit is contained in:
parent
1fd2515617
commit
d0f62d1205
@ -22,7 +22,7 @@ set cpo&vim
|
||||
" This needs to be called outside of a function
|
||||
let s:script_folder_path = escape( expand( '<sfile>:p:h' ), '\' )
|
||||
let s:searched_and_no_results_found = 0
|
||||
let s:should_use_clang = 0
|
||||
let s:should_use_filetype_completion = 0
|
||||
let s:completion_start_column = 0
|
||||
let s:omnifunc_mode = 0
|
||||
|
||||
@ -79,10 +79,9 @@ function! youcompleteme#Enable()
|
||||
py import vim
|
||||
exe 'python sys.path = sys.path + ["' . s:script_folder_path . '/../python"]'
|
||||
py import ycm
|
||||
py identcomp = ycm.IdentifierCompleter()
|
||||
py ycm_state = ycm.YouCompleteMe()
|
||||
|
||||
if g:ycm_clang_completion_enabled
|
||||
py clangcomp = ycm.ClangCompleter()
|
||||
if g:ycm_filetype_completion_enabled
|
||||
" <c-x><c-o> trigger omni completion, <c-p> deselects the first completion
|
||||
" candidate that vim selects by default
|
||||
inoremap <unique> <C-Space> <C-X><C-O><C-P>
|
||||
@ -97,27 +96,18 @@ endfunction
|
||||
|
||||
function! s:OnBufferVisit()
|
||||
call s:SetCompleteFunc()
|
||||
call s:ParseFile()
|
||||
call s:OnFileReadyToParse()
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:OnCursorHold()
|
||||
call s:ParseFile()
|
||||
call s:OnFileReadyToParse()
|
||||
call s:UpdateDiagnosticNotifications()
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:ClangEnabledForCurrentFile()
|
||||
return g:ycm_clang_completion_enabled && pyeval('ycm.ClangAvailableForFile()')
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:ParseFile()
|
||||
py identcomp.OnFileReadyToParse()
|
||||
|
||||
if s:ClangEnabledForCurrentFile()
|
||||
py clangcomp.OnFileReadyToParse()
|
||||
endif
|
||||
function! s:OnFileReadyToParse()
|
||||
py ycm_state.OnFileReadyToParse()
|
||||
endfunction
|
||||
|
||||
|
||||
@ -125,9 +115,9 @@ function! s:SetCompleteFunc()
|
||||
let &completefunc = 'youcompleteme#Complete'
|
||||
let &l:completefunc = 'youcompleteme#Complete'
|
||||
|
||||
if s:ClangEnabledForCurrentFile()
|
||||
let &omnifunc = 'youcompleteme#ClangOmniComplete'
|
||||
let &l:omnifunc = 'youcompleteme#ClangOmniComplete'
|
||||
if pyeval( 'ycm_state.FiletypeCompletionEnabledForCurrentFile()' )
|
||||
let &omnifunc = 'youcompleteme#OmniComplete'
|
||||
let &l:omnifunc = 'youcompleteme#OmniComplete'
|
||||
endif
|
||||
endfunction
|
||||
|
||||
@ -146,13 +136,14 @@ endfunction
|
||||
function! s:OnInsertLeave()
|
||||
let s:omnifunc_mode = 0
|
||||
call s:UpdateDiagnosticNotifications()
|
||||
py identcomp.AddIdentifierUnderCursor()
|
||||
py ycm_state.OnInsertLeave()
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:UpdateDiagnosticNotifications()
|
||||
if get( g:, 'loaded_syntastic_plugin', 0 ) && s:ClangEnabledForCurrentFile()
|
||||
\ && pyeval( 'clangcomp.DiagnosticsForCurrentFileReady()' )
|
||||
if get( g:, 'loaded_syntastic_plugin', 0 ) &&
|
||||
\ pyeval( 'ycm_state.FiletypeCompletionEnabledForCurrentFile()' ) &&
|
||||
\ pyeval( 'ycm_state.DiagnosticsForCurrentFileReady()' )
|
||||
SyntasticCheck
|
||||
endif
|
||||
endfunction
|
||||
@ -162,7 +153,7 @@ function! s:IdentifierFinishedOperations()
|
||||
if !pyeval( 'ycm.CurrentIdentifierFinished()' )
|
||||
return
|
||||
endif
|
||||
py identcomp.AddPreviousIdentifier()
|
||||
py ycm_state.OnCurrentIdentifierFinished()
|
||||
let s:omnifunc_mode = 0
|
||||
endfunction
|
||||
|
||||
@ -213,40 +204,31 @@ function! s:InvokeCompletion()
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:IdentifierCompletion(query)
|
||||
if strlen( a:query ) < g:ycm_min_num_of_chars_for_completion
|
||||
function! s:CompletionsForQuery( query, use_filetype_completer )
|
||||
" TODO: needed?
|
||||
if !a:use_filetype_completer &&
|
||||
\ strlen( a:query ) < g:ycm_min_num_of_chars_for_completion
|
||||
return []
|
||||
endif
|
||||
|
||||
py identcomp.CandidatesForQueryAsync( vim.eval( 'a:query' ) )
|
||||
if a:use_filetype_completer
|
||||
py completer = ycm_state.GetFiletypeCompleterForCurrentFile()
|
||||
else
|
||||
py completer = ycm_state.GetIdentifierCompleter()
|
||||
endif
|
||||
|
||||
let l:results_ready = 0
|
||||
while !l:results_ready
|
||||
let l:results_ready = pyeval( 'identcomp.AsyncCandidateRequestReady()' )
|
||||
if complete_check()
|
||||
return { 'words' : [], 'refresh' : 'always'}
|
||||
endif
|
||||
endwhile
|
||||
|
||||
let l:results = pyeval( 'identcomp.CandidatesFromStoredRequest()' )
|
||||
let s:searched_and_no_results_found = len( l:results ) == 0
|
||||
return { 'words' : l:results, 'refresh' : 'always' }
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:ClangCompletion( query )
|
||||
" TODO: don't trigger on a dot inside a string constant
|
||||
py clangcomp.CandidatesForQueryAsync( vim.eval( 'a:query' ) )
|
||||
py completer.CandidatesForQueryAsync( vim.eval( 'a:query' ) )
|
||||
|
||||
let l:results_ready = 0
|
||||
while !l:results_ready
|
||||
let l:results_ready = pyeval( 'clangcomp.AsyncCandidateRequestReady()' )
|
||||
let l:results_ready = pyeval( 'completer.AsyncCandidateRequestReady()' )
|
||||
if complete_check()
|
||||
return { 'words' : [], 'refresh' : 'always'}
|
||||
endif
|
||||
endwhile
|
||||
|
||||
let l:results = pyeval( 'clangcomp.CandidatesFromStoredRequest()' )
|
||||
let l:results = pyeval( 'completer.CandidatesFromStoredRequest()' )
|
||||
let s:searched_and_no_results_found = len( l:results ) == 0
|
||||
return { 'words' : l:results, 'refresh' : 'always' }
|
||||
endfunction
|
||||
@ -254,24 +236,25 @@ endfunction
|
||||
|
||||
" This is our main entry point. This is what vim calls to get completions.
|
||||
function! youcompleteme#Complete( findstart, base )
|
||||
" Aften the user types one character afte the call to the omnifunc, the
|
||||
" After the user types one character after the call to the omnifunc, the
|
||||
" completefunc will be called because of our mapping that calls the
|
||||
" completefunc on every keystroke. Therefore we need to delegate the call we
|
||||
" 'stole' back to the omnifunc
|
||||
if s:omnifunc_mode
|
||||
return youcompleteme#ClangOmniComplete( a:findstart, a:base )
|
||||
return youcompleteme#OmniComplete( a:findstart, a:base )
|
||||
endif
|
||||
|
||||
if a:findstart
|
||||
let s:completion_start_column = pyeval( 'ycm.CompletionStartColumn()' )
|
||||
let s:should_use_clang =
|
||||
\ pyeval( 'ycm.ShouldUseClang(' . s:completion_start_column . ')' )
|
||||
let s:should_use_filetype_completion =
|
||||
\ pyeval( 'ycm_state.ShouldUseFiletypeCompleter(' .
|
||||
\ s:completion_start_column . ')' )
|
||||
|
||||
if ( !s:should_use_clang )
|
||||
" TODO: use ShouldUseIdentifierCompleter() which checks query length
|
||||
if ( !s:should_use_filetype_completion )
|
||||
let l:current_column = col('.') - 1
|
||||
let l:query_length = current_column - s:completion_start_column
|
||||
|
||||
|
||||
if ( query_length < g:ycm_min_num_of_chars_for_completion )
|
||||
" for vim, -2 means not found but don't trigger an error message
|
||||
" see :h complete-functions
|
||||
@ -280,22 +263,18 @@ function! youcompleteme#Complete( findstart, base )
|
||||
endif
|
||||
return s:completion_start_column
|
||||
else
|
||||
if ( s:should_use_clang )
|
||||
return s:ClangCompletion( a:base )
|
||||
else
|
||||
return s:IdentifierCompletion( a:base )
|
||||
endif
|
||||
return s:CompletionsForQuery( a:base, s:should_use_filetype_completion )
|
||||
endif
|
||||
endfunction
|
||||
|
||||
|
||||
function! youcompleteme#ClangOmniComplete( findstart, base )
|
||||
function! youcompleteme#OmniComplete( findstart, base )
|
||||
if a:findstart
|
||||
let s:omnifunc_mode = 1
|
||||
let s:completion_start_column = pyeval( 'ycm.CompletionStartColumn()' )
|
||||
return s:completion_start_column
|
||||
else
|
||||
return s:ClangCompletion( a:base )
|
||||
return s:CompletionsForQuery( a:base, 1 )
|
||||
endif
|
||||
endfunction
|
||||
|
||||
@ -304,10 +283,7 @@ endfunction
|
||||
" required (currently that's on buffer save) OR when the SyntasticCheck command
|
||||
" is invoked
|
||||
function! youcompleteme#CurrentFileDiagnostics()
|
||||
if s:ClangEnabledForCurrentFile()
|
||||
return pyeval( 'clangcomp.GetDiagnosticsForCurrentFile()' )
|
||||
endif
|
||||
return []
|
||||
return pyeval( 'ycm_state.GetDiagnosticsForCurrentFile()' )
|
||||
endfunction
|
||||
|
||||
" This is basic vim plugin boilerplate
|
||||
|
@ -37,11 +37,11 @@ let g:ycm_min_num_of_chars_for_completion =
|
||||
let g:ycm_filetypes_to_ignore =
|
||||
\ get( g:, 'ycm_filetypes_to_ignore', { 'notes' : 1 } )
|
||||
|
||||
let g:ycm_clang_completion_enabled =
|
||||
\ get( g:, 'ycm_clang_completion_enabled', 1 )
|
||||
let g:ycm_filetype_completion_enabled =
|
||||
\ get( g:, 'ycm_filetype_completion_enabled', 1 )
|
||||
|
||||
let g:ycm_allow_changing_updatetime =
|
||||
\ get( g:, 'ycm_clang_completion_enabled', 1 )
|
||||
\ get( g:, 'ycm_allow_changing_updatetime', 1 )
|
||||
|
||||
|
||||
" This is basic vim plugin boilerplate
|
||||
|
88
python/completer.py
Normal file
88
python/completer.py
Normal file
@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
import abc
|
||||
|
||||
|
||||
class Completer( object ):
|
||||
__metaclass__ = abc.ABCMeta
|
||||
|
||||
|
||||
def __init__( self ):
|
||||
self.future = None
|
||||
|
||||
|
||||
def AsyncCandidateRequestReady( self ):
|
||||
if not self.future:
|
||||
# We return True so that the caller can extract the default value from the
|
||||
# future
|
||||
return True
|
||||
return self.future.ResultsReady()
|
||||
|
||||
|
||||
def CandidatesFromStoredRequest( self ):
|
||||
if not self.future:
|
||||
return []
|
||||
return self.future.GetResults()
|
||||
|
||||
|
||||
def OnFileReadyToParse( self ):
|
||||
pass
|
||||
|
||||
|
||||
def OnCursorMovedInsertMode( self ):
|
||||
pass
|
||||
|
||||
|
||||
def OnCursorMovedNormalMode( self ):
|
||||
pass
|
||||
|
||||
|
||||
def OnBufferVisit( self ):
|
||||
pass
|
||||
|
||||
|
||||
def OnCursorHold( self ):
|
||||
pass
|
||||
|
||||
|
||||
def OnInsertLeave( self ):
|
||||
pass
|
||||
|
||||
|
||||
def OnCurrentIdentifierFinished( self ):
|
||||
pass
|
||||
|
||||
|
||||
def DiagnosticsForCurrentFileReady( self ):
|
||||
return False
|
||||
|
||||
|
||||
def GetDiagnosticsForCurrentFile( self ):
|
||||
return []
|
||||
|
||||
|
||||
@abc.abstractmethod
|
||||
def SupportedFiletypes( self ):
|
||||
pass
|
||||
|
||||
|
||||
@abc.abstractmethod
|
||||
def ShouldUseNow( self, start_column ):
|
||||
pass
|
0
python/completers/__init__.py
Normal file
0
python/completers/__init__.py
Normal file
157
python/completers/all.py
Normal file
157
python/completers/all.py
Normal file
@ -0,0 +1,157 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
from completer import Completer
|
||||
import vim
|
||||
import vimsupport
|
||||
import indexer
|
||||
import utils
|
||||
|
||||
MAX_IDENTIFIER_COMPLETIONS_RETURNED = 10
|
||||
MIN_NUM_CHARS = int( vim.eval( "g:ycm_min_num_of_chars_for_completion" ) )
|
||||
|
||||
|
||||
def GetCompleter():
|
||||
return IdentifierCompleter()
|
||||
|
||||
|
||||
class IdentifierCompleter( Completer ):
|
||||
def __init__( self ):
|
||||
self.completer = indexer.IdentifierCompleter()
|
||||
self.completer.EnableThreading()
|
||||
|
||||
|
||||
def SupportedFiletypes( self ):
|
||||
# magic token meaning all filetypes
|
||||
return set( [ 'ycm_all' ] )
|
||||
|
||||
|
||||
# TODO: implement this
|
||||
def ShouldUseNow( self, start_column ):
|
||||
return True
|
||||
|
||||
|
||||
def CandidatesForQueryAsync( self, query ):
|
||||
filetype = vim.eval( "&filetype" )
|
||||
self.future = self.completer.CandidatesForQueryAndTypeAsync(
|
||||
utils.SanitizeQuery( query ),
|
||||
filetype )
|
||||
|
||||
|
||||
def AddIdentifier( self, identifier ):
|
||||
filetype = vim.eval( "&filetype" )
|
||||
filepath = vim.eval( "expand('%:p')" )
|
||||
|
||||
if not filetype or not filepath or not identifier:
|
||||
return
|
||||
|
||||
vector = indexer.StringVec()
|
||||
vector.append( identifier )
|
||||
self.completer.AddCandidatesToDatabase( vector,
|
||||
filetype,
|
||||
filepath )
|
||||
|
||||
|
||||
def AddPreviousIdentifier( self ):
|
||||
self.AddIdentifier( PreviousIdentifier() )
|
||||
|
||||
|
||||
def AddIdentifierUnderCursor( self ):
|
||||
cursor_identifier = vim.eval( 'expand("<cword>")' )
|
||||
if not cursor_identifier:
|
||||
return
|
||||
|
||||
stripped_cursor_identifier = ''.join( ( x for x in
|
||||
cursor_identifier if
|
||||
utils.IsIdentifierChar( x ) ) )
|
||||
if not stripped_cursor_identifier:
|
||||
return
|
||||
|
||||
self.AddIdentifier( stripped_cursor_identifier )
|
||||
|
||||
|
||||
def AddBufferIdentifiers( self ):
|
||||
filetype = vim.eval( "&filetype" )
|
||||
filepath = vim.eval( "expand('%:p')" )
|
||||
|
||||
if not filetype or not filepath:
|
||||
return
|
||||
|
||||
text = "\n".join( vim.current.buffer )
|
||||
self.completer.AddCandidatesToDatabaseFromBufferAsync( text,
|
||||
filetype,
|
||||
filepath )
|
||||
|
||||
|
||||
def OnFileReadyToParse( self ):
|
||||
self.AddBufferIdentifiers()
|
||||
|
||||
|
||||
def OnInsertLeave( self ):
|
||||
self.AddIdentifierUnderCursor()
|
||||
|
||||
|
||||
def OnCurrentIdentifierFinished( self ):
|
||||
self.AddPreviousIdentifier()
|
||||
|
||||
|
||||
def CandidatesFromStoredRequest( self ):
|
||||
if not self.future:
|
||||
return []
|
||||
completions = self.future.GetResults()[
|
||||
: MAX_IDENTIFIER_COMPLETIONS_RETURNED ]
|
||||
|
||||
# We will never have duplicates in completions so with 'dup':1 we tell Vim
|
||||
# to add this candidate even if it's a duplicate of an existing one (which
|
||||
# will never happen). This saves us some expensive string matching
|
||||
# operations in Vim.
|
||||
return [ { 'word': x, 'dup': 1 } for x in completions ]
|
||||
|
||||
|
||||
def PreviousIdentifier():
|
||||
line_num, column_num = vimsupport.CurrentLineAndColumn()
|
||||
buffer = vim.current.buffer
|
||||
line = buffer[ line_num ]
|
||||
|
||||
end_column = column_num
|
||||
|
||||
while end_column > 0 and not utils.IsIdentifierChar( line[ end_column - 1 ] ):
|
||||
end_column -= 1
|
||||
|
||||
# Look at the previous line if we reached the end of the current one
|
||||
if end_column == 0:
|
||||
try:
|
||||
line = buffer[ line_num - 1]
|
||||
except:
|
||||
return ""
|
||||
end_column = len( line )
|
||||
while end_column > 0 and not utils.IsIdentifierChar(
|
||||
line[ end_column - 1 ] ):
|
||||
end_column -= 1
|
||||
print end_column, line
|
||||
|
||||
start_column = end_column
|
||||
while start_column > 0 and utils.IsIdentifierChar( line[ start_column - 1 ] ):
|
||||
start_column -= 1
|
||||
|
||||
if end_column - start_column < MIN_NUM_CHARS:
|
||||
return ""
|
||||
|
||||
return line[ start_column : end_column ]
|
||||
|
281
python/completers/cpp.py
Normal file
281
python/completers/cpp.py
Normal file
@ -0,0 +1,281 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
from completer import Completer
|
||||
import vim
|
||||
import vimsupport
|
||||
import indexer
|
||||
import random
|
||||
import imp
|
||||
import os
|
||||
import string
|
||||
|
||||
CLANG_FILETYPES = set( [ 'c', 'cpp', 'objc', 'objcpp' ] )
|
||||
CLANG_OPTIONS_FILENAME = '.ycm_clang_options.py'
|
||||
|
||||
def GetCompleter():
|
||||
return ClangCompleter()
|
||||
|
||||
|
||||
class ClangCompleter( Completer ):
|
||||
def __init__( self ):
|
||||
self.completer = indexer.ClangCompleter()
|
||||
self.completer.EnableThreading()
|
||||
self.contents_holder = []
|
||||
self.filename_holder = []
|
||||
self.last_diagnostics = []
|
||||
self.possibly_new_diagnostics = False
|
||||
self.flags = Flags()
|
||||
|
||||
|
||||
def SupportedFiletypes( self ):
|
||||
return CLANG_FILETYPES
|
||||
|
||||
|
||||
def GetUnsavedFilesVector( self ):
|
||||
files = indexer.UnsavedFileVec()
|
||||
self.contents_holder = []
|
||||
self.filename_holder = []
|
||||
for buffer in vimsupport.GetUnsavedBuffers():
|
||||
if not ClangAvailableForBuffer( buffer ):
|
||||
continue
|
||||
contents = '\n'.join( buffer )
|
||||
name = buffer.name
|
||||
if not contents or not name:
|
||||
continue
|
||||
self.contents_holder.append( contents )
|
||||
self.filename_holder.append( name )
|
||||
|
||||
unsaved_file = indexer.UnsavedFile()
|
||||
unsaved_file.contents_ = self.contents_holder[ -1 ]
|
||||
unsaved_file.length_ = len( self.contents_holder[ -1 ] )
|
||||
unsaved_file.filename_ = self.filename_holder[ -1 ]
|
||||
|
||||
files.append( unsaved_file )
|
||||
|
||||
return files
|
||||
|
||||
|
||||
def CandidatesForQueryAsync( self, query ):
|
||||
if self.completer.UpdatingTranslationUnit():
|
||||
vimsupport.PostVimMessage( 'Still parsing file, no completions yet.' )
|
||||
self.future = None
|
||||
return
|
||||
|
||||
# TODO: sanitize query
|
||||
|
||||
# CAREFUL HERE! For UnsavedFile filename and contents we are referring
|
||||
# directly to Python-allocated and -managed memory since we are accepting
|
||||
# pointers to data members of python objects. We need to ensure that those
|
||||
# objects outlive our UnsavedFile objects. This is why we need the
|
||||
# contents_holder and filename_holder lists, to make sure the string objects
|
||||
# are still around when we call CandidatesForQueryAndLocationInFile. We do
|
||||
# this to avoid an extra copy of the entire file contents.
|
||||
|
||||
files = indexer.UnsavedFileVec()
|
||||
if not query:
|
||||
files = self.GetUnsavedFilesVector()
|
||||
|
||||
line, _ = vim.current.window.cursor
|
||||
column = int( vim.eval( "s:completion_start_column" ) ) + 1
|
||||
current_buffer = vim.current.buffer
|
||||
self.future = self.completer.CandidatesForQueryAndLocationInFileAsync(
|
||||
query,
|
||||
current_buffer.name,
|
||||
line,
|
||||
column,
|
||||
files,
|
||||
self.flags.FlagsForFile( current_buffer.name ) )
|
||||
|
||||
|
||||
def CandidatesFromStoredRequest( self ):
|
||||
if not self.future:
|
||||
return []
|
||||
results = [ CompletionDataToDict( x ) for x in self.future.GetResults() ]
|
||||
if not results:
|
||||
vimsupport.PostVimMessage( 'No completions found; errors in the file?' )
|
||||
return results
|
||||
|
||||
|
||||
def OnFileReadyToParse( self ):
|
||||
if vimsupport.NumLinesInBuffer( vim.current.buffer ) < 5:
|
||||
return
|
||||
|
||||
self.possibly_new_diagnostics = True
|
||||
|
||||
filename = vim.current.buffer.name
|
||||
self.completer.UpdateTranslationUnitAsync(
|
||||
filename,
|
||||
self.GetUnsavedFilesVector(),
|
||||
self.flags.FlagsForFile( filename ) )
|
||||
|
||||
|
||||
def DiagnosticsForCurrentFileReady( self ):
|
||||
return ( self.possibly_new_diagnostics and not
|
||||
self.completer.UpdatingTranslationUnit() )
|
||||
|
||||
|
||||
def GetDiagnosticsForCurrentFile( self ):
|
||||
if self.DiagnosticsForCurrentFileReady():
|
||||
self.last_diagnostics = [ DiagnosticToDict( x ) for x in
|
||||
self.completer.DiagnosticsForFile(
|
||||
vim.current.buffer.name ) ]
|
||||
self.possibly_new_diagnostics = False
|
||||
return self.last_diagnostics
|
||||
|
||||
|
||||
def ShouldUseNow( self, start_column ):
|
||||
return ShouldUseClang( start_column )
|
||||
|
||||
|
||||
class Flags( object ):
|
||||
def __init__( self ):
|
||||
# It's caches all the way down...
|
||||
self.flags_for_file = {}
|
||||
self.flags_module_for_file = {}
|
||||
self.flags_module_for_flags_module_file = {}
|
||||
|
||||
|
||||
def FlagsForFile( self, filename ):
|
||||
try:
|
||||
return self.flags_for_file[ filename ]
|
||||
except KeyError:
|
||||
flags_module = self.FlagsModuleForFile( filename )
|
||||
if not flags_module:
|
||||
return indexer.StringVec()
|
||||
|
||||
results = flags_module.FlagsForFile( filename )
|
||||
sanitized_flags = SanitizeFlags( results[ 'flags' ] )
|
||||
|
||||
if results[ 'do_cache' ]:
|
||||
self.flags_for_file[ filename ] = sanitized_flags
|
||||
return sanitized_flags
|
||||
|
||||
|
||||
def FlagsModuleForFile( self, filename ):
|
||||
try:
|
||||
return self.flags_module_for_file[ filename ]
|
||||
except KeyError:
|
||||
flags_module_file = FlagsModuleSourceFileForFile( filename )
|
||||
if not flags_module_file:
|
||||
return None
|
||||
|
||||
try:
|
||||
flags_module = self.flags_module_for_flags_module_file[
|
||||
flags_module_file ]
|
||||
except KeyError:
|
||||
flags_module = imp.load_source( RandomName(), flags_module_file )
|
||||
self.flags_module_for_flags_module_file[
|
||||
flags_module_file ] = flags_module
|
||||
|
||||
self.flags_module_for_file[ filename ] = flags_module
|
||||
return flags_module
|
||||
|
||||
|
||||
def FlagsModuleSourceFileForFile( filename ):
|
||||
parent_folder = os.path.dirname( filename )
|
||||
old_parent_folder = ''
|
||||
|
||||
while True:
|
||||
current_file = os.path.join( parent_folder, CLANG_OPTIONS_FILENAME )
|
||||
if os.path.exists( current_file ):
|
||||
return current_file
|
||||
|
||||
old_parent_folder = parent_folder
|
||||
parent_folder = os.path.dirname( parent_folder )
|
||||
|
||||
if parent_folder == old_parent_folder:
|
||||
return None
|
||||
|
||||
|
||||
|
||||
def RandomName():
|
||||
return ''.join( random.choice( string.ascii_lowercase ) for x in range( 15 ) )
|
||||
|
||||
|
||||
def SanitizeFlags( flags ):
|
||||
sanitized_flags = []
|
||||
saw_arch = False
|
||||
for i, flag in enumerate( flags ):
|
||||
if flag == '-arch':
|
||||
saw_arch = True
|
||||
continue
|
||||
elif flag.startswith( '-arch' ):
|
||||
continue
|
||||
elif saw_arch:
|
||||
saw_arch = False
|
||||
continue
|
||||
|
||||
sanitized_flags.append( flag )
|
||||
|
||||
vector = indexer.StringVec()
|
||||
for flag in sanitized_flags:
|
||||
vector.append( flag )
|
||||
return vector
|
||||
|
||||
|
||||
def CompletionDataToDict( completion_data ):
|
||||
# see :h complete-items for a description of the dictionary fields
|
||||
return {
|
||||
'word' : completion_data.TextToInsertInBuffer(),
|
||||
'abbr' : completion_data.MainCompletionText(),
|
||||
'menu' : completion_data.ExtraMenuInfo(),
|
||||
'kind' : completion_data.kind_,
|
||||
'dup' : 1,
|
||||
# TODO: add detailed_info_ as 'info'
|
||||
}
|
||||
|
||||
|
||||
def DiagnosticToDict( diagnostic ):
|
||||
# see :h getqflist for a description of the dictionary fields
|
||||
return {
|
||||
'bufnr' : int( vim.eval( "bufnr('{0}', 1)".format(
|
||||
diagnostic.filename_ ) ) ),
|
||||
'lnum' : diagnostic.line_number_,
|
||||
'col' : diagnostic.column_number_,
|
||||
'text' : diagnostic.text_,
|
||||
'type' : diagnostic.kind_,
|
||||
'valid' : 1
|
||||
}
|
||||
|
||||
|
||||
def ClangAvailableForBuffer( buffer_object ):
|
||||
filetype = vim.eval( 'getbufvar({0}, "&ft")'.format( buffer_object.number ) )
|
||||
return filetype in CLANG_FILETYPES
|
||||
|
||||
|
||||
def ShouldUseClang( start_column ):
|
||||
line = vim.current.line
|
||||
previous_char_index = start_column - 1
|
||||
if ( not len( line ) or
|
||||
previous_char_index < 0 or
|
||||
previous_char_index >= len( line ) ):
|
||||
return False
|
||||
|
||||
if line[ previous_char_index ] == '.':
|
||||
return True
|
||||
|
||||
if previous_char_index - 1 < 0:
|
||||
return False
|
||||
|
||||
two_previous_chars = line[ previous_char_index - 1 : start_column ]
|
||||
if ( two_previous_chars == '->' or two_previous_chars == '::' ):
|
||||
return True
|
||||
|
||||
return False
|
25
python/utils.py
Normal file
25
python/utils.py
Normal file
@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
def IsIdentifierChar( char ):
|
||||
return char.isalnum() or char == '_'
|
||||
|
||||
|
||||
def SanitizeQuery( query ):
|
||||
return query.strip()
|
66
python/vimsupport.py
Normal file
66
python/vimsupport.py
Normal file
@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
import vim
|
||||
|
||||
def CurrentLineAndColumn():
|
||||
# See the comment in CurrentColumn about the calculation for the line and
|
||||
# column number
|
||||
line, column = vim.current.window.cursor
|
||||
line -= 1
|
||||
return line, column
|
||||
|
||||
|
||||
def CurrentColumn():
|
||||
"""Do NOT access the CurrentColumn in vim.current.line. It doesn't exist yet.
|
||||
Only the chars before the current column exist in vim.current.line."""
|
||||
|
||||
# vim's columns are 1-based while vim.current.line columns are 0-based
|
||||
# ... but vim.current.window.cursor (which returns a (line, column) tuple)
|
||||
# columns are 0-based, while the line from that same tuple is 1-based.
|
||||
# vim.buffers buffer objects OTOH have 0-based lines and columns.
|
||||
# Pigs have wings and I'm a loopy purple duck. Everything makes sense now.
|
||||
return vim.current.window.cursor[ 1 ]
|
||||
|
||||
|
||||
def GetUnsavedBuffers():
|
||||
def BufferModified( buffer_number ):
|
||||
to_eval = 'getbufvar({0}, "&mod")'.format( buffer_number )
|
||||
return bool( int( vim.eval( to_eval ) ) )
|
||||
|
||||
return ( x for x in vim.buffers if BufferModified( x.number ) )
|
||||
|
||||
|
||||
def NumLinesInBuffer( buffer ):
|
||||
# This is actually less than obvious, that's why it's wrapped in a function
|
||||
return len( buffer )
|
||||
|
||||
|
||||
def PostVimMessage( message ):
|
||||
# TODO: escape the message string before formating it
|
||||
vim.command( 'echohl WarningMsg | echomsg "{0}" | echohl None'
|
||||
.format( message ) )
|
||||
|
||||
|
||||
def EscapeForVim( text ):
|
||||
return text.replace( "'", "''" )
|
||||
|
||||
|
||||
def CurrentFiletype():
|
||||
return vim.eval( "&filetype" )
|
482
python/ycm.py
482
python/ycm.py
@ -17,408 +17,105 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import vim
|
||||
import indexer
|
||||
import abc
|
||||
import vimsupport
|
||||
import imp
|
||||
import string
|
||||
import random
|
||||
import vim
|
||||
import utils
|
||||
import os
|
||||
|
||||
MIN_NUM_CHARS = int( vim.eval( "g:ycm_min_num_of_chars_for_completion" ) )
|
||||
CLANG_COMPLETION_ENABLED = int( vim.eval( "g:ycm_clang_completion_enabled" ) )
|
||||
CLANG_FILETYPES = set( [ 'c', 'cpp', 'objc', 'objcpp' ] )
|
||||
MAX_IDENTIFIER_COMPLETIONS_RETURNED = 10
|
||||
CLANG_OPTIONS_FILENAME = '.ycm_clang_options.py'
|
||||
from completers.all import IdentifierCompleter
|
||||
|
||||
|
||||
class Completer( object ):
|
||||
__metaclass__ = abc.ABCMeta
|
||||
|
||||
class YouCompleteMe( object ):
|
||||
def __init__( self ):
|
||||
self.future = None
|
||||
|
||||
def AsyncCandidateRequestReady( self ):
|
||||
if not self.future:
|
||||
# We return True so that the caller can extract the default value from the
|
||||
# future
|
||||
return True
|
||||
return self.future.ResultsReady()
|
||||
self.identcomp = IdentifierCompleter()
|
||||
self.filetype_completers = {}
|
||||
|
||||
|
||||
def CandidatesFromStoredRequest( self ):
|
||||
if not self.future:
|
||||
return []
|
||||
return self.future.GetResults()
|
||||
|
||||
@abc.abstractmethod
|
||||
def OnFileReadyToParse( self ):
|
||||
pass
|
||||
def GetIdentifierCompleter( self ):
|
||||
return self.identcomp
|
||||
|
||||
|
||||
class IdentifierCompleter( Completer ):
|
||||
def __init__( self ):
|
||||
self.completer = indexer.IdentifierCompleter()
|
||||
self.completer.EnableThreading()
|
||||
def GetFiletypeCompleterForCurrentFile( self ):
|
||||
filetype = vimsupport.CurrentFiletype()
|
||||
try:
|
||||
return self.filetype_completers[ filetype ]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
module_path = _PathToFiletypeCompleterPlugin( filetype )
|
||||
|
||||
completer = None
|
||||
if os.path.exists( module_path ):
|
||||
module = imp.load_source( filetype, module_path )
|
||||
completer = module.GetCompleter()
|
||||
for supported_filetype in completer.SupportedFiletypes():
|
||||
self.filetype_completers[ supported_filetype ] = completer
|
||||
else:
|
||||
self.filetype_completers[ filetype ] = None
|
||||
return completer
|
||||
|
||||
|
||||
def CandidatesForQueryAsync( self, query ):
|
||||
filetype = vim.eval( "&filetype" )
|
||||
self.future = self.completer.CandidatesForQueryAndTypeAsync(
|
||||
SanitizeQuery( query ),
|
||||
filetype )
|
||||
def ShouldUseIdentifierCompleter( self, start_column ):
|
||||
return self.identcomp.ShouldUseNow( start_column )
|
||||
|
||||
|
||||
def AddIdentifier( self, identifier ):
|
||||
filetype = vim.eval( "&filetype" )
|
||||
filepath = vim.eval( "expand('%:p')" )
|
||||
|
||||
if not filetype or not filepath or not identifier:
|
||||
return
|
||||
|
||||
vector = indexer.StringVec()
|
||||
vector.append( identifier )
|
||||
self.completer.AddCandidatesToDatabase( vector,
|
||||
filetype,
|
||||
filepath )
|
||||
def ShouldUseFiletypeCompleter( self, start_column ):
|
||||
if self.FiletypeCompletionEnabledForCurrentFile():
|
||||
return self.GetFiletypeCompleterForCurrentFile().ShouldUseNow(
|
||||
start_column )
|
||||
return False
|
||||
|
||||
|
||||
def AddPreviousIdentifier( self ):
|
||||
self.AddIdentifier( PreviousIdentifier() )
|
||||
def FiletypeCompletionAvailableForFile( self ):
|
||||
return bool( self.GetFiletypeCompleterForCurrentFile() )
|
||||
|
||||
|
||||
def AddIdentifierUnderCursor( self ):
|
||||
cursor_identifier = vim.eval( 'expand("<cword>")' )
|
||||
if not cursor_identifier:
|
||||
return
|
||||
|
||||
stripped_cursor_identifier = ''.join( ( x for x in
|
||||
cursor_identifier if
|
||||
IsIdentifierChar( x ) ) )
|
||||
if not stripped_cursor_identifier:
|
||||
return
|
||||
|
||||
self.AddIdentifier( stripped_cursor_identifier )
|
||||
|
||||
|
||||
def AddBufferIdentifiers( self ):
|
||||
filetype = vim.eval( "&filetype" )
|
||||
filepath = vim.eval( "expand('%:p')" )
|
||||
|
||||
if not filetype or not filepath:
|
||||
return
|
||||
|
||||
text = "\n".join( vim.current.buffer )
|
||||
self.completer.AddCandidatesToDatabaseFromBufferAsync( text,
|
||||
filetype,
|
||||
filepath )
|
||||
def FiletypeCompletionEnabledForCurrentFile( self ):
|
||||
return ( bool( int( vim.eval( 'g:ycm_filetype_completion_enabled' ) ) ) and
|
||||
self.FiletypeCompletionAvailableForFile() )
|
||||
|
||||
|
||||
def OnFileReadyToParse( self ):
|
||||
self.AddBufferIdentifiers()
|
||||
self.identcomp.OnFileReadyToParse()
|
||||
|
||||
if self.FiletypeCompletionEnabledForCurrentFile():
|
||||
self.GetFiletypeCompleterForCurrentFile().OnFileReadyToParse()
|
||||
|
||||
|
||||
def CandidatesFromStoredRequest( self ):
|
||||
if not self.future:
|
||||
return []
|
||||
completions = self.future.GetResults()[
|
||||
: MAX_IDENTIFIER_COMPLETIONS_RETURNED ]
|
||||
def OnInsertLeave( self ):
|
||||
self.identcomp.OnInsertLeave()
|
||||
|
||||
# We will never have duplicates in completions so with 'dup':1 we tell Vim
|
||||
# to add this candidate even if it's a duplicate of an existing one (which
|
||||
# will never happen). This saves us some expensive string matching
|
||||
# operations in Vim.
|
||||
return [ { 'word': x, 'dup': 1 } for x in completions ]
|
||||
|
||||
|
||||
class ClangCompleter( Completer ):
|
||||
def __init__( self ):
|
||||
self.completer = indexer.ClangCompleter()
|
||||
self.completer.EnableThreading()
|
||||
self.contents_holder = []
|
||||
self.filename_holder = []
|
||||
self.last_diagnostics = []
|
||||
self.possibly_new_diagnostics = False
|
||||
self.flags = Flags()
|
||||
|
||||
|
||||
def GetUnsavedFilesVector( self ):
|
||||
files = indexer.UnsavedFileVec()
|
||||
self.contents_holder = []
|
||||
self.filename_holder = []
|
||||
for buffer in GetUnsavedBuffers():
|
||||
if not ClangAvailableForBuffer( buffer ):
|
||||
continue
|
||||
contents = '\n'.join( buffer )
|
||||
name = buffer.name
|
||||
if not contents or not name:
|
||||
continue
|
||||
self.contents_holder.append( contents )
|
||||
self.filename_holder.append( name )
|
||||
|
||||
unsaved_file = indexer.UnsavedFile()
|
||||
unsaved_file.contents_ = self.contents_holder[ -1 ]
|
||||
unsaved_file.length_ = len( self.contents_holder[ -1 ] )
|
||||
unsaved_file.filename_ = self.filename_holder[ -1 ]
|
||||
|
||||
files.append( unsaved_file )
|
||||
|
||||
return files
|
||||
|
||||
|
||||
def CandidatesForQueryAsync( self, query ):
|
||||
if self.completer.UpdatingTranslationUnit():
|
||||
PostVimMessage( 'Still parsing file, no completions yet.' )
|
||||
self.future = None
|
||||
return
|
||||
|
||||
# TODO: sanitize query
|
||||
|
||||
# CAREFUL HERE! For UnsavedFile filename and contents we are referring
|
||||
# directly to Python-allocated and -managed memory since we are accepting
|
||||
# pointers to data members of python objects. We need to ensure that those
|
||||
# objects outlive our UnsavedFile objects. This is why we need the
|
||||
# contents_holder and filename_holder lists, to make sure the string objects
|
||||
# are still around when we call CandidatesForQueryAndLocationInFile. We do
|
||||
# this to avoid an extra copy of the entire file contents.
|
||||
|
||||
files = indexer.UnsavedFileVec()
|
||||
if not query:
|
||||
files = self.GetUnsavedFilesVector()
|
||||
|
||||
line, _ = vim.current.window.cursor
|
||||
column = int( vim.eval( "s:completion_start_column" ) ) + 1
|
||||
current_buffer = vim.current.buffer
|
||||
self.future = self.completer.CandidatesForQueryAndLocationInFileAsync(
|
||||
query,
|
||||
current_buffer.name,
|
||||
line,
|
||||
column,
|
||||
files,
|
||||
self.flags.FlagsForFile( current_buffer.name ) )
|
||||
|
||||
|
||||
def CandidatesFromStoredRequest( self ):
|
||||
if not self.future:
|
||||
return []
|
||||
results = [ CompletionDataToDict( x ) for x in self.future.GetResults() ]
|
||||
if not results:
|
||||
PostVimMessage( 'No completions found; errors in the file?' )
|
||||
return results
|
||||
|
||||
|
||||
def OnFileReadyToParse( self ):
|
||||
if NumLinesInBuffer( vim.current.buffer ) < 5:
|
||||
return
|
||||
|
||||
self.possibly_new_diagnostics = True
|
||||
|
||||
filename = vim.current.buffer.name
|
||||
self.completer.UpdateTranslationUnitAsync(
|
||||
filename,
|
||||
self.GetUnsavedFilesVector(),
|
||||
self.flags.FlagsForFile( filename ) )
|
||||
if self.FiletypeCompletionEnabledForCurrentFile():
|
||||
self.GetFiletypeCompleterForCurrentFile().OnInsertLeave()
|
||||
|
||||
|
||||
def DiagnosticsForCurrentFileReady( self ):
|
||||
return ( self.possibly_new_diagnostics and not
|
||||
self.completer.UpdatingTranslationUnit() )
|
||||
if self.FiletypeCompletionEnabledForCurrentFile():
|
||||
return self.GetFiletypeCompleterForCurrentFile().DiagnosticsForCurrentFileReady()
|
||||
return False
|
||||
|
||||
|
||||
def OnCurrentIdentifierFinished( self ):
|
||||
self.identcomp.OnCurrentIdentifierFinished()
|
||||
|
||||
if self.FiletypeCompletionEnabledForCurrentFile():
|
||||
self.GetFiletypeCompleterForCurrentFile().OnCurrentIdentifierFinished()
|
||||
|
||||
|
||||
def GetDiagnosticsForCurrentFile( self ):
|
||||
if self.DiagnosticsForCurrentFileReady():
|
||||
self.last_diagnostics = [ DiagnosticToDict( x ) for x in
|
||||
self.completer.DiagnosticsForFile(
|
||||
vim.current.buffer.name ) ]
|
||||
self.possibly_new_diagnostics = False
|
||||
return self.last_diagnostics
|
||||
if self.FiletypeCompletionEnabledForCurrentFile():
|
||||
return self.GetFiletypeCompleterForCurrentFile().GetDiagnosticsForCurrentFile()
|
||||
return []
|
||||
|
||||
|
||||
class Flags( object ):
|
||||
def __init__( self ):
|
||||
# It's caches all the way down...
|
||||
self.flags_for_file = {}
|
||||
self.flags_module_for_file = {}
|
||||
self.flags_module_for_flags_module_file = {}
|
||||
def _PathToCompletersFolder():
|
||||
dir_of_current_script = os.path.dirname( os.path.abspath( __file__ ) )
|
||||
return os.path.join( dir_of_current_script, 'completers' )
|
||||
|
||||
|
||||
def FlagsForFile( self, filename ):
|
||||
try:
|
||||
return self.flags_for_file[ filename ]
|
||||
except KeyError:
|
||||
flags_module = self.FlagsModuleForFile( filename )
|
||||
if not flags_module:
|
||||
return indexer.StringVec()
|
||||
def _PathToFiletypeCompleterPlugin( filetype ):
|
||||
return os.path.join( _PathToCompletersFolder(), filetype + '.py' )
|
||||
|
||||
results = flags_module.FlagsForFile( filename )
|
||||
sanitized_flags = SanitizeFlags( results[ 'flags' ] )
|
||||
|
||||
if results[ 'do_cache' ]:
|
||||
self.flags_for_file[ filename ] = sanitized_flags
|
||||
return sanitized_flags
|
||||
|
||||
|
||||
def FlagsModuleForFile( self, filename ):
|
||||
try:
|
||||
return self.flags_module_for_file[ filename ]
|
||||
except KeyError:
|
||||
flags_module_file = FlagsModuleSourceFileForFile( filename )
|
||||
if not flags_module_file:
|
||||
return None
|
||||
|
||||
try:
|
||||
flags_module = self.flags_module_for_flags_module_file[
|
||||
flags_module_file ]
|
||||
except KeyError:
|
||||
flags_module = imp.load_source( RandomName(), flags_module_file )
|
||||
self.flags_module_for_flags_module_file[
|
||||
flags_module_file ] = flags_module
|
||||
|
||||
self.flags_module_for_file[ filename ] = flags_module
|
||||
return flags_module
|
||||
|
||||
|
||||
def FlagsModuleSourceFileForFile( filename ):
|
||||
parent_folder = os.path.dirname( filename )
|
||||
old_parent_folder = ''
|
||||
|
||||
while True:
|
||||
current_file = os.path.join( parent_folder, CLANG_OPTIONS_FILENAME )
|
||||
if os.path.exists( current_file ):
|
||||
return current_file
|
||||
|
||||
old_parent_folder = parent_folder
|
||||
parent_folder = os.path.dirname( parent_folder )
|
||||
|
||||
if parent_folder == old_parent_folder:
|
||||
return None
|
||||
|
||||
|
||||
|
||||
def RandomName():
|
||||
return ''.join( random.choice( string.ascii_lowercase ) for x in range( 15 ) )
|
||||
|
||||
|
||||
def SanitizeFlags( flags ):
|
||||
sanitized_flags = []
|
||||
saw_arch = False
|
||||
for i, flag in enumerate( flags ):
|
||||
if flag == '-arch':
|
||||
saw_arch = True
|
||||
continue
|
||||
elif flag.startswith( '-arch' ):
|
||||
continue
|
||||
elif saw_arch:
|
||||
saw_arch = False
|
||||
continue
|
||||
|
||||
sanitized_flags.append( flag )
|
||||
|
||||
vector = indexer.StringVec()
|
||||
for flag in sanitized_flags:
|
||||
vector.append( flag )
|
||||
return vector
|
||||
|
||||
|
||||
def NumLinesInBuffer( buffer ):
|
||||
# This is actually less than obvious, that's why it's wrapped in a function
|
||||
return len( buffer )
|
||||
|
||||
def PostVimMessage( message ):
|
||||
# TODO: escape the message string before formating it
|
||||
vim.command( 'echohl WarningMsg | echomsg "{0}" | echohl None'
|
||||
.format( message ) )
|
||||
|
||||
|
||||
def GetUnsavedBuffers():
|
||||
def BufferModified( buffer_number ):
|
||||
to_eval = 'getbufvar({0}, "&mod")'.format( buffer_number )
|
||||
return bool( int( vim.eval( to_eval ) ) )
|
||||
|
||||
return ( x for x in vim.buffers if BufferModified( x.number ) )
|
||||
|
||||
|
||||
def CompletionDataToDict( completion_data ):
|
||||
# see :h complete-items for a description of the dictionary fields
|
||||
return {
|
||||
'word' : completion_data.TextToInsertInBuffer(),
|
||||
'abbr' : completion_data.MainCompletionText(),
|
||||
'menu' : completion_data.ExtraMenuInfo(),
|
||||
'kind' : completion_data.kind_,
|
||||
'dup' : 1,
|
||||
# TODO: add detailed_info_ as 'info'
|
||||
}
|
||||
|
||||
|
||||
def DiagnosticToDict( diagnostic ):
|
||||
# see :h getqflist for a description of the dictionary fields
|
||||
return {
|
||||
'bufnr' : int( vim.eval( "bufnr('{0}', 1)".format(
|
||||
diagnostic.filename_ ) ) ),
|
||||
'lnum' : diagnostic.line_number_,
|
||||
'col' : diagnostic.column_number_,
|
||||
'text' : diagnostic.text_,
|
||||
'type' : diagnostic.kind_,
|
||||
'valid' : 1
|
||||
}
|
||||
|
||||
|
||||
def CurrentColumn():
|
||||
"""Do NOT access the CurrentColumn in vim.current.line. It doesn't exist yet.
|
||||
Only the chars before the current column exist in vim.current.line."""
|
||||
|
||||
# vim's columns are 1-based while vim.current.line columns are 0-based
|
||||
# ... but vim.current.window.cursor (which returns a (line, column) tuple)
|
||||
# columns are 0-based, while the line from that same tuple is 1-based.
|
||||
# vim.buffers buffer objects OTOH have 0-based lines and columns.
|
||||
# Pigs have wings and I'm a loopy purple duck. Everything makes sense now.
|
||||
return vim.current.window.cursor[ 1 ]
|
||||
|
||||
|
||||
def CurrentLineAndColumn():
|
||||
# See the comment in CurrentColumn about the calculation for the line and
|
||||
# column number
|
||||
line, column = vim.current.window.cursor
|
||||
line -= 1
|
||||
return line, column
|
||||
|
||||
|
||||
def ClangAvailableForBuffer( buffer_object ):
|
||||
filetype = vim.eval( 'getbufvar({0}, "&ft")'.format( buffer_object.number ) )
|
||||
return filetype in CLANG_FILETYPES
|
||||
|
||||
|
||||
def ClangAvailableForFile():
|
||||
filetype = vim.eval( "&filetype" )
|
||||
return filetype in CLANG_FILETYPES
|
||||
|
||||
|
||||
def ShouldUseClang( start_column ):
|
||||
if not CLANG_COMPLETION_ENABLED or not ClangAvailableForFile():
|
||||
return False
|
||||
|
||||
line = vim.current.line
|
||||
previous_char_index = start_column - 1
|
||||
if ( not len( line ) or
|
||||
previous_char_index < 0 or
|
||||
previous_char_index >= len( line ) ):
|
||||
return False
|
||||
|
||||
if line[ previous_char_index ] == '.':
|
||||
return True
|
||||
|
||||
if previous_char_index - 1 < 0:
|
||||
return False
|
||||
|
||||
two_previous_chars = line[ previous_char_index - 1 : start_column ]
|
||||
if ( two_previous_chars == '->' or two_previous_chars == '::' ):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def IsIdentifierChar( char ):
|
||||
return char.isalnum() or char == '_'
|
||||
|
||||
|
||||
def CompletionStartColumn():
|
||||
@ -430,50 +127,15 @@ def CompletionStartColumn():
|
||||
"""
|
||||
|
||||
line = vim.current.line
|
||||
start_column = CurrentColumn()
|
||||
start_column = vimsupport.CurrentColumn()
|
||||
|
||||
while start_column > 0 and IsIdentifierChar( line[ start_column - 1 ] ):
|
||||
while start_column > 0 and utils.IsIdentifierChar( line[ start_column - 1 ] ):
|
||||
start_column -= 1
|
||||
return start_column
|
||||
|
||||
|
||||
def EscapeForVim( text ):
|
||||
return text.replace( "'", "''" )
|
||||
|
||||
|
||||
def PreviousIdentifier():
|
||||
line_num, column_num = CurrentLineAndColumn()
|
||||
buffer = vim.current.buffer
|
||||
line = buffer[ line_num ]
|
||||
|
||||
end_column = column_num
|
||||
|
||||
while end_column > 0 and not IsIdentifierChar( line[ end_column - 1 ] ):
|
||||
end_column -= 1
|
||||
|
||||
# Look at the previous line if we reached the end of the current one
|
||||
if end_column == 0:
|
||||
try:
|
||||
line = buffer[ line_num - 1]
|
||||
except:
|
||||
return ""
|
||||
end_column = len( line )
|
||||
while end_column > 0 and not IsIdentifierChar( line[ end_column - 1 ] ):
|
||||
end_column -= 1
|
||||
print end_column, line
|
||||
|
||||
start_column = end_column
|
||||
while start_column > 0 and IsIdentifierChar( line[ start_column - 1 ] ):
|
||||
start_column -= 1
|
||||
|
||||
if end_column - start_column < MIN_NUM_CHARS:
|
||||
return ""
|
||||
|
||||
return line[ start_column : end_column ]
|
||||
|
||||
|
||||
def CurrentIdentifierFinished():
|
||||
current_column = CurrentColumn()
|
||||
current_column = vimsupport.CurrentColumn()
|
||||
previous_char_index = current_column - 1
|
||||
if previous_char_index < 0:
|
||||
return True
|
||||
@ -483,16 +145,14 @@ def CurrentIdentifierFinished():
|
||||
except IndexError:
|
||||
return False
|
||||
|
||||
if IsIdentifierChar( previous_char ):
|
||||
if utils.IsIdentifierChar( previous_char ):
|
||||
return False
|
||||
|
||||
if ( not IsIdentifierChar( previous_char ) and
|
||||
if ( not utils.IsIdentifierChar( previous_char ) and
|
||||
previous_char_index > 0 and
|
||||
IsIdentifierChar( line[ previous_char_index - 1 ] ) ):
|
||||
utils.IsIdentifierChar( line[ previous_char_index - 1 ] ) ):
|
||||
return True
|
||||
else:
|
||||
return line[ : current_column ].isspace()
|
||||
|
||||
|
||||
def SanitizeQuery( query ):
|
||||
return query.strip()
|
||||
|
Loading…
Reference in New Issue
Block a user