Skip to content

Commit 22a5c84

Browse files
authored
Merge pull request #1749 from puremourning/java-fix-gradle-root-detection
Java: Fix gradle project root detection
2 parents 5de7052 + 779a711 commit 22a5c84

File tree

26 files changed

+822
-11
lines changed

26 files changed

+822
-11
lines changed

ycmd/completers/java/java_completer.py

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import shutil
2323
import tempfile
2424
import threading
25+
from collections import OrderedDict
2526

2627
from ycmd import responses, utils
2728
from ycmd.completers.language_server import language_server_completer
@@ -41,12 +42,13 @@
4142

4243
PATH_TO_JAVA = None
4344

44-
PROJECT_FILE_TAILS = [
45-
'.project',
46-
'pom.xml',
47-
'build.gradle',
48-
'build.gradle.kts'
49-
]
45+
PROJECT_FILE_TAILS = OrderedDict( {
46+
'pom.xml': 'maven',
47+
'build.gradle': 'gradle',
48+
'build.gradle.kts': 'gradle',
49+
'settings.gradle': 'gradle',
50+
'.project': 'eclipse',
51+
} )
5052

5153
DEFAULT_WORKSPACE_ROOT_PATH = os.path.abspath( os.path.join(
5254
os.path.dirname( __file__ ),
@@ -230,19 +232,19 @@ def _LauncherConfiguration( user_options, workspace_root, wipe_config ):
230232

231233

232234
def _MakeProjectFilesForPath( path ):
233-
for tail in PROJECT_FILE_TAILS:
234-
yield os.path.join( path, tail ), tail
235+
for tail, type in PROJECT_FILE_TAILS.items():
236+
yield os.path.join( path, tail ), tail, type
235237

236238

237239
def _FindProjectDir( starting_dir ):
238240
project_path = starting_dir
239241
project_type = None
240242

241243
for folder in utils.PathsToAllParentFolders( starting_dir ):
242-
for project_file, tail in _MakeProjectFilesForPath( folder ):
244+
for project_file, tail, type in _MakeProjectFilesForPath( folder ):
243245
if os.path.isfile( project_file ):
244246
project_path = folder
245-
project_type = tail
247+
project_type = type
246248
break
247249
if project_type:
248250
break
@@ -254,9 +256,14 @@ def _FindProjectDir( starting_dir ):
254256
LOGGER.debug( 'Found %s style project in %s. Searching for '
255257
'project root:', project_type, project_path )
256258

259+
file_types_to_search_for = [
260+
tail for tail, type in PROJECT_FILE_TAILS.items() if type == project_type
261+
]
262+
257263
for folder in utils.PathsToAllParentFolders( os.path.join( project_path,
258264
'..' ) ):
259-
if os.path.isfile( os.path.join( folder, project_type ) ):
265+
if any( os.path.isfile( os.path.join( folder, tail ) )
266+
for tail in file_types_to_search_for ):
260267
LOGGER.debug( ' %s is a parent project dir', folder )
261268
project_path = folder
262269
else:

ycmd/tests/java/server_management_test.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,11 @@
3939
StartJavaCompleterServerWithFile )
4040
from ycmd.tests.test_utils import ( BuildRequest,
4141
CompleterProjectDirectoryMatcher,
42+
LocationMatcher,
4243
MockProcessTerminationTimingOut,
44+
RangeMatcher,
4345
TemporaryTestDir,
46+
WaitForDiagnosticsToBeReady,
4447
WaitUntilCompleterServerReady )
4548
from ycmd import utils, handlers
4649

@@ -296,6 +299,45 @@ def test_ServerManagement_ProjectDetection_MavenParent_Submodule( self, app ):
296299
CompleterProjectDirectoryMatcher( project ) )
297300

298301

302+
@TidyJDTProjectFiles( PathToTestFile( 'gradle-init' ) )
303+
@IsolatedYcmd()
304+
def test_ServerManagement_ProjectDetection_GradleMultipleGradleFiles( self,
305+
app ):
306+
testfile = PathToTestFile( 'gradle-init',
307+
'app',
308+
'src',
309+
'main',
310+
'java',
311+
'org',
312+
'example',
313+
'app',
314+
'App.java' )
315+
project = PathToTestFile( 'gradle-init' )
316+
317+
StartJavaCompleterServerWithFile( app, testfile )
318+
319+
# Run the debug info to check that we have the correct project dir
320+
request_data = BuildRequest( filetype = 'java' )
321+
assert_that( app.post_json( '/debug_info', request_data ).json,
322+
CompleterProjectDirectoryMatcher( project ) )
323+
324+
# Check that we successfully actually parse the project too
325+
contents = utils.ReadFile( testfile )
326+
diags = WaitForDiagnosticsToBeReady( app, testfile, contents, 'java' )
327+
assert_that( diags, has_item(
328+
has_entries( {
329+
'kind': 'WARNING',
330+
'text': 'The value of the local variable unused is not used '
331+
'[536870973]',
332+
'location': LocationMatcher( testfile, 16, 16 ),
333+
'location_extent': RangeMatcher( testfile, ( 16, 16 ), ( 16, 22 ) ),
334+
'ranges': contains_exactly(
335+
RangeMatcher( testfile, ( 16, 16 ), ( 16, 22 ) ) ),
336+
'fixit_available': False
337+
} ),
338+
) )
339+
340+
299341
def test_ServerManagement_ProjectDetection_NoParent( self ):
300342
with TemporaryTestDir() as tmp_dir:
301343
with isolated_app() as app:
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#
2+
# https://help.github.com/articles/dealing-with-line-endings/
3+
#
4+
# Linux start script should use lf
5+
/gradlew text eol=lf
6+
7+
# These are Windows script files and should use crlf
8+
*.bat text eol=crlf
9+
10+
# Binary files should be left untouched
11+
*.jar binary
12+
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Ignore Gradle project-specific cache directory
2+
.gradle
3+
4+
# Ignore Gradle build output directory
5+
build
6+
.project
7+
.classpath
8+
.settings/
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
* This file was generated by the Gradle 'init' task.
3+
*/
4+
5+
plugins {
6+
id 'buildlogic.java-application-conventions'
7+
}
8+
9+
dependencies {
10+
implementation 'org.apache.commons:commons-text'
11+
implementation project(':utilities')
12+
}
13+
14+
application {
15+
// Define the main class for the application.
16+
mainClass = 'org.example.app.App'
17+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* This source file was generated by the Gradle 'init' task
3+
*/
4+
package org.example.app;
5+
6+
import org.example.list.LinkedList;
7+
8+
import static org.example.utilities.StringUtils.join;
9+
import static org.example.utilities.StringUtils.split;
10+
import static org.example.app.MessageUtils.getMessage;
11+
12+
import org.apache.commons.text.WordUtils;
13+
14+
public class App {
15+
public static void main(String[] args) {
16+
String unused;
17+
LinkedList tokens;
18+
tokens = split(getMessage());
19+
String result = join(tokens);
20+
System.out.println(WordUtils.capitalize(result));
21+
}
22+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/*
2+
* This source file was generated by the Gradle 'init' task
3+
*/
4+
package org.example.app;
5+
6+
class MessageUtils {
7+
public static String getMessage() {
8+
return "Hello World!";
9+
}
10+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/*
2+
* This source file was generated by the Gradle 'init' task
3+
*/
4+
package org.example.app;
5+
6+
import org.junit.jupiter.api.Test;
7+
8+
import static org.junit.jupiter.api.Assertions.assertEquals;
9+
10+
class MessageUtilsTest {
11+
@Test void testGetMessage() {
12+
assertEquals("Hello World!", MessageUtils.getMessage());
13+
}
14+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/*
2+
* This file was generated by the Gradle 'init' task.
3+
*/
4+
5+
plugins {
6+
// Support convention plugins written in Groovy. Convention plugins are build scripts in 'src/main' that automatically become available as plugins in the main build.
7+
id 'groovy-gradle-plugin'
8+
}
9+
10+
repositories {
11+
// Use the plugin portal to apply community plugins in convention plugins.
12+
gradlePluginPortal()
13+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/*
2+
* This file was generated by the Gradle 'init' task.
3+
*
4+
* This settings file is used to specify which projects to include in your build-logic build.
5+
*/
6+
7+
dependencyResolutionManagement {
8+
// Reuse version catalog from the main build.
9+
versionCatalogs {
10+
create('libs', { from(files("../gradle/libs.versions.toml")) })
11+
}
12+
}
13+
14+
rootProject.name = 'buildSrc'

0 commit comments

Comments
 (0)