Added Vectors and Matrics operations, slicing, docs.

This commit is contained in:
sergeych committed 2026-03-28 22:25:40 +03:00
1 parent a72991d1b7
commit 83d8c8b71f
31 files changed
+1972 -41

No files matched your search

@@ -23,6 +23,8 @@ import com.intellij.execution.ui.ConsoleViewContentType
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.wm.ToolWindow
import com.intellij.openapi.wm.ToolWindowAnchor
@@ -36,9 +38,10 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import net.sergeych.lyng.idea.LyngIcons
import java.io.File
class RunLyngScriptAction : AnAction(LyngIcons.FILE) {
private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
private fun getPsiFile(e: AnActionEvent): PsiFile? {
val project = e.project ?: return null
@@ -48,36 +51,99 @@ class RunLyngScriptAction : AnAction(LyngIcons.FILE) {
}
}
private fun getRunnableFile(e: AnActionEvent): PsiFile? {
val psiFile = getPsiFile(e) ?: return null
val virtualFile = psiFile.virtualFile ?: return null
if (!virtualFile.isInLocalFileSystem) return null
if (!psiFile.name.endsWith(".lyng")) return null
return psiFile
}
override fun update(e: AnActionEvent) {
val psiFile = getPsiFile(e)
val isLyng = psiFile?.name?.endsWith(".lyng") == true
e.presentation.isEnabledAndVisible = isLyng
if (isLyng) {
e.presentation.isEnabled = false
e.presentation.text = "Run '${psiFile.name}' (disabled)"
e.presentation.description = "Running scripts from the IDE is disabled; use the CLI."
val psiFile = getRunnableFile(e)
val isRunnable = psiFile != null
e.presentation.isEnabledAndVisible = isRunnable
if (isRunnable) {
e.presentation.text = "Run '${psiFile.name}'"
e.presentation.description = "Run the current Lyng script using the Lyng CLI"
} else {
e.presentation.text = "Run Lyng Script"
e.presentation.description = "Run the current Lyng script"
}
}
override fun actionPerformed(e: AnActionEvent) {
val project = e.project ?: return
val psiFile = getPsiFile(e) ?: return
val fileName = psiFile.name
val psiFile = getRunnableFile(e) ?: return
val virtualFile = psiFile.virtualFile ?: return
FileDocumentManager.getInstance().getDocument(virtualFile)?.let { document ->
FileDocumentManager.getInstance().saveDocument(document)
}
val filePath = virtualFile.path
val workingDir = virtualFile.parent?.path ?: project.basePath ?: File(filePath).parent
val (console, toolWindow) = getConsoleAndToolWindow(project)
console.clear()
toolWindow.show {
scope.launch {
console.print("--- Run is disabled ---\n", ConsoleViewContentType.SYSTEM_OUTPUT)
console.print("Lyng now runs in bytecode-only mode; the IDE no longer evaluates scripts.\n", ConsoleViewContentType.NORMAL_OUTPUT)
console.print("Use the CLI to run scripts, e.g. `lyng run $fileName`.\n", ConsoleViewContentType.NORMAL_OUTPUT)
val command = startLyngProcess(filePath, workingDir)
if (command == null) {
printToConsole(console, "Unable to start Lyng CLI.\n", ConsoleViewContentType.ERROR_OUTPUT)
printToConsole(console, "Tried commands: lyng, jlyng.\n", ConsoleViewContentType.ERROR_OUTPUT)
printToConsole(console, "Install `lyng` or `jlyng` and make sure it is available on PATH.\n", ConsoleViewContentType.NORMAL_OUTPUT)
return@launch
}
printToConsole(
console,
"Running ${command.commandLine} in ${command.workingDir}\n",
ConsoleViewContentType.SYSTEM_OUTPUT
)
streamProcess(command.process, console)
val exitCode = command.process.waitFor()
val outputType = if (exitCode == 0) ConsoleViewContentType.SYSTEM_OUTPUT else ConsoleViewContentType.ERROR_OUTPUT
printToConsole(console, "\nProcess finished with exit code $exitCode\n", outputType)
}
}
}
private suspend fun streamProcess(process: Process, console: ConsoleView) {
val stdout = scope.launch {
process.inputStream.bufferedReader().useLines { lines ->
lines.forEach { printToConsole(console, "$it\n", ConsoleViewContentType.NORMAL_OUTPUT) }
}
}
val stderr = scope.launch {
process.errorStream.bufferedReader().useLines { lines ->
lines.forEach { printToConsole(console, "$it\n", ConsoleViewContentType.ERROR_OUTPUT) }
}
}
stdout.join()
stderr.join()
}
private fun printToConsole(console: ConsoleView, text: String, type: ConsoleViewContentType) {
ApplicationManager.getApplication().invokeLater {
console.print(text, type)
}
}
private fun startLyngProcess(filePath: String, workingDir: String?): StartedProcess? {
val candidates = listOf("lyng", "jlyng")
for (candidate in candidates) {
try {
val process = ProcessBuilder(candidate, filePath)
.directory(workingDir?.let(::File))
.start()
return StartedProcess(process, "$candidate $filePath", workingDir ?: File(filePath).parent.orEmpty())
} catch (_: java.io.IOException) {
// Try the next candidate when the command is not available.
}
}
return null
}
private fun getConsoleAndToolWindow(project: Project): Pair<ConsoleView, ToolWindow> {
val toolWindowManager = ToolWindowManager.getInstance(project)
var toolWindow = toolWindowManager.getToolWindow(ToolWindowId.RUN)
@@ -106,4 +172,10 @@ class RunLyngScriptAction : AnAction(LyngIcons.FILE) {
contentManager.setSelectedContent(content)
return console to actualToolWindow
}
private data class StartedProcess(
val process: Process,
val commandLine: String,
val workingDir: String
)
}