Skip to content

Commit 6be3f4c

Browse files
committed
Version 1.6.7: Fix bugs #105, #106 and fix IDE plugin deprecated usages
This CL: - Updates plugin code to replace deprecated API usages - Fixes issue #104: Include type parameters in parameter list reordering - Fixes issue #105: Make add punctuation apply to all paragraphs, not just last A few other misc improvements (e.g. make punctuation smarter around non-letter paragraph endings, such as `, and avoids adding punctuation on paths and URLs, and also gracefully handle <>'s around type variable names in @param documentation.
1 parent 77fa637 commit 6be3f4c

File tree

11 files changed

+255
-31
lines changed

11 files changed

+255
-31
lines changed

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
# KDoc Formatter Changelog
44

5+
## [1.6.7]
6+
- Fix issue #104: Include type parameters in parameter list reordering
7+
- Fix issue #105: Make add punctuation apply to all paragraphs, not just last
8+
- IDE plugin fixes (replaced deprecated API calls)
9+
510
## [1.6.6]
611
- IDE-only update: Marked compatible with IntelliJ IDEA 2025.1.
712
- Updated dependencies
@@ -249,4 +254,4 @@
249254
- Adds hanging indents for ordered and unordered indents.
250255
- Cleans up the double spaces left by the IntelliJ "Convert to Kotlin"
251256
action right before the closing comment token.
252-
- Removes trailing spaces.
257+
- Removes trailing spaces.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ Options:
120120
@<filename>
121121
Read filenames from file.
122122
123-
kdoc-formatter: Version 1.6.6
123+
kdoc-formatter: Version 1.6.7
124124
https://github.com/tnorbye/kdoc-formatter
125125
```
126126

cli/src/main/kotlin/kdocformatter/cli/KotlinLexer.kt

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -346,14 +346,28 @@ class KotlinLexer(private val source: String) {
346346
skipSpace()
347347
val token = nextToken() ?: return null
348348
if (token == "fun") {
349-
// Find beginning of parameter list
349+
val parameters = mutableListOf<String>()
350+
351+
// Find beginning of parameter list, and pick up
352+
// any type parameters in the signature
350353
while (i < length) {
351-
val name = nextToken(matchParens = false) ?: return null
352-
if (name == "(") {
354+
val token = nextToken(matchParens = false) ?: return null
355+
if (token == "<") {
356+
var nextIsVariable = true
357+
while (i < length) {
358+
val varName = nextToken(matchParens = false) ?: return null
359+
if (nextIsVariable && varName.isIdentifier()) {
360+
parameters.add(varName)
361+
}
362+
if (varName == ">") {
363+
break
364+
}
365+
nextIsVariable = varName == ","
366+
}
367+
} else if (token == "(") {
353368
break
354369
}
355370
}
356-
val parameters = mutableListOf<String>()
357371

358372
while (i < length) {
359373
// Name is last symbol before ":"
@@ -400,6 +414,10 @@ class KotlinLexer(private val source: String) {
400414
return null
401415
}
402416

417+
private fun String.isIdentifier(): Boolean {
418+
return this[0].isJavaIdentifierStart() && this.all { it.isJavaIdentifierPart() }
419+
}
420+
403421
companion object {
404422
private const val PLAIN_TEXT = 1
405423
private const val LINE_COMMENT = 2

cli/src/test/kotlin/kdocformatter/cli/KDocFileFormatterTest.kt

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,100 @@ class KDocFileFormatterTest {
370370
reformatted.trim())
371371
}
372372

373+
@Test
374+
fun testKDocOrderingForGenerics() {
375+
// Regression test for https://github.com/tnorbye/kdoc-formatter/issues/104
376+
val source =
377+
"""
378+
class KType
379+
class Test {
380+
/**
381+
* @param type The [KType] representing the type to be instantiated.
382+
* @param T The type of the instance to be created.
383+
* @return An instance of the specified type, or `null` if instantiation fails.
384+
*/
385+
public fun <T : Any> createInstance(type: KType): T? {
386+
return null
387+
}
388+
}
389+
390+
/**
391+
* @param clazz the class
392+
* @param constructor to invoke
393+
* @param T the view
394+
* @param S the stage
395+
*/
396+
fun <S : Stage<*>?, T : StageView<*>?> bind(clazz: Class<S>, constructor: BiFunction<TaskProfilersView?, S, T>) {
397+
}
398+
399+
object Kt {
400+
/**
401+
* Run the loop and wait until condition is true or the retry limit is reached. Returns the
402+
* result afterwards.
403+
*
404+
* @param supplier a function that returns the desired result.
405+
* @param condition tests whether the result is desired.
406+
* @param retryLimit Limit to retry before return the result. If not specified, try forever.
407+
* @param <T> type of the desired result.
408+
* @return the result from the last run (condition met or timeout).
409+
*/
410+
fun <T> waitForAndReturn(
411+
supplier: () -> T,
412+
condition: (T) -> Boolean,
413+
retryLimit: Int = NO_LIMIT,
414+
): T = TODO()
415+
}
416+
"""
417+
.trimIndent()
418+
419+
val reformatted = reformatFile(source, KDocFormattingOptions(72).apply { orderDocTags = true })
420+
assertEquals(
421+
"""
422+
class KType
423+
class Test {
424+
/**
425+
* @param T The type of the instance to be created.
426+
* @param type The [KType] representing the type to be instantiated.
427+
* @return An instance of the specified type, or `null` if
428+
* instantiation fails.
429+
*/
430+
public fun <T : Any> createInstance(type: KType): T? {
431+
return null
432+
}
433+
}
434+
435+
/**
436+
* @param S the stage
437+
* @param T the view
438+
* @param clazz the class
439+
* @param constructor to invoke
440+
*/
441+
fun <S : Stage<*>?, T : StageView<*>?> bind(clazz: Class<S>, constructor: BiFunction<TaskProfilersView?, S, T>) {
442+
}
443+
444+
object Kt {
445+
/**
446+
* Run the loop and wait until condition is true or the retry limit is
447+
* reached. Returns the result afterwards.
448+
*
449+
* @param <T> type of the desired result.
450+
* @param supplier a function that returns the desired result.
451+
* @param condition tests whether the result is desired.
452+
* @param retryLimit Limit to retry before return the result. If not
453+
* specified, try forever.
454+
* @return the result from the last run (condition met or timeout).
455+
*/
456+
fun <T> waitForAndReturn(
457+
supplier: () -> T,
458+
condition: (T) -> Boolean,
459+
retryLimit: Int = NO_LIMIT,
460+
): T = TODO()
461+
}
462+
"""
463+
.trimIndent(),
464+
reformatted.trim())
465+
}
466+
373467
@Test
374468
fun testLineWidth() {
375469
// Perform in KDocFileFormatter test too to make sure we properly account

ide-plugin/CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
# KDoc Formatter Plugin Changelog
44

5+
## [1.6.7]
6+
- Updated code to replace deprecated API usages
7+
- Fix issue #104: Include type parameters in parameter list reordering
8+
- Fix issue #105: Make add punctuation apply to all paragraphs, not just last
9+
510
## [1.6.6]
611
- Add support for 2025.1 EAP, and migrate to 2.x version of IntelliJ gradle plugin.
712
- Fix https://github.com/tnorbye/kdoc-formatter/issues/106
@@ -159,4 +164,4 @@
159164
width).
160165

161166
## [1.0.0]
162-
- Initial version
167+
- Initial version

ide-plugin/gradle.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ pluginRepositoryUrl = https://github.com/tnorbye/kdoc-formatter
77

88
# See https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
99
# for insight into build numbers and IntelliJ Platform versions.
10-
pluginSinceBuild = 232
10+
pluginSinceBuild = 243
1111
pluginUntilBuild = 251.*
1212

1313
# IntelliJ Platform Properties -> https://github.com/JetBrains/gradle-intellij-plugin#intellij-platform-properties
1414
platformType = IC
15-
platformVersion = 2024.2
15+
platformVersion = 2024.3
1616
#platformVersion = 251.14649-EAP-CANDIDATE-SNAPSHOT
1717

1818
# Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html

ide-plugin/src/main/kotlin/kdocformatter/plugin/ReformatKDocAction.kt

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@ import com.facebook.ktfmt.kdoc.findSamePosition
2525
import com.facebook.ktfmt.kdoc.isLineComment
2626
import com.intellij.application.options.CodeStyle
2727
import com.intellij.lang.Language
28-
import com.intellij.openapi.actionSystem.*
28+
import com.intellij.openapi.actionSystem.ActionUiKind
29+
import com.intellij.openapi.actionSystem.ActionUpdateThread
30+
import com.intellij.openapi.actionSystem.AnAction
31+
import com.intellij.openapi.actionSystem.AnActionEvent
32+
import com.intellij.openapi.actionSystem.CommonDataKeys
2933
import com.intellij.openapi.command.WriteCommandAction
3034
import com.intellij.openapi.editor.Document
3135
import com.intellij.openapi.project.DumbAware
@@ -39,8 +43,8 @@ import com.intellij.psi.PsiFile
3943
import com.intellij.psi.PsiManager
4044
import com.intellij.psi.PsiWhiteSpace
4145
import com.intellij.psi.util.PsiTreeUtil
42-
import com.intellij.refactoring.suggested.endOffset
43-
import com.intellij.refactoring.suggested.startOffset
46+
import com.intellij.psi.util.endOffset
47+
import com.intellij.psi.util.startOffset
4448
import com.intellij.util.ThrowableRunnable
4549
import kotlin.math.min
4650
import org.jetbrains.kotlin.idea.KotlinLanguage
@@ -263,7 +267,7 @@ class ReformatKDocAction : AnAction(), DumbAware {
263267
val presentation = event.presentation
264268
val type = getApplicableCommentType(event)
265269
val available = type != CommentType.NONE
266-
if (ActionPlaces.isPopupPlace(event.place)) {
270+
if (event.uiKind == ActionUiKind.POPUP) {
267271
presentation.isEnabledAndVisible = available
268272
} else {
269273
presentation.isEnabled = available
@@ -349,7 +353,9 @@ fun createFormattingTask(
349353
if (task.type == CommentType.KDOC && options.orderDocTags) {
350354
val parent = kdoc.parent
351355
if (parent is KtCallableDeclaration) {
352-
task.orderedParameterNames = parent.valueParameters.mapNotNull { it.name }.toList()
356+
task.orderedParameterNames =
357+
parent.typeParameters.mapNotNull { it.name } +
358+
parent.valueParameters.mapNotNull { it.name }
353359
}
354360
}
355361

library/src/main/kotlin/com/facebook/ktfmt/kdoc/ParagraphListBuilder.kt

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -826,28 +826,48 @@ class ParagraphListBuilder(
826826
if (!options.addPunctuation || paragraphs.isEmpty()) {
827827
return
828828
}
829-
val last = paragraphs.last()
830-
if (last.preformatted || last.doc || last.hanging && !last.continuation || last.isEmpty()) {
831-
return
832-
}
833-
834-
val text = last.content
835-
if (!text.startsWithUpperCaseLetter()) {
836-
return
837-
}
829+
for (last in paragraphs) {
830+
if (last.preformatted || last.doc || last.hanging && !last.continuation || last.isEmpty()) {
831+
continue
832+
}
838833

839-
for (i in text.length - 1 downTo 0) {
840-
val c = text[i]
841-
if (c.isWhitespace()) {
834+
val text = last.content
835+
if (!text.startsWithUpperCaseLetter() || text.contentEquals("TODO")) {
842836
continue
843837
}
844-
if (c.isLetterOrDigit() || c.isCloseSquareBracket()) {
845-
text.setLength(i + 1)
846-
text.append('.')
838+
839+
for (i in text.length - 1 downTo 0) {
840+
val c = text[i]
841+
if (c.isWhitespace()) {
842+
continue
843+
}
844+
val isQuoteEnd = c.isCloseSquareBracket() || c == '`'
845+
if (c.isLetterOrDigit() || isQuoteEnd) {
846+
if (!isQuoteEnd && isUrlOrPathEnd(text, i)) {
847+
break
848+
}
849+
text.setLength(i + 1)
850+
text.append('.')
851+
}
852+
break
847853
}
854+
}
855+
}
856+
}
857+
858+
/** Returns true if the given string appears to be the tail end of a URL or path reference. */
859+
private fun isUrlOrPathEnd(s: CharSequence, offset: Int): Boolean {
860+
var i = offset - 1
861+
while (i >= 0) {
862+
val c = s[i]
863+
if (c == '/' || c == '.') {
864+
return true
865+
} else if (c.isWhitespace()) {
848866
break
849867
}
868+
i--
850869
}
870+
return false
851871
}
852872

853873
fun String.containsOnly(vararg s: Char): Boolean {

library/src/main/kotlin/com/facebook/ktfmt/kdoc/Utilities.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ fun String.getParamName(): String? {
161161
}
162162
}
163163

164-
if (start < length && this[start] == '[') {
164+
if (start < length && (this[start] == '[' || this[start] == '<')) {
165165
start++
166166
while (start < length) {
167167
if (this[start].isWhitespace()) {

library/src/main/resources/version.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@
1515
#
1616

1717
# Release version definition
18-
buildVersion = 1.6.6
18+
buildVersion = 1.6.7

0 commit comments

Comments
 (0)