diff --git a/Makefile b/Makefile index 660bc58..a0c6580 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ build: install: install -v -d "$(DESTDIR)$(BINDIR)/" && install -m 0755 -v hilbish "$(DESTDIR)$(BINDIR)/hilbish" mkdir -p "$(DESTDIR)$(LIBDIR)" - cp -r libs docs emmyLuaDocs prelude .hilbishrc.lua "$(DESTDIR)$(LIBDIR)" + cp -r libs docs emmyLuaDocs nature .hilbishrc.lua "$(DESTDIR)$(LIBDIR)" grep -qxF "$(DESTDIR)$(BINDIR)/hilbish" /etc/shells || echo "$(DESTDIR)$(BINDIR)/hilbish" >> /etc/shells uninstall: diff --git a/lua.go b/lua.go index 3b925c6..838ce8a 100644 --- a/lua.go +++ b/lua.go @@ -54,7 +54,7 @@ func luaInit() { fmt.Fprintln(os.Stderr, "Could not add preload paths! Libraries will be missing. This shouldn't happen.") } - err = util.DoFile(l, "prelude/init.lua") + err = util.DoFile(l, "nature/init.lua") if err != nil { err = util.DoFile(l, preloadPath) if err != nil { diff --git a/nature/commands/cd.lua b/nature/commands/cd.lua new file mode 100644 index 0000000..509e774 --- /dev/null +++ b/nature/commands/cd.lua @@ -0,0 +1,35 @@ +local bait = require 'bait' +local commander = require 'commander' +local fs = require 'fs' +local dirs = require 'nature.dirs' + +dirs.old = hilbish.cwd() +commander.register('cd', function (args) + if #args > 1 then + print("cd: too many arguments") + return 1 + elseif #args > 0 then + local path = args[1]:gsub('$%$','\0'):gsub('${([%w_]+)}', os.getenv) + :gsub('$([%w_]+)', os.getenv):gsub('%z','$'):gsub('^%s*(.-)%s*$', '%1') + + if path == '-' then + path = dirs.old + print(path) + end + dirs.setOld(hilbish.cwd()) + dirs.push(path) + + local ok, err = pcall(function() fs.cd(path) end) + if not ok then + print(err) + return 1 + end + bait.throw('cd', path) + + return + end + fs.cd(hilbish.home) + bait.throw('cd', hilbish.home) + + dirs.addRecent(hilbish.home) +end) diff --git a/nature/commands/cdr.lua b/nature/commands/cdr.lua new file mode 100644 index 0000000..0438e6f --- /dev/null +++ b/nature/commands/cdr.lua @@ -0,0 +1,39 @@ +local commander = require 'commander' +local fs = require 'fs' +local lunacolors = require 'lunacolors' +local dirs = require 'nature.dirs' + +commander.register('cdr', function(args) + if not args[1] then + print(lunacolors.format [[ +cdr: change directory to one which has been recently visied + +usage: cdr + +to get a list of recent directories, use {green}{underline}cdr list{reset}]]) + return + end + + if args[1] == 'list' then + local recentDirs = dirs.recentDirs + 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]) + if not index then + print(string.format('Received %s as index, which isn\'t a number.', index)) + return 1 + end + + if not dirs.recent(index) then + print(string.format('No recent directory found at index %s.', index)) + return 1 + end + + fs.cd(dirs.recent(index)) +end) diff --git a/nature/commands/doc.lua b/nature/commands/doc.lua new file mode 100644 index 0000000..34e81ae --- /dev/null +++ b/nature/commands/doc.lua @@ -0,0 +1,93 @@ +local commander = require 'commander' +local fs = require 'fs' +local lunacolors = require 'lunacolors' + +commander.register('doc', function(args) + local moddocPath = hilbish.dataDir .. '/docs/' + local modDocFormat = [[ +%s +%s +# Functions +]] + + if #args > 0 then + local mod = args[1] + + local f = io.open(moddocPath .. mod .. '.txt', 'rb') + local funcdocs = nil + if not f then + -- assume subdir + -- dataDir/docs//.txt + moddocPath = moddocPath .. mod .. '/' + local subdocName = args[2] + if not subdocName then + subdocName = 'index' + end + f = io.open(moddocPath .. subdocName .. '.txt', 'rb') + if not f then + print('No documentation found for ' .. mod .. '.') + return + end + funcdocs = f:read '*a' + local moddocs = table.filter(fs.readdir(moddocPath), function(f) return f ~= 'index.txt' end) + local subdocs = table.map(moddocs, function(fname) + return lunacolors.underline(lunacolors.blue(string.gsub(fname, '.txt', ''))) + end) + if subdocName == 'index' then + funcdocs = funcdocs .. '\nSubdocs: ' .. table.concat(subdocs, ', ') + end + end + + if not funcdocs then + funcdocs = f:read '*a' + end + local desc = '' + local ok = pcall(require, mod) + local backtickOccurence = 0 + local formattedFuncs = lunacolors.format(funcdocs:sub(1, #funcdocs - 1):gsub('`', function() + backtickOccurence = backtickOccurence + 1 + if backtickOccurence % 2 == 0 then + return '{reset}' + else + return '{underline}{green}' + end + end)) + + if ok then + local props = {} + local propstr = '' + local modDesc = '' + local modmt = getmetatable(require(mod)) + modDesc = modmt.__doc + if modmt.__docProp then + -- not all modules have docs for properties + props = table.map(modmt.__docProp, function(v, k) + return lunacolors.underline(lunacolors.blue(k)) .. ' > ' .. v + end) + end + if #props > 0 then + propstr = '\n# Properties\n' .. table.concat(props, '\n') .. '\n' + end + desc = string.format(modDocFormat, modDesc, propstr) + end + print(desc .. formattedFuncs) + f:close() + + return + end + local modules = table.map(fs.readdir(moddocPath), function(f) + return lunacolors.underline(lunacolors.blue(string.gsub(f, '.txt', ''))) + end) + + io.write [[ +Welcome to Hilbish's doc tool! Here you can find documentation for builtin +functions and other things. + +Usage: doc
[subdoc] +A section is a module or a literal section and a subdoc is a subsection for it. + +Available sections: ]] + io.flush() + + print(table.concat(modules, ', ')) +end) diff --git a/nature/commands/exit.lua b/nature/commands/exit.lua new file mode 100644 index 0000000..421730c --- /dev/null +++ b/nature/commands/exit.lua @@ -0,0 +1,7 @@ +local bait = require 'bait' +local commander = require 'commander' + +commander.register('exit', function() + bait.throw('hilbish.exit') + os.exit(0) +end) diff --git a/nature/commands/guide.lua b/nature/commands/guide.lua new file mode 100644 index 0000000..1c1e346 --- /dev/null +++ b/nature/commands/guide.lua @@ -0,0 +1,54 @@ +local ansikit = require 'ansikit' +local commander = require 'commander' + +local helpTexts = { +[[ +Hello there! Welcome to Hilbish, the comfy and nice little shell for +Lua users and fans. Hilbish is configured with Lua, and its +scripts are also in Lua. It also runs both Lua and shell script when +interactive (aka normal usage). +]], +[[ +What does that mean for you, the user? It means that if you prefer to +use Lua for scripting instead of shell script but still have ordinary +shell usage for interactive use. +]], +[[ +If this is your first time using Hilbish and Lua, check out the +Programming in Lua book here: https://www.lua.org/pil +After (or if you already know Lua) check out the doc command. +It is an in shell tool for documentation about Hilbish provided +functions and modules. +]], +[[ +If you've updated from a pre-1.0 version (0.7.1 as an example) +you'll want to move your config from ~/.hilbishrc.lua to +]] .. +hilbish.userDir.config .. '/hilbish/init.lua' .. +[[ + +and also change all global functions (prompt, alias) to be +in the hilbish module (hilbish.prompt, hilbish.alias as examples). + +And if this is your first time (most likely), you can copy a config +from ]] .. hilbish.dataDir, +[[ +Since 1.0 is a big release, you'll want to check the changelog +at https://github.com/Rosettea/Hilbish/releases/tag/v1.0.0 +to find more breaking changes. +]] +} +commander.register('guide', function() + ansikit.clear() + ansikit.cursorTo(0, 0) + for _, text in ipairs(helpTexts) do + print(text) + local out = hilbish.read('Hit enter to continue ') + ansikit.clear() + ansikit.cursorTo(0, 0) + if not out then + return + end + end + print 'Hope you enjoy using Hilbish!' +end) diff --git a/nature/commands/init.lua b/nature/commands/init.lua new file mode 100644 index 0000000..e824c7c --- /dev/null +++ b/nature/commands/init.lua @@ -0,0 +1,6 @@ +-- Add command builtins +require 'nature.commands.cd' +require 'nature.commands.cdr' +require 'nature.commands.doc' +require 'nature.commands.exit' +require 'nature.commands.guide' diff --git a/nature/dirs.lua b/nature/dirs.lua new file mode 100644 index 0000000..5b7ec86 --- /dev/null +++ b/nature/dirs.lua @@ -0,0 +1,75 @@ +local fs = require 'fs' + +local dirs = {} + +--- Last (current working) directory. Separate from recentDirs mainly for +--- easier use. +dirs.old = '' +--- Table of recent directories. For use, look at public functions. +dirs.recentDirs = {} +--- Size of the recentDirs table. +dirs.recentSize = 10 + +--- Get (and remove) a `num` of entries from recent directories. +--- @param num number +--- @param remove boolean Whether to remove items +function dirRecents(num, remove) + num = num or 1 + local entries = {} + + if #dirs.recentDirs ~= 0 then + for i = 1, num do + local idx = remove and 1 or i + if not dirs.recentDirs[idx] then break end + table.insert(entries, dirs.recentDirs[idx]) + if remove then table.remove(dirs.recentDirs, 1) end + end + end + + if #entries == 1 then + return entries[1] + end + + return entries +end + +--- Look at `num` amount of recent directories, starting from the latest. +--- @param num? number +function dirs.peak(num) + return dirRecents(num) +end + +--- Add `d` to the recent directories. +function dirs.push(d) + dirs.recentDirs[dirs.recentSize + 1] = nil + if dirs.recentDirs[#dirs.recentDirs - 1] ~= d then + ok, d = pcall(fs.abs, d) + assert(ok, 'could not turn "' .. d .. '"into an absolute path') + + table.insert(dirs.recentDirs, 1, d) + end +end + +--- Remove `num` amount of dirs from the recent directories. +--- @param num number +function dirs.pop(num) + return dirRecents(num, true) +end + +--- Get entry from recent directories. +--- @param idx number +function dirs.recent(idx) + return dirs.recentDirs[idx] +end + +--- Sets the old directory. +--- @param d string +function dirs.setOld(d) + ok, d = pcall(fs.abs, d) + assert(ok, 'could not turn "' .. d .. '"into an absolute path') + + os.setenv('OLDPWD', d) + dirs.old = d +end + +return dirs diff --git a/nature/hooks.lua b/nature/hooks.lua new file mode 100644 index 0000000..a9bd0b1 --- /dev/null +++ b/nature/hooks.lua @@ -0,0 +1,11 @@ +local bait = require 'bait' + +-- Hook handles +bait.catch('command.not-found', function(cmd) + print(string.format('hilbish: %s not found', cmd)) +end) + +bait.catch('command.not-executable', function(cmd) + print(string.format('hilbish: %s: not executable', cmd)) +end) + diff --git a/nature/init.lua b/nature/init.lua new file mode 100644 index 0000000..5e07298 --- /dev/null +++ b/nature/init.lua @@ -0,0 +1,44 @@ +-- Prelude initializes everything else for our shell +local _ = require 'succulent' -- Function additions + +package.path = package.path .. ';' .. hilbish.dataDir .. '/?/init.lua' +.. ';' .. hilbish.dataDir .. '/?/?.lua' + +require 'nature.hooks' +require 'nature.commands' + +local shlvl = tonumber(os.getenv 'SHLVL') +if shlvl ~= nil then + os.setenv('SHLVL', tostring(shlvl + 1)) +else + os.setenv('SHLVL', '0') +end + +do + local virt_G = { } + + setmetatable(_G, { + __index = function (_, key) + local got_virt = virt_G[key] + if got_virt ~= nil then + return got_virt + end + + virt_G[key] = os.getenv(key) + return virt_G[key] + end, + + __newindex = function (_, key, value) + if type(value) == 'string' then + os.setenv(key, value) + virt_G[key] = value + else + if type(virt_G[key]) == 'string' then + os.setenv(key, '') + end + virt_G[key] = value + end + end, + }) +end + diff --git a/prelude/init.lua b/prelude/init.lua deleted file mode 100644 index 21467b0..0000000 --- a/prelude/init.lua +++ /dev/null @@ -1,265 +0,0 @@ --- The preload file initializes everything else for our shell -local ansikit = require 'ansikit' -local bait = require 'bait' -local commander = require 'commander' -local fs = require 'fs' -local lunacolors = require 'lunacolors' -local _ = require 'succulent' -- Function additions -local oldDir = hilbish.cwd() - -local shlvl = tonumber(os.getenv 'SHLVL') -if shlvl ~= nil then os.setenv('SHLVL', tostring(shlvl + 1)) else os.setenv('SHLVL', '0') end - --- Builtins -local recentDirs = {} -commander.register('cd', function (args) - if #args > 0 then - local path = table.concat(args, ' '):gsub('$%$','\0'):gsub('${([%w_]+)}', os.getenv) - :gsub('$([%w_]+)', os.getenv):gsub('%z','$'):gsub('^%s*(.-)%s*$', '%1') - - if path == '-' then - path = oldDir - print(path) - end - oldDir = hilbish.cwd() - - local ok, err = pcall(function() fs.cd(path) end) - if not ok then - print(err:sub(17)) - return 1 - end - bait.throw('cd', path) - - -- add to table of recent dirs - recentDirs[11] = nil - if recentDirs[#recentDirs - 1] ~= path then - table.insert(recentDirs, 1, path) - end - - return - end - fs.cd(hilbish.home) - bait.throw('cd', hilbish.home) - - table.insert(recentDirs, 1, hilbish.home) - recentDirs[11] = nil -end) - -commander.register('exit', function() - bait.throw('hilbish.exit') - os.exit(0) -end) - -commander.register('doc', function(args) - local moddocPath = hilbish.dataDir .. '/docs/' - local modDocFormat = [[ -%s -%s -# Functions -]] - - if #args > 0 then - local mod = args[1] - - local f = io.open(moddocPath .. mod .. '.txt', 'rb') - local funcdocs = nil - if not f then - -- assume subdir - -- dataDir/docs//.txt - moddocPath = moddocPath .. mod .. '/' - local subdocName = args[2] - if not subdocName then - subdocName = 'index' - end - f = io.open(moddocPath .. subdocName .. '.txt', 'rb') - if not f then - print('No documentation found for ' .. mod .. '.') - return - end - funcdocs = f:read '*a' - local moddocs = table.filter(fs.readdir(moddocPath), function(f) return f ~= 'index.txt' end) - local subdocs = table.map(moddocs, function(fname) - return lunacolors.underline(lunacolors.blue(string.gsub(fname, '.txt', ''))) - end) - if subdocName == 'index' then - funcdocs = funcdocs .. '\nSubdocs: ' .. table.concat(subdocs, ', ') - end - end - - if not funcdocs then - funcdocs = f:read '*a' - end - local desc = '' - local ok = pcall(require, mod) - local backtickOccurence = 0 - local formattedFuncs = lunacolors.format(funcdocs:sub(1, #funcdocs - 1):gsub('`', function() - backtickOccurence = backtickOccurence + 1 - if backtickOccurence % 2 == 0 then - return '{reset}' - else - return '{underline}{green}' - end - end)) - - if ok then - local props = {} - local propstr = '' - local modDesc = '' - local modmt = getmetatable(require(mod)) - modDesc = modmt.__doc - if modmt.__docProp then - -- not all modules have docs for properties - props = table.map(modmt.__docProp, function(v, k) - return lunacolors.underline(lunacolors.blue(k)) .. ' > ' .. v - end) - end - if #props > 0 then - propstr = '\n# Properties\n' .. table.concat(props, '\n') .. '\n' - end - desc = string.format(modDocFormat, modDesc, propstr) - end - print(desc .. formattedFuncs) - f:close() - - return - end - local modules = table.map(fs.readdir(moddocPath), function(f) - return lunacolors.underline(lunacolors.blue(string.gsub(f, '.txt', ''))) - end) - - io.write [[ -Welcome to Hilbish's doc tool! Here you can find documentation for builtin -functions and other things. - -Usage: doc
[subdoc] -A section is a module or a literal section and a subdoc is a subsection for it. - -Available sections: ]] - io.flush() - - print(table.concat(modules, ', ')) -end) - -local helpTexts = { -[[ -Hello there! Welcome to Hilbish, the comfy and nice little shell for -Lua users and fans. Hilbish is configured with Lua, and its -scripts are also in Lua. It also runs both Lua and shell script when -interactive (aka normal usage). -]], -[[ -What does that mean for you, the user? It means that if you prefer to -use Lua for scripting instead of shell script but still have ordinary -shell usage for interactive use. -]], -[[ -If this is your first time using Hilbish and Lua, check out the -Programming in Lua book here: https://www.lua.org/pil -After (or if you already know Lua) check out the doc command. -It is an in shell tool for documentation about Hilbish provided -functions and modules. -]], -[[ -If you've updated from a pre-1.0 version (0.7.1 as an example) -you'll want to move your config from ~/.hilbishrc.lua to -]] .. -hilbish.userDir.config .. '/hilbish/init.lua' .. -[[ - -and also change all global functions (prompt, alias) to be -in the hilbish module (hilbish.prompt, hilbish.alias as examples). - -And if this is your first time (most likely), you can copy a config -from ]] .. hilbish.dataDir, -[[ -Since 1.0 is a big release, you'll want to check the changelog -at https://github.com/Rosettea/Hilbish/releases/tag/v1.0.0 -to find more breaking changes. -]] -} -commander.register('guide', function() - ansikit.clear() - ansikit.cursorTo(0, 0) - for _, text in ipairs(helpTexts) do - print(text) - local out = hilbish.read('Hit enter to continue ') - ansikit.clear() - ansikit.cursorTo(0, 0) - if not out then - return - end - end - print 'Hope you enjoy using Hilbish!' -end) - -do - local virt_G = { } - - setmetatable(_G, { - __index = function (_, key) - local got_virt = virt_G[key] - if got_virt ~= nil then - return got_virt - end - - virt_G[key] = os.getenv(key) - return virt_G[key] - end, - - __newindex = function (_, key, value) - if type(value) == 'string' then - os.setenv(key, value) - virt_G[key] = value - else - if type(virt_G[key]) == 'string' then - os.setenv(key, '') - end - virt_G[key] = value - end - end, - }) -end - -commander.register('cdr', function(args) - if not args[1] then - print(lunacolors.format [[ -cdr: change directory to one which has been recently visied - -usage: cdr - -to get a list of recent directories, use {green}{underline}cdr list{reset}]]) - return - end - - if args[1] == 'list' then - 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]) - if not index then - print(string.format('received %s as index, which isn\'t a number', index)) - return 1 - end - - if not recentDirs[index] then - print(string.format('no recent directory found at index %s', index)) - return 1 - end - - fs.cd(recentDirs[index]) -end) - --- Hook handles -bait.catch('command.not-found', function(cmd) - print(string.format('hilbish: %s not found', cmd)) -end) - -bait.catch('command.not-executable', function(cmd) - print(string.format('hilbish: %s: not executable', cmd)) -end) - diff --git a/vars_darwin.go b/vars_darwin.go index b780c23..8ec83ba 100644 --- a/vars_darwin.go +++ b/vars_darwin.go @@ -15,7 +15,7 @@ var ( .. hilbish.userDir.config .. '/hilbish/?/?.lua;' .. hilbish.userDir.config .. '/hilbish/?.lua'` dataDir = "/usr/local/share/hilbish" - preloadPath = dataDir + "/prelude/init.lua" + preloadPath = dataDir + "/nature/init.lua" sampleConfPath = dataDir + "/.hilbishrc.lua" // Path to default/sample config defaultConfDir = getenv("XDG_CONFIG_HOME", "~/.config") ) diff --git a/vars_linux.go b/vars_linux.go index 5ea3ac5..815ba6a 100644 --- a/vars_linux.go +++ b/vars_linux.go @@ -15,7 +15,7 @@ var ( .. hilbish.userDir.config .. '/hilbish/?/?.lua;' .. hilbish.userDir.config .. '/hilbish/?.lua'` dataDir = "/usr/share/hilbish" - preloadPath = dataDir + "/prelude/init.lua" + preloadPath = dataDir + "/nature/init.lua" sampleConfPath = dataDir + "/.hilbishrc.lua" // Path to default/sample config defaultConfDir = "" ) diff --git a/vars_windows.go b/vars_windows.go index 5e9878c..a257baf 100644 --- a/vars_windows.go +++ b/vars_windows.go @@ -9,7 +9,7 @@ var ( .. hilbish.userDir.config .. '\\Hilbish\\libs\\?\\?.lua;' .. hilbish.userDir.config .. '\\Hilbish\\libs\\?.lua;'` dataDir = "~\\Appdata\\Roaming\\Hilbish" // ~ and \ gonna cry? - preloadPath = dataDir + "\\prelude\\init.lua" + preloadPath = dataDir + "\\nature\\init.lua" sampleConfPath = dataDir + "\\hilbishrc.lua" // Path to default/sample config defaultConfDir = "" )