示例#1
0
  def _Format( self, request_data ):
    filepath = request_data[ 'filepath' ]

    self._Reload( request_data )

    # TODO: support all formatting options. See
    # https://github.com/Microsoft/TypeScript/blob/72e92a055823f1ade97d03d7526dbab8be405dde/lib/protocol.d.ts#L2060-L2077
    # for the list of options. While not standard, a way to support these
    # options, which is already adopted by a number of clients, would be to read
    # the "formatOptions" field in the tsconfig.json file.
    options = request_data[ 'options' ]
    self._SendRequest( 'configure', {
      'file': filepath,
      'formatOptions': {
        'tabSize': options[ 'tab_size' ],
        'indentSize': options[ 'tab_size' ],
        'convertTabsToSpaces': options[ 'insert_spaces' ],
      }
    } )

    response = self._SendRequest( 'format',
                                  _BuildTsFormatRange( request_data ) )

    contents = GetFileLines( request_data, filepath )
    chunks = [ _BuildFixItChunkForRange( text_edit[ 'newText' ],
                                         contents,
                                         filepath,
                                         text_edit ) for text_edit in response ]

    location = responses.Location( request_data[ 'line_num' ],
                                   request_data[ 'column_num' ],
                                   filepath )
    return responses.BuildFixItResponse( [
      responses.FixIt( location, chunks )
    ] )
示例#2
0
    def _RefactorRename(self, request_data, args):
        if len(args) != 1:
            raise ValueError('Please specify a new name to rename it to.\n'
                             'Usage: RefactorRename <new name>')

        self._Reload(request_data)

        response = self._SendRequest(
            'rename', {
                'file': request_data['filepath'],
                'line': request_data['line_num'],
                'offset': request_data['column_codepoint'],
                'findInComments': False,
                'findInStrings': False,
            })

        if not response['info']['canRename']:
            raise RuntimeError(
                'Value cannot be renamed: '
                f'{ response[ "info" ][ "localizedErrorMessage" ] }')

        # The format of the response is:
        #
        # body {
        #   info {
        #     ...
        #     triggerSpan: {
        #       length: original_length
        #     }
        #   }
        #
        #   locs [ {
        #     file: file_path
        #     locs: [
        #       start: {
        #         line: line_num
        #         offset: offset
        #       }
        #       end {
        #         line: line_num
        #         offset: offset
        #       }
        #     ] }
        #   ]
        # }
        #
        new_name = args[0]
        location = responses.Location(request_data['line_num'],
                                      request_data['column_num'],
                                      request_data['filepath'])

        chunks = []
        for file_replacement in response['locs']:
            chunks.extend(
                _BuildFixItChunksForFile(request_data, new_name,
                                         file_replacement))

        return responses.BuildFixItResponse(
            [responses.FixIt(location, chunks)])
示例#3
0
def BuildExtraData(completion_data):
    extra_data = {}
    fixit = completion_data.fixit_
    if fixit.chunks:
        extra_data.update(responses.BuildFixItResponse([fixit]))
    if completion_data.DocString():
        extra_data['doc_string'] = completion_data.DocString()
    return extra_data
示例#4
0
    def OrganizeImports(self, request_data):
        workspace_edit = self.GetCommandResponse(
            request_data, 'java.edit.organizeImports',
            [lsp.FilePathToUri(request_data['filepath'])])

        fixit = language_server_completer.WorkspaceEditToFixIt(
            request_data, workspace_edit)
        return responses.BuildFixItResponse([fixit])
def WorkspaceEditToFixIt_test():
    if utils.OnWindows():
        filepath = 'C:\\test.test'
        uri = 'file:///c:/test.test'
    else:
        filepath = '/test.test'
        uri = 'file:/test.test'

    contents = 'line1\nline2\nline3'

    request_data = RequestWrap(
        BuildRequest(filetype='ycmtest', filepath=filepath, contents=contents))

    # We don't support versioned documentChanges
    assert_that(
        lsc.WorkspaceEditToFixIt(request_data, {'documentChanges': []}),
        equal_to(None))

    workspace_edit = {
        'changes': {
            uri: [
                {
                    'newText': 'blah',
                    'range': {
                        'start': {
                            'line': 0,
                            'character': 5
                        },
                        'end': {
                            'line': 0,
                            'character': 5
                        },
                    }
                },
            ]
        }
    }

    response = responses.BuildFixItResponse(
        [lsc.WorkspaceEditToFixIt(request_data, workspace_edit, 'test')])

    print('Response: {0}'.format(response))
    print('Type Response: {0}'.format(type(response)))

    assert_that(
        response,
        has_entries({
            'fixits':
            contains(
                has_entries({
                    'text':
                    'test',
                    'chunks':
                    contains(
                        ChunkMatcher('blah', LocationMatcher(filepath, 1, 6),
                                     LocationMatcher(filepath, 1, 6)))
                }))
        }))
示例#6
0
 def _RefactorRename(self, request_data, args):
     request = self._DefaultParameters(request_data)
     if len(args) != 1:
         raise ValueError('Please specify a new name to rename it to.\n'
                          'Usage: RefactorRename <new name>')
     request['RenameTo'] = args[0]
     request['WantsTextChanges'] = True
     response = self._GetResponse('/rename', request)
     fixit = _ModifiedFilesToFixIt(response['Changes'], request_data)
     return responses.BuildFixItResponse([fixit])
示例#7
0
def BuildExtraData(completion_data):
    extra_data = {}
    fixit = completion_data.fixit_
    if fixit.chunks:
        extra_data.update(responses.BuildFixItResponse([fixit]))
    try:
        if completion_data.DocString():
            extra_data['doc_string'] = completion_data.DocString()
    except UnicodeDecodeError:
        pass
    return extra_data
示例#8
0
def _BuildCompletionFixIts(request_data, entry):
    if 'codeActions' in entry:
        location = responses.Location(request_data['line_num'],
                                      request_data['column_num'],
                                      request_data['filepath'])
        return responses.BuildFixItResponse([
            responses.FixIt(
                location, _BuildFixItForChanges(request_data,
                                                action['changes']),
                action['description']) for action in entry['codeActions']
        ])
    return {}
示例#9
0
  def _FixIt( self, request_data ):
    request = self._DefaultParameters( request_data )

    result = self._GetResponse( '/fixcodeissue', request )
    replacement_text = result[ "Text" ]
    location = CsharpDiagnosticLocation( request_data['line_num'],
                                         request_data['column_num'],
                                         request_data['filepath'] )
    fixits = [ CsharpFixIt( location,
                            _BuildChunks( request_data, replacement_text ) ) ]

    return responses.BuildFixItResponse( fixits )
示例#10
0
    def _RefactorRename(self, request_data, args):
        if len(args) < 1:
            raise RuntimeError('Must specify a new name')

        new_name = args[0]
        with self._jedi_lock:
            refactoring = self._GetJediScript(request_data).rename(
                line=request_data['line_num'],
                column=request_data['column_codepoint'] - 1,
                new_name=new_name)

            return responses.BuildFixItResponse(
                [_RefactoringToFixIt(refactoring)])
示例#11
0
  def _FixIt( self, request_data ):
    request = self._DefaultParameters( request_data )

    result = self._GetResponse( '/fixcodeissue', request )
    replacement_text = result[ "Text" ]
    # Note: column_num is already a byte offset so we don't need to use
    # _BuildLocation.
    location = responses.Location( request_data[ 'line_num' ],
                                   request_data[ 'column_num' ],
                                   request_data[ 'filepath' ] )
    fixits = [ responses.FixIt( location,
                                _BuildChunks( request_data,
                                              replacement_text ) ) ]

    return responses.BuildFixItResponse( fixits )
示例#12
0
    def _FixIt(self, request_data, args):
        self._Reload(request_data)

        filepath = request_data['filepath']
        line_num = request_data['line_num']

        fixits = []
        with self._latest_diagnostics_for_file_lock:
            for diagnostic in self._latest_diagnostics_for_file[filepath]:
                if diagnostic.location_.line_number_ != line_num:
                    continue

                fixits.extend(diagnostic.fixits_)

        return responses.BuildFixItResponse(fixits)
示例#13
0
    def _FixIt(self, request_data):
        request = self._DefaultParameters(request_data)
        request['WantsTextChanges'] = True

        result = self._GetResponse('/getcodeactions', request)

        fixits = []
        for i, code_action_name in enumerate(result['CodeActions']):
            fixit = responses.UnresolvedFixIt({'index': i}, code_action_name)
            fixits.append(fixit)

        if len(fixits) == 1:
            fixit = fixits[0]
            fixit = {'command': fixit.command, 'resolve': fixit.resolve}
            return self._ResolveFixIt(request_data, fixit)

        return responses.BuildFixItResponse(fixits)
示例#14
0
    def _FixIt(self, request_data):
        flags, filename = self._FlagsForRequest(request_data)
        if not flags:
            raise ValueError(NO_COMPILE_FLAGS_MESSAGE)

        files = self.GetUnsavedFilesVector(request_data)
        line = request_data['line_num']
        column = request_data['column_num']

        fixits = getattr(self._completer, "GetFixItsForLocationInFile")(
            ToCppStringCompatible(filename),
            ToCppStringCompatible(request_data['filepath']), line, column,
            files, flags, True)

        # don't raise an error if not fixits: - leave that to the client to respond
        # in a nice way

        return responses.BuildFixItResponse(fixits)
示例#15
0
    def _OrganizeImports(self, request_data):
        self._Reload(request_data)

        filepath = request_data['filepath']
        changes = self._SendRequest(
            'organizeImports',
            {'scope': {
                'type': 'file',
                'args': {
                    'file': filepath
                }
            }})

        location = responses.Location(request_data['line_num'],
                                      request_data['column_num'], filepath)
        return responses.BuildFixItResponse([
            responses.FixIt(location,
                            _BuildFixItForChanges(request_data, changes))
        ])
示例#16
0
  def _Format( self, request_data ):
    request = self._DefaultParameters( request_data )
    request[ 'WantsTextChanges' ] = True
    if 'range' in request_data:
      lines = request_data[ 'lines' ]
      start = request_data[ 'range' ][ 'start' ]
      start_line_num = start[ 'line_num' ]
      start_line_value = lines[ start_line_num ]

      start_codepoint = ByteOffsetToCodepointOffset( start_line_value,
                                                     start[ 'column_num' ] )

      end = request_data[ 'range' ][ 'end' ]
      end_line_num = end[ 'line_num' ]
      end_line_value = lines[ end_line_num ]
      end_codepoint = ByteOffsetToCodepointOffset( end_line_value,
                                                   end[ 'column_num' ] )
      request.update( {
        'line': start_line_num,
        'column': start_codepoint,
        'EndLine': end_line_num,
        'EndColumn': end_codepoint
      } )
      result = self._GetResponse( '/formatRange', request )
    else:
      result = self._GetResponse( '/codeformat', request )

    fixit = responses.FixIt(
      _BuildLocation(
        request_data,
        request_data[ 'filepath' ],
        request_data[ 'line_num' ],
        request_data[ 'column_codepoint' ] ),
      _LinePositionSpanTextChangeToFixItChunks(
        result[ 'Changes' ],
        request_data[ 'filepath' ],
        request_data ) )
    return responses.BuildFixItResponse( [ fixit ] )
示例#17
0
def _ConvertDetailedCompletionData( request_data, completion_data ):
  name = completion_data[ 'name' ]
  display_parts = completion_data[ 'displayParts' ]
  signature = ''.join( [ part[ 'text' ] for part in display_parts ] )
  if name == signature:
    extra_menu_info = None
    detailed_info = []
  else:
    # Strip new lines and indentation from the signature to display it on one
    # line.
    extra_menu_info = re.sub( '\\s+', ' ', signature )
    detailed_info = [ signature ]

  docs = completion_data.get( 'documentation', [] )
  detailed_info += [ doc[ 'text' ].strip() for doc in docs if doc ]
  detailed_info = '\n\n'.join( detailed_info )

  fixits = None
  if 'codeActions' in completion_data:
    location = responses.Location( request_data[ 'line_num' ],
                                   request_data[ 'column_num' ],
                                   request_data[ 'filepath' ] )
    fixits = responses.BuildFixItResponse( [
      responses.FixIt( location,
                       _BuildFixItForChanges( request_data,
                                              action[ 'changes' ] ),
                       action[ 'description' ] )
      for action in completion_data[ 'codeActions' ]
    ] )

  return responses.BuildCompletionData(
    insertion_text  = name,
    extra_menu_info = extra_menu_info,
    detailed_info   = detailed_info,
    kind            = completion_data[ 'kind' ],
    extra_data      = fixits
  )
示例#18
0
 def _ResolveFixIt(self, request_data, unresolved_fixit=None):
     fixit = unresolved_fixit if unresolved_fixit else request_data['fixit']
     if not fixit['resolve']:
         return {'fixits': [fixit]}
     fixit = fixit['command']
     code_action = fixit['index']
     request = self._DefaultParameters(request_data)
     request.update({
         'CodeAction': code_action,
         'WantsTextChanges': True,
     })
     response = self._GetResponse('/runcodeaction', request)
     fixit = responses.FixIt(
         _BuildLocation(request_data, request_data['filepath'],
                        request_data['line_num'],
                        request_data['column_codepoint']),
         _LinePositionSpanTextChangeToFixItChunks(response['Changes'],
                                                  request_data['filepath'],
                                                  request_data),
         response['Text'])
     # The sort is necessary to keep the tests stable.
     # Python's sort() is stable, so it won't mess up the order within a file.
     fixit.chunks.sort(key=lambda c: c.range.start_.filename_)
     return responses.BuildFixItResponse([fixit])
示例#19
0
def WorkspaceEditToFixIt_test():
  if utils.OnWindows():
    filepath = 'C:\\test.test'
    uri = 'file:///c:/test.test'
  else:
    filepath = '/test.test'
    uri = 'file:/test.test'

  contents = 'line1\nline2\nline3'

  request_data = RequestWrap( BuildRequest(
    filetype = 'ycmtest',
    filepath = filepath,
    contents = contents
  ) )


  # Null response to textDocument/codeActions is valid
  assert_that( lsc.WorkspaceEditToFixIt( request_data, None ),
               equal_to( None ) )
  # Empty WorkspaceEdit is not explicitly forbidden
  assert_that( lsc.WorkspaceEditToFixIt( request_data, {} ), equal_to( None ) )
  # We don't support versioned documentChanges
  workspace_edit = {
    'documentChanges': [
      {
        'textDocument': {
          'version': 1,
          'uri': uri
        },
        'edits': [
          {
            'newText': 'blah',
            'range': {
              'start': { 'line': 0, 'character': 5 },
              'end': { 'line': 0, 'character': 5 },
            }
          }
        ]
      }
    ]
  }
  response = responses.BuildFixItResponse( [
    lsc.WorkspaceEditToFixIt( request_data, workspace_edit, 'test' )
  ] )

  print( f'Response: { response }' )
  assert_that(
    response,
    has_entries( {
      'fixits': contains_exactly( has_entries( {
        'text': 'test',
        'chunks': contains_exactly( ChunkMatcher( 'blah',
                                          LocationMatcher( filepath, 1, 6 ),
                                          LocationMatcher( filepath, 1, 6 ) ) )
      } ) )
    } )
  )

  workspace_edit = {
    'changes': {
      uri: [
        {
          'newText': 'blah',
          'range': {
            'start': { 'line': 0, 'character': 5 },
            'end': { 'line': 0, 'character': 5 },
          }
        },
      ]
    }
  }

  response = responses.BuildFixItResponse( [
    lsc.WorkspaceEditToFixIt( request_data, workspace_edit, 'test' )
  ] )

  print( f'Response: { response }' )
  print( f'Type Response: { type( response ) }' )

  assert_that(
    response,
    has_entries( {
      'fixits': contains_exactly( has_entries( {
        'text': 'test',
        'chunks': contains_exactly( ChunkMatcher( 'blah',
                                          LocationMatcher( filepath, 1, 6 ),
                                          LocationMatcher( filepath, 1, 6 ) ) )
      } ) )
    } )
  )
示例#20
0
    def _Rename(self, request_data, args):
        if len(args) != 1:
            raise ValueError('Please specify a new name to rename it to.\n'
                             'Usage: RefactorRename <new name>')

        query = {
            'type': 'rename',
            'newName': args[0],
        }

        response = self._GetResponse(query, request_data['column_codepoint'],
                                     request_data)

        # Tern response format:
        # 'changes': [
        #     {
        #         'file' (potentially relative path)
        #         'start' {
        #             'line'
        #             'ch' (codepoint offset)
        #         }
        #         'end' {
        #             'line'
        #             'ch' (codepoint offset)
        #         }
        #         'text'
        #     }
        # ]

        # ycmd response format:
        #
        # {
        #     'fixits': [
        #         'chunks': (list<Chunk>) [
        #             {
        #                  'replacement_text',
        #                  'range' (Range) {
        #                      'start_' (Location): {
        #                          'line_number_',
        #                          'column_number_', (byte offset)
        #                          'filename_' (note: absolute path!)
        #                      },
        #                      'end_' (Location): {
        #                          'line_number_',
        #                          'column_number_', (byte offset)
        #                          'filename_' (note: absolute path!)
        #                      }
        #                  }
        #              }
        #         ],
        #         'location' (Location) {
        #              'line_number_',
        #              'column_number_',
        #              'filename_' (note: absolute path!)
        #         }
        #
        #     ]
        # }

        def BuildRange(file_contents, filename, start, end):
            return responses.Range(
                _BuildLocation(file_contents, filename, start['line'],
                               start['ch']),
                _BuildLocation(file_contents, filename, end['line'],
                               end['ch']))

        def BuildFixItChunk(change):
            filepath = self._ServerPathToAbsolute(change['file'])
            file_contents = utils.SplitLines(
                GetFileContents(request_data, filepath))
            return responses.FixItChunk(
                change['text'],
                BuildRange(file_contents, filepath, change['start'],
                           change['end']))

        # From an API perspective, Refactor and FixIt are the same thing - it just
        # applies a set of changes to a set of files. So we re-use all of the
        # existing FixIt infrastructure.
        return responses.BuildFixItResponse([
            responses.FixIt(
                responses.Location(request_data['line_num'],
                                   request_data['column_num'],
                                   request_data['filepath']),
                [BuildFixItChunk(x) for x in response['changes']])
        ])
示例#21
0
    def _Rename(self, request_data, args):
        if len(args) != 1:
            raise ValueError('Please specify a new name to rename it to.\n'
                             'Usage: RefactorRename <new name>')

        query = {
            'type': 'rename',
            'newName': args[0],
        }

        response = self._GetResponse(query, request_data)

        # Tern response format:
        # 'changes': [
        #     {
        #         'file'
        #         'start' {
        #             'line'
        #             'ch'
        #         }
        #         'end' {
        #             'line'
        #             'ch'
        #         }
        #         'text'
        #     }
        # ]

        # ycmd response format:
        #
        # {
        #     'fixits': [
        #         'chunks': (list<Chunk>) [
        #             {
        #                  'replacement_text',
        #                  'range' (Range) {
        #                      'start_' (Location): {
        #                          'line_number_',
        #                          'column_number_',
        #                          'filename_'
        #                      },
        #                      'end_' (Location): {
        #                          'line_number_',
        #                          'column_number_',
        #                          'filename_'
        #                      }
        #                  }
        #              }
        #         ],
        #         'location' (Location) {
        #              'line_number_',
        #              'column_number_',
        #              'filename_'
        #         }
        #
        #     ]
        # }

        def BuildRange(filename, start, end):
            return responses.Range(
                responses.Location(start['line'] + 1, start['ch'] + 1,
                                   filename),
                responses.Location(end['line'] + 1, end['ch'] + 1, filename))

        def BuildFixItChunk(change):
            return responses.FixItChunk(
                change['text'],
                BuildRange(os.path.abspath(change['file']), change['start'],
                           change['end']))

        # From an API perspective, Refactor and FixIt are the same thing - it just
        # applies a set of changes to a set of files. So we re-use all of the
        # existing FixIt infrastructure.
        return responses.BuildFixItResponse([
            responses.FixIt(
                responses.Location(request_data['line_num'],
                                   request_data['column_num'],
                                   request_data['filepath']),
                [BuildFixItChunk(x) for x in response['changes']])
        ])