diff options
| author | s-ol <s-ol@users.noreply.github.com> | 2020-05-15 11:22:56 +0000 |
|---|---|---|
| committer | s-ol <s+removethis@s-ol.nu> | 2025-03-02 14:24:49 +0000 |
| commit | dceb06938c1295d1a4a7eafff20d5007e48dab81 (patch) | |
| tree | 5984aa544f188d4033fda2b40c91e4a10005f4a8 /alv/copilot | |
| parent | add array and struct constructors (diff) | |
| download | alive-dceb06938c1295d1a4a7eafff20d5007e48dab81.tar.gz alive-dceb06938c1295d1a4a7eafff20d5007e48dab81.zip | |
move copilot logic, make startup scripts lua
Diffstat (limited to 'alv/copilot')
| -rw-r--r-- | alv/copilot/base.moon | 152 | ||||
| -rw-r--r-- | alv/copilot/cli.moon | 34 | ||||
| -rw-r--r-- | alv/copilot/fltk.moon | 111 | ||||
| -rw-r--r-- | alv/copilot/wx.moon | 139 |
4 files changed, 436 insertions, 0 deletions
diff --git a/alv/copilot/base.moon b/alv/copilot/base.moon new file mode 100644 index 0000000..8669d93 --- /dev/null +++ b/alv/copilot/base.moon @@ -0,0 +1,152 @@ +---- +-- File watcher and runtime entrypoint. +-- +-- @classmod Copilot +lfs = require 'lfs' +import Scope from require 'alv.scope' +import Module from require 'alv.module' +import Error from require 'alv.error' +import RTNode from require 'alv.rtnode' +import Constant from require 'alv.result' + +parse_args = (args, out={}) -> + local key + for a in *args + if key + out[key] = a + key = nil + else if match = a\match '^%-%-(.*)' + if 'boolean' == type out[match] + out[match] = true + else + key = match + else + table.insert out, a + assert not key, "value for option '--#{key}' missing!" + out + +export COPILOT + +class Copilot +--- static functions +-- @section static + + --- create a new Copilot. + -- @classmethod + -- @tparam string file name/path of the alive file to watch and execute + new: (file) => + @T = 0 + @last_modification = 0 + @last_modules = {} + @open file if file + +--- members +-- @section members + + --- current tick + -- @tfield number T + + --- change the running script. + -- @tparam string file + open: (file) => + assert not COPILOT, "another Copilot is already running!" + COPILOT = @ + + if old = @last_modules.__root + old\destroy! + + @last_modules.__root = Module file + @active_module = @last_modules.__root + + COPILOT = nil + + --- require a module. + -- @tparam string name + -- @treturn RTNode root + require: (name) => + Error.wrap "loading module '#{name}'", -> + ok, lua = pcall require, "alv-lib.#{name}" + if ok + RTNode result: Constant.wrap lua + elseif not lua\match "not found" + error lua + else + assert @modules, "no current eval cycle?" + if mod = @modules[name] + mod.root\make_ref! + else + last = @active_module + prefix = if b = last.file\match'(.*)/[^/]*$' then b .. '/' else '' + mod = @last_modules[name] or Module "#{prefix}#{name}.alv" + L\trace "entering module #{mod}" + @modules[name] = mod + @active_module = mod + ok, err = pcall mod\eval + L\trace "returning to module #{mod}" + @active_module = last + if ok + mod.root + else + error err + + --- poll for changes and tick. + tick: => + assert not COPILOT, "another Copilot is already running!" + return unless @last_modules.__root + + COPILOT = @ + @T += 1 + + @poll! + + root = @last_modules.__root + if root and root.root + L\set_time 'run' + ok, error = Error.try "updating", -> + root.root\poll_io! + root.root\tick! + if not ok + L\print error + + COPILOT = nil + + --- poll all loaded modules for changes. + -- + -- Call `eval` if there are any, and write changed and newly added modules + -- back to disk. + poll: => + dirty = {} + for name, mod in pairs @last_modules + if mod\poll! > @last_modification + table.insert dirty, mod + + return if #dirty == 0 + + @last_modification = os.time! + L\set_time 'eval' + L\print "changes to files: #{table.concat [m.file for m in *dirty], ', '}" + + @modules = { __root: @last_modules.__root } + ok, err = Error.try "processing changes", @modules.__root\eval + + if not ok + for name, mod in pairs @modules + mod\rollback! + @modules = nil + L\error err + return + + for name, mod in pairs @last_modules + if not @modules[name] + mod\destroy! + + for name, mod in pairs @modules + mod\finish! + + @last_modification = os.time! + @last_modules, @modules = @modules, nil + +{ + :parse_args + :Copilot +} diff --git a/alv/copilot/cli.moon b/alv/copilot/cli.moon new file mode 100644 index 0000000..3686491 --- /dev/null +++ b/alv/copilot/cli.moon @@ -0,0 +1,34 @@ +---- +-- CLI Copilot entrypoint. +-- +-- @classmod CLICopilot +import Logger, version from require 'alv' +import parse_args, Copilot from require 'alv.copilot.base' +import sleep from require 'system' + +class ColorLogger extends Logger + set_time: (time) => + super time + @stream\write switch time + when 'eval' then '\27[92m' + when 'run' then '\27[0m' + +class CLICopilot extends Copilot + new: (arg) => + @args = parse_args arg, { nocolor: false } + assert @args[1], "no filename given" + super @args[1] + + run: => + if @args.nocolor + Logger\init @args.log + else + ColorLogger\init @args.log + + while true + @tick! + sleep 1 / 1000 + +{ + :CLICopilot +} diff --git a/alv/copilot/fltk.moon b/alv/copilot/fltk.moon new file mode 100644 index 0000000..8821358 --- /dev/null +++ b/alv/copilot/fltk.moon @@ -0,0 +1,111 @@ +---- +-- fltk4lua Copilot GUI. +-- +-- @classmod FLTKCopilot +import Logger, version from require 'alv' +import parse_args, Copilot from require 'alv.copilot.base' +import sleep from require 'system' +fl = require 'fltk4lua' + +class FLTKLogger extends Logger + new: (level, @eval, @run) => + super level + + set_time: (time) => + @output = switch time + when 'eval' then @eval + when 'run' then @run + else error "invalid time '#{time}'" + + put: (message) => + @output.browser\add message + if true or @output.sticky.value + @output.browser.bottomline = @output.browser.nitems + +class FLTKCopilot extends Copilot + new: (arg) => + @args = parse_args arg + + @window = with fl.Window { 400, 220, "alv copilot", xclass: 'alv' } + \size_range 400, 220, nil, nil, 20, 20 + + @menubar = with fl.Menu_Bar 0, 0, 400, 20 + \add "&File/About", nil, @\about, nil, fl.MENU_DIVIDER + \add "&File/&Open script", '^o', -> @open! + \add "&File/&Quit", '^q', -> @window\hide! + @pause = \add "Run (^P)", '^p', @\pause, nil, fl.MENU_TOGGLE + \add "Help", nil, -> fl.open_uri version.web + + tile = fl.Tile 5, 40, 390, 160 + @window.resizable = tile + resize_box = fl.Box { 5, 60, 390, 120, labeltype: fl.NO_LABEL } + tile.resizable = resize_box + + @eval_out = do + browser = fl.Browser { + 5, 40, 390, 80, "eval", + box: fl.GTK_DOWN_BOX, align: fl.ALIGN_TOP_RIGHT + } + -- sticky = fl.Check_Button 5, 20, 80, 20, 'sticky' + -- sticky\set! + :browser, :sticky + + @run_out = do + browser = fl.Browser { + 5, 120, 390, 80, "run", + box: fl.GTK_DOWN_BOX, align: fl.ALIGN_BOTTOM_RIGHT + } + -- sticky = fl.Check_Button 5, 200, 80, 20, 'sticky' + -- sticky\set! + :browser, :sticky + + tile\end_group! + @window\end_group! + + @update_status! + super @args[1] + + about: => + fl.alert "alive #{version.tag} fltkCopilot. + +visit #{version.web} for more information." + + open: (file) => + file or= fl.file_chooser "Open Script", '*.alv', 'script.alv' + + if file + @paused = false + super file + @update_status! + + pause: => + @paused = not @paused + @update_status! + + update_status: => + state = fl.MENU_TOGGLE + if not @paused + state += fl.MENU_VALUE + @menubar\menuitem_setp @pause, 'flags', state + + if @paused + "Paused." + else + "Running." + + run: => + FLTKLogger\init @args.log, @eval_out, @run_out + @window\show! + + run = true + while run + run = if @paused + fl.check 1 + else + @tick! + sleep 1 / 1000 + fl.check! + +{ + :FLTKCopilot +} diff --git a/alv/copilot/wx.moon b/alv/copilot/wx.moon new file mode 100644 index 0000000..b740794 --- /dev/null +++ b/alv/copilot/wx.moon @@ -0,0 +1,139 @@ +---- +-- wxLua Copilot GUI. +-- +-- @classmod WXCopilot +import Logger, version from require 'alv' +import parse_args, Copilot from require 'alv.copilot.base' + +require 'wx' +import + wxID_ABOUT, wxID_OPEN, wxID_EXIT, wxID_ANY, wxVERTICAL, + wxFrame, wxMenuBar, wxMenu, wxPanel, wxBoxSizer, wxTextCtrl, wxSplitterWindow +from wx + +STARTSTOP = 100 + +class WXLogger extends Logger + new: (level, @eval_ctrl, @run_ctrl) => + super level + + set_time: (time) => + @ctrl = switch time + when 'eval' then @eval_ctrl + when 'run' then @run_ctrl + else error "invalid time '#{time}'" + + put: (message) => + @ctrl\AppendText message .. '\n' + +class WXCopilot extends Copilot + new: (arg) => + @args = parse_args arg + super @args[1] + + @app = wx.wxGetApp! + @app.VendorName = 'alive' + @app.AppName = 'alive wxCopilot' + + fileMenu = wxMenu { + { wxID_ABOUT, '&About', 'About alive wxCopilot' } + { wxID_OPEN, '&Open\tCtrl-O', 'Open Script' } + { } + { wxID_EXIT, 'E&xit\tCtrl-Q', 'Exit Program' } + } + runMenu = wxMenu { + { STARTSTOP, '&Start/Stop\tCtrl-P', 'Start/Stop Script Execution' } + } + @menuBar = with wxMenuBar! + \Append fileMenu, '&File' + \Append runMenu, '&Run' + + @frame = wxFrame wx.NULL, wxID_ANY, @app\GetAppName! + @frame\SetMenuBar @menuBar + @status = @frame\CreateStatusBar 1 + + @update_status! + + splitter = wxSplitterWindow @frame, wx.wxID_ANY + + eval, @eval_ctrl = @mkPanel splitter, 'eval-time messages' + + run, @run_ctrl = @mkPanel splitter, 'run-time messages' + + splitter\SetMinimumPaneSize 60 + splitter\SplitHorizontally eval, run, 0 + + sizer = with wxBoxSizer wx.wxVERTICAL + \Add splitter, 1, wx.wxEXPAND + wx.wxALL, 10 + + @frame\SetAutoLayout true + @frame\SetSizer sizer + @frame\Show true + + @frame\Connect wxID_ABOUT, wx.wxEVT_COMMAND_MENU_SELECTED, @\do_about + @frame\Connect wxID_OPEN, wx.wxEVT_COMMAND_MENU_SELECTED, @\do_open + @frame\Connect wxID_EXIT, wx.wxEVT_COMMAND_MENU_SELECTED, -> @frame\Close! + @frame\Connect STARTSTOP, wx.wxEVT_COMMAND_MENU_SELECTED, @\do_startstop + @frame\Connect wxID_ANY, wx.wxEVT_IDLE, @\do_idle + + do_about: => + wx.wxMessageBox "alive #{version.tag} wxCopilot. + +built using #{wxlua.wxLUA_VERSION_STRING} on #{wx.wxVERSION_STRING}", + "About wxCopilot", + wx.wxOK + wx.wxICON_INFORMATION, + @frame + + do_open: => + dialog = wx.wxFileDialog @frame, 'Change Script', '', '', + 'Alive scripts (*.alv)|*.alv|All files (*)|*', + wx.wxFD_OPEN + wx.wxFD_FILE_MUST_EXIST + + if dialog\ShowModal! == wx.wxID_OK + @paused = false + @open dialog\GetPath! + @update_status! + + dialog\Destroy! + + do_startstop: (event) => + if @file + @paused = not @paused + @update_status! + + do_idle: (event) => + if not @paused + event\RequestMore true + @tick! + + update_status: => + startstop = @menuBar\FindItem STARTSTOP + if not @file + @status\SetStatusText "No script loaded." + startstop\Enable false + else + startstop\Enable true + if @paused + @status\SetStatusText "Paused." + else + @status\SetStatusText "Running." + + mkPanel: (parent, name) => + panel = wxPanel parent, wxID_ANY + sizer = wxBoxSizer wxVERTICAL + panel\SetSizer sizer + + sizer\Add wx.wxStaticText panel, wx.wxID_ANY, name + log = wxTextCtrl panel, wxID_ANY, '', wx.wxDefaultPosition, + wx.wxDefaultSize, wx.wxTE_MULTILINE + wx.wxTE_READONLY + sizer\Add log, 1, wx.wxEXPAND | wx.wxBOTTOM, 5 + + panel, log + + run: => + WXLogger\init @args.log, copilot.eval_ctrl, copilot.run_ctrl + @app\MainLoop! + +{ + :WXCopilot +} |
