Skip to content
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
- Added Move Kotlin File and Directory refactoring: move the active `.kt` file from **Refactor → Move Kotlin File...**, or right-click a folder and choose **Move Kotlin Directory...** to recursively move its Kotlin files while retaining the selected directory and nested structure below a selected source root/package; matching package directives and supported Kotlin code references are updated, and **Undo Last Refactoring** restores the original locations and contents.
- Improved Kotlin Inline Variable: local `var` declarations with one initializer and no subsequent writes can now be inlined from either a declaration or selected usage; assignments and increment/decrement writes are rejected before changing the source with the conflicting expression identified.
- Improved Kotlin Introduce Type Alias: selecting a complete generic type now creates a concrete alias, while selecting only its type constructor creates a parameterized alias that preserves nested and qualified type structures and each same-file usage's own type arguments; star projections and local type parameters that cannot form a valid top-level alias are rejected.
- Improved Kotlin hierarchy-aware refactorings: Pull Members Up, Push Members Down, and compatible IDEA refactoring paths now discover Kotlin override chains and direct supertypes across all source files in the active Maven or Gradle build; Java/library and full IDEA-index conflict scenarios remain unavailable.
Expand Down
2 changes: 1 addition & 1 deletion CoreImpl/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>io.github.nbplugins</groupId>
<artifactId>netbeans-plugin-kotlin</artifactId>
<version>0.11.231-SNAPSHOT</version>
<version>0.11.245-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>netbeans-plugin-kotlin-core-impl</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion KotlinCompilerCliBase/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>io.github.nbplugins</groupId>
<artifactId>netbeans-plugin-kotlin</artifactId>
<version>0.11.231-SNAPSHOT</version>
<version>0.11.245-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>netbeans-plugin-kotlin-compiler-cli-base</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion KotlinFixesImpl/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>io.github.nbplugins</groupId>
<artifactId>netbeans-plugin-kotlin</artifactId>
<version>0.11.231-SNAPSHOT</version>
<version>0.11.245-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>netbeans-plugin-kotlin-fixes-impl</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion KotlinFormatter/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>io.github.nbplugins</groupId>
<artifactId>netbeans-plugin-kotlin</artifactId>
<version>0.11.231-SNAPSHOT</version>
<version>0.11.245-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>netbeans-plugin-kotlin-formatter</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion KotlinHighlighting/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>io.github.nbplugins</groupId>
<artifactId>netbeans-plugin-kotlin</artifactId>
<version>0.11.231-SNAPSHOT</version>
<version>0.11.245-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>netbeans-plugin-kotlin-highlighting</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion KotlinIcons/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>io.github.nbplugins</groupId>
<artifactId>netbeans-plugin-kotlin</artifactId>
<version>0.11.231-SNAPSHOT</version>
<version>0.11.245-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>netbeans-plugin-kotlin-icons</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion KotlinIntentionUtils/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>io.github.nbplugins</groupId>
<artifactId>netbeans-plugin-kotlin</artifactId>
<version>0.11.231-SNAPSHOT</version>
<version>0.11.245-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>netbeans-plugin-kotlin-intention-utils</artifactId>
Expand Down
148 changes: 90 additions & 58 deletions KotlinRefactoring/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>io.github.nbplugins</groupId>
<artifactId>netbeans-plugin-kotlin</artifactId>
<version>0.11.231-SNAPSHOT</version>
<version>0.11.245-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>netbeans-plugin-kotlin-refactoring</artifactId>
Expand Down Expand Up @@ -741,16 +741,19 @@
</resources>
</configuration>
</execution>
<!-- kotlin.refactorings.move.k2: Move Declaration engine (E9.7). Real IDEA algorithm for
moving a top-level declaration to another file/package: usage retargeting (built on
K2MoveRenameUsageInfo, already ported for Copy Declaration/E9.19), conflict detection,
and outer-instance-parameter handling for moved nested declarations.
<!-- kotlin.refactorings.move.k2: Move Declaration engine (E9.7) plus the F4.1 Kotlin
file/directory semantic handler. Move Declaration supplies the real top-level
declaration move/retarget algorithm. K2MoveFilesHandler supplies the upstream K2
rules for deciding package rewrites, conflict discovery, and Kotlin usage retargeting.
Its processor superclass (MoveFilesOrDirectoriesProcessor) is deliberately NOT copied:
it owns IDEA's indexed generic-file search, physical VFS mutations, command/listener
lifecycle, and modal progress/UI. NetBeans performs physical moves transactionally;
KaMoveFileComputer invokes the K2 handler semantics through a lifecycle-free adapter.
Excluded: K2MoveHandler.kt (IDE action-system dispatch), ui/** (Swing dialog/model —
NB provides its own text-field UI), K2MoveFilesOrDirectoriesRefactoringProcessor.kt +
K2MoveDirectoryWithClassesHelper.kt (file/directory moves, out of scope),
K2ChangePackageDescriptor.kt/K2ChangePackageRefactoringProcessor.kt (separate feature),
NB provides its own UI), K2MoveDirectoryWithClassesHelper.kt (generic filesystem move),
K2ChangePackageDescriptor.kt/K2ChangePackageRefactoringProcessor.kt (F4.2), and
K2MoveDeclarationsRefactoringListener.kt (message-bus event topic, stripped from the
processor below since NB does not have other listeners subscribed to it). -->
declaration processor because no standalone listeners are subscribed). -->
<execution>
<id>copy-kotlin-refactoring-move-k2-declarations</id>
<phase>initialize</phase>
Expand All @@ -768,6 +771,7 @@
<include>org/jetbrains/kotlin/idea/k2/refactoring/move/descriptor/K2MoveTargetDescriptor.kt</include>
<include>org/jetbrains/kotlin/idea/k2/refactoring/move/descriptor/K2MoveOperationDescriptor.kt</include>
<include>org/jetbrains/kotlin/idea/k2/refactoring/move/processor/K2MoveDeclarationsRefactoringProcessor.kt</include>
<include>org/jetbrains/kotlin/idea/k2/refactoring/move/processor/K2MoveFilesOrDirectoriesRefactoringProcessor.kt</include>
<include>org/jetbrains/kotlin/idea/k2/refactoring/move/processor/moveUtil.kt</include>
<include>org/jetbrains/kotlin/idea/k2/refactoring/move/processor/moveUsageUtil.kt</include>
<include>org/jetbrains/kotlin/idea/k2/refactoring/move/processor/moveConflictUtil.kt</include>
Expand Down Expand Up @@ -1928,46 +1932,60 @@ if (k2MoveProcessor.exists()) {
log.info("Patched K2MoveDeclarationsRefactoringProcessor.kt: decoupled from BaseRefactoringProcessor.run() pipeline and IDE-only listener/UI paths")
}

// 17. K2MoveOperationDescriptor.kt (E9.7): drop the Files-move variant (out of scope — see the
// pom.xml comment on copy-kotlin-refactoring-move-k2-declarations) and Kotlin Multiplatform
// expect/actual fan-out (this plugin does not support Kotlin Multiplatform, consistent with
// ExpectActualUtils being dropped everywhere else it was pulled in, e.g. CodeInliner.kt).
// 16a. K2MoveFilesOrDirectoriesRefactoringProcessor.kt (F4.1): retain the real K2MoveFilesHandler
// semantics but remove the outer processor. The outer class is inseparable from IDEA's generic
// MoveFilesOrDirectoriesProcessor (indexed ReferencesSearch, writable IntelliJ VFS, command/
// listener lifecycle, and modal UI). In NetBeans, KaMoveFileComputer calls this handler-shaped
// adapter while KotlinRefactoringTransaction performs physical moves and undo. This preserves
// IDEA's Kotlin-specific package eligibility, conflict collection, package-directive rewrite,
// declaration mapping, and K2 usage retargeting without pretending the unavailable platform
// lifecycle is portable.
def k2MoveFilesProcessor = new File(srcDir, 'org/jetbrains/kotlin/idea/k2/refactoring/move/processor/K2MoveFilesOrDirectoriesRefactoringProcessor.kt')
if (k2MoveFilesProcessor.exists()) {
def text = k2MoveFilesProcessor.text
text = text.replace('import com.intellij.openapi.application.runWriteAction\n', '')
text = text.replace('import com.intellij.openapi.util.Ref\n', '')
text = text.replace('import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFileHandler\n', '')
text = text.replace('import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesProcessor\n', '')
text = text.replace('import org.jetbrains.kotlin.idea.core.getImplicitPackagePrefix\n', '')
text = text.replace('import org.jetbrains.kotlin.idea.k2.refactoring.move.descriptor.K2MoveOperationDescriptor\n', '')

def processorStart = text.indexOf('internal class K2MoveFilesOrDirectoriesRefactoringProcessor(')
def handlerStart = text.indexOf('class K2MoveFilesHandler')
if (processorStart >= 0 && handlerStart > processorStart) {
text = text.substring(0, processorStart) + text.substring(handlerStart)
}
text = text.replace('class K2MoveFilesHandler : MoveFileHandler() {',
'class K2MoveFilesHandler {')
text = text.replace(' override fun canProcessElement(', ' fun canProcessElement(')
text = text.replace(' override fun findUsages(', ' fun findUsages(')
text = text.replace(' override fun detectConflicts(', ' fun detectConflicts(')
text = text.replace(' override fun prepareMovedFile(', ' fun prepareMovedFile(')
text = text.replace(' override fun updateMovedFile(', ' fun updateMovedFile(')
text = text.replace(' override fun retargetUsages(', ' fun retargetUsages(')
k2MoveFilesProcessor.text = text
log.info("Patched K2MoveFilesOrDirectoriesRefactoringProcessor.kt: retained lifecycle-free K2MoveFilesHandler only")
}

// 17. K2MoveOperationDescriptor.kt (E9.7/F4.1): preserve the Files descriptor so its upstream
// source/target data model stays available to the lifecycle-free F4.1 adapter. The descriptor's
// refactoringProcessor() would construct the full IDEA MoveFilesOrDirectoriesProcessor, which
// is intentionally not compiled standalone: NetBeans owns physical VFS moves, command/undo and
// UI. Keep the real file-move descriptor but fail explicitly if an accidental caller invokes the
// unavailable IDEA lifecycle. Also remove Kotlin Multiplatform expect/actual fan-out, which this
// plugin does not support (consistent with the rest of the refactoring ports).
def k2MoveOpDescriptor = new File(srcDir, 'org/jetbrains/kotlin/idea/k2/refactoring/move/descriptor/K2MoveOperationDescriptor.kt')
if (k2MoveOpDescriptor.exists()) {
def text = k2MoveOpDescriptor.text

text = text.replace('import org.jetbrains.kotlin.idea.k2.refactoring.move.processor.K2MoveFilesOrDirectoriesRefactoringProcessor\n', '')
text = text.replace('import org.jetbrains.kotlin.idea.search.ExpectActualUtils\n', '')

text = text.replace(
''' class Files(
project: Project,
moveDescriptors: List<K2MoveDescriptor.Files>,
searchForText: Boolean,
searchInComments: Boolean,
searchReferences: Boolean,
dirStructureMatchesPkg: Boolean,
isMoveToExplicitPackage: Boolean,
moveCallBack: MoveCallback? = null,
) : K2MoveOperationDescriptor<K2MoveDescriptor.Files>(
project,
moveDescriptors,
searchForText,
searchInComments,
searchReferences,
dirStructureMatchesPkg,
isMoveToExplicitPackage,
moveCallBack
) {
override val sourceElements: List<PsiFileSystemItem> get() = moveDescriptors.flatMap { it.source.elements }

override fun refactoringProcessor(): BaseRefactoringProcessor {
''' override fun refactoringProcessor(): BaseRefactoringProcessor {
return K2MoveFilesOrDirectoriesRefactoringProcessor(this)
}
}

class Declarations(''',
''' class Declarations('''
}''',
''' override fun refactoringProcessor(): BaseRefactoringProcessor =
error("The standalone F4.1 adapter drives K2MoveFilesHandler directly; NetBeans owns the file-move lifecycle.")'''
)

text = text.replace(
Expand Down Expand Up @@ -1998,7 +2016,7 @@ if (k2MoveOpDescriptor.exists()) {
)

k2MoveOpDescriptor.text = text
log.info("Patched K2MoveOperationDescriptor.kt: dropped Files-move variant and multiplatform expect/actual fan-out")
log.info("Patched K2MoveOperationDescriptor.kt: retained Files data descriptor, blocked IDEA lifecycle, dropped MPP fan-out")
}

// 18. K2MoveTargetDescriptor.kt (E9.7): DirectoryUtil/VfsUtilCore (com.intellij.ide.util /
Expand Down Expand Up @@ -2105,14 +2123,13 @@ if (k2MoveProcessorOrder.exists()) {
// org.jetbrains.kotlin.idea.base.projectStructure.kaModuleBridge.kt (KotlinRefactoring local
// sources). No text changes needed beyond what that bridge already resolves; this entry exists
// to document why moveConflictUtil.kt compiles without further patching.
// 20. moveUsageUtil.kt (E9.7): TextOccurrencesUtil (searchInCommentsAndStrings/searchForText —
// 20. moveUsageUtil.kt (E9.7/F4.1): TextOccurrencesUtil (searchInCommentsAndStrings/searchForText —
// opt-in, default-off "also update text mentions" scan) is not on our classpath and would
// otherwise need a whole-project text-search engine to implement for real; simplify
// findNonCodeUsages to always report no non-code usages (matches behaviour when the user
// leaves those checkboxes off, which IDEA itself defaults to). KtFile.findUsages (the
// whole-file-move overload, calling the now-removed K2MoveRenameUsageInfo.findExternalUsages)
// is dead code for Move Declaration — only K2MoveDeclarationsRefactoringProcessor.getUsages()
// is ever called, which uses the KtNamedDeclaration.findUsages overload below it.
// leaves those checkboxes off, which IDEA itself defaults to). F4.1 restores the whole-file
// overload and backs it with the real KotlinMoveUsageSearchService scan, instead of IDEA's
// unavailable Java/light-class ReferencesSearch path.
def moveUsageUtilFile = new File(srcDir, 'org/jetbrains/kotlin/idea/k2/refactoring/move/processor/moveUsageUtil.kt')
if (moveUsageUtilFile.exists()) {
def text = moveUsageUtilFile.text
Expand All @@ -2133,9 +2150,17 @@ if (moveUsageUtilFile.exists()) {
K2MoveRenameUsageInfo.findExternalUsages(decl) + decl.findNonCodeUsages(searchInCommentsAndStrings, searchForText, newPkgName)
}
}''',
'''// KtFile.findUsages (whole-file-move overload) intentionally omitted: file/directory moves are
// out of scope for Move Declaration (see pom.xml), and it depended on the now-removed
// K2MoveRenameUsageInfo.findExternalUsages (Java-interop-only, dropped in patch #14).'''
'''internal fun KtFile.findUsages(
searchInCommentsAndStrings: Boolean,
searchForText: Boolean,
newPkgName: FqName
): List<UsageInfo> {
markInternalUsages(this, this)
return topLevelDeclarationsToUpdate.flatMap { declaration ->
org.jetbrains.kotlin.idea.k2.refactoring.move.processor.usages.findMoveDeclarationUsages(declaration) +
declaration.findNonCodeUsages(searchInCommentsAndStrings, searchForText, newPkgName)
}
}'''
)

text = text.replace(
Expand Down Expand Up @@ -2234,18 +2259,19 @@ private fun KtNamedDeclaration.findNonCodeUsages(
text = text.replace('import com.intellij.refactoring.rename.RenameUtil\n', '')

moveUsageUtilFile.text = text
log.info("Patched moveUsageUtil.kt: dropped whole-file-move overload, wired findMoveDeclarationUsages, dropped TextOccurrencesUtil-based non-code-usage search")
log.info("Patched moveUsageUtil.kt: restored standalone whole-file Kotlin usage search and dropped TextOccurrencesUtil-based non-code search")
}

// 21. moveUtil.kt (E9.7): K2ChangePackageDescriptor (Change Package is a separate, out-of-scope
// feature — see pom.xml) and forcedTargetPackage/getPossiblyForcedPackageFqName (dead code:
// only used by K2MoveFilesOrDirectoriesRefactoringProcessor, also out of scope) are removed.
// 21. moveUtil.kt (E9.7/F4.1): K2ChangePackageDescriptor remains out of scope until F4.2. F4.1
// restores forcedTargetPackage/getPossiblyForcedPackageFqName, but stores it directly on the
// session/disk PsiDirectory rather than IDEA's sourceRoot project-model abstraction. NetBeans
// has no implicit package prefixes, so the field safely scopes the chosen destination package
// through conflict detection and the K2 handler's package rewrite.
def moveUtilFile = new File(srcDir, 'org/jetbrains/kotlin/idea/k2/refactoring/move/processor/moveUtil.kt')
if (moveUtilFile.exists()) {
def text = moveUtilFile.text
text = text.replace('import org.jetbrains.kotlin.idea.k2.refactoring.move.descriptor.K2ChangePackageDescriptor\n', '')
text = text.replace('import org.jetbrains.kotlin.idea.util.sourceRoot\n', '')
text = text.replace('import com.intellij.openapi.util.Key\n', '')
text = text.replace('import com.intellij.openapi.vfs.VirtualFile\n', '')

text = text.replace(
Expand All @@ -2268,12 +2294,18 @@ internal fun PsiDirectory.getPossiblyForcedPackageFqName(): FqName {
val forcedPkg = parentsWithSelf.filterIsInstance<PsiDirectory>().map { it.forcedTargetPackage }.firstNotNullOfOrNull { it }
return forcedPkg ?: getFqNameWithImplicitPrefixOrRoot()
}''',
'''// forcedTargetPackage/getPossiblyForcedPackageFqName intentionally omitted: only used by
// K2MoveFilesOrDirectoriesRefactoringProcessor (file/directory moves), which is out of scope.'''
'''private var PsiDirectory.forcedTargetPackage: FqName? by UserDataProperty(Key.create("FORCED_TARGET_PACKAGE"))

internal fun PsiDirectory.getPossiblyForcedPackageFqName(): FqName {
val forcedPkg = parentsWithSelf.filterIsInstance<PsiDirectory>()
.map { it.forcedTargetPackage }
.firstNotNullOfOrNull { it }
return forcedPkg ?: getFqNameWithImplicitPrefixOrRoot()
}'''
)

moveUtilFile.text = text
log.info("Patched moveUtil.kt: dropped Change Package support and dead forced-target-package caching")
log.info("Patched moveUtil.kt: kept standalone forced-target package support; Change Package remains deferred to F4.2")
}

// 22. implicitPackagePrefixConflict.kt (E9.7): getImplicitPackagePrefix() always returns null now
Expand Down
Loading
Loading