Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
3b1210e
upgrade to Scala 3.8.0-RC5, update IntelliJ build, and enhance scalac…
halotukozak Jan 5, 2026
43d37d2
migrate to Scala 3.8.0 syntax by replacing `_` with `?`, introducing …
halotukozak Jan 5, 2026
9092354
migrate to Scala 3 syntax by replacing `implicit` with `given` and `e…
halotukozak Jan 5, 2026
6f98d22
update `ResolutionCtx` to use Scala 3 union types and nullability ann…
halotukozak Jan 5, 2026
561e9cf
refactor: inline extensions in `package.scala` and clean up `.scalafm…
halotukozak Jan 5, 2026
6896d8d
migrate `HoconBlock` and `HoconFormatter` to use Scala 3 union types …
halotukozak Jan 5, 2026
abbfd2e
refactor: adopt Scala 3 chaining syntax with `.orElse` and `.map`, cl…
halotukozak Jan 5, 2026
aa4987f
refactor: update inline extensions to use `T | Null` for improved nul…
halotukozak Jan 5, 2026
ecc1ce5
refactor: migrate `State` in `HoconLexer` to Scala 3 opaque type and …
halotukozak Jan 5, 2026
78cd7ca
remove unused @nowarn annotation in HoconTestUtils and relevant impor…
halotukozak Jan 5, 2026
c8b3272
Merge branch 'enable-source-3' into scala-3
halotukozak Jan 5, 2026
3ce2b3e
Merge branch 'idea25' into scala-3
halotukozak Jan 7, 2026
ab50684
refactor: migrate remaining sealed classes and case classes to Scala …
halotukozak Jan 7, 2026
4ca68ac
refactor: replace `Iterator.takeWhile(_ != null)` with new `Iterator.…
halotukozak Jan 7, 2026
9d44596
update: bump Scala version in CI workflow to `3.8.0-RC5`
halotukozak Jan 7, 2026
3b38c00
Update src/org/jetbrains/plugins/hocon/lexer/HoconLexer.scala
halotukozak Jan 7, 2026
473f1e0
Update src/org/jetbrains/plugins/hocon/parser/HoconElementType.scala
halotukozak Jan 7, 2026
acafd06
refactor: replace `ClassTag` with `Typeable` in `findChildren` for im…
halotukozak Jan 7, 2026
025f4ad
Merge remote-tracking branch 'origin/scala-3' into scala-3
halotukozak Jan 7, 2026
ec5f5f0
refactor: adjust `plugin.xml` inspection class references and migrate…
halotukozak Jan 7, 2026
39bf137
Merge branch 'idea25' into scala-3
halotukozak Jan 7, 2026
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
10 changes: 5 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest]
scala: [2.13.18]
scala: [3.8.0-RC5]
java: [temurin@21]
runs-on: ${{ matrix.os }}
steps:
Expand Down Expand Up @@ -64,7 +64,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest]
scala: [2.13.18]
scala: [3.8.0-RC5]
java: [temurin@21]
runs-on: ${{ matrix.os }}
steps:
Expand All @@ -84,12 +84,12 @@ jobs:
- name: Setup sbt
uses: sbt/setup-sbt@v1

- name: Download target directories (2.13.18)
- name: Download target directories (3.8.0-RC5)
uses: actions/download-artifact@v6
with:
name: target-${{ matrix.os }}-2.13.18-${{ matrix.java }}
name: target-${{ matrix.os }}-3.8.0-RC5-${{ matrix.java }}

- name: Inflate target directories (2.13.18)
- name: Inflate target directories (3.8.0-RC5)
run: |
tar xf targets.tar
rm targets.tar
Expand Down
1 change: 1 addition & 0 deletions .idea/codeStyles/codeStyleConfig.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 1 addition & 8 deletions .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//description of properties: https://scalameta.org/scalafmt/docs/configuration.html
version = "3.10.3"
runner.dialect = scala213source3
runner.dialect = scala3
maxColumn = 120

continuationIndent {
Expand Down Expand Up @@ -81,13 +81,6 @@ rewrite.neverInfix.excludeFilters = [
theSameElementsAs
//below extends default
theSameElementsInOrderAs
like
zip
orElse
getOrElse
matchOpt
map
flatMap
]

xmlLiterals.assumeFormatted = true
Expand Down
74 changes: 46 additions & 28 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import org.jetbrains.sbtidea.Keys._

ThisBuild / scalaVersion := "2.13.18"
ThisBuild / scalaVersion := "3.8.0-RC5"
ThisBuild / intellijPluginName := "intellij-hocon"
ThisBuild / intellijBuild := "253.29346.138"
ThisBuild / githubWorkflowJavaVersions := Seq(JavaSpec.temurin("21"))
Expand All @@ -11,30 +11,48 @@ val junitVersion = "4.13.2"
val commonsTextVersion = "1.15.0"
val opentest4jVersion = "1.3.0"

lazy val hocon = project.in(file(".")).enablePlugins(SbtIdeaPlugin).settings(
version := "2026.1.2-SNAPSHOT",
Compile / scalaSource := baseDirectory.value / "src",
Test / scalaSource := baseDirectory.value / "test",
Compile / resourceDirectory := baseDirectory.value / "resources",
Global / javacOptions ++= Seq("--release", "21"),
Global / scalacOptions ++= Seq(
"-deprecation",
"-feature",
"-unchecked",
"-Xfatal-warnings",
"-Xsource:3"
),
ideBasePackages := Seq("org.jetbrains.plugins.hocon"),
intellijPlugins := Seq("com.intellij.java-i18n", "com.intellij.modules.json").map(_.toPlugin),
intellijExtraRuntimePluginsInTests := Seq("org.jetbrains.kotlin").map(_.toPlugin),
libraryDependencies ++= Seq(
"org.apache.commons" % "commons-text" % commonsTextVersion,
"com.github.sbt" % "junit-interface" % junitInterfaceVersion % Test,
"junit" % "junit" % junitVersion % Test,
"org.opentest4j" % "opentest4j" % opentest4jVersion % Test,
),
packageLibraryMappings := Seq.empty, // allow scala-library
patchPluginXml := pluginXmlOptions { xml =>
xml.version = version.value
}
)
lazy val hocon = project
.in(file("."))
.enablePlugins(SbtIdeaPlugin)
.settings(
version := "2026.1.2-SNAPSHOT",
Compile / scalaSource := baseDirectory.value / "src",
Test / scalaSource := baseDirectory.value / "test",
Compile / resourceDirectory := baseDirectory.value / "resources",
Global / javacOptions ++= Seq("--release", "21"),
Global / scalacOptions ++= Seq(
"-deprecation",
"-feature",
"-unchecked",
"-deprecation",
"-explain",
"-old-syntax",
"-unchecked",
"-language:noAutoTupling",
"-Vprofile",
"-Ycheck:all",
"-Ycheck:macros",
"-Ydebug-missing-refs",
"-Yexplain-lowlevel",
"-Yexplicit-nulls",
"-Wsafe-init",
"-Yshow-suppressed-errors",
"-Yshow-var-bounds",
"-Werror",
"-Wunused:all",
"-preview",
),
ideBasePackages := Seq("org.jetbrains.plugins.hocon"),
intellijPlugins := Seq("com.intellij.java-i18n", "com.intellij.modules.json").map(_.toPlugin),
intellijExtraRuntimePluginsInTests := Seq("org.jetbrains.kotlin").map(_.toPlugin),
libraryDependencies ++= Seq(
"org.apache.commons" % "commons-text" % commonsTextVersion,
"com.github.sbt" % "junit-interface" % junitInterfaceVersion % Test,
"junit" % "junit" % junitVersion % Test,
"org.opentest4j" % "opentest4j" % opentest4jVersion % Test,
),
packageLibraryMappings := Seq.empty, // allow scala-library
patchPluginXml := pluginXmlOptions { xml =>
xml.version = version.value
},
)
8 changes: 4 additions & 4 deletions resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,11 @@
displayName="HOCON"/>

<localInspection language="HOCON"
implementationClass="org.jetbrains.plugins.hocon.ref.HoconIncludeResolutionInspection"
implementationClass="org.jetbrains.plugins.hocon.ref.AbstractHoconIncludeResolutionInspection.HoconIncludeResolutionInspection$"
displayName="Include resolution" groupName="HOCON" enabledByDefault="true"
level="WARNING"/>
<localInspection language="HOCON"
implementationClass="org.jetbrains.plugins.hocon.ref.HoconRequiredIncludeResolutionInspection"
implementationClass="org.jetbrains.plugins.hocon.ref.AbstractHoconIncludeResolutionInspection.HoconRequiredIncludeResolutionInspection$"
displayName="Required include resolution" groupName="HOCON" enabledByDefault="true"
level="ERROR"/>

Expand Down Expand Up @@ -108,11 +108,11 @@
</extensions>

<actions>
<action class="org.jetbrains.plugins.hocon.navigation.HoconGotoNextAction" id="HoconGotoNext"
<action class="org.jetbrains.plugins.hocon.navigation.HoconGotoPrevNextAction$HoconGotoNextAction" id="HoconGotoNext"
text="HOCON: Go to Next Definition"
description="Navigates to the next definition of a property in a HOCON file"
use-shortcut-of="GotoImplementation"/>
<action class="org.jetbrains.plugins.hocon.navigation.HoconGotoPrevAction" id="HoconGotoPrev"
<action class="org.jetbrains.plugins.hocon.navigation.HoconGotoPrevNextAction$HoconGotoPrevAction" id="HoconGotoPrev"
text="HOCON: Go to Previous Definition"
description="Navigates to the previous definition of a property in a HOCON file"
use-shortcut-of="GotoSuperMethod"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ class HoconLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSettingsPr
private val ObjectFieldsWithAssignmentWrap = "Object fields with '=' or '+='"

override def customizeSettings(consumer: CodeStyleSettingsCustomizable, settingsType: SettingsType): Unit = {
def showCustomOption(name: String, title: String, group: String, options: AnyRef*): Unit =
consumer.showCustomOption(classOf[HoconCustomCodeStyleSettings], name, title, group, options: _*)
def showCustomOption(name: String, title: String, group: String | Null, options: AnyRef*): Unit =
consumer.showCustomOption(classOf[HoconCustomCodeStyleSettings], name, title, group, options*)

import CodeStyleSettingsCustomizable.{WRAP_VALUES, WRAP_VALUES_FOR_SINGLETON}
import com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider.SettingsType.*
Expand All @@ -37,7 +37,7 @@ class HoconLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSettingsPr
SpacingOption.SPACE_WITHIN_METHOD_CALL_PARENTHESES,
SpacingOption.SPACE_BEFORE_COMMA,
SpacingOption.SPACE_AFTER_COMMA,
).map(_.name): _*
).map(_.name)*
)

consumer.renameStandardOption(SpacingOption.SPACE_WITHIN_BRACES.name, "Object braces")
Expand Down
18 changes: 9 additions & 9 deletions src/org/jetbrains/plugins/hocon/editor/HoconCommenter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,23 @@ class HoconCommenter extends CodeDocumentationAwareCommenter {

def getLineCommentTokenType: IElementType = HoconTokenType.DoubleSlashComment

def getBlockCommentSuffix: String = null
def getBlockCommentSuffix: String | Null = null

def getBlockCommentPrefix: String = null
def getBlockCommentPrefix: String | Null = null

def getCommentedBlockCommentPrefix: String = null
def getCommentedBlockCommentPrefix: String | Null = null

def getCommentedBlockCommentSuffix: String = null
def getCommentedBlockCommentSuffix: String | Null = null

def getDocumentationCommentLinePrefix: String = null
def getDocumentationCommentLinePrefix: String | Null = null

def getBlockCommentTokenType: IElementType = null
def getBlockCommentTokenType: IElementType | Null = null

def getDocumentationCommentTokenType: IElementType = null
def getDocumentationCommentTokenType: IElementType | Null = null

def isDocumentationComment(element: PsiComment): Boolean = false

def getDocumentationCommentSuffix: String = null
def getDocumentationCommentSuffix: String | Null = null

def getDocumentationCommentPrefix: String = null
def getDocumentationCommentPrefix: String | Null = null
}
79 changes: 43 additions & 36 deletions src/org/jetbrains/plugins/hocon/editor/HoconObjectEntryMover.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import com.intellij.openapi.editor.Editor
import com.intellij.openapi.util.Key
import com.intellij.psi.{PsiElement, PsiFile}
import org.jetbrains.plugins.hocon.editor.HoconObjectEntryMover.{PrefixModKey, PrefixModification}
import org.jetbrains.plugins.hocon.psi.*
import org.jetbrains.plugins.hocon.psi.{HObjectField, *}

import scala.annotation.tailrec

Expand Down Expand Up @@ -133,18 +133,21 @@ class HoconObjectEntryMover extends LineMover {
def canInsertInto(field: HObjectField) =
!inSingleLine(field) && {
val lineToInsertAfter = if (down) firstNonCommentLine(field) else endLine(field) - 1
file.elementsAt(document.getLineEndOffset(lineToInsertAfter)).collectFirst {
case entries: HObjectEntries => entries.prefixingField.map(_.enclosingObjectField).contains(field)
case _: HKeyedField => false
} getOrElse false
file
.elementsAt(document.getLineEndOffset(lineToInsertAfter))
.collectFirst {
case entries: HObjectEntries => entries.prefixingField.map(_.enclosingObjectField).contains(field)
case _: HKeyedField => false
}
.getOrElse(false)
}

def adjacentEntry(entry: HObjectEntry) =
if (down) entry.nextEntry else entry.prevEntry

def fieldToDescendInto(field: HObjectField): Option[(HObjectField, List[String])] =
for {
adjacentField <- adjacentEntry(field).collect { case f: HObjectField => f }.filter(canInsertInto)
adjacentField <- adjacentEntry(field).collectOnly[HObjectField].filter(canInsertInto)
prefixToRemove <- {
val prefix = adjacentField.keyedField.fieldsInPathForward.map(keyString).toList
val removablePrefix = field.keyedField.fieldsInPathForward
Expand All @@ -161,43 +164,47 @@ class HoconObjectEntryMover extends LineMover {
def trySpecializedFieldMove(objField: HObjectField) = {
val sourceRange = lineRange(objField)

fieldToAscendOutOf(objField).map { case (enclosingField, prefixToAdd) =>
val targetRange =
if (down) new LineRange(sourceRange.endLine, endLine(enclosingField) + 1)
else new LineRange(startLine(enclosingField), sourceRange.startLine)
val mod = PrefixModification(objField.getTextOffset, 0, prefixToAdd.mkString("", ".", "."))
(sourceRange, targetRange, Some(mod))

} orElse fieldToDescendInto(objField).map { case (adjacentField, prefixToRemove) =>
val targetRange =
if (down) new LineRange(sourceRange.endLine, firstNonCommentLine(adjacentField) + 1)
else new LineRange(endLine(adjacentField), sourceRange.startLine)
val prefixStr = prefixToRemove.mkString("", ".", ".")
val needsGuard = document.getCharsSequence.charAt(objField.getTextOffset + prefixStr.length).isWhitespace
val mod = PrefixModification(objField.getTextOffset, prefixStr.length, if (needsGuard) "\"\"" else "")
(sourceRange, targetRange, Some(mod))
}
fieldToAscendOutOf(objField)
.map { case (enclosingField, prefixToAdd) =>
val targetRange =
if (down) new LineRange(sourceRange.endLine, endLine(enclosingField) + 1)
else new LineRange(startLine(enclosingField), sourceRange.startLine)
val mod = (objField.getTextOffset, 0, prefixToAdd.mkString("", ".", "."))
(sourceRange, targetRange, Some(mod))

}
.orElse(fieldToDescendInto(objField).map { case (adjacentField, prefixToRemove) =>
val targetRange =
if (down) new LineRange(sourceRange.endLine, firstNonCommentLine(adjacentField) + 1)
else new LineRange(endLine(adjacentField), sourceRange.startLine)
val prefixStr = prefixToRemove.mkString("", ".", ".")
val needsGuard = document.getCharsSequence.charAt(objField.getTextOffset + prefixStr.length).isWhitespace
val mod = (objField.getTextOffset, prefixStr.length, if (needsGuard) "\"\"" else "")
(sourceRange, targetRange, Some(mod))
})
}

def tryEntryMove(entry: HObjectEntry) = {
val sourceRange = lineRange(entry)
adjacentMovableEntry(entry).map { adjacentEntry =>
(sourceRange, lineRange(adjacentEntry), None)
} orElse {
val maxLinePos = editor.offsetToLogicalPosition(document.getTextLength)
val maxLine = if (maxLinePos.column == 0) maxLinePos.line else maxLinePos.line + 1
val nearLine = if (down) sourceRange.endLine else sourceRange.startLine - 1

if (nearLine >= 0 && nearLine < maxLine)
Some((sourceRange, singleLineRange(nearLine), None))
else None
}
adjacentMovableEntry(entry)
.map { adjacentEntry =>
(sourceRange, lineRange(adjacentEntry), None)
}
.orElse {
val maxLinePos = editor.offsetToLogicalPosition(document.getTextLength)
val maxLine = if (maxLinePos.column == 0) maxLinePos.line else maxLinePos.line + 1
val nearLine = if (down) sourceRange.endLine else sourceRange.startLine - 1

if (nearLine >= 0 && nearLine < maxLine)
Some((sourceRange, singleLineRange(nearLine), None))
else None
}
}

val rangesOpt: Option[(LineRange, LineRange, Option[PrefixModification])] =
enclosingAnchoredEntry(element).flatMap {
case objField: HObjectField =>
trySpecializedFieldMove(objField) orElse tryEntryMove(objField)
trySpecializedFieldMove(objField).orElse(tryEntryMove(objField))
case include: HInclude =>
tryEntryMove(include)
}
Expand All @@ -211,7 +218,7 @@ class HoconObjectEntryMover extends LineMover {
}

override def beforeMove(editor: Editor, info: MoveInfo, down: Boolean): Unit =
info.getUserData(PrefixModKey).foreach { case PrefixModification(offset, length, replacement) =>
info.getUserData(PrefixModKey).foreach { case (offset, length, replacement) =>
// we need to move caret manually when adding prefix exactly at caret position
val caretModel = editor.getCaretModel
val shouldMoveCaret = length == 0 && caretModel.getOffset == offset
Expand All @@ -224,7 +231,7 @@ class HoconObjectEntryMover extends LineMover {

object HoconObjectEntryMover {

case class PrefixModification(offset: Int, length: Int, replacement: String)
type PrefixModification = (offset: Int, length: Int, replacement: String)

val PrefixModKey = new Key[Option[PrefixModification]]("PrefixMod")
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class HoconQuoteHandler extends SimpleTokenSetQuoteHandler(HoconTokenType.Quoted

def getConcatenatableStringTokenTypes: TokenSet = TokenSet.EMPTY

def getStringConcatenationOperatorRepresentation: String = null
def getStringConcatenationOperatorRepresentation: String | Null = null

def getStringTokenTypes: TokenSet = myLiteralTokenSet

Expand Down
Loading