Skip to content

Commit

Permalink
Add basic process plugin api
Browse files Browse the repository at this point in the history
  • Loading branch information
Nimaoth committed Oct 26, 2024
1 parent 7e6d279 commit 8c43928
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 7 deletions.
2 changes: 2 additions & 0 deletions scripting/plugin_api.nim
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import plugins_api_wasm
export plugins_api_wasm
import popup_selector_api_wasm
export popup_selector_api_wasm
import process_api_wasm
export process_api_wasm
import registers_api_wasm
export registers_api_wasm
import session_api_wasm
Expand Down
12 changes: 12 additions & 0 deletions scripting/plugin_runtime.nim
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,18 @@ proc loadWorkspaceFile*(path: string, action: proc(content: Option[string]): voi
addScriptAction(key, "", @[("path", "string"), ("callback", "proc(content: Option[string])")], "", false)
loadWorkspaceFile(path, key)

proc runProcess*(process: string, args: seq[string], workingDir: Option[string] = string.none, eval: bool = false, callback: proc(output: string, err: string): void {.closure, gcsafe.} = nil) =
let key = "$runProcess" & $callbackId
callbackId += 1
scriptActions[key] = proc(args: JsonNode): JsonNode =
let args = args.jsonTo(Option[tuple[output: string, err: string]])
if callback != nil and args.isSome:
callback(args.get.output, args.get.err)
scriptActions.del(key)
return newJNull()
addScriptAction(key, "", @[("process", "string"), ("args", "seq[string]"), ("workingDir", "Option[string]"), ("callback", "proc(content: Option[string])")], "", false)
runProcess(process, args, key.some, workingDir, eval)

macro addCommand*(context: string, keys: string, action: string, args: varargs[untyped]): untyped =
let (stmts, str) = bindArgs(args)
return genAst(stmts, context, keys, action, str):
Expand Down
22 changes: 22 additions & 0 deletions scripting/process_api_wasm.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import std/[json, options]
import scripting_api, misc/myjsonutils

## This file is auto generated, don't modify.


proc process_runProcess_void_PluginService_string_seq_string_Option_string_Option_string_bool_wasm(
arg: cstring): cstring {.importc.}
proc runProcess*(process: string; args: seq[string];
callback: Option[string] = string.none;
workingDir: Option[string] = string.none; eval: bool = false) {.
gcsafe, raises: [].} =
var argsJson = newJArray()
argsJson.add process.toJson()
argsJson.add args.toJson()
argsJson.add callback.toJson()
argsJson.add workingDir.toJson()
argsJson.add eval.toJson()
let argsJsonString = $argsJson
let res {.used.} = process_runProcess_void_PluginService_string_seq_string_Option_string_Option_string_bool_wasm(
argsJsonString.cstring)

1 change: 1 addition & 0 deletions src/desktop_main.nim
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ import scripting/scripting_base
import vcs/vcs_api
import wasm3, wasm3/[wasm3c, wasmconversions]
import selector_popup, collab, layout, config_provider, document_editor, session, events, register, selector_popup_builder_impl, vfs_service
import plugin_api/[process]

generatePluginBindings()
static:
Expand Down
19 changes: 12 additions & 7 deletions src/misc/async_process.nim
Original file line number Diff line number Diff line change
Expand Up @@ -417,14 +417,19 @@ type RunProcessThreadArgs = tuple
workingDir: string
captureOut: bool = true
captureErr: bool = true
evalCommand: bool = false

proc readProcessOutputThread(args: RunProcessThreadArgs): (seq[string], seq[string], ref Exception) {.gcsafe.} =
try:
when debugAsyncProcess:
asyncProcessDebugOutput.send(fmt"Start process {args}")

var options: set[ProcessOption] = {poUsePath, poDaemon}
if args.evalCommand:
options.incl poEvalCommand

let process = startProcess(args.processName, workingDir=args.workingDir, args=args.args,
options={poUsePath, poDaemon})
options=options)

if args.captureOut:
var outp = process.outputStream
Expand Down Expand Up @@ -457,21 +462,21 @@ proc readProcessOutputThread(args: RunProcessThreadArgs): (seq[string], seq[stri
result[2] = getCurrentException()

proc runProcessAsync*(name: string, args: seq[string] = @[], workingDir: string = "",
maxLines: int = int.high): Future[seq[string]] {.async.} =
maxLines: int = int.high, eval: bool = false): Future[seq[string]] {.async.} =

log lvlInfo, fmt"[runProcessAsync] {name}, {args}, '{workingDir}', {maxLines}"
let (lines, _, err) = await spawnAsync(readProcessOutputThread, (name, args, maxLines, workingDir, true, false))
let (lines, _, err) = await spawnAsync(readProcessOutputThread, (name, args, maxLines, workingDir, true, false, eval))
if err != nil:
raise newException(IOError, "", err)
raise newException(IOError, err.msg, err)
return lines

proc runProcessAsyncOutput*(name: string, args: seq[string] = @[], workingDir: string = "",
maxLines: int = int.high): Future[tuple[output: string, err: string]] {.async.} =
maxLines: int = int.high, eval: bool = false): Future[tuple[output: string, err: string]] {.async.} =

log lvlInfo, fmt"[runProcessAsync] {name}, {args}, '{workingDir}', {maxLines}"
let (outLines, errLines, err) = await spawnAsync(readProcessOutputThread, (name, args, maxLines, workingDir, true, true))
let (outLines, errLines, err) = await spawnAsync(readProcessOutputThread, (name, args, maxLines, workingDir, true, true, eval))
if err != nil:
raise newException(IOError, "", err)
raise newException(IOError, err.msg, err)
return (outLines.join("\n"), errLines.join("\n"))

proc readProcessOutputCallback(process: AsyncProcess,
Expand Down
39 changes: 39 additions & 0 deletions src/plugin_api/process.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import std/[tables, options, json, os, strutils]
import results
import misc/[custom_async, custom_logger, myjsonutils, util, async_process]
import scripting/[expose, scripting_base]
import workspaces/workspace
import service, dispatch_tables, vfs, vfs_local, vfs_service

{.push gcsafe.}
{.push raises: [].}

logCategory "plugin-api-process"

###########################################################################

proc runProcessImpl(self: PluginService, process: string, args: seq[string], callback: Option[string] = string.none, workingDir: Option[string] = string.none, eval: bool = false) {.async: (raises: []).} =
type ResultType = tuple[output: string, err: string]

let workingDir = if workingDir.getSome(wd):
let vfs = self.services.getService(VFSService).get.vfs
vfs.localize(wd)
else:
let workspace = self.services.getService(Workspace).get
workspace.path

try:
let (output, err) = await runProcessAsyncOutput(process, args, workingDir=workingDir, eval=eval)
log lvlDebug, &"runProcess '{process} {args.join($' ')}' in '{workingDir}'"
if callback.getSome(c):
discard self.callScriptAction(c, (output: output, err: err).some.toJson)

except CatchableError as e:
log lvlError, &"Failed to run process '{process}': {e.msg}"
if callback.getSome(c):
discard self.callScriptAction(c, ResultType.none.toJson)

proc runProcess*(self: PluginService, process: string, args: seq[string], callback: Option[string] = string.none, workingDir: Option[string] = string.none, eval: bool = false) {.expose("process").} =
asyncSpawn self.runProcessImpl(process, args, callback, workingDir, eval)

addGlobalDispatchTable "process", genDispatchTable("process")

0 comments on commit 8c43928

Please sign in to comment.