예제 #1
0
class ClangCompleter(Completer):
    def __init__(self, user_options):
        super(ClangCompleter, self).__init__(user_options)
        self._max_diagnostics_to_display = user_options[
            'max_diagnostics_to_display']
        self._completer = ycm_core.ClangCompleter()
        self._flags = Flags()
        self._diagnostic_store = None
        self._files_being_compiled = EphemeralValuesSet()

    def SupportedFiletypes(self):
        return CLANG_FILETYPES

    def GetUnsavedFilesVector(self, request_data):
        files = ycm_core.UnsavedFileVector()
        for filename, file_data in request_data['file_data'].iteritems():
            if not ClangAvailableForFiletypes(file_data['filetypes']):
                continue
            contents = file_data['contents']
            if not contents or not filename:
                continue

            unsaved_file = ycm_core.UnsavedFile()
            utf8_contents = ToUtf8IfNeeded(contents)
            unsaved_file.contents_ = utf8_contents
            unsaved_file.length_ = len(utf8_contents)
            unsaved_file.filename_ = ToUtf8IfNeeded(filename)

            files.append(unsaved_file)
        return files

    def ComputeCandidatesInner(self, request_data):
        filename = request_data['filepath']
        if not filename:
            return

        if self._completer.UpdatingTranslationUnit(ToUtf8IfNeeded(filename)):
            raise RuntimeError(PARSING_FILE_MESSAGE)

        flags = self._FlagsForRequest(request_data)
        if not flags:
            raise RuntimeError(NO_COMPILE_FLAGS_MESSAGE)

        files = self.GetUnsavedFilesVector(request_data)
        line = request_data['line_num']
        column = request_data['start_column']
        with self._files_being_compiled.GetExclusive(filename):
            results = self._completer.CandidatesForLocationInFile(
                ToUtf8IfNeeded(filename), line, column, files, flags)

        if not results:
            raise RuntimeError(NO_COMPLETIONS_MESSAGE)

        return [ConvertCompletionData(x) for x in results]

    def GetSubcommandsMap(self):
        return {
            'GoToDefinition':
            (lambda self, request_data: self._GoToDefinition(request_data)),
            'GoToDeclaration':
            (lambda self, request_data: self._GoToDeclaration(request_data)),
            'GoTo': (lambda self, request_data: self._GoTo(request_data)),
            'GoToImprecise':
            (lambda self, request_data: self._GoToImprecise(request_data)),
            'GoToInclude':
            (lambda self, request_data: self._GoToInclude(request_data)),
            'ClearCompilationFlagCache':
            (lambda self, request_data: self._ClearCompilationFlagCache()),
            'GetType': (lambda self, request_data: self._GetSemanticInfo(
                request_data, func='GetTypeAtLocation')),
            'GetParent': (lambda self, request_data: self._GetSemanticInfo(
                request_data, func='GetEnclosingFunctionAtLocation')),
            'FixIt': (lambda self, request_data: self._FixIt(request_data)),
            'GetDoc': (lambda self, request_data: self._GetSemanticInfo(
                request_data,
                reparse=True,
                func='GetDocsForLocationInFile',
                response_builder=_BuildGetDocResponse)),
            'GetDocQuick': (lambda self, request_data: self._GetSemanticInfo(
                request_data,
                reparse=False,
                func='GetDocsForLocationInFile',
                response_builder=_BuildGetDocResponse)),
        }

    def _LocationForGoTo(self, goto_function, request_data, reparse=True):
        filename = request_data['filepath']
        if not filename:
            raise ValueError(INVALID_FILE_MESSAGE)

        flags = 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']
        return getattr(self._completer,
                       goto_function)(ToUtf8IfNeeded(filename), line, column,
                                      files, flags, reparse)

    def _GoToDefinition(self, request_data):
        location = self._LocationForGoTo('GetDefinitionLocation', request_data)
        if not location or not location.IsValid():
            raise RuntimeError('Can\'t jump to definition.')
        return _ResponseForLocation(location)

    def _GoToDeclaration(self, request_data):
        location = self._LocationForGoTo('GetDeclarationLocation',
                                         request_data)
        if not location or not location.IsValid():
            raise RuntimeError('Can\'t jump to declaration.')
        return _ResponseForLocation(location)

    def _GoTo(self, request_data):
        include_response = self._ResponseForInclude(request_data)
        if include_response:
            return include_response

        location = self._LocationForGoTo('GetDefinitionLocation', request_data)
        if not location or not location.IsValid():
            location = self._LocationForGoTo('GetDeclarationLocation',
                                             request_data)
        if not location or not location.IsValid():
            raise RuntimeError('Can\'t jump to definition or declaration.')
        return _ResponseForLocation(location)

    def _GoToImprecise(self, request_data):
        include_response = self._ResponseForInclude(request_data)
        if include_response:
            return include_response

        location = self._LocationForGoTo('GetDefinitionLocation',
                                         request_data,
                                         reparse=False)
        if not location or not location.IsValid():
            location = self._LocationForGoTo('GetDeclarationLocation',
                                             request_data,
                                             reparse=False)
        if not location or not location.IsValid():
            raise RuntimeError('Can\'t jump to definition or declaration.')
        return _ResponseForLocation(location)

    def _ResponseForInclude(self, request_data):
        """Returns response for include file location if cursor is on the
       include statement, None otherwise.
       Throws RuntimeError if cursor is on include statement and corresponding
       include file not found."""
        current_line = request_data['line_value']
        include_file_name, quoted_include = GetIncludeStatementValue(
            current_line)
        if not include_file_name:
            return None

        current_file_path = ToUtf8IfNeeded(request_data['filepath'])
        client_data = request_data.get('extra_conf_data', None)
        quoted_include_paths, include_paths = (self._flags.UserIncludePaths(
            current_file_path, client_data))
        if quoted_include:
            include_file_path = _GetAbsolutePath(include_file_name,
                                                 quoted_include_paths)
            if include_file_path:
                return responses.BuildGoToResponse(include_file_path,
                                                   line_num=1,
                                                   column_num=1)

        include_file_path = _GetAbsolutePath(include_file_name, include_paths)
        if include_file_path:
            return responses.BuildGoToResponse(include_file_path,
                                               line_num=1,
                                               column_num=1)
        raise RuntimeError('Include file not found.')

    def _GoToInclude(self, request_data):
        include_response = self._ResponseForInclude(request_data)
        if not include_response:
            raise RuntimeError('Not an include/import line.')
        return include_response

    def _GetSemanticInfo(
            self,
            request_data,
            func,
            response_builder=responses.BuildDisplayMessageResponse,
            reparse=True):
        filename = request_data['filepath']
        if not filename:
            raise ValueError(INVALID_FILE_MESSAGE)

        flags = 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']

        message = getattr(self._completer,
                          func)(ToUtf8IfNeeded(filename), line, column, files,
                                flags, reparse)

        if not message:
            message = "No semantic information available"

        return response_builder(message)

    def _ClearCompilationFlagCache(self):
        self._flags.Clear()

    def _FixIt(self, request_data):
        filename = request_data['filepath']
        if not filename:
            raise ValueError(INVALID_FILE_MESSAGE)

        flags = 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")(
            ToUtf8IfNeeded(filename), 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)

    def OnFileReadyToParse(self, request_data):
        filename = request_data['filepath']
        if not filename:
            raise ValueError(INVALID_FILE_MESSAGE)

        flags = self._FlagsForRequest(request_data)
        if not flags:
            raise ValueError(NO_COMPILE_FLAGS_MESSAGE)

        with self._files_being_compiled.GetExclusive(filename):
            diagnostics = self._completer.UpdateTranslationUnit(
                ToUtf8IfNeeded(filename),
                self.GetUnsavedFilesVector(request_data), flags)

        diagnostics = _FilterDiagnostics(diagnostics)
        self._diagnostic_store = DiagnosticsToDiagStructure(diagnostics)
        return [
            responses.BuildDiagnosticData(x)
            for x in diagnostics[:self._max_diagnostics_to_display]
        ]

    def OnBufferUnload(self, request_data):
        self._completer.DeleteCachesForFile(
            ToUtf8IfNeeded(request_data['unloaded_buffer']))

    def GetDetailedDiagnostic(self, request_data):
        current_line = request_data['line_num']
        current_column = request_data['column_num']
        current_file = request_data['filepath']

        if not self._diagnostic_store:
            raise ValueError(NO_DIAGNOSTIC_MESSAGE)

        diagnostics = self._diagnostic_store[current_file][current_line]
        if not diagnostics:
            raise ValueError(NO_DIAGNOSTIC_MESSAGE)

        closest_diagnostic = None
        distance_to_closest_diagnostic = 999

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

        return responses.BuildDisplayMessageResponse(
            closest_diagnostic.long_formatted_text_)

    def DebugInfo(self, request_data):
        filename = request_data['filepath']
        if not filename:
            return ''
        flags = self._FlagsForRequest(request_data) or []
        source = extra_conf_store.ModuleFileForSourceFile(filename)
        return 'Flags for {0} loaded from {1}:\n{2}'.format(
            filename, source, list(flags))

    def _FlagsForRequest(self, request_data):
        filename = ToUtf8IfNeeded(request_data['filepath'])
        if 'compilation_flags' in request_data:
            return PrepareFlagsForClang(request_data['compilation_flags'],
                                        filename)
        client_data = request_data.get('extra_conf_data', None)
        return self._flags.FlagsForFile(filename, client_data=client_data)
예제 #2
0
class ClangCompleter( Completer ):
  def __init__( self, user_options ):
    super( ClangCompleter, self ).__init__( user_options )
    self._max_diagnostics_to_display = user_options[
      'max_diagnostics_to_display' ]
    self._completer = ycm_core.ClangCompleter()
    self._flags = Flags()
    self._diagnostic_store = None
    self._files_being_compiled = EphemeralValuesSet()


  def SupportedFiletypes( self ):
    return CLANG_FILETYPES


  def GetUnsavedFilesVector( self, request_data ):
    files = ycm_core.UnsavedFileVector()
    for filename, file_data in iteritems( request_data[ 'file_data' ] ):
      if not ClangAvailableForFiletypes( file_data[ 'filetypes' ] ):
        continue
      contents = file_data[ 'contents' ]
      if not contents or not filename:
        continue

      unsaved_file = ycm_core.UnsavedFile()
      utf8_contents = ToCppStringCompatible( contents )
      unsaved_file.contents_ = utf8_contents
      unsaved_file.length_ = len( utf8_contents )
      unsaved_file.filename_ = ToCppStringCompatible( filename )

      files.append( unsaved_file )
    return files


  def ComputeCandidatesInner( self, request_data ):
    filename = request_data[ 'filepath' ]
    if not filename:
      return

    if self._completer.UpdatingTranslationUnit(
        ToCppStringCompatible( filename ) ):
      raise RuntimeError( PARSING_FILE_MESSAGE )

    flags = self._FlagsForRequest( request_data )
    if not flags:
      raise RuntimeError( NO_COMPILE_FLAGS_MESSAGE )

    files = self.GetUnsavedFilesVector( request_data )
    line = request_data[ 'line_num' ]
    column = request_data[ 'start_column' ]
    with self._files_being_compiled.GetExclusive( filename ):
      results = self._completer.CandidatesForLocationInFile(
          ToCppStringCompatible( filename ),
          line,
          column,
          files,
          flags )

    if not results:
      raise RuntimeError( NO_COMPLETIONS_MESSAGE )

    return [ ConvertCompletionData( x ) for x in results ]


  def GetSubcommandsMap( self ):
    return {
      'GoToDefinition'           : ( lambda self, request_data, args:
         self._GoToDefinition( request_data ) ),
      'GoToDeclaration'          : ( lambda self, request_data, args:
         self._GoToDeclaration( request_data ) ),
      'GoTo'                     : ( lambda self, request_data, args:
         self._GoTo( request_data ) ),
      'GoToImprecise'            : ( lambda self, request_data, args:
         self._GoToImprecise( request_data ) ),
      'GoToInclude'              : ( lambda self, request_data, args:
         self._GoToInclude( request_data ) ),
      'ClearCompilationFlagCache': ( lambda self, request_data, args:
         self._ClearCompilationFlagCache() ),
      'GetType'                  : ( lambda self, request_data, args:
         self._GetSemanticInfo( request_data, func = 'GetTypeAtLocation' ) ),
      'GetTypeImprecise'         : ( lambda self, request_data, args:
         self._GetSemanticInfo( request_data,
                                func = 'GetTypeAtLocation',
                                reparse = False ) ),
      'GetParent'                : ( lambda self, request_data, args:
         self._GetSemanticInfo( request_data,
                                func = 'GetEnclosingFunctionAtLocation' ) ),
      'FixIt'                    : ( lambda self, request_data, args:
         self._FixIt( request_data ) ),
      'GetDoc'                   : ( lambda self, request_data, args:
         self._GetSemanticInfo( request_data,
                                reparse = True,
                                func = 'GetDocsForLocationInFile',
                                response_builder = _BuildGetDocResponse ) ),
      'GetDocImprecise'          : ( lambda self, request_data, args:
         self._GetSemanticInfo( request_data,
                                reparse = False,
                                func = 'GetDocsForLocationInFile',
                                response_builder = _BuildGetDocResponse ) ),
    }


  def _LocationForGoTo( self, goto_function, request_data, reparse = True ):
    filename = request_data[ 'filepath' ]
    if not filename:
      raise ValueError( INVALID_FILE_MESSAGE )

    flags = 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' ]
    return getattr( self._completer, goto_function )(
        ToCppStringCompatible( filename ),
        line,
        column,
        files,
        flags,
        reparse )


  def _GoToDefinition( self, request_data ):
    location = self._LocationForGoTo( 'GetDefinitionLocation', request_data )
    if not location or not location.IsValid():
      raise RuntimeError( 'Can\'t jump to definition.' )
    return _ResponseForLocation( location )


  def _GoToDeclaration( self, request_data ):
    location = self._LocationForGoTo( 'GetDeclarationLocation', request_data )
    if not location or not location.IsValid():
      raise RuntimeError( 'Can\'t jump to declaration.' )
    return _ResponseForLocation( location )


  def _GoTo( self, request_data ):
    include_response = self._ResponseForInclude( request_data )
    if include_response:
      return include_response

    location = self._LocationForGoTo( 'GetDefinitionLocation', request_data )
    if not location or not location.IsValid():
      location = self._LocationForGoTo( 'GetDeclarationLocation', request_data )
    if not location or not location.IsValid():
      raise RuntimeError( 'Can\'t jump to definition or declaration.' )
    return _ResponseForLocation( location )


  def _GoToImprecise( self, request_data ):
    include_response = self._ResponseForInclude( request_data )
    if include_response:
      return include_response

    location = self._LocationForGoTo( 'GetDefinitionLocation',
                                      request_data,
                                      reparse = False )
    if not location or not location.IsValid():
      location = self._LocationForGoTo( 'GetDeclarationLocation',
                                        request_data,
                                        reparse = False )
    if not location or not location.IsValid():
      raise RuntimeError( 'Can\'t jump to definition or declaration.' )
    return _ResponseForLocation( location )


  def _ResponseForInclude( self, request_data ):
    """Returns response for include file location if cursor is on the
       include statement, None otherwise.
       Throws RuntimeError if cursor is on include statement and corresponding
       include file not found."""
    current_line = request_data[ 'line_value' ]
    include_file_name, quoted_include = GetIncludeStatementValue( current_line )
    if not include_file_name:
      return None

    current_file_path = request_data[ 'filepath' ]
    client_data = request_data.get( 'extra_conf_data', None )
    quoted_include_paths, include_paths = (
            self._flags.UserIncludePaths( current_file_path, client_data ) )
    if quoted_include:
      include_file_path = _GetAbsolutePath( include_file_name,
                                            quoted_include_paths )
      if include_file_path:
        return responses.BuildGoToResponse( include_file_path,
                                            line_num = 1,
                                            column_num = 1 )

    include_file_path = _GetAbsolutePath( include_file_name, include_paths )
    if include_file_path:
      return responses.BuildGoToResponse( include_file_path,
                                          line_num = 1,
                                          column_num = 1 )
    raise RuntimeError( 'Include file not found.' )


  def _GoToInclude( self, request_data ):
    include_response = self._ResponseForInclude( request_data )
    if not include_response:
      raise RuntimeError( 'Not an include/import line.' )
    return include_response


  def _GetSemanticInfo(
      self,
      request_data,
      func,
      response_builder = responses.BuildDisplayMessageResponse,
      reparse = True ):
    filename = request_data[ 'filepath' ]
    if not filename:
      raise ValueError( INVALID_FILE_MESSAGE )

    flags = 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' ]

    message = getattr( self._completer, func )(
        ToCppStringCompatible( filename ),
        line,
        column,
        files,
        flags,
        reparse)

    if not message:
      message = "No semantic information available"

    return response_builder( message )

  def _ClearCompilationFlagCache( self ):
    self._flags.Clear()

  def _FixIt( self, request_data ):
    filename = request_data[ 'filepath' ]
    if not filename:
      raise ValueError( INVALID_FILE_MESSAGE )

    flags = 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 ),
        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 )

  def OnFileReadyToParse( self, request_data ):
    filename = request_data[ 'filepath' ]
    if not filename:
      raise ValueError( INVALID_FILE_MESSAGE )

    flags = self._FlagsForRequest( request_data )
    if not flags:
      raise ValueError( NO_COMPILE_FLAGS_MESSAGE )

    with self._files_being_compiled.GetExclusive( filename ):
      diagnostics = self._completer.UpdateTranslationUnit(
        ToCppStringCompatible( filename ),
        self.GetUnsavedFilesVector( request_data ),
        flags )

    diagnostics = _FilterDiagnostics( diagnostics )
    self._diagnostic_store = DiagnosticsToDiagStructure( diagnostics )
    return [ responses.BuildDiagnosticData( x ) for x in
             diagnostics[ : self._max_diagnostics_to_display ] ]


  def OnBufferUnload( self, request_data ):
    self._completer.DeleteCachesForFile(
        ToCppStringCompatible( request_data[ 'filepath' ] ) )


  def GetDetailedDiagnostic( self, request_data ):
    current_line = request_data[ 'line_num' ]
    current_column = request_data[ 'column_num' ]
    current_file = request_data[ 'filepath' ]

    if not self._diagnostic_store:
      raise ValueError( NO_DIAGNOSTIC_MESSAGE )

    diagnostics = self._diagnostic_store[ current_file ][ current_line ]
    if not diagnostics:
      raise ValueError( NO_DIAGNOSTIC_MESSAGE )

    closest_diagnostic = None
    distance_to_closest_diagnostic = 999

    # FIXME: all of these calculations are currently working with byte
    # offsets, which are technically incorrect. We should be working with
    # codepoint offsets, as we want the nearest character-wise diagnostic
    for diagnostic in diagnostics:
      distance = abs( current_column - diagnostic.location_.column_number_ )
      if distance < distance_to_closest_diagnostic:
        distance_to_closest_diagnostic = distance
        closest_diagnostic = diagnostic

    return responses.BuildDisplayMessageResponse(
      closest_diagnostic.long_formatted_text_ )


  def DebugInfo( self, request_data ):
    filename = request_data[ 'filepath' ]
    try:
      extra_conf = extra_conf_store.ModuleFileForSourceFile( filename )
    except UnknownExtraConf as error:
      return ( 'C-family completer debug information:\n'
               '  Configuration file found but not loaded\n'
               '  Configuration path: {0}'.format(
                 error.extra_conf_file ) )

    try:
      # Note that it only raises NoExtraConfDetected:
      #  - when extra_conf is None and,
      #  - there is no compilation database
      flags = self._FlagsForRequest( request_data )
    except NoExtraConfDetected:
      # No flags
      return ( 'C-family completer debug information:\n'
               '  No configuration file found\n'
               '  No compilation database found' )

    # If _FlagsForRequest returns None or raises, we use an empty list in
    # practice.
    flags = flags or []

    if extra_conf:
      # We got the flags from the extra conf file
      return ( 'C-family completer debug information:\n'
               '  Configuration file found and loaded\n'
               '  Configuration path: {0}\n'
               '  Flags: {1}'.format( extra_conf, list( flags ) ) )

    try:
      database = self._flags.FindCompilationDatabase(
          os.path.dirname( filename ) )
    except NoCompilationDatabase:
      # No flags
      return ( 'C-family completer debug information:\n'
               '  No configuration file found\n'
               '  No compilation database found' )

    # We got the flags from the compilation database
    return ( 'C-family completer debug information:\n'
             '  No configuration file found\n'
             '  Using compilation database from: {0}\n'
             '  Flags: {1}'.format( database.database_directory,
                                    list( flags ) ) )


  def _FlagsForRequest( self, request_data ):
    filename = request_data[ 'filepath' ]
    if 'compilation_flags' in request_data:
      return PrepareFlagsForClang( request_data[ 'compilation_flags' ],
                                   filename )
    client_data = request_data.get( 'extra_conf_data', None )
    return self._flags.FlagsForFile( filename, client_data = client_data )
예제 #3
0
class FilenameCompleter( Completer ):
  """
  General completer that provides filename and filepath completions.
  """

  def __init__( self, user_options ):
    super( FilenameCompleter, self ).__init__( user_options )
    self._flags = Flags()

    # On Windows, backslashes are also valid path separators.
    self._triggers = [ '/', '\\' ] if OnWindows() else [ '/' ]

    self._path_regex = re.compile( """
      # Head part
      (?:
        # 'D:/'-like token
        [A-z]+:[%(sep)s]|

        # '/', './', '../', or '~'
        \.{0,2}[%(sep)s]|~|

        # '$var/'
        \$[A-Za-z0-9{}_]+[%(sep)s]
      )+

      # Tail part
      (?:
        # any alphanumeric, symbol or space literal
        [ %(sep)sa-zA-Z0-9(){}$+_~.\x80-\xff-\[\]]|

        # skip any special symbols
        [^\x20-\x7E]|

        # backslash and 1 char after it
        \\.
      )*$
      """ % { 'sep': '/\\\\' if OnWindows() else '/' }, re.X )


  def ShouldCompleteIncludeStatement( self, request_data ):
    start_column = request_data[ 'start_column' ] - 1
    current_line = request_data[ 'line_value' ]
    filepath = request_data[ 'filepath' ]
    filetypes = request_data[ 'file_data' ][ filepath ][ 'filetypes' ]
    return ( InCFamilyFile( filetypes ) and
             AtIncludeStatementStart( current_line[ :start_column ] ) )


  def ShouldUseNowInner( self, request_data ):
    start_column = request_data[ 'start_column' ] - 1
    current_line = request_data[ 'line_value' ]
    return ( start_column and
             ( current_line[ start_column - 1 ] in self._triggers or
               self.ShouldCompleteIncludeStatement( request_data ) ) )


  def SupportedFiletypes( self ):
    return []


  def ComputeCandidatesInner( self, request_data ):
    current_line = request_data[ 'line_value' ]
    start_column = request_data[ 'start_column' ] - 1
    orig_filepath = request_data[ 'filepath' ]
    filetypes = request_data[ 'file_data' ][ orig_filepath ][ 'filetypes' ]
    line = current_line[ :start_column ]
    utf8_filepath = ToUtf8IfNeeded( orig_filepath )

    if InCFamilyFile( filetypes ):
      path_dir, quoted_include = (
              GetIncludeStatementValue( line, check_closing = False ) )
      if path_dir is not None:
        # We do what GCC does for <> versus "":
        # http://gcc.gnu.org/onlinedocs/cpp/Include-Syntax.html
        client_data = request_data.get( 'extra_conf_data', None )
        return _GenerateCandidatesForPaths(
          self.GetPathsIncludeCase( path_dir,
                                    quoted_include,
                                    utf8_filepath,
                                    client_data ) )

    path_match = self._path_regex.search( line )
    path_dir = os.path.expanduser(
      os.path.expandvars( path_match.group() ) ) if path_match else ''

    # If the client supplied its working directory, use that instead of the
    # working directory of ycmd
    working_dir = request_data.get( 'working_dir' )

    return _GenerateCandidatesForPaths(
      _GetPathsStandardCase(
        path_dir,
        self.user_options[ 'filepath_completion_use_working_dir' ],
        utf8_filepath,
        working_dir) )


  def GetPathsIncludeCase( self, path_dir, quoted_include, filepath,
                           client_data ):
    paths = []
    quoted_include_paths, include_paths = (
            self._flags.UserIncludePaths( filepath, client_data ) )

    if quoted_include:
      include_paths.extend( quoted_include_paths )

    for include_path in include_paths:
      unicode_path = ToUnicodeIfNeeded( os.path.join( include_path, path_dir ) )
      try:
        # We need to pass a unicode string to get unicode strings out of
        # listdir.
        relative_paths = os.listdir( unicode_path )
      except:
        relative_paths = []

      paths.extend( os.path.join( include_path, path_dir, relative_path ) for
                    relative_path in relative_paths  )

    return sorted( set( paths ) )
예제 #4
0
class FilenameCompleter(Completer):
    """
  General completer that provides filename and filepath completions.
  """
    def __init__(self, user_options):
        super(FilenameCompleter, self).__init__(user_options)
        self._flags = Flags()

        self._path_regex = re.compile(
            """
      # 1 or more 'D:/'-like token or '/' or '~' or './' or '../' or '$var/'
      (?:[A-z]+:/|[/~]|\./|\.+/|\$[A-Za-z0-9{}_]+/)+

      # any alphanumeric, symbol or space literal
      (?:[ /a-zA-Z0-9(){}$+_~.\x80-\xff-\[\]]|

      # skip any special symbols
      [^\x20-\x7E]|

      # backslash and 1 char after it. + matches 1 or more of whole group
      \\.)*$
      """, re.X)

        include_regex_common = '^\s*#(?:include|import)\s*(?:"|<)'
        self._include_start_regex = re.compile(include_regex_common + '$')
        self._include_regex = re.compile(include_regex_common)

    def AtIncludeStatementStart(self, request_data):
        start_column = request_data['start_column'] - 1
        current_line = request_data['line_value']
        filepath = request_data['filepath']
        filetypes = request_data['file_data'][filepath]['filetypes']
        return (InCFamilyFile(filetypes) and self._include_start_regex.match(
            current_line[:start_column]))

    def ShouldUseNowInner(self, request_data):
        start_column = request_data['start_column'] - 1
        current_line = request_data['line_value']
        return (start_column
                and (current_line[start_column - 1] == '/'
                     or self.AtIncludeStatementStart(request_data)))

    def SupportedFiletypes(self):
        return []

    def ComputeCandidatesInner(self, request_data):
        current_line = request_data['line_value']
        start_column = request_data['start_column'] - 1
        orig_filepath = request_data['filepath']
        filetypes = request_data['file_data'][orig_filepath]['filetypes']
        line = current_line[:start_column]
        utf8_filepath = ToUtf8IfNeeded(orig_filepath)

        if InCFamilyFile(filetypes):
            include_match = self._include_regex.search(line)
            if include_match:
                path_dir = line[include_match.end():]
                # We do what GCC does for <> versus "":
                # http://gcc.gnu.org/onlinedocs/cpp/Include-Syntax.html
                include_current_file_dir = '<' not in include_match.group()
                return _GenerateCandidatesForPaths(
                    self.GetPathsIncludeCase(path_dir,
                                             include_current_file_dir,
                                             utf8_filepath))

        path_match = self._path_regex.search(line)
        path_dir = os.path.expanduser(os.path.expandvars(
            path_match.group())) if path_match else ''

        return _GenerateCandidatesForPaths(
            _GetPathsStandardCase(
                path_dir,
                self.user_options['filepath_completion_use_working_dir'],
                utf8_filepath))

    def GetPathsIncludeCase(self, path_dir, include_current_file_dir,
                            filepath):
        paths = []
        include_paths = self._flags.UserIncludePaths(filepath)

        if include_current_file_dir:
            include_paths.append(os.path.dirname(filepath))

        for include_path in include_paths:
            unicode_path = ToUnicodeIfNeeded(
                os.path.join(include_path, path_dir))
            try:
                # We need to pass a unicode string to get unicode strings out of
                # listdir.
                relative_paths = os.listdir(unicode_path)
            except:
                relative_paths = []

            paths.extend(
                os.path.join(include_path, path_dir, relative_path)
                for relative_path in relative_paths)

        return sorted(set(paths))