Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Improve rust project detection #1779

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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 @@
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

Check warning on line 2385 in ycmd/completers/language_server/language_server_completer.py

View check run for this annotation

Codecov / codecov/patch

ycmd/completers/language_server/language_server_completer.py#L2385

Added line #L2385 was not covered by tests
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 @@
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 @@
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' ],

Check warning on line 118 in ycmd/completers/rust/rust_completer.py

View check run for this annotation

Codecov / codecov/patch

ycmd/completers/rust/rust_completer.py#L118

Added line #L118 was not covered by tests
self._extra_conf_dir )

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

Check warning on line 124 in ycmd/completers/rust/rust_completer.py

View check run for this annotation

Codecov / codecov/patch

ycmd/completers/rust/rust_completer.py#L121-L124

Added lines #L121 - L124 were not covered by tests

# 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
Loading