Skip to content

Commit

Permalink
[ #194 ] support varinfo
Browse files Browse the repository at this point in the history
  • Loading branch information
zxj5470 committed Dec 11, 2018
1 parent 69acfdf commit 4b914de
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 55 deletions.
1 change: 1 addition & 0 deletions res/META-INF/change-notes.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<li>Auto add a default SDK into External Libraries</li>
<li>Support sending selected text to REPL (#238)</li>
<li>Support customizing the REPL prompt (#269)</li>
<li>Support SciView Data Panel</li>
</ul>
0.3.4<br/>
<ul>
Expand Down
51 changes: 48 additions & 3 deletions res/org/ice1000/julia/lang/action/IntelliJ.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ IntelliJ.jl:

if VERSION >= v"0.7.0"
using Pkg
using DelimitedFiles
using Sockets
end

println("Initialize julia-intellij REPL environment...")
Expand All @@ -20,13 +22,56 @@ if "PyCall" in keys(Pkg.installed())
println("set plot outputs redirect to intellij.")
end

println("Initialize done.\n")
println("""execute `use_intellij_backend()` to enable JuliaSciView Plots,
supported backends if you installed:
`matplotlib.pyplot`""")

else
println("""PyCall package not found, to use JuliaSciView Plots,
please add PyCall and install matplotlib.pyplot package.""")
println("Initialize done.")
end
end

const _intellij_plot_key = "JULIA_INTELLIJ_PLOT_PORT"

_intellij_filter_names(v) = string(v) ["Base", "Core", "Main", "InteractiveUtils", "matplotlib"]

function _intellij_x_to_bytes(x)
sz = sizeof(x)
if VERSION >= v"0.7.0"
ret = Vector{UInt8}(undef,sz)
src_ptr = convert(Ptr{UInt8}, pointer_from_objref(x))
unsafe_copyto!(pointer(ba), src_ptr, sz)
else
ret = Vector{UInt8}(sz)
src_ptr = convert(Ptr{UInt8}, pointer_from_objref(x))
unsafe_copy!(pointer(ba), src_ptr, sz)
end
return ret
end

function _intellij_send_to(rows)
open("$(tempdir())/tempJuliaVarInfo.tsv","w") do f
writedlm(f,rows,'\t')
end
end

function _intellij_varinfo(m=Main)
@eval using Base:summarysize, format_bytes
pattern="_intellij_"
rows = Array[ let value = getfield(m, v)
name = string(v)
size = value === Main || value === Base || value === Core ? "" : format_bytes(summarysize(value))
summary_info = summary(value)
info = repr(repr(value))
if size == "0 bytes" && summary_info == "typeof($(name))"
summary_info = "function"
end
String[name, size, info, summary_info]
end
for v in sort!(names(m)) if isdefined(m, v) &&
!occursin(pattern, string(v)) && _intellij_filter_names(v)]
_intellij_send_to(rows)
return
end

println("\nInitialize done.\n")
15 changes: 6 additions & 9 deletions src/org/ice1000/julia/lang/action/julia-repl-actions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import com.intellij.openapi.vfs.VirtualFile
import com.intellij.ui.JBColor
import icons.JuliaIcons
import org.ice1000.julia.lang.*
import org.ice1000.julia.lang.module.JuliaConsoleView
import org.ice1000.julia.lang.module.juliaSettings
import java.awt.Font
import java.awt.event.KeyAdapter
Expand Down Expand Up @@ -108,7 +109,7 @@ class JuliaReplAction : JuliaAction(
class JuliaReplRunner(
private val cmdLine: GeneralCommandLine,
myProject: Project,
title: String,
val title: String,
path: String?
) : AbstractConsoleRunnerWithHistory<LanguageConsoleView>(myProject, title, path) {
override fun initAndRun() {
Expand Down Expand Up @@ -151,12 +152,8 @@ class JuliaReplRunner(
}

override fun createConsoleView(): LanguageConsoleView {
val builder = LanguageConsoleBuilder()

val project = project
val consoleView = builder.gutterContentProvider(object : BasicGutterContentProvider() {
override fun beforeEvaluate(editor: Editor) = Unit
}).build(project, JuliaLanguage.INSTANCE)
val consoleView = JuliaConsoleView(project,title)
consoleView.prompt = project.juliaSettings.settings.replPrompt
val consoleEditor = consoleView.consoleEditor
setupPlaceholder(consoleEditor)
Expand All @@ -175,7 +172,6 @@ class JuliaReplRunner(
}
}
executeAction.registerCustomShortcutSet(KeyEvent.VK_ENTER, KeyEvent.SHIFT_DOWN_MASK, consoleView.consoleEditor.component)

return consoleView
}

Expand Down Expand Up @@ -222,8 +218,9 @@ class CommandExecutor(private val runner: JuliaReplRunner) {
val processHandler = runner.processHandler
val processInputOS = processHandler.processInput
?: return errorNotification(runner.project, "Error")
val bytes = ("$command\n").toByteArray()

val intellijCode = """_intellij_varinfo()"""
val bytes = ("$command\n$intellijCode\n").toByteArray()
(runner.consoleView as JuliaConsoleView).showVariables()
if (showCommand) {
val historyDocumentRange = runner.historyUpdater.printNewCommandInHistory(command)
val commandHistory = runner.commandHistory
Expand Down
4 changes: 4 additions & 0 deletions src/org/ice1000/julia/lang/julia-constants.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package org.ice1000.julia.lang

import com.intellij.openapi.util.Key
import org.ice1000.julia.lang.action.JuliaReplRunner
import org.ice1000.julia.lang.module.JuliaDebugValue
import org.ice1000.julia.lang.module.JuliaVariablesView
import org.intellij.lang.annotations.Language
import org.jetbrains.annotations.NonNls

Expand Down Expand Up @@ -57,6 +59,8 @@ Pkg.build("DocumentFormat")"""
@NonNls const val DOCFMT_EXTENSION = "julia-config"
@NonNls const val DOCFMT_LANGUAGE_NAME = "DocumentFormat"
val JULIA_SCI_PORT_KEY = Key<String>("JuliaSciPortKey")
val JULIA_SCI_DATA_KEY = Key<JuliaVariablesView>("JuliaVariablesViewKey")
val JULIA_VAR_LIST_KEY = Key<List<JuliaDebugValue>>("JuliaVarListKey")
val JULIA_REPL_RUNNER_KEY = Key<JuliaReplRunner>("JuliaReplRunnerKey")

const val REPL_ERROR_TAG = "Julia REPL ERROR"
155 changes: 112 additions & 43 deletions src/org/ice1000/julia/lang/module/julia-sci-mode.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@

package org.ice1000.julia.lang.module

import com.intellij.execution.console.LanguageConsoleImpl
import com.intellij.execution.impl.ConsoleViewUtil
import com.intellij.execution.ui.ObservableConsoleView
import com.intellij.icons.AllIcons.Actions
import com.intellij.openapi.Disposable
import com.intellij.openapi.actionSystem.*
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.editor.colors.*
import com.intellij.openapi.fileChooser.FileChooserFactory
import com.intellij.openapi.fileChooser.FileSaverDescriptor
import com.intellij.openapi.fileEditor.FileEditor
Expand All @@ -23,15 +27,19 @@ import com.intellij.ui.SimpleColoredComponent
import com.intellij.ui.awt.RelativePoint
import com.intellij.ui.awt.RelativeRectangle
import com.intellij.ui.content.ContentFactory.SERVICE
import com.intellij.ui.docking.DockContainer.*
import com.intellij.ui.docking.*
import com.intellij.ui.docking.DockContainer.ContentResponse
import com.intellij.ui.docking.DockContainer.Listener
import com.intellij.ui.tabs.*
import com.intellij.ui.tabs.TabInfo.DragOutDelegate
import com.intellij.ui.tabs.impl.*
import com.intellij.util.ui.JBUI
import com.intellij.util.ui.UIUtil
import com.intellij.xdebugger.frame.*
import com.intellij.xdebugger.impl.frame.XStandaloneVariablesView
import icons.JuliaIcons
import org.apache.commons.lang.StringEscapeUtils
import org.ice1000.julia.lang.*
import org.ice1000.julia.lang.execution.JuliaEditorsProvider
import java.awt.*
import java.awt.event.MouseEvent
import java.awt.image.*
Expand Down Expand Up @@ -63,7 +71,7 @@ interface DockableContentFigureFactory {
fun createFigure(content: DockableContent<*>): Figure
}

class JuliaSciToolWindow(private val d: Project) : JPanel(BorderLayout()), DumbAware {
class JuliaSciToolWindow(private val project: Project) : JPanel(BorderLayout()), DumbAware {
private val tabs: JBEditorTabs
private val map: MutableMap<TabInfo, Any>
var lastPlotIndex: Int = 0
Expand All @@ -75,7 +83,7 @@ class JuliaSciToolWindow(private val d: Project) : JPanel(BorderLayout()), DumbA
this.map = HashMap()
this.lastPlotIndex = 0
this.list = ArrayList()
this.tabs = JuliaSciToolWindow.MyTabs(this.d)
this.tabs = JuliaSciToolWindow.MyTabs(this.project)
this.tabs.tabsPosition = JBTabsPosition.right
this.tabs.setPopupGroup(DefaultActionGroup(SaveAsFileAction(), CloseAllPlotsAction()), "unknown", true)
this.tabs.isTabDraggingEnabled = true
Expand All @@ -93,21 +101,25 @@ class JuliaSciToolWindow(private val d: Project) : JPanel(BorderLayout()), DumbA
}

fun init(toolWindow: ToolWindow) {
val var4 = SERVICE.getInstance()
val var5 = var4.createContent(this, "Plots", false)
var5.isCloseable = false
toolWindow.contentManager.addContent(var5)
if (this.dockContainer == null) {
this.dockContainer = MyDockContainer(toolWindow)
Disposer.register(this.d, this.dockContainer!!)
DockManager.getInstance(this.d).register(this.dockContainer)
val sciPanel = SERVICE.getInstance()
val plotsContent = sciPanel.createContent(this, "Plots", false)
plotsContent.isCloseable = false

val stackFrame = JuliaVariableStackFrame(project)
val view = JuliaVariablesView(project, stackFrame)
project.putUserData(JULIA_SCI_DATA_KEY, view)
val dataContent = sciPanel.createContent(view.panel, "Data", false)

toolWindow.contentManager.addContent(dataContent)
toolWindow.contentManager.addContent(plotsContent)

if (dockContainer == null) {
dockContainer = MyDockContainer(toolWindow)
Disposer.register(this.project, dockContainer!!)
DockManager.getInstance(this.project).register(dockContainer)
}
}

fun addDockableContentFigureFactory(factory: DockableContentFigureFactory) {
this.list.add(factory)
}

fun addFigure(figure: Figure) {
val tabInfo = figure.getTabInfo()
tabInfo.setTabLabelActions(DefaultActionGroup(ClosePlotAction(tabInfo)), "unknown")
Expand Down Expand Up @@ -190,9 +202,7 @@ class JuliaSciToolWindow(private val d: Project) : JPanel(BorderLayout()), DumbA
this.dragSession = this.getDockManager().createDragSession(mouseEvent, this.a.createDockableContent())
}

private fun getDockManager(): DockManager {
return DockManager.getInstance(this@JuliaSciToolWindow.d)
}
private fun getDockManager(): DockManager = DockManager.getInstance(this@JuliaSciToolWindow.project)

override fun processDragOut(event: MouseEvent, source: TabInfo) {
this.dragSession!!.process(event)
Expand Down Expand Up @@ -267,19 +277,14 @@ class JuliaSciToolWindow(private val d: Project) : JPanel(BorderLayout()), DumbA
private class MyTabs constructor(project: Project) : JBEditorTabs(project, ActionManager.getInstance(), IdeFocusManager.findInstance(), project) {
init {
this.myDefaultPainter = object : DefaultEditorTabsPainter(this) {
override fun getBackgroundColor(): Color {
return JBColor.LIGHT_GRAY
}
override fun getBackgroundColor(): Color = JBColor.LIGHT_GRAY
}
}

override fun createTabLabel(info: TabInfo): TabLabel {
return JuliaSciToolWindow.MyTabLabel(this, info)
}
override fun createTabLabel(info: TabInfo): TabLabel = JuliaSciToolWindow.MyTabLabel(this, info)
}

companion object {
private val h = 80
private val logger = Logger.getInstance(JuliaSciToolWindow::class.java)
@JvmStatic
fun getInstance(project: Project): JuliaSciToolWindow {
Expand All @@ -303,17 +308,11 @@ class ImageFigure @JvmOverloads constructor(imageVirtualFile: ImageVirtualFile,
}

init {
this.tabInfo = a(imageVirtualFile, project)
}

override fun getTabInfo(): TabInfo {
return this.tabInfo
}

override fun hasSearchKey(): Boolean {
return this.key != null
tabInfo = a(imageVirtualFile, project)
}

override fun getTabInfo(): TabInfo = this.tabInfo
override fun hasSearchKey(): Boolean = this.key != null
override fun getSearchKey(): Any {
// TODO use contract
if (!this.hasSearchKey()) throw RuntimeException("Search key is not defined")
Expand Down Expand Up @@ -405,17 +404,11 @@ object FigureUtil {
}

@JvmStatic
fun fit(image: Image, width: Int, height: Int): Image {
return image.getScaledInstance(JBUI.scale(width), JBUI.scale(height), 4)
}
fun fit(image: Image, width: Int, height: Int): Image = image.getScaledInstance(JBUI.scale(width), JBUI.scale(height), 4)

@JvmStatic
fun componentToByteArray(component: JComponent): ByteArray {
return if (component is WithBinaryContent) {
component.getBytes()
} else {
componentImage(component).toByteArray()
}
return if (component is WithBinaryContent) component.getBytes() else componentImage(component).toByteArray()
}

@JvmName("toByteArrayExt")
Expand Down Expand Up @@ -450,4 +443,80 @@ class JuliaSciToolWindowFactory : ToolWindowFactory, DumbAware {
override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) {
JuliaSciToolWindow.getInstance(project).init(toolWindow)
}
}

class JuliaConsoleView(project: Project, title: String) : LanguageConsoleImpl(project, title, JuliaLanguage.INSTANCE), ObservableConsoleView {
var lastModified: Long = 0L
private val myPyHighlighter: JuliaHighlighter
private val myScheme: EditorColorsScheme

init {
historyViewer.putUserData(ConsoleViewUtil.EDITOR_IS_CONSOLE_HISTORY_VIEW, true)
setUpdateFoldingsEnabled(false)
myPyHighlighter = JuliaHighlighter
myScheme = consoleEditor.colorsScheme
}

fun showVariables() {
ApplicationManager.getApplication().executeOnPooledThread {
try {
while (true) {
val tmpdir = System.getProperty("java.io.tmpdir")
val tempDataFile = Paths.get(tmpdir, "tempJuliaVarInfo.tsv").toFile()
val timeStamp = tempDataFile.lastModified()
Thread.sleep(100)
if (lastModified != timeStamp) {
lastModified = timeStamp
val list = tempDataFile.readLines().map {
val (name, bytes, value, summary) = StringEscapeUtils.unescapeJava(it).split("\t")
JuliaDebugValue(name, value, summary)
}
project.putUserData(JULIA_VAR_LIST_KEY, list)
project.getUserData(JULIA_SCI_DATA_KEY)?.rebuildView()
return@executeOnPooledThread
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}

override fun createCenterComponent(): JComponent {
val centerComponent = super.createCenterComponent()
historyViewer.settings.additionalLinesCount = 0
historyViewer.settings.isUseSoftWraps = false
consoleEditor.gutterComponentEx.background = consoleEditor.backgroundColor
consoleEditor.gutterComponentEx.revalidate()
consoleEditor.colorsScheme.setColor(EditorColors.GUTTER_BACKGROUND, consoleEditor.backgroundColor)
// settings.set
return centerComponent
}
}

class JuliaVariablesView(project: Project, stackFrame: JuliaVariableStackFrame) : XStandaloneVariablesView(project, JuliaEditorsProvider(), stackFrame)

class JuliaVariableStackFrame(val project: Project) : XStackFrame() {
override fun computeChildren(node: XCompositeNode) {
val list = project.getUserData(JULIA_VAR_LIST_KEY) ?: return super.computeChildren(node)
val childrenList = XValueChildrenList()
list.forEach(childrenList::add)
node.addChildren(childrenList, true)
}
}

class JuliaDebugValue(name: String,
var type: String = "",
var value: String = "",
var container: Boolean = false,
var parent: JuliaDebugValue? = null) : XNamedValue(name) {
override fun computePresentation(node: XValueNode, place: XValuePlace) {
val icon =
when (type) {
"function" -> JuliaIcons.JULIA_FUNCTION_ICON
else -> JuliaIcons.JULIA_VARIABLE_ICON
}
// param `type` is value, vise versa
node.setPresentation(icon, value, type, container)
}
}

0 comments on commit 4b914de

Please sign in to comment.