Initial support for scratch files
This commit is contained in:
@@ -245,4 +245,8 @@ debugger.field.watchpoints.properties.panel.field.modification.label=Field &modi
|
|||||||
debugger.field.watchpoints.properties.panel.field.initialization.label=Field &initialization
|
debugger.field.watchpoints.properties.panel.field.initialization.label=Field &initialization
|
||||||
|
|
||||||
# Android Lint
|
# Android Lint
|
||||||
android.klint.inspections.group.name=Android Lint for Kotlin
|
android.klint.inspections.group.name=Android Lint for Kotlin
|
||||||
|
|
||||||
|
# Scratch Files
|
||||||
|
scratch.run.button=Run Scratch File
|
||||||
|
scratch.clear.button=Clear results
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.scratch
|
||||||
|
|
||||||
|
import com.intellij.psi.PsiDocumentManager
|
||||||
|
import com.intellij.psi.PsiWhiteSpace
|
||||||
|
import com.intellij.psi.util.PsiUtil
|
||||||
|
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.getLineNumber
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.getLineStartOffset
|
||||||
|
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlin.psi.KtImportDirective
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||||
|
|
||||||
|
class KtScratchFile(psiFile: KtFile) : ScratchFile(psiFile) {
|
||||||
|
|
||||||
|
override fun getExpressions(): List<ScratchExpression> {
|
||||||
|
// todo multiple expressions at one line
|
||||||
|
val doc = PsiDocumentManager.getInstance(psiFile.project).getDocument(psiFile) ?: return emptyList()
|
||||||
|
var line = 0
|
||||||
|
val result = arrayListOf<ScratchExpression>()
|
||||||
|
while (line < doc.lineCount) {
|
||||||
|
val start = psiFile.getLineStartOffset(line) ?: continue
|
||||||
|
val elementAtOffset = CodeInsightUtils.getTopmostElementAtOffset(
|
||||||
|
PsiUtil.getElementAtOffset(psiFile, start),
|
||||||
|
start,
|
||||||
|
KtImportDirective::class.java,
|
||||||
|
KtDeclaration::class.java
|
||||||
|
)
|
||||||
|
|
||||||
|
if (elementAtOffset is PsiWhiteSpace) {
|
||||||
|
line = doc.getLineNumber(elementAtOffset.endOffset) + 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elementAtOffset == null) {
|
||||||
|
line += 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
result.add(ScratchExpression(elementAtOffset, elementAtOffset.getLineNumber(true), elementAtOffset.getLineNumber(false)))
|
||||||
|
line = elementAtOffset.getLineNumber(false) + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.scratch
|
||||||
|
|
||||||
|
import com.intellij.psi.PsiFile
|
||||||
|
import org.jetbrains.kotlin.idea.scratch.repl.KtScratchReplExecutor
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
|
||||||
|
class KtScratchFileLanguageProvider: ScratchFileLanguageProvider() {
|
||||||
|
override fun createFile(psiFile: PsiFile): ScratchFile? {
|
||||||
|
return (psiFile as? KtFile)?.let { KtScratchFile(psiFile)}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.scratch
|
||||||
|
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.PsiFile
|
||||||
|
|
||||||
|
abstract class ScratchFile(val psiFile: PsiFile) {
|
||||||
|
abstract fun getExpressions(): List<ScratchExpression>
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ScratchExpression(val element: PsiElement, val lineStart: Int, val lineEnd: Int = lineStart)
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.scratch
|
||||||
|
|
||||||
|
import com.intellij.lang.Language
|
||||||
|
import com.intellij.lang.LanguageExtension
|
||||||
|
import com.intellij.openapi.fileTypes.FileType
|
||||||
|
import com.intellij.openapi.fileTypes.LanguageFileType
|
||||||
|
import com.intellij.psi.PsiFile
|
||||||
|
|
||||||
|
abstract class ScratchFileLanguageProvider {
|
||||||
|
abstract fun createFile(psiFile: PsiFile): ScratchFile?
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val EXTENSION = LanguageExtension<ScratchFileLanguageProvider>("org.jetbrains.kotlin.scratchFileLanguageProvider")
|
||||||
|
|
||||||
|
fun get(language: Language): ScratchFileLanguageProvider? {
|
||||||
|
return ScratchFileLanguageProvider.EXTENSION.forLanguage(language)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun get(fileType: FileType): ScratchFileLanguageProvider? {
|
||||||
|
return (fileType as? LanguageFileType)?.language?.let { get(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createFile(psiFile: PsiFile): ScratchFile? {
|
||||||
|
return get(psiFile.language)?.createFile(psiFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.scratch
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer
|
||||||
|
import com.intellij.ide.scratch.ScratchFileService
|
||||||
|
import com.intellij.openapi.components.AbstractProjectComponent
|
||||||
|
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||||
|
import com.intellij.openapi.fileEditor.FileEditorManagerListener
|
||||||
|
import com.intellij.openapi.module.Module
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.roots.ProjectRootManager
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
|
import com.intellij.psi.PsiManager
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.NotUnderContentRootModuleInfo
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.productionSourceInfo
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.testSourceInfo
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlin.psi.moduleInfo
|
||||||
|
|
||||||
|
class ScratchFileModuleInfoProvider(project: Project) : AbstractProjectComponent(project) {
|
||||||
|
|
||||||
|
override fun projectOpened() {
|
||||||
|
myProject.messageBus.connect(myProject).subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, ScratchFileModuleListener())
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class ScratchFileModuleListener : FileEditorManagerListener {
|
||||||
|
override fun fileOpened(source: FileEditorManager, file: VirtualFile) {
|
||||||
|
if (!file.isValid) return
|
||||||
|
if (!ScratchFileService.isInScratchRoot(file)) return
|
||||||
|
|
||||||
|
val ktFile = PsiManager.getInstance(myProject).findFile(file) as? KtFile ?: return
|
||||||
|
|
||||||
|
getScratchPanel(ktFile)?.let { panel ->
|
||||||
|
ktFile.moduleInfo = getModuleInfo(panel.getModule())
|
||||||
|
|
||||||
|
panel.addModuleListener {
|
||||||
|
ktFile.moduleInfo = getModuleInfo(it)
|
||||||
|
// Drop caches for old module
|
||||||
|
ProjectRootManager.getInstance(myProject).incModificationCount()
|
||||||
|
// Force re-highlighting
|
||||||
|
DaemonCodeAnalyzer.getInstance(myProject).restart(ktFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getModuleInfo(it: Module?) =
|
||||||
|
it?.testSourceInfo() ?: it?.productionSourceInfo() ?: NotUnderContentRootModuleInfo
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.scratch.actions
|
||||||
|
|
||||||
|
import com.intellij.icons.AllIcons
|
||||||
|
import com.intellij.openapi.actionSystem.AnAction
|
||||||
|
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||||
|
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||||
|
import org.jetbrains.kotlin.idea.scratch.ScratchFileLanguageProvider
|
||||||
|
import org.jetbrains.kotlin.idea.scratch.getScratchPanelFromSelectedEditor
|
||||||
|
|
||||||
|
class ClearScratchAction : AnAction(
|
||||||
|
KotlinBundle.message("scratch.clear.button"),
|
||||||
|
KotlinBundle.message("scratch.clear.button"),
|
||||||
|
AllIcons.Actions.GC
|
||||||
|
) {
|
||||||
|
override fun actionPerformed(e: AnActionEvent) {
|
||||||
|
val project = e.project ?: return
|
||||||
|
val panel = getScratchPanelFromSelectedEditor(project) ?: return
|
||||||
|
val scratchFile = panel.scratchFile
|
||||||
|
|
||||||
|
ScratchFileLanguageProvider.get(scratchFile.psiFile.language)?.getOutputHandler()?.clear(scratchFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.scratch.actions
|
||||||
|
|
||||||
|
import com.intellij.icons.AllIcons
|
||||||
|
import com.intellij.openapi.actionSystem.AnAction
|
||||||
|
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||||
|
import com.intellij.openapi.compiler.CompilerManager
|
||||||
|
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||||
|
import org.jetbrains.kotlin.idea.scratch.ScratchFileLanguageProvider
|
||||||
|
import org.jetbrains.kotlin.idea.scratch.getScratchPanelFromSelectedEditor
|
||||||
|
|
||||||
|
class RunScratchAction : AnAction(
|
||||||
|
KotlinBundle.message("scratch.run.button"),
|
||||||
|
KotlinBundle.message("scratch.run.button"),
|
||||||
|
AllIcons.Actions.Execute
|
||||||
|
) {
|
||||||
|
override fun actionPerformed(e: AnActionEvent) {
|
||||||
|
val project = e.project ?: return
|
||||||
|
|
||||||
|
val scratchTopPanel = getScratchPanelFromSelectedEditor(project) ?: return
|
||||||
|
val scratchFile = scratchTopPanel.scratchFile
|
||||||
|
|
||||||
|
val isMakeBeforeRun = false // todo use property from panel
|
||||||
|
val isRepl = true //todo use property from panel
|
||||||
|
|
||||||
|
val provider = ScratchFileLanguageProvider.get(scratchFile.psiFile.language) ?: return
|
||||||
|
|
||||||
|
val module = scratchTopPanel.getModule()
|
||||||
|
if (module == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val runnable = r@ {
|
||||||
|
// todo
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isMakeBeforeRun) {
|
||||||
|
CompilerManager.getInstance(project)
|
||||||
|
.make(module) { aborted, errors, _, _ -> if (!aborted && errors == 0) runnable() }
|
||||||
|
} else {
|
||||||
|
runnable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||||
|
* that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.scratch
|
||||||
|
|
||||||
|
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||||
|
import com.intellij.openapi.fileEditor.TextEditor
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.util.Key
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
|
import com.intellij.psi.PsiFile
|
||||||
|
import org.jetbrains.kotlin.idea.scratch.ui.ScratchTopPanel
|
||||||
|
import org.jetbrains.kotlin.psi.UserDataProperty
|
||||||
|
|
||||||
|
fun getEditorWithoutScratchPanel(project: Project, virtualFile: VirtualFile): TextEditor? {
|
||||||
|
val allTextEditors = getAllTextEditors(project, virtualFile)
|
||||||
|
for (editor in allTextEditors) {
|
||||||
|
if (editor.scratchTopPanel != null) return null
|
||||||
|
}
|
||||||
|
return allTextEditors.firstOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getEditorWithScratchPanel(project: Project, virtualFile: VirtualFile): Pair<TextEditor, ScratchTopPanel>? {
|
||||||
|
val firstWithPanel = getAllTextEditors(project, virtualFile).firstOrNull { it.scratchTopPanel != null }
|
||||||
|
return firstWithPanel?.let { firstWithPanel to firstWithPanel.scratchTopPanel!! }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getScratchPanel(psiFile: PsiFile): ScratchTopPanel? {
|
||||||
|
return getAllTextEditors(psiFile.project, psiFile.virtualFile).mapNotNull { it.scratchTopPanel }.firstOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAllEditorsWithScratchPanel(project: Project): List<Pair<TextEditor, ScratchTopPanel>> =
|
||||||
|
FileEditorManager.getInstance(project).allEditors.filterIsInstance<TextEditor>().mapNotNull {
|
||||||
|
val panel = it.scratchTopPanel
|
||||||
|
if (panel != null) it to panel else null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getScratchPanelFromSelectedEditor(project: Project): ScratchTopPanel? =
|
||||||
|
FileEditorManager.getInstance(project).selectedEditors.asSequence()
|
||||||
|
.filterIsInstance<TextEditor>()
|
||||||
|
.mapNotNull { it.scratchTopPanel }
|
||||||
|
.firstOrNull()
|
||||||
|
|
||||||
|
private fun getAllTextEditors(project: Project, virtualFile: VirtualFile) =
|
||||||
|
FileEditorManager.getInstance(project).getAllEditors(virtualFile).filterIsInstance<TextEditor>().asSequence()
|
||||||
|
|
||||||
|
fun TextEditor.addScratchPanel(panel: ScratchTopPanel) {
|
||||||
|
scratchTopPanel = panel
|
||||||
|
FileEditorManager.getInstance(panel.scratchFile.psiFile.project).addTopComponent(this, panel)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun TextEditor.removeScratchPanel(panel: ScratchTopPanel) {
|
||||||
|
scratchTopPanel = null
|
||||||
|
FileEditorManager.getInstance(panel.scratchFile.psiFile.project).removeTopComponent(this, panel)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var TextEditor.scratchTopPanel: ScratchTopPanel? by UserDataProperty<TextEditor, ScratchTopPanel>(Key.create("scratch.panel"))
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.scratch.ui
|
||||||
|
|
||||||
|
import com.intellij.ide.scratch.ScratchFileService
|
||||||
|
import com.intellij.openapi.components.AbstractProjectComponent
|
||||||
|
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||||
|
import com.intellij.openapi.fileEditor.FileEditorManagerListener
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
|
import com.intellij.psi.PsiManager
|
||||||
|
import org.jetbrains.kotlin.idea.scratch.*
|
||||||
|
|
||||||
|
class ScratchFileHook(project: Project) : AbstractProjectComponent(project) {
|
||||||
|
|
||||||
|
override fun projectOpened() {
|
||||||
|
myProject.messageBus.connect(myProject).subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, ScratchEditorListener())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun projectClosed() {
|
||||||
|
getAllEditorsWithScratchPanel(myProject).forEach { (editor, panel) -> editor.removeScratchPanel(panel) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class ScratchEditorListener : FileEditorManagerListener {
|
||||||
|
private fun isPluggable(file: VirtualFile): Boolean {
|
||||||
|
if (!file.isValid) return false
|
||||||
|
if (!ScratchFileService.isInScratchRoot(file)) return false
|
||||||
|
val psiFile = PsiManager.getInstance(myProject).findFile(file) ?: return false
|
||||||
|
return ScratchFileLanguageProvider.get(psiFile.fileType) != null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun fileOpened(source: FileEditorManager, file: VirtualFile) {
|
||||||
|
if (!isPluggable(file)) return
|
||||||
|
|
||||||
|
val panel = ScratchTopPanel.createPanel(myProject, file) ?: return
|
||||||
|
getEditorWithoutScratchPanel(myProject, file)?.let { editor ->
|
||||||
|
editor.addScratchPanel(panel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun fileClosed(source: FileEditorManager, file: VirtualFile) {
|
||||||
|
if (!isPluggable(file)) return
|
||||||
|
|
||||||
|
getEditorWithScratchPanel(myProject, file)?.let { (editor, panel) ->
|
||||||
|
editor.removeScratchPanel(panel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.scratch.ui
|
||||||
|
|
||||||
|
|
||||||
|
import com.intellij.application.options.ModulesComboBox
|
||||||
|
import com.intellij.openapi.actionSystem.ActionManager
|
||||||
|
import com.intellij.openapi.actionSystem.ActionPlaces
|
||||||
|
import com.intellij.openapi.actionSystem.DefaultActionGroup
|
||||||
|
import com.intellij.openapi.module.Module
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
|
import com.intellij.psi.PsiManager
|
||||||
|
import com.intellij.ui.components.panels.HorizontalLayout
|
||||||
|
import com.intellij.uiDesigner.core.Spacer
|
||||||
|
import org.jetbrains.kotlin.idea.scratch.ScratchFile
|
||||||
|
import org.jetbrains.kotlin.idea.scratch.ScratchFileLanguageProvider
|
||||||
|
import org.jetbrains.kotlin.idea.scratch.actions.ClearScratchAction
|
||||||
|
import org.jetbrains.kotlin.idea.scratch.actions.RunScratchAction
|
||||||
|
import org.jetbrains.kotlin.idea.scratch.getScratchPanel
|
||||||
|
import javax.swing.JComponent
|
||||||
|
import javax.swing.JLabel
|
||||||
|
import javax.swing.JPanel
|
||||||
|
import javax.swing.JSeparator
|
||||||
|
|
||||||
|
val ScratchFile.scratchTopPanel: ScratchTopPanel?
|
||||||
|
get() = getScratchPanel(psiFile).firstOrNull { it.scratchFile == this@scratchTopPanel }
|
||||||
|
|
||||||
|
class ScratchTopPanel private constructor(val scratchFile: ScratchFile) : JPanel(HorizontalLayout(5)) {
|
||||||
|
companion object {
|
||||||
|
fun createPanel(project: Project, virtualFile: VirtualFile): ScratchTopPanel? {
|
||||||
|
val psiFile = PsiManager.getInstance(project).findFile(virtualFile) ?: return null
|
||||||
|
val scratchFile = ScratchFileLanguageProvider.createFile(psiFile) ?: return null
|
||||||
|
return ScratchTopPanel(scratchFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val moduleChooser: ModulesComboBox
|
||||||
|
|
||||||
|
init {
|
||||||
|
add(createActionsToolbar())
|
||||||
|
add(JSeparator())
|
||||||
|
add(JLabel("Use classpath of module: "))
|
||||||
|
moduleChooser = createModuleChooser(scratchFile.psiFile.project)
|
||||||
|
add(moduleChooser)
|
||||||
|
add(Spacer())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getModule(): Module? = moduleChooser.selectedModule
|
||||||
|
|
||||||
|
fun addModuleListener(f: (Module) -> Unit) {
|
||||||
|
moduleChooser.addActionListener {
|
||||||
|
moduleChooser.selectedModule?.let { f(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createActionsToolbar(): JComponent {
|
||||||
|
val toolbarGroup = DefaultActionGroup().apply {
|
||||||
|
add(RunScratchAction())
|
||||||
|
addSeparator()
|
||||||
|
add(ClearScratchAction())
|
||||||
|
}
|
||||||
|
|
||||||
|
return ActionManager.getInstance().createActionToolbar(ActionPlaces.EDITOR_TOOLBAR, toolbarGroup, true).component
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createModuleChooser(project: Project): ModulesComboBox {
|
||||||
|
return ModulesComboBox().apply {
|
||||||
|
fillModules(project)
|
||||||
|
selectedIndex = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,6 +16,11 @@
|
|||||||
|
|
||||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.highlighterExtension"
|
<extensionPoint qualifiedName="org.jetbrains.kotlin.highlighterExtension"
|
||||||
interface="org.jetbrains.kotlin.idea.highlighter.HighlighterExtension"/>
|
interface="org.jetbrains.kotlin.idea.highlighter.HighlighterExtension"/>
|
||||||
|
|
||||||
|
<extensionPoint name="scratchFileLanguageProvider" beanClass="com.intellij.lang.LanguageExtensionPoint">
|
||||||
|
<with attribute="implementationClass" implements="org.jetbrains.kotlin.idea.scratch.ScratchFileLanguageProvider"/>
|
||||||
|
</extensionPoint>
|
||||||
|
|
||||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.binaryExtension"
|
<extensionPoint qualifiedName="org.jetbrains.kotlin.binaryExtension"
|
||||||
interface="org.jetbrains.kotlin.idea.util.KotlinBinaryExtension"/>
|
interface="org.jetbrains.kotlin.idea.util.KotlinBinaryExtension"/>
|
||||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.facetValidatorCreator"
|
<extensionPoint qualifiedName="org.jetbrains.kotlin.facetValidatorCreator"
|
||||||
@@ -48,5 +53,7 @@
|
|||||||
<idePlatformSupport implementation="org.jetbrains.kotlin.caches.resolve.JvmPlatformSupport"/>
|
<idePlatformSupport implementation="org.jetbrains.kotlin.caches.resolve.JvmPlatformSupport"/>
|
||||||
<idePlatformSupport implementation="org.jetbrains.kotlin.caches.resolve.JsPlatformSupport"/>
|
<idePlatformSupport implementation="org.jetbrains.kotlin.caches.resolve.JsPlatformSupport"/>
|
||||||
<idePlatformSupport implementation="org.jetbrains.kotlin.caches.resolve.CommonPlatformSupport"/>
|
<idePlatformSupport implementation="org.jetbrains.kotlin.caches.resolve.CommonPlatformSupport"/>
|
||||||
|
|
||||||
|
<scratchFileLanguageProvider language="kotlin" implementationClass="org.jetbrains.kotlin.idea.scratch.KtScratchFileLanguageProvider"/>
|
||||||
</extensions>
|
</extensions>
|
||||||
</idea-plugin>
|
</idea-plugin>
|
||||||
|
|||||||
@@ -52,6 +52,12 @@
|
|||||||
<interface-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</interface-class>
|
<interface-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</interface-class>
|
||||||
<implementation-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</implementation-class>
|
<implementation-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</implementation-class>
|
||||||
</component>
|
</component>
|
||||||
|
<component>
|
||||||
|
<implementation-class>org.jetbrains.kotlin.idea.scratch.ui.ScratchFileHook</implementation-class>
|
||||||
|
</component>
|
||||||
|
<component>
|
||||||
|
<implementation-class>org.jetbrains.kotlin.idea.scratch.ScratchFileModuleInfoProvider</implementation-class>
|
||||||
|
</component>
|
||||||
</project-components>
|
</project-components>
|
||||||
|
|
||||||
<application-components>
|
<application-components>
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import com.intellij.psi.util.PsiTreeUtil;
|
|||||||
import com.intellij.psi.util.PsiUtilCore;
|
import com.intellij.psi.util.PsiUtilCore;
|
||||||
import com.intellij.refactoring.util.CommonRefactoringUtil;
|
import com.intellij.refactoring.util.CommonRefactoringUtil;
|
||||||
import com.intellij.util.text.CharArrayUtil;
|
import com.intellij.util.text.CharArrayUtil;
|
||||||
|
import kotlin.collections.ArraysKt;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind;
|
import org.jetbrains.kotlin.descriptors.ClassKind;
|
||||||
@@ -361,9 +362,9 @@ public class CodeInsightUtils {
|
|||||||
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public static <T> T getTopmostElementAtOffset(@NotNull PsiElement element, int offset, @NotNull Class<T> klass) {
|
public static <T> T getTopmostElementAtOffset(@NotNull PsiElement element, int offset, @NotNull Class<? extends T>... classes) {
|
||||||
T lastElementOfType = null;
|
T lastElementOfType = null;
|
||||||
if (klass.isInstance(element)) {
|
if (anyIsInstance(element, classes)) {
|
||||||
lastElementOfType = (T) element;
|
lastElementOfType = (T) element;
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
@@ -371,7 +372,7 @@ public class CodeInsightUtils {
|
|||||||
if (parent == null || (parent.getTextOffset() < offset) || parent instanceof KtBlockExpression) {
|
if (parent == null || (parent.getTextOffset() < offset) || parent instanceof KtBlockExpression) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (klass.isInstance(parent)) {
|
if (anyIsInstance(parent, classes)) {
|
||||||
lastElementOfType = (T) parent;
|
lastElementOfType = (T) parent;
|
||||||
}
|
}
|
||||||
element = parent;
|
element = parent;
|
||||||
@@ -380,4 +381,8 @@ public class CodeInsightUtils {
|
|||||||
|
|
||||||
return lastElementOfType;
|
return lastElementOfType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static <T> boolean anyIsInstance(PsiElement finalElement, @NotNull Class<? extends T>[] klass) {
|
||||||
|
return ArraysKt.any(klass, aClass -> aClass.isInstance(finalElement));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -321,7 +321,10 @@ fun PsiFile.getLineStartOffset(line: Int): Int? {
|
|||||||
val startOffset = doc.getLineStartOffset(line)
|
val startOffset = doc.getLineStartOffset(line)
|
||||||
val element = findElementAt(startOffset) ?: return startOffset
|
val element = findElementAt(startOffset) ?: return startOffset
|
||||||
|
|
||||||
return PsiTreeUtil.skipSiblingsForward(element, PsiWhiteSpace::class.java, PsiComment::class.java)?.startOffset ?: startOffset
|
if (element is PsiWhiteSpace || element is PsiComment) {
|
||||||
|
return PsiTreeUtil.skipSiblingsForward(element, PsiWhiteSpace::class.java, PsiComment::class.java)?.startOffset ?: startOffset
|
||||||
|
}
|
||||||
|
return startOffset
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
|
|||||||
Reference in New Issue
Block a user