Skip to content

Commit

Permalink
Merge pull request #1749 from puremourning/java-fix-gradle-root-detec…
Browse files Browse the repository at this point in the history
…tion

Java: Fix gradle project root detection
  • Loading branch information
mergify[bot] authored Aug 23, 2024
2 parents 5de7052 + 779a711 commit 22a5c84
Show file tree
Hide file tree
Showing 26 changed files with 822 additions and 11 deletions.
29 changes: 18 additions & 11 deletions ycmd/completers/java/java_completer.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import shutil
import tempfile
import threading
from collections import OrderedDict

from ycmd import responses, utils
from ycmd.completers.language_server import language_server_completer
Expand All @@ -41,12 +42,13 @@

PATH_TO_JAVA = None

PROJECT_FILE_TAILS = [
'.project',
'pom.xml',
'build.gradle',
'build.gradle.kts'
]
PROJECT_FILE_TAILS = OrderedDict( {
'pom.xml': 'maven',
'build.gradle': 'gradle',
'build.gradle.kts': 'gradle',
'settings.gradle': 'gradle',
'.project': 'eclipse',
} )

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


def _MakeProjectFilesForPath( path ):
for tail in PROJECT_FILE_TAILS:
yield os.path.join( path, tail ), tail
for tail, type in PROJECT_FILE_TAILS.items():
yield os.path.join( path, tail ), tail, type


def _FindProjectDir( starting_dir ):
project_path = starting_dir
project_type = None

for folder in utils.PathsToAllParentFolders( starting_dir ):
for project_file, tail in _MakeProjectFilesForPath( folder ):
for project_file, tail, type in _MakeProjectFilesForPath( folder ):
if os.path.isfile( project_file ):
project_path = folder
project_type = tail
project_type = type
break
if project_type:
break
Expand All @@ -254,9 +256,14 @@ def _FindProjectDir( starting_dir ):
LOGGER.debug( 'Found %s style project in %s. Searching for '
'project root:', project_type, project_path )

file_types_to_search_for = [
tail for tail, type in PROJECT_FILE_TAILS.items() if type == project_type
]

for folder in utils.PathsToAllParentFolders( os.path.join( project_path,
'..' ) ):
if os.path.isfile( os.path.join( folder, project_type ) ):
if any( os.path.isfile( os.path.join( folder, tail ) )
for tail in file_types_to_search_for ):
LOGGER.debug( ' %s is a parent project dir', folder )
project_path = folder
else:
Expand Down
42 changes: 42 additions & 0 deletions ycmd/tests/java/server_management_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,11 @@
StartJavaCompleterServerWithFile )
from ycmd.tests.test_utils import ( BuildRequest,
CompleterProjectDirectoryMatcher,
LocationMatcher,
MockProcessTerminationTimingOut,
RangeMatcher,
TemporaryTestDir,
WaitForDiagnosticsToBeReady,
WaitUntilCompleterServerReady )
from ycmd import utils, handlers

Expand Down Expand Up @@ -296,6 +299,45 @@ def test_ServerManagement_ProjectDetection_MavenParent_Submodule( self, app ):
CompleterProjectDirectoryMatcher( project ) )


@TidyJDTProjectFiles( PathToTestFile( 'gradle-init' ) )
@IsolatedYcmd()
def test_ServerManagement_ProjectDetection_GradleMultipleGradleFiles( self,
app ):
testfile = PathToTestFile( 'gradle-init',
'app',
'src',
'main',
'java',
'org',
'example',
'app',
'App.java' )
project = PathToTestFile( 'gradle-init' )

StartJavaCompleterServerWithFile( app, testfile )

# Run the debug info to check that we have the correct project dir
request_data = BuildRequest( filetype = 'java' )
assert_that( app.post_json( '/debug_info', request_data ).json,
CompleterProjectDirectoryMatcher( project ) )

# Check that we successfully actually parse the project too
contents = utils.ReadFile( testfile )
diags = WaitForDiagnosticsToBeReady( app, testfile, contents, 'java' )
assert_that( diags, has_item(
has_entries( {
'kind': 'WARNING',
'text': 'The value of the local variable unused is not used '
'[536870973]',
'location': LocationMatcher( testfile, 16, 16 ),
'location_extent': RangeMatcher( testfile, ( 16, 16 ), ( 16, 22 ) ),
'ranges': contains_exactly(
RangeMatcher( testfile, ( 16, 16 ), ( 16, 22 ) ) ),
'fixit_available': False
} ),
) )


def test_ServerManagement_ProjectDetection_NoParent( self ):
with TemporaryTestDir() as tmp_dir:
with isolated_app() as app:
Expand Down
12 changes: 12 additions & 0 deletions ycmd/tests/java/testdata/gradle-init/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#
# https://help.github.com/articles/dealing-with-line-endings/
#
# Linux start script should use lf
/gradlew text eol=lf

# These are Windows script files and should use crlf
*.bat text eol=crlf

# Binary files should be left untouched
*.jar binary

8 changes: 8 additions & 0 deletions ycmd/tests/java/testdata/gradle-init/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Ignore Gradle project-specific cache directory
.gradle

# Ignore Gradle build output directory
build
.project
.classpath
.settings/
17 changes: 17 additions & 0 deletions ycmd/tests/java/testdata/gradle-init/app/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* This file was generated by the Gradle 'init' task.
*/

plugins {
id 'buildlogic.java-application-conventions'
}

dependencies {
implementation 'org.apache.commons:commons-text'
implementation project(':utilities')
}

application {
// Define the main class for the application.
mainClass = 'org.example.app.App'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* This source file was generated by the Gradle 'init' task
*/
package org.example.app;

import org.example.list.LinkedList;

import static org.example.utilities.StringUtils.join;
import static org.example.utilities.StringUtils.split;
import static org.example.app.MessageUtils.getMessage;

import org.apache.commons.text.WordUtils;

public class App {
public static void main(String[] args) {
String unused;
LinkedList tokens;
tokens = split(getMessage());
String result = join(tokens);
System.out.println(WordUtils.capitalize(result));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* This source file was generated by the Gradle 'init' task
*/
package org.example.app;

class MessageUtils {
public static String getMessage() {
return "Hello World!";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* This source file was generated by the Gradle 'init' task
*/
package org.example.app;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;

class MessageUtilsTest {
@Test void testGetMessage() {
assertEquals("Hello World!", MessageUtils.getMessage());
}
}
13 changes: 13 additions & 0 deletions ycmd/tests/java/testdata/gradle-init/buildSrc/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* This file was generated by the Gradle 'init' task.
*/

plugins {
// Support convention plugins written in Groovy. Convention plugins are build scripts in 'src/main' that automatically become available as plugins in the main build.
id 'groovy-gradle-plugin'
}

repositories {
// Use the plugin portal to apply community plugins in convention plugins.
gradlePluginPortal()
}
14 changes: 14 additions & 0 deletions ycmd/tests/java/testdata/gradle-init/buildSrc/settings.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* This file was generated by the Gradle 'init' task.
*
* This settings file is used to specify which projects to include in your build-logic build.
*/

dependencyResolutionManagement {
// Reuse version catalog from the main build.
versionCatalogs {
create('libs', { from(files("../gradle/libs.versions.toml")) })
}
}

rootProject.name = 'buildSrc'
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* This file was generated by the Gradle 'init' task.
*/

plugins {
// Apply the common convention plugin for shared build configuration between library and application projects.
id 'buildlogic.java-common-conventions'

// Apply the application plugin to add support for building a CLI application in Java.
id 'application'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* This file was generated by the Gradle 'init' task.
*/

plugins {
// Apply the java Plugin to add support for Java.
id 'java'
}

repositories {
// Use Maven Central for resolving dependencies.
mavenCentral()
}

dependencies {
constraints {
// Define dependency versions as constraints
implementation 'org.apache.commons:commons-text:1.11.0'
}

// Use JUnit Jupiter for testing.
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2'

testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

// Apply a specific Java toolchain to ease working on different environments.
java {
toolchain {
languageVersion = JavaLanguageVersion.of(11)
}
}

tasks.named('test') {
// Use JUnit Platform for unit tests.
useJUnitPlatform()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* This file was generated by the Gradle 'init' task.
*/

plugins {
// Apply the common convention plugin for shared build configuration between library and application projects.
id 'buildlogic.java-common-conventions'

// Apply the java-library plugin for API and implementation separation.
id 'java-library'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# This file was generated by the Gradle 'init' task.
# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Loading

0 comments on commit 22a5c84

Please sign in to comment.