Commit d81fc097 authored by Onur Aslan's avatar Onur Aslan

Imported Upstream version 0+20150806+gitc5a5145

parent de3c62ed
......@@ -51,10 +51,10 @@ Here are the things you should do when creating an issue:
let g:ycm_server_log_level = 'debug'
```
Then, if possible, start gvim/macvim (not console vim) from the console.
As you use Vim, you'll see the `ycmd` debug output stream in the console.
If you can not use gvim/macvim, run `:YcmDebugInfo` in vim to see what
temporary files (listed under "Server logfiles") the debug output streams
Then, if possible, start gvim/macvim (not console vim) from the console.
As you use Vim, you'll see the `ycmd` debug output stream in the console.
If you can not use gvim/macvim, run `:YcmDebugInfo` in vim to see what
temporary files (listed under "Server logfiles") the debug output streams
are written to. Attach the debug output stream to your issue.
3. **Create a test case for your issue**. This is critical. Don't talk about how
"when I have X in my file" or similar, _create a file with X in it_ and put
......@@ -109,13 +109,6 @@ Creating good pull requests
sometimes what you want can be done in a different way if the reason for the
change is known. _What goal is your change trying to accomplish?_
6. **Sign the Google [Contributor License Agreement][cla]** (you can sign
online at the bottom of that page). You _must_ sign this form, otherwise we
cannot merge in your changes. **_Always_ mention in the pull request that
you've signed it**, even if you signed it for a previous pull request (you
only need to sign the CLA once).
[build-bots]: https://travis-ci.org/Valloric/YouCompleteMe
[ycm-users]: https://groups.google.com/forum/?hl=en#!forum/ycm-users
[cla]: https://developers.google.com/open-source/cla/individual
......@@ -655,7 +655,15 @@ You may want to map this command to a key; try putting `nnoremap <F5>
### The `:YcmDiags` command
Calling this command will fill Vim's `locationlist` with errors or warnings if
any were detected in your file and then open it.
any were detected in your file and then open it. If a given error or warning can
be fixed by a call to `:YcmCompleter FixIt`, then ` (FixIt available)` is
appended to the error or warning text. See the `FixIt` completer subcommand for
more information.
NOTE: The absense of ` (FixIt available)` does not strictly imply a fix-it is
not available as not all completers are able to provide this indication. For
example, the c-sharp completer provides many fix-its but does not add this
additional indication.
The `g:ycm_open_loclist_on_ycm_diags` option can be used to prevent the location
list from opening, but still have it filled with new diagnostic data. See the
......@@ -817,6 +825,40 @@ NOTE: Causes reparsing of the current translation unit.
Supported in filetypes: `c, cpp, objc, objcpp`
### The `FixIt` subcommand
Where available, attempts to make changes to the buffer to correct the
diagnostic closest to the cursor position.
Completers which provide diagnostics may also provide trivial modifications to
the source in order to correct the diagnostic. Examples include syntax errors
such as missing trailing semi-colons, spurious characters, or other errors which
the semantic engine can deterministically suggest corrections.
If no fix-it is available for the current line, or there is no diagnostic on the
current line, this command has no effect on the current buffer. If any
modifications are made, the number of changes made to the buffer is echo'd and
the user may use the editor's undo command to revert.
When a diagnostic is available, and `g:ycm_echo_current_diagnostic` is set to 1,
then the text ` (FixIt)` is appended to the echo'd diagnostic when the
completer is able to add this indication. The text ` (FixIt available)` is
also appended to the diagnostic text in the output of the `:YcmDiags` command
for any diagnostics with available fix-its (where the completer can provide this
indication).
NOTE: Causes re-parsing of the current translation unit.
NOTE: After applying a fix-it, the diagnostics UI is not immediately updated.
This is due to a technical restriction in vim, and moving the cursor, or issuing
the the `:YcmForceCompileAndDiagnostics` command will refresh the diagnostics.
Repeated invocations of the `FixIt` command on a given line, however, _do_ apply
all diagnostics as expected without requiring refreshing of the diagnostics UI.
This is particularly useful where there are multiple diagnostics on one line, or
where after fixing one diagnostic, another fix-it is available.
Supported in filetypes: `c, cpp, objc, objcpp, cs`
### The `StartServer` subcommand
Starts the semantic-engine-as-localhost-server for those semantic engines that
......@@ -1075,7 +1117,8 @@ Default: `1`
### The `g:ycm_echo_current_diagnostic` option
When this option is set, YCM will echo the text of the diagnostic present on the
current line when you move your cursor to that line.
current line when you move your cursor to that line. If a `FixIt` is available
for the current diagnostic, then ` (FixIt)` is appended.
This option is part of the Syntastic compatibility layer; if the option is not
set, YCM will fall back to the value of the `g:syntastic_echo_current_error`
......@@ -1523,13 +1566,13 @@ Default: `[see next line]`
let g:ycm_semantic_triggers = {
\ 'c' : ['->', '.'],
\ 'objc' : ['->', '.'],
\ 'objc' : ['->', '.', 're!\[[_a-zA-Z]+\w*\s', 're!^\s*[^\W\d]\w*\s',
\ 're!\[.*\]\s'],
\ 'ocaml' : ['.', '#'],
\ 'cpp,objcpp' : ['->', '.', '::'],
\ 'perl' : ['->'],
\ 'php' : ['->', '::'],
\ 'cs,java,javascript,d,python,perl6,scala,vb,elixir,go' : ['.'],
\ 'vim' : ['re![_a-zA-Z]+[_\w]*\.'],
\ 'cs,java,javascript,typescript,d,python,perl6,scala,vb,elixir,go' : ['.'],
\ 'ruby' : ['.', '::'],
\ 'lua' : ['.', ':'],
\ 'erlang' : [':'],
......@@ -1957,20 +2000,11 @@ The latest version of the plugin is available at
The author's homepage is <http://val.markovic.io>.
Project Management
------------------
This open-source project is run by me, Strahinja Val Markovic. I also happen to
work for Google and the code I write here is under Google copyright (for the
sake of simplicity and other reasons). This does **NOT** mean that this is an
official Google product (it isn't) or that Google has (or wants to have)
anything to do with it.
License
-------
This software is licensed under the [GPL v3 license][gpl].
© 2013 Google Inc.
© 2015 YouCompleteMe contributors
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/Valloric/youcompleteme/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
......@@ -1980,7 +2014,7 @@ This software is licensed under the [GPL v3 license][gpl].
[clang-download]: http://llvm.org/releases/download.html
[brew]: http://mxcl.github.com/homebrew/
[cmake-download]: http://www.cmake.org/cmake/resources/software.html
[macvim]: http://code.google.com/p/macvim/#Download
[macvim]: https://github.com/macvim-dev/macvim/releases
[vimrc]: http://vimhelp.appspot.com/starting.txt.html#vimrc
[gpl]: http://www.gnu.org/copyleft/gpl.html
[vim]: http://www.vim.org/
......
......@@ -26,7 +26,7 @@ let s:omnifunc_mode = 0
let s:old_cursor_position = []
let s:cursor_moved = 0
let s:moved_vertically_in_insert_mode = 0
let s:previous_num_chars_on_current_line = -1
let s:previous_num_chars_on_current_line = strlen( getline('.') )
let s:diagnostic_ui_filetypes = {
\ 'cpp': 1,
......@@ -110,34 +110,40 @@ endfunction
function! s:SetUpPython() abort
py import sys
py import vim
exe 'python sys.path.insert( 0, "' . s:script_folder_path . '/../python" )'
exe 'python sys.path.insert( 0, "' . s:script_folder_path .
\ '/../third_party/ycmd" )'
py from ycmd import utils
exe 'py utils.AddNearestThirdPartyFoldersToSysPath("'
\ . s:script_folder_path . '")'
" We need to import ycmd's third_party folders as well since we import and
" use ycmd code in the client.
py utils.AddNearestThirdPartyFoldersToSysPath( utils.__file__ )
py from ycm import base
py base.LoadJsonDefaultsIntoVim()
py from ycmd import user_options_store
py user_options_store.SetAll( base.BuildServerConf() )
py from ycm import vimsupport
if !pyeval( 'base.CompatibleWithYcmCore()')
echohl WarningMsg |
\ echomsg "YouCompleteMe unavailable: YCM support libs too old, PLEASE RECOMPILE" |
\ echohl None
return 0
endif
py from ycm.youcompleteme import YouCompleteMe
py ycm_state = YouCompleteMe( user_options_store.GetAll() )
return 1
python << EOF
import sys
import vim
import os
import subprocess
script_folder = vim.eval( 's:script_folder_path' )
sys.path.insert( 0, os.path.join( script_folder, '../python' ) )
sys.path.insert( 0, os.path.join( script_folder, '../third_party/ycmd' ) )
from ycmd import utils
utils.AddNearestThirdPartyFoldersToSysPath( script_folder )
# We need to import ycmd's third_party folders as well since we import and
# use ycmd code in the client.
utils.AddNearestThirdPartyFoldersToSysPath( utils.__file__ )
from ycm import base
base.LoadJsonDefaultsIntoVim()
from ycmd import user_options_store
user_options_store.SetAll( base.BuildServerConf() )
from ycm import vimsupport
popen_args = [ utils.PathToPythonInterpreter(),
os.path.join( script_folder,
'../third_party/ycmd/check_core_version.py') ]
if utils.SafePopen( popen_args ).wait() == 2:
vimsupport.PostVimMessage(
'YouCompleteMe unavailable: YCM support libs too old, PLEASE RECOMPILE' )
vim.command( 'return 0')
from ycm.youcompleteme import YouCompleteMe
ycm_state = YouCompleteMe( user_options_store.GetAll() )
vim.command( 'return 1')
EOF
endfunction
......@@ -440,7 +446,6 @@ function! s:SetOmnicompleteFunc()
endif
endfunction
function! s:OnCursorMovedInsertMode()
if !s:AllowedToCompleteInCurrentFile()
return
......@@ -504,11 +509,13 @@ endfunction
function! s:OnInsertEnter()
let s:previous_num_chars_on_current_line = strlen( getline('.') )
if !s:AllowedToCompleteInCurrentFile()
return
endif
if get( b:, 'ycm_omnicomplete', 0 )
if !get( b:, 'ycm_omnicomplete', 0 )
let b:ycm_omnicomplete = 1
call s:SetOmnicompleteFunc()
endif
......@@ -529,14 +536,9 @@ endfunction
function! s:BufferTextChangedSinceLastMoveInInsertMode()
if s:moved_vertically_in_insert_mode
let s:previous_num_chars_on_current_line = -1
return 0
endif
let num_chars_in_current_cursor_line = strlen( getline('.') )
if s:previous_num_chars_on_current_line == -1
if s:moved_vertically_in_insert_mode
let s:previous_num_chars_on_current_line = num_chars_in_current_cursor_line
return 0
endif
......
......@@ -1746,13 +1746,13 @@ Default: '[see next line]'
>
let g:ycm_semantic_triggers = {
\ 'c' : ['->', '.'],
\ 'objc' : ['->', '.'],
\ 'objc' : ['->', '.', 're!\[[_a-zA-Z]+\w*\s', 're!^\s*[^\W\d]\w*\s',
\ 're!\[.*\]\s'],
\ 'ocaml' : ['.', '#'],
\ 'cpp,objcpp' : ['->', '.', '::'],
\ 'perl' : ['->'],
\ 'php' : ['->', '::'],
\ 'cs,java,javascript,d,python,perl6,scala,vb,elixir,go' : ['.'],
\ 'vim' : ['re![_a-zA-Z]+[_\w]*\.'],
\ 'cs,java,javascript,typescript,d,python,perl6,scala,vb,elixir,go' : ['.'],
\ 'ruby' : ['.', '::'],
\ 'lua' : ['.', ':'],
\ 'erlang' : [':'],
......
......@@ -21,7 +21,6 @@ from ycm import vimsupport
from ycmd import user_options_store
from ycmd import request_wrap
from ycmd import identifier_utils
import ycm_client_support
YCM_VAR_PREFIX = 'ycm_'
......@@ -169,16 +168,3 @@ def OverlapLength( left_string, right_string ):
if left_string[ -length: ] == right_string[ :length ]:
best = length
length += 1
COMPATIBLE_WITH_CORE_VERSION = 16
def CompatibleWithYcmCore():
try:
current_core_version = ycm_client_support.YcmCoreVersion()
except AttributeError:
return False
return current_core_version == COMPATIBLE_WITH_CORE_VERSION
......@@ -36,6 +36,8 @@ class CommandRequest( BaseRequest ):
else 'filetype_default' )
self._is_goto_command = (
self._arguments and self._arguments[ 0 ].startswith( 'GoTo' ) )
self._is_fixit_command = (
self._arguments and self._arguments[ 0 ].startswith( 'FixIt' ) )
self._response = None
......@@ -49,31 +51,79 @@ class CommandRequest( BaseRequest ):
self._response = self.PostDataToHandler( request_data,
'run_completer_command' )
except ServerError as e:
vimsupport.PostVimMessage( e )
vimsupport.PostMultiLineNotice( e )
def Response( self ):
return self._response
def RunPostCommandActionsIfNeeded( self ):
if not self.Done() or not self._response:
return
if self._is_goto_command:
if isinstance( self._response, list ):
defs = [ _BuildQfListItem( x ) for x in self._response ]
vim.eval( 'setqflist( %s )' % repr( defs ) )
vim.eval( 'youcompleteme#OpenGoToList()' )
else:
vimsupport.JumpToLocation( self._response[ 'filepath' ],
self._response[ 'line_num' ],
self._response[ 'column_num' ] )
if self._is_goto_command:
self._HandleGotoResponse()
elif self._is_fixit_command:
self._HandleFixitResponse()
elif 'message' in self._response:
vimsupport.EchoText( self._response['message'] )
self._HandleMessageResponse()
def _HandleGotoResponse( self ):
if isinstance( self._response, list ):
defs = [ _BuildQfListItem( x ) for x in self._response ]
vim.eval( 'setqflist( %s )' % repr( defs ) )
vim.eval( 'youcompleteme#OpenGoToList()' )
else:
vimsupport.JumpToLocation( self._response[ 'filepath' ],
self._response[ 'line_num' ],
self._response[ 'column_num' ] )
def _HandleFixitResponse( self ):
if not len( self._response[ 'fixits' ] ):
vimsupport.EchoText( "No fixits found for current line" )
else:
fixit = self._response[ 'fixits' ][ 0 ]
# We need to track the difference in length, but ensuring we apply fixes
# in ascending order of insertion point.
fixit[ 'chunks' ].sort( key = lambda chunk: (
str(chunk[ 'range' ][ 'start' ][ 'line_num' ])
+ ','
+ str(chunk[ 'range' ][ 'start' ][ 'column_num' ])
))
# Remember the line number we're processing. Negative line number means we
# haven't processed any lines yet (by nature of being not equal to any
# real line number).
last_line = -1
# Counter of changes applied, so the user has a mental picture of the
# undo history this change is creating.
num_fixed = 0
line_delta = 0
for chunk in fixit[ 'chunks' ]:
if chunk[ 'range' ][ 'start' ][ 'line_num' ] != last_line:
# If this chunk is on a different line than the previous chunk,
# then ignore previous deltas (as offsets won't have changed).
last_line = chunk[ 'range' ][ 'end' ][ 'line_num' ]
char_delta = 0
(new_line_delta, new_char_delta) = vimsupport.ReplaceChunk(
chunk[ 'range' ][ 'start' ],
chunk[ 'range' ][ 'end' ],
chunk[ 'replacement_text' ],
line_delta, char_delta )
line_delta += new_line_delta
char_delta += new_char_delta
num_fixed = num_fixed + 1
vimsupport.EchoTextVimWidth("FixIt applied "
+ str(num_fixed)
+ " changes")
def _HandleMessageResponse( self ):
vimsupport.EchoText( self._response[ 'message' ] )
def SendCommandRequest( arguments, completer ):
request = CommandRequest( arguments, completer )
......
......@@ -43,7 +43,6 @@ class DiagnosticInterface( object ):
if self._user_options[ 'echo_current_diagnostic' ]:
self._EchoDiagnosticForLine( line )
def UpdateWithNewDiagnostics( self, diags ):
normalized_diags = [ _NormalizeDiagnostic( x ) for x in diags ]
self._buffer_number_to_line_to_diags = _ConvertDiagListToDict(
......@@ -62,7 +61,6 @@ class DiagnosticInterface( object ):
vimsupport.SetLocationList(
vimsupport.ConvertDiagnosticsToQfList( normalized_diags ) )
def _EchoDiagnosticForLine( self, line_num ):
buffer_num = vim.current.buffer.number
diags = self._buffer_number_to_line_to_diags[ buffer_num ][ line_num ]
......@@ -72,7 +70,12 @@ class DiagnosticInterface( object ):
vimsupport.EchoText( '', False )
self._diag_message_needs_clearing = False
return
vimsupport.EchoTextVimWidth( diags[ 0 ][ 'text' ] )
text = diags[ 0 ][ 'text' ]
if diags[ 0 ].get( 'fixit_available', False ):
text += ' (FixIt)'
vimsupport.EchoTextVimWidth( text )
self._diag_message_needs_clearing = True
......
This diff is collapsed.
......@@ -245,11 +245,15 @@ def ConvertDiagnosticsToQfList( diagnostics ):
if line_num < 1:
line_num = 1
text = diagnostic[ 'text' ]
if diagnostic.get( 'fixit_available', False ):
text += ' (FixIt available)'
return {
'bufnr' : GetBufferNumberForFilename( location[ 'filepath' ] ),
'lnum' : line_num,
'col' : location[ 'column_num' ],
'text' : ToUtf8IfNeeded( diagnostic[ 'text' ] ),
'text' : ToUtf8IfNeeded( text ),
'type' : diagnostic[ 'kind' ][ 0 ],
'valid' : 1
}
......@@ -358,11 +362,13 @@ def NumLinesInBuffer( buffer_object ):
return len( buffer_object )
# Calling this function from the non-GUI thread will sometimes crash Vim. At the
# time of writing, YCM only uses the GUI thread inside Vim (this used to not be
# the case).
# Calling this function from the non-GUI thread will sometimes crash Vim. At
# the time of writing, YCM only uses the GUI thread inside Vim (this used to
# not be the case).
# We redraw the screen before displaying the message to avoid the "Press ENTER
# or type command to continue" prompt when editing a new C-family file.
def PostVimMessage( message ):
vim.command( "echohl WarningMsg | echom '{0}' | echohl None"
vim.command( "redraw | echohl WarningMsg | echom '{0}' | echohl None"
.format( EscapeForVim( str( message ) ) ) )
......@@ -452,3 +458,48 @@ def GetBoolValue( variable ):
def GetIntValue( variable ):
return int( vim.eval( variable ) )
# Replace the chunk of text specified by a contiguous range with the supplied
# text.
# * start and end are objects with line_num and column_num properties
# * the range is inclusive
# * indices are all 1-based
# * the returned character delta is the delta for the last line
#
# returns the delta (in lines and characters) that any position after the end
# needs to be adjusted by.
def ReplaceChunk( start, end, replacement_text, line_delta, char_delta,
vim_buffer = None ):
if vim_buffer is None:
vim_buffer = vim.current.buffer
# ycmd's results are all 1-based, but vim's/python's are all 0-based
# (so we do -1 on all of the values)
start_line = start[ 'line_num' ] - 1 + line_delta
end_line = end[ 'line_num' ] - 1 + line_delta
source_lines_count = end_line - start_line + 1
start_column = start[ 'column_num' ] - 1 + char_delta
end_column = end[ 'column_num' ] - 1
if source_lines_count == 1:
end_column += char_delta
replacement_lines = replacement_text.splitlines( False )
if not replacement_lines:
replacement_lines = [ '' ]
replacement_lines_count = len( replacement_lines )
end_existing_text = vim_buffer[ end_line ][ end_column : ]
start_existing_text = vim_buffer[ start_line ][ : start_column ]
new_char_delta = ( len( replacement_lines[ -1 ] )
- ( end_column - start_column ) )
if replacement_lines_count > 1:
new_char_delta -= start_column
replacement_lines[ 0 ] = start_existing_text + replacement_lines[ 0 ]
replacement_lines[ -1 ] = replacement_lines[ -1 ] + end_existing_text
vim_buffer[ start_line : end_line + 1 ] = replacement_lines[:]
new_line_delta = replacement_lines_count - source_lines_count
return ( new_line_delta, new_char_delta )
......@@ -130,7 +130,8 @@ class YouCompleteMe( object ):
if self._user_options[ 'server_keep_logfiles' ]:
args.append('--keep_logfiles')
self._server_popen = utils.SafePopen( args, stdout = PIPE, stderr = PIPE)
self._server_popen = utils.SafePopen( args, stdin_windows = PIPE,
stdout = PIPE, stderr = PIPE)
BaseRequest.server_location = 'http://127.0.0.1:' + str( server_port )
BaseRequest.hmac_secret = hmac_secret
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment