Compare commits

..

No commits in common. "a8ecd44efb9098d031771d258d1c87fe4068ae36" and "6b065dc035da73f5f80d60dca121c925b6f55acf" have entirely different histories.

16 changed files with 64 additions and 234 deletions

6
.gitmodules vendored
View File

@ -1,9 +1,3 @@
[submodule "libs/lunacolors"] [submodule "libs/lunacolors"]
path = libs/lunacolors path = libs/lunacolors
url = https://github.com/Hilbis/Lunacolors url = https://github.com/Hilbis/Lunacolors
[submodule "libs/succulent"]
path = libs/succulent
url = https://github.com/Rosettea/Succulent
[submodule "libs/inspect"]
path = libs/inspect
url = https://github.com/kikito/inspect.lua

View File

@ -2,42 +2,6 @@
This is the changelog for the Hilbish shell made in Go and Lua. This is the changelog for the Hilbish shell made in Go and Lua.
## [0.6.0] - 2021-10-17
## Added
- Hilbish will expand `~` in the preloadPath and samplePathConf variables. These are for compile time.
- On Windows, the hostname in `%u` has been removed.
- Made it easier to compile on Windows by adding Windows-tailored vars and paths.
- Add require paths `./libs/?/?.lua`
- Hilbish will now respect $XDG_CONFIG_HOME and will load its config and history there first and use Lua libraries in there and $XDG_DATA_HOME if they are set. (#71)
- If not, Hilbish will still default to `~`
- Added some new hooks
- `command.precmd` is thrown right before Hilbish prompts for input
- `command.preexec` is thrown right before Hilbish executes a command. It passes 2 arguments: the command as the user typed, and what Hilbish will actually execute (resolved alias)
- `hilbish.dataDir` is now available to know the directory of Hilbish data files (default config, docs, preload, etc)
- A `docgen` program has been added to `cmd/docgen` in the GitHub repository, As the name suggests, it will output docs in a `docs` folder for functions implemented in Go
- All hilbish modules/libraries now have a `__doc` metatable entry which is simply a short description of the module.
- `fs.readdir(dir)` has been added. It will return a table of files in `dir`
- Errors in the `fs.mkdir` function are now handled.
- **Breaking Change:** `fs.cd` no longer returns a numeric code to indicate error. Instead, it returns an error message.
- The `doc` command has been added to document functions of Hilbish libraries. Run the command for more details.
- `link(url, text)` has been added to `ansikit`. It returns a string which can be printed to produce a hyperlink in a terminal. Note that not all terminals support this feature.
- The [Succulent](https://github.com/Rosettea/Succulent) library has been added. This includes more utility functions and expansions to the Lua standard library itself.
- The command string is now passed to the `command.exit` hook
# Changed
- Hilbish won't print an extra newline at exit with ctrl + d
- `command.exit` with 0 exit code will now be thrown if input is nothing
- **Breaking Change:** `fs.stat` has been made better. It returns a proper table instead of userdata, and has fields instead of functions
- It includes `name`, `mode` as a octal representation in a string, `isDir`, and `size`
# Fixed
- `timeout()` is now blocking
- Directories with spaces in them can now be `cd`'d to
- An alias with the same name as the command will now not cause a freeze (#73)
- Userdata is no longer returned in the following cases:
- Commander arguments
- `fs` functions
## [0.5.1] - 2021-06-16 ## [0.5.1] - 2021-06-16
## Added ## Added
@ -239,7 +203,6 @@ This input for example will prompt for more input to complete:
First "stable" release of Hilbish. First "stable" release of Hilbish.
[0.6.0]: https://github.com/Rosettea/Hilbish/compare/v0.5.1...v0.6.0
[0.5.1]: https://github.com/Rosettea/Hilbish/compare/v0.5.0...v0.5.1 [0.5.1]: https://github.com/Rosettea/Hilbish/compare/v0.5.0...v0.5.1
[0.5.0]: https://github.com/Rosettea/Hilbish/compare/v0.4.0...v0.5.0 [0.5.0]: https://github.com/Rosettea/Hilbish/compare/v0.4.0...v0.5.0
[0.4.0]: https://github.com/Rosettea/Hilbish/compare/v0.3.2...v0.4.0 [0.4.0]: https://github.com/Rosettea/Hilbish/compare/v0.3.2...v0.4.0

View File

@ -43,7 +43,6 @@ func main() {
"fs": "f", "fs": "f",
"commander": "c", "commander": "c",
"bait": "b", "bait": "b",
"terminal": "term",
} }
docs := make(map[string][]string) docs := make(map[string][]string)

View File

@ -1,9 +0,0 @@
setRaw() > Puts the terminal in raw mode
restoreState() > Restores the last saved state of the terminal
saveState() > Saves the current state of the terminal
size() > Gets the dimensions of the terminal. Returns a table with `width` and `height`
Note: this is not the size in relation to the dimensions of the display

View File

@ -9,6 +9,7 @@ import (
"hilbish/util" "hilbish/util"
"github.com/yuin/gopher-lua" "github.com/yuin/gopher-lua"
"layeh.com/gopher-luar"
) )
func Loader(L *lua.LState) int { func Loader(L *lua.LState) int {
@ -22,6 +23,10 @@ addition to the Lua standard library's I/O and fs functions.`)
return 1 return 1
} }
func luaErr(L *lua.LState, msg string) {
L.Error(lua.LString(msg), 2)
}
var exports = map[string]lua.LGFunction{ var exports = map[string]lua.LGFunction{
"cd": fcd, "cd": fcd,
"mkdir": fmkdir, "mkdir": fmkdir,
@ -37,7 +42,7 @@ func fcd(L *lua.LState) int {
err := os.Chdir(strings.TrimSpace(path)) err := os.Chdir(strings.TrimSpace(path))
if err != nil { if err != nil {
e := err.(*os.PathError).Err.Error() e := err.(*os.PathError).Err.Error()
L.RaiseError(e) luaErr(L, e)
} }
return 0 return 0
@ -57,7 +62,7 @@ func fmkdir(L *lua.LState) int {
err = os.Mkdir(path, 0744) err = os.Mkdir(path, 0744)
} }
if err != nil { if err != nil {
L.RaiseError(err.Error()) luaErr(L, err.Error())
} }
return 0 return 0
@ -70,7 +75,7 @@ func fstat(L *lua.LState) int {
pathinfo, err := os.Stat(path) pathinfo, err := os.Stat(path)
if err != nil { if err != nil {
L.RaiseError(err.Error()) luaErr(L, err.Error())
return 0 return 0
} }
statTbl := L.NewTable() statTbl := L.NewTable()
@ -87,18 +92,18 @@ func fstat(L *lua.LState) int {
// Returns a table of files in `dir` // Returns a table of files in `dir`
func freaddir(L *lua.LState) int { func freaddir(L *lua.LState) int {
dir := L.CheckString(1) dir := L.CheckString(1)
names := L.NewTable() names := []string{}
dirEntries, err := os.ReadDir(dir) dirEntries, err := os.ReadDir(dir)
if err != nil { if err != nil {
L.RaiseError(err.Error()) luaErr(L, err.Error())
return 0 return 0
} }
for _, entry := range dirEntries { for _, entry := range dirEntries {
names.Append(lua.LString(entry.Name())) names = append(names, entry.Name())
} }
L.Push(names) L.Push(luar.New(L, names))
return 1 return 1
} }

View File

@ -1,80 +0,0 @@
package terminal
import (
"os"
"hilbish/util"
"golang.org/x/term"
"github.com/yuin/gopher-lua"
)
var termState *term.State
func Loader(L *lua.LState) int {
mod := L.SetFuncs(L.NewTable(), exports)
util.Document(L, mod, "The terminal library is a simple and lower level library for certain terminal interactions.")
L.Push(mod)
return 1
}
var exports = map[string]lua.LGFunction{
"setRaw": termraw,
"restoreState": termrestoreState,
"size": termsize,
"saveState": termsaveState,
}
// size()
// Gets the dimensions of the terminal. Returns a table with `width` and `height`
// Note: this is not the size in relation to the dimensions of the display
func termsize(L *lua.LState) int {
w, h, err := term.GetSize(int(os.Stdin.Fd()))
if err != nil {
L.RaiseError(err.Error())
return 0
}
dimensions := L.NewTable()
L.SetField(dimensions, "width", lua.LNumber(w))
L.SetField(dimensions, "height", lua.LNumber(h))
L.Push(dimensions)
return 1
}
// saveState()
// Saves the current state of the terminal
func termsaveState(L *lua.LState) int {
state, err := term.GetState(int(os.Stdin.Fd()))
if err != nil {
L.RaiseError(err.Error())
return 0
}
termState = state
return 0
}
// restoreState()
// Restores the last saved state of the terminal
func termrestoreState(L *lua.LState) int {
err := term.Restore(int(os.Stdin.Fd()), termState)
if err != nil {
L.RaiseError(err.Error())
}
return 0
}
// setRaw()
// Puts the terminal in raw mode
func termraw(L *lua.LState) int {
_, err := term.MakeRaw(int(os.Stdin.Fd()))
if err != nil {
L.RaiseError(err.Error())
}
return 0
}

View File

@ -35,7 +35,6 @@ func HilbishLoader(L *lua.LState) int {
L.SetField(mod, "user", lua.LString(username)) L.SetField(mod, "user", lua.LString(username))
L.SetField(mod, "host", lua.LString(host)) L.SetField(mod, "host", lua.LString(host))
L.SetField(mod, "home", lua.LString(homedir)) L.SetField(mod, "home", lua.LString(homedir))
L.SetField(mod, "dataDir", lua.LString(dataDir))
xdg := L.NewTable() xdg := L.NewTable()
L.SetField(xdg, "config", lua.LString(confDir)) L.SetField(xdg, "config", lua.LString(confDir))

View File

@ -1,7 +1,7 @@
-- We're basically porting Ansikit to lua -- We're basically porting Ansikit to lua
-- https://github.com/Luvella/AnsiKit/blob/master/lib/index.js -- https://github.com/Luvella/AnsiKit/blob/master/lib/index.js
-- which is made by yours truly sammy :^) -- which is made by yours truly sammy :^)
local lunacolors = require 'lunacolors'
local ansikit = {} local ansikit = {}
ansikit.clear = function(scrollback) ansikit.clear = function(scrollback)
@ -77,12 +77,6 @@ ansikit.hideCursor = function()
return ansikit.printCSI('?25', 'l') return ansikit.printCSI('?25', 'l')
end end
ansikit.link = function(url, text)
if not url then error 'ansikit: missing url for hyperlink' end
local text = (text and text or 'link')
return lunacolors.blue('\27]8;;' .. url .. '\27\\' .. text .. '\27]8;;\27\\\n')
end
ansikit.print = function(text) ansikit.print = function(text)
io.write(ansikit.format(text)) io.write(ansikit.format(text))
return ansikit return ansikit

@ -1 +0,0 @@
Subproject commit 96dc95c24989be6596ccb9f7f7244d37f03d4acd

4
lua.go
View File

@ -11,7 +11,6 @@ import (
"hilbish/golibs/bait" "hilbish/golibs/bait"
"hilbish/golibs/commander" "hilbish/golibs/commander"
"hilbish/golibs/fs" "hilbish/golibs/fs"
"hilbish/golibs/terminal"
"github.com/yuin/gopher-lua" "github.com/yuin/gopher-lua"
"layeh.com/gopher-luar" "layeh.com/gopher-luar"
@ -41,9 +40,8 @@ func LuaInit() {
l.PreloadModule("hilbish", HilbishLoader) l.PreloadModule("hilbish", HilbishLoader)
l.DoString("hilbish = require 'hilbish'") l.DoString("hilbish = require 'hilbish'")
// Add fs and terminal module module to Lua // Add fs module to Lua
l.PreloadModule("fs", fs.Loader) l.PreloadModule("fs", fs.Loader)
l.PreloadModule("terminal", terminal.Loader)
cmds := commander.New() cmds := commander.New()
// When a command from Lua is added, register it for use // When a command from Lua is added, register it for use

View File

@ -175,6 +175,7 @@ input:
if err == io.EOF { if err == io.EOF {
// Exit if user presses ^D (ctrl + d) // Exit if user presses ^D (ctrl + d)
fmt.Println("")
break break
} }
if err != nil { if err != nil {

View File

@ -3,17 +3,19 @@
local fs = require 'fs' local fs = require 'fs'
local commander = require 'commander' local commander = require 'commander'
local bait = require 'bait' local bait = require 'bait'
require 'succulent' -- Function additions
local oldDir = hilbish.cwd() local oldDir = hilbish.cwd()
local shlvl = tonumber(os.getenv 'SHLVL') local shlvl = tonumber(os.getenv 'SHLVL')
if shlvl ~= nil then os.setenv('SHLVL', shlvl + 1) else os.setenv('SHLVL', 1) end if shlvl ~= nil then os.setenv('SHLVL', shlvl + 1) else os.setenv('SHLVL', 1) end
-- Builtins -- Builtins
local recentDirs = {}
commander.register('cd', function (args) commander.register('cd', function (args)
if #args > 0 then if #args > 0 then
local path = table.concat(args, ' '):gsub('$%$','\0'):gsub('${([%w_]+)}', os.getenv) local path = ''
for i = 1, #args do
path = path .. tostring(args[i]) .. ' '
end
path = path:gsub('$%$','\0'):gsub('${([%w_]+)}', os.getenv)
:gsub('$([%w_]+)', os.getenv):gsub('%z','$'):gsub('^%s*(.-)%s*$', '%1') :gsub('$([%w_]+)', os.getenv):gsub('%z','$'):gsub('^%s*(.-)%s*$', '%1')
if path == '-' then if path == '-' then
@ -24,15 +26,12 @@ commander.register('cd', function (args)
local ok, err = pcall(function() fs.cd(path) end) local ok, err = pcall(function() fs.cd(path) end)
if not ok then if not ok then
print(err:sub(17)) if err == 1 then
return 1 print('directory does not exist')
end
return err
end end
bait.throw('cd', path) bait.throw('cd', path)
-- add to table of recent dirs
table.insert(recentDirs, 1, path)
recentDirs[11] = nil
return return
end end
fs.cd(hilbish.home) fs.cd(hilbish.home)
@ -50,7 +49,11 @@ commander.register('doc', function(args)
local globalDesc = [[ local globalDesc = [[
These are the global Hilbish functions that are always available and not part of a module.]] These are the global Hilbish functions that are always available and not part of a module.]]
if #args > 0 then if #args > 0 then
local mod = table.concat(args, ' '):gsub('^%s*(.-)%s*$', '%1') local mod = ''
for i = 1, #args do
mod = mod .. tostring(args[i]) .. ' '
end
mod = mod:gsub('^%s*(.-)%s*$', '%1')
local f = io.open(moddocPath .. mod .. '.txt', 'rb') local f = io.open(moddocPath .. mod .. '.txt', 'rb')
if not f then if not f then
@ -66,16 +69,14 @@ These are the global Hilbish functions that are always available and not part of
if backtickOccurence % 2 == 0 then if backtickOccurence % 2 == 0 then
return '{reset}' return '{reset}'
else else
return '{underline}{green}' return '{invert}'
end end
end))) end)))
f:close() f:close()
return return
end end
local modules = table.map(fs.readdir(moddocPath), function(f) local modules = fs.readdir(moddocPath)
return lunacolors.underline(lunacolors.blue(f:sub(1, -5)))
end)
io.write [[ io.write [[
Welcome to Hilbish's doc tool! Here you can find documentation for builtin Welcome to Hilbish's doc tool! Here you can find documentation for builtin
@ -85,7 +86,11 @@ Usage: doc <module>
Available modules: ]] Available modules: ]]
print(table.concat(modules, ', ')) local mods = ''
for i = 1, #modules do
mods = mods .. tostring(modules[i]):gsub('.txt', '') .. ', '
end
print(mods)
return return
end) end)
@ -126,41 +131,24 @@ do
end) end)
end end
commander.register('cdr', function(args) -- Function additions to Lua standard library
if not args[1] then function string.split(str, delimiter)
print(lunacolors.format [[ local result = {}
cdr: change directory to one which has been recently visied local from = 1
usage: cdr <index> local delim_from, delim_to = string.find(str, delimiter, from)
to get a list of recent directories, use {green}{underline}cdr list{reset}]]) while delim_from do
return table.insert(result, string.sub(str, from, delim_from - 1))
from = delim_to + 1
delim_from, delim_to = string.find(str, delimiter, from)
end end
if args[1] == 'list' then table.insert(result, string.sub(str, from))
if #recentDirs == 0 then
print 'No directories have been visited.'
return 1
end
print(table.concat(recentDirs, '\n'))
return
end
local index = tonumber(args[1]) return result
if not index then
print(string.format('received %s as index, which isn\'t a number', index))
return 1
end end
if not recentDirs[index] then
print(string.format('no recent directory found at index %s', index))
return 1
end
fs.cd(recentDirs[index])
return
end)
-- Hook handles -- Hook handles
bait.catch('command.not-found', function(cmd) bait.catch('command.not-found', function(cmd)
print(string.format('hilbish: %s not found', cmd)) print(string.format('hilbish: %s not found', cmd))

View File

@ -10,6 +10,7 @@ import (
// "github.com/bobappleyard/readline" // "github.com/bobappleyard/readline"
"github.com/yuin/gopher-lua" "github.com/yuin/gopher-lua"
// "github.com/yuin/gopher-lua/parse" // "github.com/yuin/gopher-lua/parse"
"layeh.com/gopher-luar"
"mvdan.cc/sh/v3/interp" "mvdan.cc/sh/v3/interp"
"mvdan.cc/sh/v3/syntax" "mvdan.cc/sh/v3/syntax"
) )
@ -50,24 +51,19 @@ func RunInput(input string) {
err = l.PCall(0, lua.MultRet, nil) err = l.PCall(0, lua.MultRet, nil)
} }
if err == nil { if err == nil {
cmdFinish(0, cmdString) hooks.Em.Emit("command.exit", 0)
return return
} }
if commands[cmdArgs[0]] != nil { if commands[cmdArgs[0]] != nil {
luacmdArgs := l.NewTable()
for _, str := range cmdArgs[1:] {
luacmdArgs.Append(lua.LString(str))
}
err := l.CallByParam(lua.P{ err := l.CallByParam(lua.P{
Fn: commands[cmdArgs[0]], Fn: commands[cmdArgs[0]],
NRet: 1, NRet: 1,
Protect: true, Protect: true,
}, luacmdArgs) }, luar.New(l, cmdArgs[1:]))
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, fmt.Fprintln(os.Stderr,
"Error in command:\n\n" + err.Error()) "Error in command:\n\n" + err.Error())
cmdFinish(1, cmdString) hooks.Em.Emit("command.exit", 1)
return return
} }
luaexitcode := l.Get(-1) luaexitcode := l.Get(-1)
@ -79,7 +75,7 @@ func RunInput(input string) {
exitcode = uint8(code) exitcode = uint8(code)
} }
cmdFinish(exitcode, cmdString) hooks.Em.Emit("command.exit", exitcode)
return return
} }
@ -97,22 +93,22 @@ func RunInput(input string) {
if syntax.IsIncomplete(err) || strings.HasSuffix(input, "\\") { if syntax.IsIncomplete(err) || strings.HasSuffix(input, "\\") {
continue continue
} else if code, ok := interp.IsExitStatus(err); ok { } else if code, ok := interp.IsExitStatus(err); ok {
cmdFinish(code, cmdString) hooks.Em.Emit("command.exit", code)
} else if err != nil { } else if err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
cmdFinish(1, cmdString) hooks.Em.Emit("command.exit", 1)
} }
break break
} }
} else { } else {
if code, ok := interp.IsExitStatus(err); ok { if code, ok := interp.IsExitStatus(err); ok {
cmdFinish(code, cmdString) hooks.Em.Emit("command.exit", code)
} else { } else {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
} }
} }
} else { } else {
cmdFinish(0, cmdString) hooks.Em.Emit("command.exit", 0)
} }
} }
@ -128,32 +124,20 @@ func execCommand(cmd string) error {
_, argstring := splitInput(strings.Join(args, " ")) _, argstring := splitInput(strings.Join(args, " "))
// If alias was found, use command alias // If alias was found, use command alias
for aliases[args[0]] != "" { if aliases[args[0]] != "" {
alias := aliases[args[0]] alias := aliases[args[0]]
argstring = alias + strings.TrimPrefix(argstring, args[0]) argstring = alias + strings.TrimPrefix(argstring, args[0])
cmdArgs, _ := splitInput(argstring) cmdArgs, _ := splitInput(argstring)
args = cmdArgs args = cmdArgs
if aliases[args[0]] == alias {
break
}
if aliases[args[0]] != "" {
continue
}
} }
// If command is defined in Lua then run it // If command is defined in Lua then run it
luacmdArgs := l.NewTable()
for _, str := range args[1:] {
luacmdArgs.Append(lua.LString(str))
}
if commands[args[0]] != nil { if commands[args[0]] != nil {
err := l.CallByParam(lua.P{ err := l.CallByParam(lua.P{
Fn: commands[args[0]], Fn: commands[args[0]],
NRet: 1, NRet: 1,
Protect: true, Protect: true,
}, luacmdArgs) }, luar.New(l, args[1:]))
luaexitcode := l.Get(-1) luaexitcode := l.Get(-1)
var exitcode uint8 = 0 var exitcode uint8 = 0
@ -167,7 +151,7 @@ func execCommand(cmd string) error {
fmt.Fprintln(os.Stderr, fmt.Fprintln(os.Stderr,
"Error in command:\n\n" + err.Error()) "Error in command:\n\n" + err.Error())
} }
cmdFinish(exitcode, argstring) hooks.Em.Emit("command.exit", exitcode)
return interp.NewExitStatus(exitcode) return interp.NewExitStatus(exitcode)
} }
@ -240,6 +224,3 @@ func splitInput(input string) ([]string, string) {
return cmdArgs, cmdstr.String() return cmdArgs, cmdstr.String()
} }
func cmdFinish(code uint8, cmdstr string) {
hooks.Em.Emit("command.exit", code, cmdstr)
}

View File

@ -2,10 +2,9 @@ package main
// String vars that are free to be changed at compile time // String vars that are free to be changed at compile time
var ( var (
version = "v0.6.0" version = "v0.5.1"
defaultConfDir = "" // ~ will be substituted for home, path for user's default config defaultConfDir = "" // ~ will be substituted for home, path for user's default config
defaultHistDir = "" defaultHistDir = ""
commonRequirePaths = "';./libs/?/init.lua;./?/init.lua;./?/?.lua'"
prompt string // Prompt will always get changed anyway prompt string // Prompt will always get changed anyway
multilinePrompt = "> " multilinePrompt = "> "

View File

@ -4,10 +4,9 @@ package main
// String vars that are free to be changed at compile time // String vars that are free to be changed at compile time
var ( var (
requirePaths = commonRequirePaths + ` requirePaths = `';./libs/?/?.lua;./libs/?/init.lua;./?/init.lua;./?/?.lua'
.. ';/usr/share/hilbish/libs/?/init.lua;' .. ';/usr/share/hilbish/libs/?/init.lua;'
.. ';/usr/share/hilbish/libs/?/?.lua;'` + linuxUserPaths .. ';/usr/share/hilbish/libs/?/?.lua;'
linuxUserPaths = `
.. hilbish.xdg.data .. '/hilbish/libs/?/init.lua;' .. hilbish.xdg.data .. '/hilbish/libs/?/init.lua;'
.. hilbish.xdg.data .. '/hilbish/libs/?/?.lua;' .. hilbish.xdg.data .. '/hilbish/libs/?/?.lua;'
.. hilbish.xdg.data .. '/hilbish/libs/?.lua' .. hilbish.xdg.data .. '/hilbish/libs/?.lua'

View File

@ -4,7 +4,7 @@ package main
// String vars that are free to be changed at compile time // String vars that are free to be changed at compile time
var ( var (
requirePaths = commonRequirePaths + ` requirePaths = `';./libs/?/init.lua;./?/init.lua;./?/?.lua'
.. hilbish.home .. '\\Appdata\\Roaming\\Hilbish\\libs\\?\\init.lua;' .. hilbish.home .. '\\Appdata\\Roaming\\Hilbish\\libs\\?\\init.lua;'
.. hilbish.home .. '\\Appdata\\Roaming\\Hilbish\\libs\\?\\?.lua;'` .. hilbish.home .. '\\Appdata\\Roaming\\Hilbish\\libs\\?\\?.lua;'`
dataDir = "~\\Appdata\\Roaming\\Hilbish" // ~ and \ gonna cry? dataDir = "~\\Appdata\\Roaming\\Hilbish" // ~ and \ gonna cry?