Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 43 additions & 5 deletions ycmd/completers/language_server/language_server_completer.py
Original file line number Diff line number Diff line change
Expand Up @@ -2350,6 +2350,44 @@ def GetProjectDirectory( self, request_data ):
return os.path.dirname( filepath )


def FindProjectFromRootFiles( self,
filepath,
project_root_files,
nearest=True ):

project_folder = None
project_root_type = None

# First, find the nearest dir that has one of the root file types
for folder in utils.PathsToAllParentFolders( filepath ):
f = Path( folder )
for root_file in project_root_files:
if next( f.glob( root_file ), [] ):
# Found one, store the root file and the current nearest folder
project_root_type = root_file
project_folder = folder
break
if project_folder:
break

if not project_folder:
return None

# If asking for the nearest, return the one found
if nearest:
return str( project_folder )

# Otherwise keep searching up from the nearest until we don't find any more
for folder in utils.PathsToAllParentFolders( os.path.join( project_folder,
'..' ) ):
f = Path( folder )
if next( f.glob( project_root_type ), [] ):
project_folder = folder
else:
break
return project_folder


def GetWorkspaceForFilepath( self, filepath, strict = False ):
"""Return the workspace of the provided filepath. This could be a subproject
or a completely unrelated project to the root directory.
Expand All @@ -2360,12 +2398,12 @@ def GetWorkspaceForFilepath( self, filepath, strict = False ):
reuse this implementation.
"""
project_root_files = self.GetProjectRootFiles()
workspace = None
if project_root_files:
for folder in utils.PathsToAllParentFolders( filepath ):
for root_file in project_root_files:
if next( Path( folder ).glob( root_file ), [] ):
return folder
return None if strict else os.path.dirname( filepath )
workspace = self.FindProjectFromRootFiles( filepath,
project_root_files,
nearest = True )
return workspace or ( None if strict else os.path.dirname( filepath ) )


def _SendInitialize( self, request_data ):
Expand Down
43 changes: 38 additions & 5 deletions ycmd/completers/rust/rust_completer.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import logging
import os
from subprocess import PIPE
from pathlib import Path

from ycmd import responses, utils
from ycmd.completers.language_server import language_server_completer
Expand Down Expand Up @@ -106,12 +107,44 @@ def GetServerEnvironment( self ):
return env


def GetProjectRootFiles( self ):
# Without LSP workspaces support, RA relies on the rootUri to detect a
def GetWorkspaceForFilepath( self, filepath, strict = False ):
# For every unique workspace, rust analyzer launches a nuclear
# weapon^h^h^h^h new server and indexes the internet. Try to minimise the
# number of such launches.

# If filepath is a subdirectory of the manually-specified project root, use
# the project root
if 'project_directory' in self._settings:
project_root = utils.AbsolutePath( self._settings[ 'project_directory' ],
self._extra_conf_dir )

prp = Path( project_root )
for parent in Path( filepath ).absolute().parents:
if parent == prp:
return project_root

# Otherwise, we might not have one configured, or it' a totally different
# project.
# TODO: add support for LSP workspaces to allow users to change project
# without having to restart RA.
return [ 'Cargo.toml' ]
#
# Our main heuristic is:
# - find the nearest Cargo.lock, and assume that's the root
# - otherwise find the _furthest_ Cargo.toml and assume that's the root
# - otherwise use the project root directory that we previously calculated.
#
# We never use the directory of the file as that could just be anything
# random, and we might as well just use the original project in that case
if candidate := self.FindProjectFromRootFiles( filepath,
[ 'Cargo.lock' ],
nearest = True ):
return candidate

if candidate := self.FindProjectFromRootFiles( filepath,
[ 'Cargo.toml' ],
nearest = False ):
return candidate

# Never use the
return None if strict else self._project_directory


def ServerIsReady( self ):
Expand Down