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])
def Range( request_data ): lines = request_data[ 'lines' ] start = request_data[ 'range' ][ 'start' ] start_line_num = start[ 'line_num' ] start_line_value = lines[ start_line_num - 1 ] 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 - 1 ] end_codepoint = ByteOffsetToCodepointOffset( end_line_value, end[ 'column_num' ] ) # LSP requires to use the start of the next line as the end position for a # range that ends with a newline. if end_codepoint >= len( end_line_value ): end_line_num += 1 end_line_value = '' end_codepoint = 1 return { 'start': Position( start_line_num, start_line_value, start_codepoint ), 'end': Position( end_line_num, end_line_value, end_codepoint ) }
def CurrentLineContentsAndCodepointColumn(): """Returns the line contents as a unicode string and the 0-based current column as a codepoint offset. If the current column is outside the line, returns the column position at the end of the line.""" line = CurrentLineContents() byte_column = CurrentColumn() # ByteOffsetToCodepointOffset expects 1-based offset. column = ByteOffsetToCodepointOffset(line, byte_column + 1) - 1 return line, column
def __init__( self, request, validate = True ): if validate: EnsureRequestValid( request ) self._request = request # Maps the keys returned by this objects __getitem__ to a # tuple of # ( getter_method, setter_method ). Values computed by getter_method (or set # by setter_method) are cached in _cached_computed. setter_method may be # None for read-only items. self._computed_key = { # Unicode string representation of the current line. If the line requested # is not in the file, returns ''. 'line_value': ( self._CurrentLine, None ), # The calculated start column, as a codepoint offset into the # unicode string line_value 'start_codepoint': ( self._GetCompletionStartCodepoint, self._SetCompletionStartCodepoint ), # The 'column_num' as a unicode codepoint offset 'column_codepoint': ( lambda: ByteOffsetToCodepointOffset( self[ 'line_bytes' ], self[ 'column_num' ] ), None ), # Bytes string representation of the current line 'line_bytes': ( lambda: ToBytes( self[ 'line_value' ] ), None ), # The calculated start column, as a byte offset into the UTF-8 encoded # bytes returned by line_bytes 'start_column': ( self._GetCompletionStartColumn, self._SetCompletionStartColumn ), # Note: column_num is the byte offset into the UTF-8 encoded bytes # returned by line_bytes # unicode string representation of the 'query' after the beginning # of the identifier to be completed 'query': ( self._Query, None ), # Unicode string representation of the line value up to the character # before the start of 'query' 'prefix': ( self._Prefix, None ), 'filetypes': ( self._Filetypes, None ), 'first_filetype': ( self._FirstFiletype, None ), 'force_semantic': ( self._GetForceSemantic, None ), 'lines': ( self._CurrentLines, None ) } self._cached_computed = {}
def Range(request_data): lines = request_data['lines'] if 'range' not in request_data: start_codepoint = request_data['start_codepoint'] start_line_num = request_data['line_num'] start_line_value = request_data['line_value'] end_codepoint = start_codepoint + 1 end_line_num = start_line_num end_line_value = start_line_value else: start = request_data['range']['start'] start_line_num = start['line_num'] end = request_data['range']['end'] end_line_num = end['line_num'] try: start_line_value = lines[start_line_num - 1] start_codepoint = ByteOffsetToCodepointOffset( start_line_value, start['column_num']) end_line_value = lines[end_line_num - 1] end_codepoint = ByteOffsetToCodepointOffset( end_line_value, end['column_num']) except IndexError: raise RuntimeError("Invalid range") # LSP requires to use the start of the next line as the end position for a # range that ends with a newline. if end_codepoint >= len(end_line_value): end_line_num += 1 end_line_value = '' end_codepoint = 1 return { 'start': Position(start_line_num, start_line_value, start_codepoint), 'end': Position(end_line_num, end_line_value, end_codepoint) }
def _SetCompletionStartColumn(self, column_num): self._cached_computed['start_column'] = column_num # Note: We must pre-compute (and cache) the codepoint equivalent. This is # because the value calculated by the getter (_GetCompletionStartCodepoint) # would be based on self[ 'column_codepoint' ] which would be incorrect; it # does not know that the user has forced this value to be independent of the # column. self._cached_computed['start_codepoint'] = ByteOffsetToCodepointOffset( self['line_value'], column_num) # The same applies to the 'query' (the bit after the start column up to the # cursor column). It's dependent on the 'start_codepoint' so we must reset # it. self._cached_computed.pop('query', None)
def CompletionStartCodepoint(line_value, column_num, filetype): """Returns the 1-based codepoint index where the completion query should start. So if the user enters: ƒøø.∫å®^ with the cursor being at the location of the caret (so the character *AFTER* '®'), then the starting column would be the index of the character '∫' (i.e. 5, not its byte index).""" # NOTE: column_num and other numbers on the wire are byte indices, but we need # to walk codepoints for identifier checks. codepoint_column_num = ByteOffsetToCodepointOffset(line_value, column_num) unicode_line_value = ToUnicode(line_value) # -1 and then +1 to account for difference between 0-based and 1-based # indices/columns codepoint_start_column = StartOfLongestIdentifierEndingAtIndex( unicode_line_value, codepoint_column_num - 1, filetype) + 1 return codepoint_start_column
def __init__(self, request, validate=True): if validate: EnsureRequestValid(request) self._request = request self._computed_key = { # Unicode string representation of the current line 'line_value': self._CurrentLine, # The calculated start column, as a codepoint offset into the # unicode string line_value 'start_codepoint': self.CompletionStartCodepoint, # The 'column_num' as a unicode codepoint offset 'column_codepoint': (lambda: ByteOffsetToCodepointOffset( self['line_bytes'], self['column_num'])), # Bytes string representation of the current line 'line_bytes': lambda: ToBytes(self['line_value']), # The calculated start column, as a byte offset into the UTF-8 encoded # bytes returned by line_bytes 'start_column': self.CompletionStartColumn, # Note: column_num is the byte offset into the UTF-8 encoded bytes # returned by line_bytes # unicode string representation of the 'query' after the beginning # of the identifier to be completed 'query': self._Query, 'filetypes': self._Filetypes, } self._cached_computed = {}