Beispiel #1
0
def BuildRequestData(filepath=None):
    """Build request for the current buffer or the buffer corresponding to
  |filepath| if specified."""
    current_filepath = vimsupport.GetCurrentBufferFilepath()
    working_dir = GetCurrentDirectory()

    if filepath and current_filepath != filepath:
        # Cursor position is irrelevant when filepath is not the current buffer.
        return {
            'filepath': filepath,
            'line_num': 1,
            'column_num': 1,
            'working_dir': working_dir,
            'file_data': vimsupport.GetUnsavedAndSpecifiedBufferData(filepath)
        }

    line, column = vimsupport.CurrentLineAndColumn()

    return {
        'filepath': current_filepath,
        'line_num': line + 1,
        'column_num': column + 1,
        'working_dir': working_dir,
        'file_data':
        vimsupport.GetUnsavedAndSpecifiedBufferData(current_filepath)
    }
def BuildRequestData( buffer_number = None ):
  """Build request for the current buffer or the buffer with number
  |buffer_number| if specified."""
  working_dir = GetCurrentDirectory()
  current_buffer = vim.current.buffer

  if buffer_number and current_buffer.number != buffer_number:
    # Cursor position is irrelevant when filepath is not the current buffer.
    buffer_object = vim.buffers[ buffer_number ]
    filepath = vimsupport.GetBufferFilepath( buffer_object )
    return {
      'filepath': filepath,
      'line_num': 1,
      'column_num': 1,
      'working_dir': working_dir,
      'file_data': vimsupport.GetUnsavedAndSpecifiedBufferData( buffer_object,
                                                                filepath )
    }

  current_filepath = vimsupport.GetBufferFilepath( current_buffer )
  line, column = vimsupport.CurrentLineAndColumn()

  return {
    'filepath': current_filepath,
    'line_num': line + 1,
    'column_num': column + 1,
    'working_dir': working_dir,
    'file_data': vimsupport.GetUnsavedAndSpecifiedBufferData( current_buffer,
                                                              current_filepath )
  }
Beispiel #3
0
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

    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]
Beispiel #4
0
    def ShowDetailedDiagnostic(self):
        current_line, current_column = vimsupport.CurrentLineAndColumn()

        # CurrentLineAndColumn() numbers are 0-based, clang numbers are 1-based
        current_line += 1
        current_column += 1

        current_file = vim.current.buffer.name

        if not self.diagnostic_store:
            vimsupport.PostVimMessage("No diagnostic for current line!")
            return

        diagnostics = self.diagnostic_store[current_file][current_line]
        if not diagnostics:
            vimsupport.PostVimMessage("No diagnostic for current line!")
            return

        closest_diagnostic = None
        distance_to_closest_diagnostic = 999

        for diagnostic in diagnostics:
            distance = abs(current_column - diagnostic.column_number_)
            if distance < distance_to_closest_diagnostic:
                distance_to_closest_diagnostic = distance
                closest_diagnostic = diagnostic

        vimsupport.EchoText(closest_diagnostic.long_formatted_text_)
Beispiel #5
0
def OmniCompleter_GetCompletions_RestoreCursorPositionAfterOmnifuncCall_test(
        ycm):

    # This omnifunc moves the cursor to the test definition like
    # ccomplete#Complete would.
    def Omnifunc(findstart, base):
        vimsupport.SetCurrentLineAndColumn(0, 0)
        if findstart:
            return 5
        return ['length']

    current_buffer = VimBuffer('buffer',
                               contents=['String test', '', 'test.'],
                               filetype=FILETYPE,
                               omnifunc=Omnifunc)

    with MockVimBuffers([current_buffer], [current_buffer], (3, 5)):
        ycm.SendCompletionRequest()
        assert_that(vimsupport.CurrentLineAndColumn(), contains(2, 5))
        assert_that(
            ycm.GetCompletionResponse(),
            has_entries({
                'completions': ToBytesOnPY2([{
                    'word': 'length'
                }]),
                'completion_start_column': 6
            }))
Beispiel #6
0
def OmniCompleter_GetCompletions_MoveCursorPositionAtStartColumn_test(ycm):
    # This omnifunc relies on the cursor being moved at the start column when
    # called the second time like LanguageClient#complete from the
    # LanguageClient-neovim plugin.
    def Omnifunc(findstart, base):
        if findstart:
            return 5
        if vimsupport.CurrentColumn() == 5:
            return ['length']
        return []

    current_buffer = VimBuffer('buffer',
                               contents=['String test', '', 'test.le'],
                               filetype=FILETYPE,
                               omnifunc=Omnifunc)

    with MockVimBuffers([current_buffer], [current_buffer], (3, 7)):
        ycm.SendCompletionRequest()
        assert_that(vimsupport.CurrentLineAndColumn(), contains(2, 7))
        assert_that(
            ycm.GetCompletionResponse(),
            has_entries({
                'completions': ToBytesOnPY2([{
                    'word': 'length'
                }]),
                'completion_start_column': 6
            }))
    def OnCursorMoved(self):
        line, _ = vimsupport.CurrentLineAndColumn()
        line += 1  # Convert to 1-based
        if line != self._previous_line_number:
            self._previous_line_number = line

            if self._user_options['echo_current_diagnostic']:
                self._EchoDiagnosticForLine(line)
    def _GetJediScript(self):
        contents = '\n'.join(vim.current.buffer)
        line, column = vimsupport.CurrentLineAndColumn()
        # Jedi expects lines to start at 1, not 0
        line += 1
        filename = vim.current.buffer.name

        return jedi.Script(contents, line, column, filename)
Beispiel #9
0
 def _DefaultParameters(self):
     """ Some very common request parameters """
     line, column = vimsupport.CurrentLineAndColumn()
     parameters = {}
     parameters['line'], parameters['column'] = line + 1, column + 1
     parameters['buffer'] = '\n'.join(vim.current.buffer)
     parameters['filename'] = vim.current.buffer.name
     return parameters
Beispiel #10
0
    def CandidatesFromStoredRequest(self):
        if not self.completions_future:
            return []

        lines = vim.current.buffer
        line_num, cursor = vimsupport.CurrentLineAndColumn()

        for i in range(0, line_num - 1):
            cursor += len(lines[i]) + 1  # that +1 is for the "\n" char

        count = 0
        positions = {}
        text = "\n".join(lines)

        if cursor > DISTANCE_RANGE:
            text = text[cursor - DISTANCE_RANGE:]
            cursor = DISTANCE_RANGE

        if len(text) > cursor + DISTANCE_RANGE:
            text = text[:cursor + DISTANCE_RANGE]

        for match in self.identifier_regex.finditer(text):
            count += 1
            identifier = match.group()
            position = match.start()
            if identifier in positions:
                positions[identifier].append(position)
            else:
                positions[identifier] = [position]

        completions = self.completions_future.GetResults()  # all

        completions_with_distance = []
        rest = []

        for word in completions:
            if len(word) > MIN_NUM_CHARS:
                if word in positions:
                    distance = min(
                        [abs(cursor - pos) for pos in positions[word]])
                    count_factor = (len(positions[word])) / float(count)
                    distance -= distance * count_factor
                    completions_with_distance.append((word, distance))
                else:
                    rest.append(word)

        completions_with_distance.sort(key=lambda pair: pair[1])
        completions = [pair[0] for pair in completions_with_distance] + rest

        # 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[:MAX_IDENTIFIER_COMPLETIONS_RETURNED]]
Beispiel #11
0
 def CandidatesFromStoredRequest( self ):
   if self.completions_cache:
     return self.completions_cache.filtered_completions
   else:
     self.completions_cache = CompletionsCache()
     self.completions_cache.raw_completions = self.CandidatesFromStoredRequestInner()
     self.completions_cache.line, _ = vimsupport.CurrentLineAndColumn()
     self.completions_cache.column = self.completion_start_column
     return self.completions_cache.raw_completions
Beispiel #12
0
 def OnCursorMoved(self):
     if self._user_options['echo_current_diagnostic']:
         line, _ = vimsupport.CurrentLineAndColumn()
         line += 1  # Convert to 1-based
         if (not self.ShouldUpdateDiagnosticsUINow()
                 and self._diag_message_needs_clearing):
             # Clear any previously echo'd diagnostic in insert mode
             self._EchoDiagnosticText(line, None, None)
         elif line != self._previous_diag_line_number:
             self._EchoDiagnosticForLine(line)
Beispiel #13
0
    def _GetCompletions(self):
        """ Ask server for completions """
        line, column = vimsupport.CurrentLineAndColumn()

        parameters = {}
        parameters['line'], parameters['column'] = line + 1, column + 1
        parameters['buffer'] = '\n'.join(vim.current.buffer)
        parameters['filename'] = vim.current.buffer.name

        completions = self._GetResponse('/autocomplete', parameters)
        return completions if completions != None else []
Beispiel #14
0
    def ComputeCandidatesInner(self, request_data):
        if not self._omnifunc:
            return []

        # Calling directly the omnifunc may move the cursor position. This is the
        # case with the default Vim omnifunc for C-family languages
        # (ccomplete#Complete) which calls searchdecl to find a declaration. This
        # function is supposed to move the cursor to the found declaration but it
        # doesn't when called through the omni completion mapping (CTRL-X CTRL-O).
        # So, we restore the cursor position after the omnifunc calls.
        line, column = vimsupport.CurrentLineAndColumn()

        try:
            start_column = vimsupport.GetIntValue(self._omnifunc + '(1,"")')
            if start_column < 0:
                # FIXME: Technically, if the returned value is -1 we should raise an
                # error.
                return []

            # Use the start column calculated by the omnifunc, rather than our own
            # interpretation. This is important for certain languages where our
            # identifier detection is either incorrect or not compatible with the
            # behaviour of the omnifunc. Note: do this before calling the omnifunc
            # because it affects the value returned by 'query'.
            request_data['start_column'] = start_column + 1

            # Vim internally moves the cursor to the start column before calling again
            # the omnifunc. Some omnifuncs like the one defined by the
            # LanguageClient-neovim plugin depend on this behavior to compute the list
            # of candidates.
            vimsupport.SetCurrentLineAndColumn(line, start_column)

            omnifunc_call = [
                self._omnifunc, "(0,'",
                vimsupport.EscapeForVim(request_data['query']), "')"
            ]
            items = vim.eval(''.join(omnifunc_call))

            if isinstance(items, dict) and 'words' in items:
                items = items['words']

            if not hasattr(items, '__iter__'):
                raise TypeError(OMNIFUNC_NOT_LIST)

            return list(filter(bool, items))

        except (TypeError, ValueError, vim.error) as error:
            vimsupport.PostVimMessage(OMNIFUNC_RETURNED_BAD_VALUE + ' ' +
                                      str(error))
            return []

        finally:
            vimsupport.SetCurrentLineAndColumn(line, column)
Beispiel #15
0
def BuildRequestData( include_buffer_data = True ):
  line, column = vimsupport.CurrentLineAndColumn()
  filepath = vimsupport.GetCurrentBufferFilepath()
  request_data = {
    'line_num': line + 1,
    'column_num': column + 1,
    'filepath': filepath
  }

  if include_buffer_data:
    request_data[ 'file_data' ] = vimsupport.GetUnsavedAndCurrentBufferData()

  return request_data
Beispiel #16
0
    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)
Beispiel #17
0
def BuildRequestData(start_column=None, query=None, include_buffer_data=True):
    line, column = vimsupport.CurrentLineAndColumn()
    filepath = vimsupport.GetCurrentBufferFilepath()
    request_data = {
        'filetypes': vimsupport.CurrentFiletypes(),
        'line_num': line,
        'column_num': column,
        'start_column': start_column,
        'line_value': vim.current.line,
        'filepath': filepath
    }

    if include_buffer_data:
        request_data['file_data'] = vimsupport.GetUnsavedAndCurrentBufferData()
    if query:
        request_data['query'] = query

    return request_data
 def _EchoDiagnostic(self):
     line, _ = vimsupport.CurrentLineAndColumn()
     line += 1  # Convert to 1-based
     self._EchoDiagnosticForLine(line)
Beispiel #19
0
 def CacheValid(self, start_column):
     completion_line, _ = vimsupport.CurrentLineAndColumn()
     completion_column = start_column
     return completion_line == self.line and completion_column == self.column
Beispiel #20
0
def UpdateSignatureHelp(state, signature_info):  # noqa
    if not ShouldUseSignatureHelp():
        return state

    signatures = signature_info.get('signatures') or []

    if not signatures:
        if state.popup_win_id:
            # TODO/FIXME: Should we use popup_hide() instead ?
            vim.eval("popup_close( {} )".format(state.popup_win_id))
        return SignatureHelpState(None, SignatureHelpState.INACTIVE)

    if state.state != SignatureHelpState.ACTIVE:
        state.anchor = vimsupport.CurrentLineAndColumn()

    state.state = SignatureHelpState.ACTIVE

    # Generate the buffer as a list of lines
    buf_lines = _MakeSignatureHelpBuffer(signature_info)
    screen_pos = vimsupport.ScreenPositionForLineColumnInWindow(
        vim.current.window,
        state.anchor[0] + 1,  # anchor 0-based
        state.anchor[1] + 1)  # anchor 0-based

    # Simulate 'flip' at the screen boundaries by using screenpos and hiding the
    # signature help menu if it overlaps the completion popup (pum).
    #
    # FIXME: revert to cursor-relative positioning and the 'flip' option when that
    # is implemented (if that is indeed better).

    # By default display above the anchor
    line = int(screen_pos['row']) - 1  # -1 to display above the cur line
    pos = "botleft"

    cursor_line = vimsupport.CurrentLineAndColumn()[0] + 1
    if int(screen_pos['row']) <= len(buf_lines):
        # No room at the top, display below
        line = int(screen_pos['row']) + 1
        pos = "topleft"

    # Don't allow the popup to overlap the cursor
    if (pos == 'topleft' and line < cursor_line
            and line + len(buf_lines) >= cursor_line):
        line = 0

    # Don't allow the popup to overlap the pum
    if line > 0 and GetIntValue('pumvisible()'):
        pum_line = GetIntValue(vim.eval('pum_getpos().row')) + 1
        if pos == 'botleft' and pum_line <= line:
            line = 0
        elif (pos == 'topleft' and pum_line >= line and pum_line <
              (line + len(buf_lines))):
            line = 0

    if line <= 0:
        # Nowhere to put it so hide it
        if state.popup_win_id:
            # TODO/FIXME: Should we use popup_hide() instead ?
            vim.eval("popup_close( {} )".format(state.popup_win_id))
        return SignatureHelpState(None, SignatureHelpState.INACTIVE)

    if int(screen_pos['curscol']) <= 1:
        col = 1
    else:
        # -1 for padding,
        # -1 for the trigger character inserted (the anchor is set _after_ the
        # character is inserted, so we remove it).
        # FIXME: multi-byte characters would be wrong. Need to set anchor before
        # inserting the char ?
        col = int(screen_pos['curscol']) - 2

    if col <= 0:
        col = 1

    options = {
        "line": line,
        "col": col,
        "pos": pos,
        "wrap": 0,
        "flip": 1,
        "padding": [0, 1, 0,
                    1],  # Pad 1 char in X axis to match completion menu
    }

    if not state.popup_win_id:
        state.popup_win_id = GetIntValue(
            vim.eval("popup_create( {}, {} )".format(json.dumps(buf_lines),
                                                     json.dumps(options))))
    else:
        vim.eval('popup_settext( {}, {} )'.format(state.popup_win_id,
                                                  json.dumps(buf_lines)))

    # Should do nothing if already visible
    vim.eval('popup_move( {}, {} )'.format(state.popup_win_id,
                                           json.dumps(options)))
    vim.eval('popup_show( {} )'.format(state.popup_win_id))

    return state
Beispiel #21
0
def UpdateSignatureHelp(state, signature_info):  # noqa
    if not ShouldUseSignatureHelp():
        return state

    signatures = signature_info.get('signatures') or []

    if not signatures:
        if state.popup_win_id:
            # TODO/FIXME: Should we use popup_hide() instead ?
            vim.eval(f"popup_close( { state.popup_win_id } )")
        return SignatureHelpState(None, SignatureHelpState.INACTIVE)

    if state.state != SignatureHelpState.ACTIVE:
        state.anchor = vimsupport.CurrentLineAndColumn()

    state.state = SignatureHelpState.ACTIVE

    # Generate the buffer as a list of lines
    buf_lines = _MakeSignatureHelpBuffer(signature_info)
    screen_pos = vimsupport.ScreenPositionForLineColumnInWindow(
        vim.current.window,
        state.anchor[0] + 1,  # anchor 0-based
        state.anchor[1] + 1)  # anchor 0-based

    # Simulate 'flip' at the screen boundaries by using screenpos and hiding the
    # signature help menu if it overlaps the completion popup (pum).
    #
    # FIXME: revert to cursor-relative positioning and the 'flip' option when that
    # is implemented (if that is indeed better).

    # By default display above the anchor
    line = int(screen_pos['row']) - 1  # -1 to display above the cur line
    pos = "botleft"

    cursor_line = vimsupport.CurrentLineAndColumn()[0] + 1
    if int(screen_pos['row']) <= len(buf_lines):
        # No room at the top, display below
        line = int(screen_pos['row']) + 1
        pos = "topleft"

    # Don't allow the popup to overlap the cursor
    if (pos == 'topleft' and line < cursor_line
            and line + len(buf_lines) >= cursor_line):
        line = 0

    # Don't allow the popup to overlap the pum
    if line > 0 and GetIntValue('pumvisible()'):
        pum_line = GetIntValue('pum_getpos().row') + 1
        if pos == 'botleft' and pum_line <= line:
            line = 0
        elif (pos == 'topleft' and pum_line >= line and pum_line <
              (line + len(buf_lines))):
            line = 0

    if line <= 0:
        # Nowhere to put it so hide it
        if state.popup_win_id:
            # TODO/FIXME: Should we use popup_hide() instead ?
            vim.eval(f"popup_close( { state.popup_win_id } )")
        return SignatureHelpState(None, SignatureHelpState.INACTIVE)

    if int(screen_pos['curscol']) <= 1:
        col = 1
    else:
        # -1 for padding,
        # -1 for the trigger character inserted (the anchor is set _after_ the
        # character is inserted, so we remove it).
        # FIXME: multi-byte characters would be wrong. Need to set anchor before
        # inserting the char ?
        col = int(screen_pos['curscol']) - 2

    if col <= 0:
        col = 1

    options = {
        "line": line,
        "col": col,
        "pos": pos,
        "wrap": 0,
        # NOTE: We *dont'* use "cursorline" here - that actually uses PMenuSel,
        # which is just too invasive for us (it's more selected item than actual
        # cursorline. So instead, we manually set 'cursorline' in the popup window
        # and enable sytax based on the current file syntax)
        "flip": 1,
        "padding": [0, 1, 0,
                    1],  # Pad 1 char in X axis to match completion menu
    }

    if not state.popup_win_id:
        state.popup_win_id = GetIntValue(
            f'popup_create( { json.dumps( buf_lines ) }, '
            f'{ json.dumps( options ) } )')
    else:
        vim.eval(f'popup_settext( { state.popup_win_id }, '
                 f'{ json.dumps( buf_lines ) } )')

    # Should do nothing if already visible
    vim.eval(
        f'popup_move( { state.popup_win_id }, { json.dumps( options ) } )')
    vim.eval(f'popup_show( { state.popup_win_id } )')

    syntax = utils.ToUnicode(vim.current.buffer.options['syntax'])
    active_signature = int(signature_info.get('activeSignature', 0))
    vim.eval(f"win_execute( { state.popup_win_id }, "
             f"'set syntax={ syntax } cursorline | "
             f"call cursor( [ { active_signature + 1 }, 1 ] )' )")

    return state
Beispiel #22
0
    def ComputeCandidatesInner(self, request_data):
        if not self._omnifunc:
            return []

        # Calling directly the omnifunc may move the cursor position. This is the
        # case with the default Vim omnifunc for C-family languages
        # (ccomplete#Complete) which calls searchdecl to find a declaration. This
        # function is supposed to move the cursor to the found declaration but it
        # doesn't when called through the omni completion mapping (CTRL-X CTRL-O).
        # So, we restore the cursor position after the omnifunc calls.
        line, column = vimsupport.CurrentLineAndColumn()

        try:
            start_column = vimsupport.GetIntValue(self._omnifunc + '(1,"")')

            # Vim only stops completion if the value returned by the omnifunc is -3 or
            # -2. In other cases, if the value is negative or greater than the current
            # column, the start column is set to the current column; otherwise, the
            # value is used as the start column.
            if start_column in (-3, -2):
                return []
            if start_column < 0 or start_column > column:
                start_column = column

            # Use the start column calculated by the omnifunc, rather than our own
            # interpretation. This is important for certain languages where our
            # identifier detection is either incorrect or not compatible with the
            # behaviour of the omnifunc. Note: do this before calling the omnifunc
            # because it affects the value returned by 'query'.
            request_data['start_column'] = start_column + 1

            # Vim internally moves the cursor to the start column before calling again
            # the omnifunc. Some omnifuncs like the one defined by the
            # LanguageClient-neovim plugin depend on this behavior to compute the list
            # of candidates.
            vimsupport.SetCurrentLineAndColumn(line, start_column)

            omnifunc_call = [
                self._omnifunc, "(0,'",
                vimsupport.EscapeForVim(request_data['query']), "')"
            ]
            items = vim.eval(''.join(omnifunc_call))

            if isinstance(items, dict) and 'words' in items:
                items = items['words']

            if not hasattr(items, '__iter__'):
                raise TypeError(OMNIFUNC_NOT_LIST)

            # Vim allows each item of the list to be either a string or a dictionary
            # but ycmd only supports lists where items are all strings or all
            # dictionaries. Convert all strings into dictionaries.
            for index, item in enumerate(items):
                if not isinstance(item, dict):
                    items[index] = {'word': item}

            return items

        except (TypeError, ValueError, vim.error) as error:
            vimsupport.PostVimMessage(OMNIFUNC_RETURNED_BAD_VALUE + ' ' +
                                      str(error))
            return []

        finally:
            vimsupport.SetCurrentLineAndColumn(line, column)