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
Original file line number Diff line number Diff line change
Expand Up @@ -256,21 +256,39 @@ private static String getIdeaOutName(final SourceSet sourceSet) {
* Convert a project and source set to an IntelliJ module name.
* Do not use {@link ModuleRef} as it does not correctly handle projects with a space in their name!
*/
private static String getIntellijModuleName(Project project, SourceSet sourceSet) {
static String getIntellijModuleName(Project project, SourceSet sourceSet) {
// IntelliJ escapes each element of the Gradle path on its own and joins them with '.',
// so an element that itself contains a '.' (a subproject named "1.21.1-neoforge", say)
// becomes "1_21_1-neoforge". Joining the raw path with '.' instead produces a module
// name that does not exist, and the generated run configuration ends up without a module.
// See GradleProjectResolverUtil#getHolderModuleName and #escapeModuleNameElement:
// https://github.com/JetBrains/intellij-community/blob/711ef1fbda55f43c3943d379f3f16dccfa6760eb/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolverUtil.java
var moduleName = new StringBuilder();
// The `replace` call here is our bug fix compared to ModuleRef!
// The actual IDEA logic is more complicated, but this should cover the majority of use cases.
// See https://github.com/JetBrains/intellij-community/blob/a32fd0c588a6da11fd6d5d2fb0362308da3206f3/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolverUtil.java#L205
// which calls https://github.com/JetBrains/intellij-community/blob/a32fd0c588a6da11fd6d5d2fb0362308da3206f3/platform/util-rt/src/com/intellij/util/PathUtilRt.java#L120
moduleName.append(project.getRootProject().getName().replace(" ", "_"));
moduleName.append(escapeModuleNameElement(project.getRootProject().getName()));
if (project != project.getRootProject()) {
moduleName.append(project.getPath().replaceAll(":", "."));
for (var element : project.getPath().split(":")) {
if (!element.isEmpty()) {
moduleName.append(".").append(escapeModuleNameElement(element));
}
}
}
moduleName.append(".");
moduleName.append(sourceSet.getName());
moduleName.append(escapeModuleNameElement(sourceSet.getName()));
return moduleName.toString();
}

/**
* Escapes a single element of an IntelliJ module name, mirroring
* {@code GradleProjectResolverUtil#escapeModuleNameElement}.
*/
static String escapeModuleNameElement(String element) {
return element
.replace(" ", "_")
.replace("/", "_")
.replace("\\", "_")
.replace(".", "_");
}

private static Map<String, Object> getExtraIntelijRunProperties(RunModel run) {
var extraProperties = new HashMap<String, Object>();
if (!run.getIdeFolderName().get().isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package net.neoforged.moddevgradle.internal;

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

import net.neoforged.moddevgradle.internal.utils.ExtensionUtils;
import org.gradle.api.Project;
import org.gradle.api.tasks.SourceSet;
import org.gradle.testfixtures.ProjectBuilder;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

class IntelliJIntegrationTest {
@ParameterizedTest
@CsvSource(textBlock = """
plain|plain
with space|with_space
1.21.1-neoforge|1_21_1-neoforge
with/slash|with_slash
""", delimiter = '|')
void testEscapeModuleNameElement(String element, String escaped) {
assertEquals(escaped, IntelliJIntegration.escapeModuleNameElement(element));
}

@Test
void testModuleNameOfRootProject() {
var root = ProjectBuilder.builder().withName("xaeronav").build();
assertEquals("xaeronav.main", getMainModuleName(root));
}

/**
* IntelliJ escapes each element of the path separately, so a '.' in a subproject name becomes
* '_'. Joining the raw path would produce "xaeronav.1.21.1-neoforge.main", which is not a
* module that exists, and the run configuration would be written without a module.
*/
@Test
void testModuleNameOfSubprojectContainingDots() {
var root = ProjectBuilder.builder().withName("xaeronav").build();
var subproject = ProjectBuilder.builder().withName("1.21.1-neoforge").withParent(root).build();
assertEquals("xaeronav.1_21_1-neoforge.main", getMainModuleName(subproject));
}

@Test
void testModuleNameOfNestedSubproject() {
var root = ProjectBuilder.builder().withName("root").build();
var parent = ProjectBuilder.builder().withName("versions").withParent(root).build();
var subproject = ProjectBuilder.builder().withName("1.21.1").withParent(parent).build();
assertEquals("root.versions.1_21_1.main", getMainModuleName(subproject));
}

private static String getMainModuleName(Project project) {
project.getPluginManager().apply("java");
SourceSet sourceSet = ExtensionUtils.getSourceSets(project).getByName(SourceSet.MAIN_SOURCE_SET_NAME);
return IntelliJIntegration.getIntellijModuleName(project, sourceSet);
}
}
Loading