User: joe
Date: 12 Nov 26 01:07
Revision: ceb4f92ff2e14a4279da90e5b7df7c47577c2782
Summary:
Try to block formatting only outside the Javadoc comment
TeamCity URL: http://ci.mcdev.io:80/viewModification.html?tab=vcsModificationFiles&modId=10720&personal=false
Index: src/main/kotlin/platform/fabric/annotater/javadoc/AnnotaterJavadocFormattingModelBuilder.kt
===================================================================
--- src/main/kotlin/platform/fabric/annotater/javadoc/AnnotaterJavadocFormattingModelBuilder.kt (revision ceb4f92ff2e14a4279da90e5b7df7c47577c2782)
+++ src/main/kotlin/platform/fabric/annotater/javadoc/AnnotaterJavadocFormattingModelBuilder.kt (revision ceb4f92ff2e14a4279da90e5b7df7c47577c2782)
@@ -0,0 +1,85 @@
+/*
+ * Minecraft Development for IntelliJ
+ *
+ * https://mcdev.io/
+ *
+ * Copyright (C) 2026 minecraft-dev
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, version 3.0 only.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+
+package com.demonwav.mcdev.platform.fabric.annotater.javadoc
+
+import com.intellij.formatting.CustomFormattingModelBuilder
+import com.intellij.formatting.FormattingContext
+import com.intellij.formatting.FormattingModel
+import com.intellij.formatting.FormattingModelBuilder
+import com.intellij.formatting.FormattingModelEx
+import com.intellij.lang.ASTNode
+import com.intellij.lang.LanguageFormatting
+import com.intellij.lang.java.JavaLanguage
+import com.intellij.openapi.util.TextRange
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiFile
+
+/**
+ * Formats annotater Javadoc editors with Java's formatter, but only changes whitespace outside the guarded blocks, so
+ * that the read-only copied source code is left as it is.
+ */
+class AnnotaterJavadocFormattingModelBuilder : CustomFormattingModelBuilder {
+ override fun isEngagedToFormat(context: PsiElement) = AnnotaterJavadocVirtualFile.of(context.containingFile) != null
+
+ override fun createModel(formattingContext: FormattingContext): FormattingModel =
+ EditableRangeFormattingModel(javaBuilder().createModel(formattingContext), formattingContext.containingFile)
+
+ override fun getRangeAffectingIndent(file: PsiFile, offset: Int, elementAtOffset: ASTNode): TextRange? =
+ javaBuilder().getRangeAffectingIndent(file, offset, elementAtOffset)
+
+ private fun javaBuilder(): FormattingModelBuilder =
+ LanguageFormatting.INSTANCE.allForLanguage(JavaLanguage.INSTANCE).first { it !is CustomFormattingModelBuilder }
+
+ /**
+ * The formatter shifts the following offsets by how much the returned range's length changed, so returning the
+ * original range leaves the whitespace unchanged.
+ */
+ private class EditableRangeFormattingModel(
+ private val delegate: FormattingModel,
+ file: PsiFile,
+ ) : FormattingModelEx, FormattingModel by delegate {
+ private val document = file.viewProvider.document
+
+ private fun isEditable(range: TextRange) = document.getRangeGuard(range.startOffset, range.endOffset) == null
+
+ override fun replaceWhiteSpace(textRange: TextRange, whiteSpace: String): TextRange =
+ if (isEditable(textRange)) {
+ delegate.replaceWhiteSpace(textRange, whiteSpace)
+ } else {
+ textRange
+ }
+
+ override fun replaceWhiteSpace(textRange: TextRange, nodeAfter: ASTNode?, whiteSpace: String): TextRange =
+ when {
+ !isEditable(textRange) -> textRange
+ delegate is FormattingModelEx -> delegate.replaceWhiteSpace(textRange, nodeAfter, whiteSpace)
+ else -> delegate.replaceWhiteSpace(textRange, whiteSpace)
+ }
+
+ override fun shiftIndentInsideRange(node: ASTNode?, range: TextRange, indent: Int): TextRange =
+ if (isEditable(range)) {
+ delegate.shiftIndentInsideRange(node, range, indent)
+ } else {
+ range
+ }
+ }
+}
Index: src/main/kotlin/platform/fabric/annotater/javadoc/AnnotaterJavadocFormattingRestriction.kt
===================================================================
--- src/main/kotlin/platform/fabric/annotater/javadoc/AnnotaterJavadocFormattingRestriction.kt (revision 00e8c96bd7e8e8b7612ddee6b5f67b013ebac087)
+++ src/main/kotlin/platform/fabric/annotater/javadoc/AnnotaterJavadocFormattingRestriction.kt (revision 00e8c96bd7e8e8b7612ddee6b5f67b013ebac087)
@@ -1,32 +0,0 @@
-/*
- * Minecraft Development for IntelliJ
- *
- * https://mcdev.io/
- *
- * Copyright (C) 2026 minecraft-dev
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation, version 3.0 only.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-
-package com.demonwav.mcdev.platform.fabric.annotater.javadoc
-
-import com.intellij.lang.LanguageFormattingRestriction
-import com.intellij.psi.PsiElement
-
-/**
- * Prevents reformatting annotater Javadoc editors, which would change the read-only copied source code.
- */
-class AnnotaterJavadocFormattingRestriction : LanguageFormattingRestriction {
- override fun isFormatterAllowed(context: PsiElement) =
- AnnotaterJavadocVirtualFile.of(context.containingFile) == null
-}
Index: src/main/resources/META-INF/plugin.xml
===================================================================
--- src/main/resources/META-INF/plugin.xml (revision 00e8c96bd7e8e8b7612ddee6b5f67b013ebac087)
+++ src/main/resources/META-INF/plugin.xml (revision ceb4f92ff2e14a4279da90e5b7df7c47577c2782)
@@ -721,8 +721,8 @@
implementation="com.demonwav.mcdev.platform.fabric.annotater.javadoc.AnnotaterJavadocIntentionActionFilter"/>
-
+
JAVA
com.demonwav.mcdev.platform.fabric.annotater.javadoc.EditAnnotaterJavadocIntention
Index: src/test/kotlin/platform/fabric/annotater/AnnotaterJavadocEditorTest.kt
===================================================================
--- src/test/kotlin/platform/fabric/annotater/AnnotaterJavadocEditorTest.kt (revision 00e8c96bd7e8e8b7612ddee6b5f67b013ebac087)
+++ src/test/kotlin/platform/fabric/annotater/AnnotaterJavadocEditorTest.kt (revision ceb4f92ff2e14a4279da90e5b7df7c47577c2782)
@@ -51,10 +51,12 @@
import com.intellij.openapi.util.TextRange
import com.intellij.psi.JavaPsiFacade
import com.intellij.psi.PsiClass
+import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiMember
import com.intellij.psi.PsiMethod
+import com.intellij.psi.codeStyle.CodeStyleManager
import com.intellij.psi.codeStyle.modifier.TransientCodeStyleSettings
import com.intellij.psi.javadoc.PsiDocComment
import com.intellij.psi.search.GlobalSearchScope
@@ -132,7 +134,7 @@
public class Level {
public boolean addEntity(net.minecraft.util.Ticker> ticker, String[] names, int count) {
- return true;
+ return true ;
}
}
""".trimIndent(),
@@ -511,4 +513,57 @@
val hidden = AnnotaterJavadocRendering.generateDoc(clampMethod, null)!!
assertFalse("Existing method docs" in hidden, hidden)
}
+
+ // TODO: uncomment in 2026.3
+ // @Test
+ // @DisplayName("Inserting a Javadoc stub formats it")
+ // fun javadocStub() {
+ // val inlineEditor = startEditing(clampCopy(null, placeholderFor = clamp), clamp)
+ // try {
+ // fixture.openFileInEditor(inlineEditor.file)
+ // fixture.editor.caretModel.moveToOffset(fixture.editor.document.text.indexOf("/// ") + 4)
+ // withAnnotaterCodeStyle { fixture.completeBasic() }
+ // fixture.lookup.currentItem = fixture.lookupElements!!.single { it.lookupString == "Insert Javadoc Stub" }
+ // withAnnotaterCodeStyle { fixture.finishLookup(Lookup.NORMAL_SELECT_CHAR) }
+ // val text = fixture.editor.document.text
+ // assertTrue(
+ // text.contains(
+ // " /// \n /// @param value\n /// @param min\n /// @param max\n /// @return\n" +
+ // " public static int clamp(",
+ // ),
+ // text,
+ // )
+ // } finally {
+ // Disposer.dispose(inlineEditor)
+ // }
+ // }
+
+ @Test
+ @DisplayName("Formatting only changes the edited Javadoc")
+ fun formatting() {
+ val addEntity = AnnotaterJavadocTarget.MethodTarget(
+ "net/minecraft/world/level/Level",
+ "addEntity",
+ "(Lnet/minecraft/util/Ticker;[Ljava/lang/String;I)Z",
+ )
+ val copy = AnnotaterJavadocSourceCopy.create(findMember("net.minecraft.world.level.Level", "addEntity"), null) {
+ if (it == addEntity) "Adds an entity." else null
+ }!!
+ val inlineEditor = startEditing(copy, addEntity)
+ val document = FileDocumentManager.getInstance().getDocument(inlineEditor.file)!!
+ try {
+ WriteCommandAction.runWriteCommandAction(project) {
+ val offset = document.text.indexOf("/// Adds an entity.") + "/// Adds an entity.".length
+ document.insertString(offset, "\n /// More.")
+ val documentManager = PsiDocumentManager.getInstance(project)
+ documentManager.commitDocument(document)
+ CodeStyleManager.getInstance(project).reformat(documentManager.getPsiFile(document)!!)
-}
+ }
+ val text = document.text
+ assertTrue(text.contains(" /// Adds an entity.\n /// More.\n"), text)
+ assertTrue(text.contains("return true ;"), text)
+ } finally {
+ Disposer.dispose(inlineEditor)
+ }
+ }
+}