2021-05-16 19:53:21 +00:00
// Here is the core api for the hilbi shell itself
// Basically, stuff about the shell itself and other functions
// go here.
package main
2021-05-16 21:13:28 +00:00
import (
2022-04-04 10:40:02 +00:00
"bytes"
"errors"
2022-01-26 19:51:52 +00:00
"fmt"
2021-05-16 21:13:28 +00:00
"os"
2022-01-26 19:51:52 +00:00
"os/exec"
2021-09-26 01:39:06 +00:00
"runtime"
2021-07-08 10:44:57 +00:00
"strings"
2022-01-26 19:51:52 +00:00
"syscall"
"time"
2021-05-16 21:13:28 +00:00
2021-10-16 19:40:08 +00:00
"hilbish/util"
2022-04-04 10:40:02 +00:00
rt "github.com/arnodel/golua/runtime"
"github.com/arnodel/golua/lib/packagelib"
2022-03-02 02:00:46 +00:00
"github.com/maxlandon/readline"
2022-03-02 23:11:57 +00:00
"github.com/blackfireio/osinfo"
2021-05-16 21:13:28 +00:00
"mvdan.cc/sh/v3/interp"
)
2022-04-04 10:40:02 +00:00
var exports = map [ string ] util . LuaExport {
"alias" : { hlalias , 2 , false } ,
"appendPath" : { hlappendPath , 1 , false } ,
"complete" : { hlcomplete , 2 , false } ,
"cwd" : { hlcwd , 0 , false } ,
"exec" : { hlexec , 1 , false } ,
"runnerMode" : { hlrunnerMode , 1 , false } ,
"goro" : { hlgoro , 1 , true } ,
"highlighter" : { hlhighlighter , 1 , false } ,
"hinter" : { hlhinter , 1 , false } ,
"multiprompt" : { hlmultiprompt , 1 , false } ,
"prependPath" : { hlprependPath , 1 , false } ,
2022-04-13 14:13:46 +00:00
"prompt" : { hlprompt , 1 , true } ,
2022-04-04 10:40:02 +00:00
"inputMode" : { hlinputMode , 1 , false } ,
"interval" : { hlinterval , 2 , false } ,
"read" : { hlread , 1 , false } ,
"run" : { hlrun , 1 , true } ,
"timeout" : { hltimeout , 2 , false } ,
"which" : { hlwhich , 1 , false } ,
2021-05-16 21:13:28 +00:00
}
2021-12-31 17:25:53 +00:00
var greeting string
2022-04-04 10:40:02 +00:00
var hshMod * rt . Table
var hilbishLoader = packagelib . Loader {
Load : hilbishLoad ,
Name : "hilbish" ,
}
2021-12-31 17:25:53 +00:00
2022-04-04 10:40:02 +00:00
func hilbishLoad ( rtm * rt . Runtime ) ( rt . Value , func ( ) ) {
2022-04-22 00:39:38 +00:00
fakeMod := rt . NewTable ( )
modmt := rt . NewTable ( )
2022-04-04 10:40:02 +00:00
mod := rt . NewTable ( )
2022-04-22 00:39:38 +00:00
modIndex := func ( t * rt . Thread , c * rt . GoCont ) ( rt . Cont , error ) {
arg := c . Arg ( 1 )
val := mod . Get ( arg )
return c . PushingNext1 ( t . Runtime , val ) , nil
}
modNewIndex := func ( t * rt . Thread , c * rt . GoCont ) ( rt . Cont , error ) {
2022-04-22 02:19:27 +00:00
k , err := c . StringArg ( 1 )
if err != nil {
return nil , err
}
2022-04-22 00:39:38 +00:00
v := c . Arg ( 2 )
2022-04-22 02:19:27 +00:00
if k == "highlighter" {
var err error
// fine to assign, since itll be either nil or a closure
highlighter , err = c . ClosureArg ( 2 )
if err != nil {
return nil , errors . New ( "hilbish.highlighter has to be a function" )
}
} else if k == "hinter" {
var err error
hinter , err = c . ClosureArg ( 2 )
if err != nil {
return nil , errors . New ( "hilbish.hinter has to be a function" )
}
} else if modVal := mod . Get ( rt . StringValue ( k ) ) ; modVal != rt . NilValue {
2022-04-22 00:39:38 +00:00
return nil , errors . New ( "not allowed to override in hilbish table" )
}
2022-04-22 02:19:27 +00:00
mod . Set ( rt . StringValue ( k ) , v )
2022-04-22 00:39:38 +00:00
return c . Next ( ) , nil
}
modmt . Set ( rt . StringValue ( "__newindex" ) , rt . FunctionValue ( rt . NewGoFunction ( modNewIndex , "__newindex" , 3 , false ) ) )
modmt . Set ( rt . StringValue ( "__index" ) , rt . FunctionValue ( rt . NewGoFunction ( modIndex , "__index" , 2 , false ) ) )
fakeMod . SetMetatable ( modmt )
2022-04-04 10:40:02 +00:00
util . SetExports ( rtm , mod , exports )
2022-03-02 02:00:46 +00:00
hshMod = mod
2021-05-16 21:13:28 +00:00
host , _ := os . Hostname ( )
2021-09-26 01:39:06 +00:00
username := curuser . Username
2021-12-31 17:25:53 +00:00
2021-09-26 01:39:06 +00:00
if runtime . GOOS == "windows" {
username = strings . Split ( username , "\\" ) [ 1 ] // for some reason Username includes the hostname on windows
}
2021-05-16 21:13:28 +00:00
2022-03-16 22:42:38 +00:00
greeting = ` Welcome to { magenta}Hilbish { reset}, { cyan} ` + username + ` { reset } .
The nice lil shell for { blue } Lua { reset } fanatics !
Check out the { blue } { bold } guide { reset } command to get started .
`
2022-05-16 23:36:34 +00:00
util . SetFieldProtected ( fakeMod , mod , "ver" , rt . StringValue ( getVersion ( ) ) , "Hilbish version" )
2022-04-22 14:42:04 +00:00
util . SetFieldProtected ( fakeMod , mod , "user" , rt . StringValue ( username ) , "Username of user" )
util . SetFieldProtected ( fakeMod , mod , "host" , rt . StringValue ( host ) , "Host name of the machine" )
util . SetFieldProtected ( fakeMod , mod , "home" , rt . StringValue ( curuser . HomeDir ) , "Home directory of the user" )
util . SetFieldProtected ( fakeMod , mod , "dataDir" , rt . StringValue ( dataDir ) , "Directory for Hilbish's data files" )
util . SetFieldProtected ( fakeMod , mod , "interactive" , rt . BoolValue ( interactive ) , "If this is an interactive shell" )
util . SetFieldProtected ( fakeMod , mod , "login" , rt . BoolValue ( login ) , "Whether this is a login shell" )
util . SetFieldProtected ( fakeMod , mod , "greeting" , rt . StringValue ( greeting ) , "Hilbish's welcome message for interactive shells. It has Lunacolors formatting." )
util . SetFieldProtected ( fakeMod , mod , "vimMode" , rt . NilValue , "Current Vim mode of Hilbish (nil if not in Vim mode)" )
util . SetFieldProtected ( fakeMod , mod , "exitCode" , rt . IntValue ( 0 ) , "Exit code of last exected command" )
util . Document ( fakeMod , "Hilbish's core API, containing submodules and functions which relate to the shell itself." )
2021-05-16 21:13:28 +00:00
2021-12-15 00:54:23 +00:00
// hilbish.userDir table
2022-04-04 10:40:02 +00:00
hshuser := rt . NewTable ( )
2021-12-14 00:13:17 +00:00
2022-04-04 10:40:02 +00:00
util . SetField ( rtm , hshuser , "config" , rt . StringValue ( confDir ) , "User's config directory" )
util . SetField ( rtm , hshuser , "data" , rt . StringValue ( userDataDir ) , "XDG data directory" )
util . Document ( hshuser , "User directories to store configs and/or modules." )
mod . Set ( rt . StringValue ( "userDir" ) , rt . TableValue ( hshuser ) )
2022-03-05 19:59:00 +00:00
// hilbish.os table
2022-04-04 10:40:02 +00:00
hshos := rt . NewTable ( )
2022-03-02 23:11:57 +00:00
info , _ := osinfo . GetOSInfo ( )
2022-03-05 19:59:00 +00:00
2022-04-04 10:40:02 +00:00
util . SetField ( rtm , hshos , "family" , rt . StringValue ( info . Family ) , "Family name of the current OS" )
util . SetField ( rtm , hshos , "name" , rt . StringValue ( info . Name ) , "Pretty name of the current OS" )
util . SetField ( rtm , hshos , "version" , rt . StringValue ( info . Version ) , "Version of the current OS" )
util . Document ( hshos , "OS info interface" )
mod . Set ( rt . StringValue ( "os" ) , rt . TableValue ( hshos ) )
2021-10-08 00:58:07 +00:00
2021-12-15 00:54:23 +00:00
// hilbish.aliases table
2022-03-19 16:44:26 +00:00
aliases = newAliases ( )
2022-04-04 10:40:02 +00:00
aliasesModule := aliases . Loader ( rtm )
util . Document ( aliasesModule , "Alias inferface for Hilbish." )
mod . Set ( rt . StringValue ( "aliases" ) , rt . TableValue ( aliasesModule ) )
2021-12-15 00:54:23 +00:00
2022-01-27 21:02:21 +00:00
// hilbish.history table
2022-04-04 10:40:02 +00:00
historyModule := lr . Loader ( rtm )
mod . Set ( rt . StringValue ( "history" ) , rt . TableValue ( historyModule ) )
util . Document ( historyModule , "History interface for Hilbish." )
2022-01-27 21:02:21 +00:00
2022-04-04 10:40:02 +00:00
// hilbish.completion table
2022-04-23 01:16:35 +00:00
hshcomp := completionLoader ( rtm )
2022-04-04 10:40:02 +00:00
util . Document ( hshcomp , "Completions interface for Hilbish." )
mod . Set ( rt . StringValue ( "completion" ) , rt . TableValue ( hshcomp ) )
2022-03-05 19:59:00 +00:00
2022-03-20 23:10:12 +00:00
// hilbish.runner table
2022-04-04 10:40:02 +00:00
runnerModule := runnerModeLoader ( rtm )
util . Document ( runnerModule , "Runner/exec interface for Hilbish." )
mod . Set ( rt . StringValue ( "runner" ) , rt . TableValue ( runnerModule ) )
2022-03-20 19:15:44 +00:00
2022-03-20 23:10:12 +00:00
// hilbish.jobs table
2022-03-19 17:10:50 +00:00
jobs = newJobHandler ( )
2022-04-04 10:40:02 +00:00
jobModule := jobs . loader ( rtm )
util . Document ( jobModule , "(Background) job interface." )
mod . Set ( rt . StringValue ( "jobs" ) , rt . TableValue ( jobModule ) )
2022-05-14 00:43:40 +00:00
// hilbish.timers table
2022-04-12 23:28:25 +00:00
timers = newTimerHandler ( )
timerModule := timers . loader ( rtm )
util . Document ( timerModule , "Timer interface, for control of all intervals and timeouts." )
mod . Set ( rt . StringValue ( "timers" ) , rt . TableValue ( timerModule ) )
2021-05-16 21:13:28 +00:00
2022-05-14 00:43:40 +00:00
editorModule := editorLoader ( rtm )
util . Document ( editorModule , "" )
mod . Set ( rt . StringValue ( "editor" ) , rt . TableValue ( editorModule ) )
2022-04-22 00:39:38 +00:00
return rt . TableValue ( fakeMod ) , nil
2021-05-16 21:13:28 +00:00
}
2022-04-04 10:40:02 +00:00
func getenv ( key , fallback string ) string {
value := os . Getenv ( key )
if len ( value ) == 0 {
return fallback
}
return value
}
2022-03-05 19:59:00 +00:00
2022-03-02 02:00:46 +00:00
func setVimMode ( mode string ) {
2022-04-04 10:40:02 +00:00
util . SetField ( l , hshMod , "vimMode" , rt . StringValue ( mode ) , "Current Vim mode of Hilbish (nil if not in Vim mode)" )
2022-03-06 16:08:00 +00:00
hooks . Em . Emit ( "hilbish.vimMode" , mode )
2022-03-02 02:00:46 +00:00
}
func unsetVimMode ( ) {
2022-04-04 10:40:02 +00:00
util . SetField ( l , hshMod , "vimMode" , rt . NilValue , "Current Vim mode of Hilbish (nil if not in Vim mode)" )
2022-03-02 02:00:46 +00:00
}
2022-04-04 10:40:02 +00:00
// run(cmd, returnOut) -> exitCode, stdout, stderr
2021-12-14 00:13:17 +00:00
// Runs `cmd` in Hilbish's sh interpreter.
2022-04-04 10:40:02 +00:00
// If returnOut is true, the outputs of `cmd` will be returned as the 2nd and
// 3rd values instead of being outputted to the terminal.
2022-02-26 15:36:04 +00:00
// --- @param cmd string
2022-04-04 10:40:02 +00:00
func hlrun ( t * rt . Thread , c * rt . GoCont ) ( rt . Cont , error ) {
if err := c . Check1Arg ( ) ; err != nil {
return nil , err
}
cmd , err := c . StringArg ( 0 )
if err != nil {
return nil , err
}
var terminalOut bool
if len ( c . Etc ( ) ) != 0 {
tout := c . Etc ( ) [ 0 ]
termOut , ok := tout . TryBool ( )
terminalOut = termOut
if ! ok {
return nil , errors . New ( "bad argument to run (expected boolean, got " + tout . TypeName ( ) + ")" )
}
} else {
terminalOut = true
}
2021-12-07 21:27:07 +00:00
var exitcode uint8
2022-04-04 10:40:02 +00:00
stdout , stderr , err := execCommand ( cmd , terminalOut )
2021-05-16 21:13:28 +00:00
if code , ok := interp . IsExitStatus ( err ) ; ok {
exitcode = code
} else if err != nil {
exitcode = 1
}
2022-04-04 10:40:02 +00:00
stdoutStr := ""
stderrStr := ""
if ! terminalOut {
stdoutStr = stdout . ( * bytes . Buffer ) . String ( )
stderrStr = stderr . ( * bytes . Buffer ) . String ( )
}
return c . PushingNext ( t . Runtime , rt . IntValue ( int64 ( exitcode ) ) , rt . StringValue ( stdoutStr ) , rt . StringValue ( stderrStr ) ) , nil
2021-05-16 19:53:21 +00:00
}
2021-10-16 16:40:53 +00:00
// cwd()
// Returns the current directory of the shell
2022-04-04 10:40:02 +00:00
func hlcwd ( t * rt . Thread , c * rt . GoCont ) ( rt . Cont , error ) {
2021-05-27 23:06:17 +00:00
cwd , _ := os . Getwd ( )
2022-04-04 10:40:02 +00:00
return c . PushingNext1 ( t . Runtime , rt . StringValue ( cwd ) ) , nil
2021-05-27 23:06:17 +00:00
}
2021-10-30 23:53:42 +00:00
// read(prompt) -> input?
// Read input from the user, using Hilbish's line editor/input reader.
// This is a separate instance from the one Hilbish actually uses.
// Returns `input`, will be nil if ctrl + d is pressed, or an error occurs (which shouldn't happen)
2022-02-26 15:36:04 +00:00
// --- @param prompt string
2022-04-04 10:40:02 +00:00
func hlread ( t * rt . Thread , c * rt . GoCont ) ( rt . Cont , error ) {
if err := c . Check1Arg ( ) ; err != nil {
return nil , err
}
luaprompt , err := c . StringArg ( 0 )
if err != nil {
return nil , err
}
2022-03-06 18:38:27 +00:00
lualr := newLineReader ( "" , true )
2022-03-06 18:23:41 +00:00
lualr . SetPrompt ( luaprompt )
2021-10-30 23:53:42 +00:00
input , err := lualr . Read ( )
if err != nil {
2022-04-04 10:40:02 +00:00
return c . Next ( ) , nil
2021-10-30 23:53:42 +00:00
}
2022-04-04 10:40:02 +00:00
return c . PushingNext1 ( t . Runtime , rt . StringValue ( input ) ) , nil
2021-10-30 23:53:42 +00:00
}
2022-02-26 15:36:04 +00:00
/ *
2022-04-13 14:13:46 +00:00
prompt ( str , typ ? )
2022-01-26 19:51:52 +00:00
Changes the shell prompt to ` str `
There are a few verbs that can be used in the prompt text .
These will be formatted and replaced with the appropriate values .
` %d ` - Current working directory
` %u ` - Name of current user
2022-02-26 15:36:04 +00:00
` %h ` - Hostname of device
-- - @ param str string
2022-04-13 14:13:46 +00:00
-- - @ param typ string Type of prompt , being left or right . Left by default .
2022-02-26 15:36:04 +00:00
* /
2022-04-04 10:40:02 +00:00
func hlprompt ( t * rt . Thread , c * rt . GoCont ) ( rt . Cont , error ) {
err := c . Check1Arg ( )
2022-04-13 14:13:46 +00:00
if err != nil {
return nil , err
2022-04-04 10:40:02 +00:00
}
2022-04-13 14:13:46 +00:00
p , err := c . StringArg ( 0 )
2022-04-04 10:40:02 +00:00
if err != nil {
return nil , err
}
2022-04-13 14:13:46 +00:00
typ := "left"
2022-04-18 03:39:53 +00:00
// optional 2nd arg
2022-04-13 14:13:46 +00:00
if len ( c . Etc ( ) ) != 0 {
ltyp := c . Etc ( ) [ 0 ]
var ok bool
typ , ok = ltyp . TryString ( )
if ! ok {
return nil , errors . New ( "bad argument to run (expected string, got " + ltyp . TypeName ( ) + ")" )
}
}
switch typ {
case "left" :
prompt = p
lr . SetPrompt ( fmtPrompt ( prompt ) )
case "right" : lr . SetRightPrompt ( fmtPrompt ( p ) )
default : return nil , errors . New ( "expected prompt type to be right or left, got " + typ )
}
2022-01-26 19:51:52 +00:00
2022-04-04 10:40:02 +00:00
return c . Next ( ) , nil
2022-01-26 19:51:52 +00:00
}
// multiprompt(str)
// Changes the continued line prompt to `str`
2022-02-26 15:36:04 +00:00
// --- @param str string
2022-04-04 10:40:02 +00:00
func hlmultiprompt ( t * rt . Thread , c * rt . GoCont ) ( rt . Cont , error ) {
if err := c . Check1Arg ( ) ; err != nil {
return nil , err
}
prompt , err := c . StringArg ( 0 )
if err != nil {
return nil , err
}
multilinePrompt = prompt
2022-01-26 19:51:52 +00:00
2022-04-04 10:40:02 +00:00
return c . Next ( ) , nil
2022-01-26 19:51:52 +00:00
}
// alias(cmd, orig)
2022-03-20 21:54:02 +00:00
// Sets an alias of `cmd` to `orig`
2022-02-26 15:36:04 +00:00
// --- @param cmd string
// --- @param orig string
2022-04-04 10:40:02 +00:00
func hlalias ( t * rt . Thread , c * rt . GoCont ) ( rt . Cont , error ) {
if err := c . CheckNArgs ( 2 ) ; err != nil {
return nil , err
}
cmd , err := c . StringArg ( 0 )
if err != nil {
return nil , err
}
orig , err := c . StringArg ( 1 )
if err != nil {
return nil , err
}
2022-01-26 19:51:52 +00:00
2022-04-04 10:40:02 +00:00
aliases . Add ( cmd , orig )
2022-01-26 19:51:52 +00:00
2022-04-04 10:40:02 +00:00
return c . Next ( ) , nil
2022-01-26 19:51:52 +00:00
}
// appendPath(dir)
// Appends `dir` to $PATH
2022-02-26 15:36:04 +00:00
// --- @param dir string|table
2022-04-04 10:40:02 +00:00
func hlappendPath ( t * rt . Thread , c * rt . GoCont ) ( rt . Cont , error ) {
if err := c . Check1Arg ( ) ; err != nil {
return nil , err
}
arg := c . Arg ( 0 )
2022-01-26 19:51:52 +00:00
// check if dir is a table or a string
2022-04-04 10:40:02 +00:00
if arg . Type ( ) == rt . TableType {
2022-04-21 18:01:59 +00:00
util . ForEach ( arg . AsTable ( ) , func ( k rt . Value , v rt . Value ) {
if v . Type ( ) == rt . StringType {
appendPath ( v . AsString ( ) )
2022-04-04 10:40:02 +00:00
}
2022-04-21 18:01:59 +00:00
} )
2022-04-04 10:40:02 +00:00
} else if arg . Type ( ) == rt . StringType {
appendPath ( arg . AsString ( ) )
2022-01-26 19:51:52 +00:00
} else {
2022-04-04 10:40:02 +00:00
return nil , errors . New ( "bad argument to appendPath (expected string or table, got " + arg . TypeName ( ) + ")" )
2022-01-26 19:51:52 +00:00
}
2022-04-04 10:40:02 +00:00
return c . Next ( ) , nil
2022-01-26 19:51:52 +00:00
}
func appendPath ( dir string ) {
dir = strings . Replace ( dir , "~" , curuser . HomeDir , 1 )
pathenv := os . Getenv ( "PATH" )
// if dir isnt already in $PATH, add it
if ! strings . Contains ( pathenv , dir ) {
os . Setenv ( "PATH" , pathenv + string ( os . PathListSeparator ) + dir )
}
}
// exec(cmd)
// Replaces running hilbish with `cmd`
2022-02-26 15:36:04 +00:00
// --- @param cmd string
2022-04-04 10:40:02 +00:00
func hlexec ( t * rt . Thread , c * rt . GoCont ) ( rt . Cont , error ) {
if err := c . Check1Arg ( ) ; err != nil {
return nil , err
}
cmd , err := c . StringArg ( 0 )
if err != nil {
return nil , err
}
2022-01-26 19:51:52 +00:00
cmdArgs , _ := splitInput ( cmd )
2022-03-02 02:14:55 +00:00
if runtime . GOOS != "windows" {
cmdPath , err := exec . LookPath ( cmdArgs [ 0 ] )
if err != nil {
fmt . Println ( err )
// if we get here, cmdPath will be nothing
// therefore nothing will run
}
// syscall.Exec requires an absolute path to a binary
// path, args, string slice of environments
syscall . Exec ( cmdPath , cmdArgs , os . Environ ( ) )
} else {
cmd := exec . Command ( cmdArgs [ 0 ] , cmdArgs [ 1 : ] ... )
cmd . Stdout = os . Stdout
cmd . Stderr = os . Stderr
cmd . Stdin = os . Stdin
cmd . Run ( )
os . Exit ( 0 )
2022-01-26 19:51:52 +00:00
}
2022-04-04 10:40:02 +00:00
return c . Next ( ) , nil
2022-01-26 19:51:52 +00:00
}
// goro(fn)
// Puts `fn` in a goroutine
2022-02-26 15:36:04 +00:00
// --- @param fn function
2022-04-04 10:40:02 +00:00
func hlgoro ( t * rt . Thread , c * rt . GoCont ) ( rt . Cont , error ) {
if err := c . Check1Arg ( ) ; err != nil {
return nil , err
}
fn , err := c . ClosureArg ( 0 )
if err != nil {
return nil , err
2022-01-26 19:51:52 +00:00
}
// call fn
go func ( ) {
2022-04-04 10:40:02 +00:00
_ , err := rt . Call1 ( l . MainThread ( ) , rt . FunctionValue ( fn ) , c . Etc ( ) ... )
if err != nil {
2022-03-05 02:21:34 +00:00
fmt . Fprintln ( os . Stderr , "Error in goro function:\n\n" , err )
}
2022-01-26 19:51:52 +00:00
} ( )
2022-04-04 10:40:02 +00:00
return c . Next ( ) , nil
2022-01-26 19:51:52 +00:00
}
// timeout(cb, time)
// Runs the `cb` function after `time` in milliseconds
2022-04-12 23:37:15 +00:00
// Returns a `timer` object (see `doc timers`).
2022-02-26 15:36:04 +00:00
// --- @param cb function
// --- @param time number
2022-04-12 23:37:15 +00:00
// --- @return table
2022-04-04 10:40:02 +00:00
func hltimeout ( t * rt . Thread , c * rt . GoCont ) ( rt . Cont , error ) {
if err := c . CheckNArgs ( 2 ) ; err != nil {
return nil , err
}
cb , err := c . ClosureArg ( 0 )
if err != nil {
return nil , err
}
ms , err := c . IntArg ( 1 )
if err != nil {
return nil , err
}
2022-01-26 19:51:52 +00:00
2022-04-12 23:28:25 +00:00
interval := time . Duration ( ms ) * time . Millisecond
timer := timers . create ( timerTimeout , interval , cb )
timer . start ( )
return c . PushingNext1 ( t . Runtime , timer . lua ( ) ) , nil
2022-01-26 19:51:52 +00:00
}
// interval(cb, time)
2022-04-12 23:37:15 +00:00
// Runs the `cb` function every `time` milliseconds.
// Returns a `timer` object (see `doc timers`).
2022-02-26 15:36:04 +00:00
// --- @param cb function
// --- @param time number
2022-04-12 23:37:15 +00:00
// --- @return table
2022-04-04 10:40:02 +00:00
func hlinterval ( t * rt . Thread , c * rt . GoCont ) ( rt . Cont , error ) {
if err := c . CheckNArgs ( 2 ) ; err != nil {
return nil , err
}
cb , err := c . ClosureArg ( 0 )
if err != nil {
return nil , err
}
ms , err := c . IntArg ( 1 )
if err != nil {
return nil , err
}
2022-01-26 19:51:52 +00:00
2022-04-12 23:28:25 +00:00
interval := time . Duration ( ms ) * time . Millisecond
2022-04-12 23:41:50 +00:00
timer := timers . create ( timerInterval , interval , cb )
2022-04-12 23:28:25 +00:00
timer . start ( )
2022-01-26 19:51:52 +00:00
2022-04-12 23:28:25 +00:00
return c . PushingNext1 ( t . Runtime , timer . lua ( ) ) , nil
2022-01-26 19:51:52 +00:00
}
// complete(scope, cb)
// Registers a completion handler for `scope`.
// A `scope` is currently only expected to be `command.<cmd>`,
// replacing <cmd> with the name of the command (for example `command.git`).
2022-03-05 20:12:46 +00:00
// `cb` must be a function that returns a table of "completion groups."
2022-04-23 01:25:39 +00:00
// Check `doc completions` for more information.
2022-03-02 02:12:48 +00:00
// --- @param scope string
// --- @param cb function
2022-04-04 10:40:02 +00:00
func hlcomplete ( t * rt . Thread , c * rt . GoCont ) ( rt . Cont , error ) {
scope , cb , err := util . HandleStrCallback ( t , c )
if err != nil {
return nil , err
}
2022-01-26 19:51:52 +00:00
luaCompletions [ scope ] = cb
2022-04-04 10:40:02 +00:00
return c . Next ( ) , nil
2022-01-26 19:51:52 +00:00
}
// prependPath(dir)
// Prepends `dir` to $PATH
2022-03-02 02:12:48 +00:00
// --- @param dir string
2022-04-04 10:40:02 +00:00
func hlprependPath ( t * rt . Thread , c * rt . GoCont ) ( rt . Cont , error ) {
if err := c . Check1Arg ( ) ; err != nil {
return nil , err
}
dir , err := c . StringArg ( 0 )
if err != nil {
return nil , err
}
2022-01-26 19:51:52 +00:00
dir = strings . Replace ( dir , "~" , curuser . HomeDir , 1 )
pathenv := os . Getenv ( "PATH" )
// if dir isnt already in $PATH, add in
if ! strings . Contains ( pathenv , dir ) {
os . Setenv ( "PATH" , dir + string ( os . PathListSeparator ) + pathenv )
}
2022-04-04 10:40:02 +00:00
return c . Next ( ) , nil
2022-01-26 19:51:52 +00:00
}
2022-03-01 22:59:44 +00:00
2022-04-22 02:16:04 +00:00
// which(name)
// Checks if `name` is a valid command
2022-03-02 02:12:48 +00:00
// --- @param binName string
2022-04-04 10:40:02 +00:00
func hlwhich ( t * rt . Thread , c * rt . GoCont ) ( rt . Cont , error ) {
if err := c . Check1Arg ( ) ; err != nil {
return nil , err
}
2022-04-22 02:16:04 +00:00
name , err := c . StringArg ( 0 )
2022-04-04 10:40:02 +00:00
if err != nil {
return nil , err
}
2022-04-22 02:16:04 +00:00
cmd := aliases . Resolve ( name )
// check for commander
if commands [ cmd ] != nil {
// they dont resolve to a path, so just send the cmd
return c . PushingNext1 ( t . Runtime , rt . StringValue ( cmd ) ) , nil
}
path , err := exec . LookPath ( cmd )
2022-03-01 22:59:44 +00:00
if err != nil {
2022-04-04 10:40:02 +00:00
return c . Next ( ) , nil
2022-03-01 22:59:44 +00:00
}
2022-03-05 19:46:38 +00:00
2022-04-04 10:40:02 +00:00
return c . PushingNext1 ( t . Runtime , rt . StringValue ( path ) ) , nil
2022-03-01 22:59:44 +00:00
}
2022-03-02 02:00:46 +00:00
// inputMode(mode)
2022-04-21 16:19:11 +00:00
// Sets the input mode for Hilbish's line reader. Accepts either emacs or vim
2022-03-02 02:12:48 +00:00
// --- @param mode string
2022-04-04 10:40:02 +00:00
func hlinputMode ( t * rt . Thread , c * rt . GoCont ) ( rt . Cont , error ) {
if err := c . Check1Arg ( ) ; err != nil {
return nil , err
}
mode , err := c . StringArg ( 0 )
if err != nil {
return nil , err
}
2022-03-02 02:00:46 +00:00
switch mode {
case "emacs" :
unsetVimMode ( )
lr . rl . InputMode = readline . Emacs
case "vim" :
setVimMode ( "insert" )
lr . rl . InputMode = readline . Vim
2022-04-04 10:40:02 +00:00
default :
return nil , errors . New ( "inputMode: expected vim or emacs, received " + mode )
2022-03-02 02:00:46 +00:00
}
2022-04-04 10:40:02 +00:00
return c . Next ( ) , nil
2022-03-02 02:00:46 +00:00
}
2022-03-20 19:15:44 +00:00
// runnerMode(mode)
// Sets the execution/runner mode for interactive Hilbish. This determines whether
// Hilbish wll try to run input as Lua and/or sh or only do one of either.
// Accepted values for mode are hybrid (the default), hybridRev (sh first then Lua),
// sh, and lua. It also accepts a function, to which if it is passed one
// will call it to execute user input instead.
2022-03-22 22:33:11 +00:00
// --- @param mode string|function
2022-04-04 10:40:02 +00:00
func hlrunnerMode ( t * rt . Thread , c * rt . GoCont ) ( rt . Cont , error ) {
if err := c . Check1Arg ( ) ; err != nil {
return nil , err
}
mode := c . Arg ( 0 )
2022-03-20 19:15:44 +00:00
switch mode . Type ( ) {
2022-04-04 10:40:02 +00:00
case rt . StringType :
switch mode . AsString ( ) {
case "hybrid" , "hybridRev" , "lua" , "sh" : runnerMode = mode
default : return nil , errors . New ( "execMode: expected either a function or hybrid, hybridRev, lua, sh. Received " + mode . AsString ( ) )
2022-03-20 19:15:44 +00:00
}
2022-04-04 10:40:02 +00:00
case rt . FunctionType : runnerMode = mode
default : return nil , errors . New ( "execMode: expected either a function or hybrid, hybridRev, lua, sh. Received " + mode . TypeName ( ) )
2022-03-20 19:15:44 +00:00
}
2022-04-04 10:40:02 +00:00
return c . Next ( ) , nil
2022-03-20 19:15:44 +00:00
}
2022-03-26 21:34:09 +00:00
2022-04-22 02:19:27 +00:00
// hinter(line, pos)
// The command line hint handler. It gets called on every key insert to
// determine what text to use as an inline hint. It is passed the current
// line and cursor position. It is expected to return a string which is used
// as the text for the hint. This is by default a shim. To set hints,
// override this function with your custom handler.
// --- @param line string
// --- @param pos int
2022-04-04 10:40:02 +00:00
func hlhinter ( t * rt . Thread , c * rt . GoCont ) ( rt . Cont , error ) {
2022-04-22 02:19:27 +00:00
return c . Next ( ) , nil
2022-03-26 21:34:09 +00:00
}
2022-03-26 22:25:19 +00:00
2022-04-22 02:19:27 +00:00
// highlighter(line)
// Line highlighter handler. This is mainly for syntax highlighting, but in
// reality could set the input of the prompt to *display* anything. The
// callback is passed the current line and is expected to return a line that
// will be used as the input display.
// --- @param line string
2022-04-04 10:40:02 +00:00
func hlhighlighter ( t * rt . Thread , c * rt . GoCont ) ( rt . Cont , error ) {
2022-04-22 02:19:27 +00:00
return c . Next ( ) , nil
2022-03-26 22:25:19 +00:00
}