chore: merge from master
							
								
								
									
										24
									
								
								.github/workflows/website.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						@ -1,10 +1,8 @@
 | 
			
		||||
name: Build website
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches:
 | 
			
		||||
      - master
 | 
			
		||||
      - docs-refactor
 | 
			
		||||
  - push
 | 
			
		||||
  - pull_request
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  deploy:
 | 
			
		||||
@ -21,11 +19,29 @@ jobs:
 | 
			
		||||
          hugo-version: 'latest'
 | 
			
		||||
          extended: true
 | 
			
		||||
 | 
			
		||||
      - name: Set branch name
 | 
			
		||||
        id: branch
 | 
			
		||||
        run: echo "BRANCH_NAME=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> "$GITHUB_ENV"
 | 
			
		||||
 | 
			
		||||
      - name: Fix base URL
 | 
			
		||||
        if: env.BRANCH_NAME != 'master' && github.repository_owner == 'Rosettea'      
 | 
			
		||||
        run: sed -i "s%baseURL = 'https://rosettea.github.io/Hilbish/'%baseURL = 'https://rosettea.github.io/Hilbish/versions/${{ env.BRANCH_NAME }}'%" website/config.toml
 | 
			
		||||
 | 
			
		||||
      - name: Build
 | 
			
		||||
        run: 'cd website && hugo --minify'
 | 
			
		||||
 | 
			
		||||
      - name: Deploy
 | 
			
		||||
        if: env.BRANCH_NAME == 'master' && github.repository_owner == 'Rosettea'
 | 
			
		||||
        uses: peaceiris/actions-gh-pages@v3
 | 
			
		||||
        with:
 | 
			
		||||
          github_token: ${{ secrets.GITHUB_TOKEN }}
 | 
			
		||||
          publish_dir: ./website/public
 | 
			
		||||
          keep_files: true
 | 
			
		||||
      - name: Deploy
 | 
			
		||||
        if: env.BRANCH_NAME != 'master' && github.repository_owner == 'Rosettea'
 | 
			
		||||
        uses: peaceiris/actions-gh-pages@v3
 | 
			
		||||
        with:
 | 
			
		||||
          github_token: ${{ secrets.GITHUB_TOKEN }}
 | 
			
		||||
          publish_dir: ./website/public
 | 
			
		||||
          destination_dir: versions/${{ env.BRANCH_NAME }}
 | 
			
		||||
          keep_files: true
 | 
			
		||||
 | 
			
		||||
@ -1,18 +1,39 @@
 | 
			
		||||
-- Default Hilbish config
 | 
			
		||||
local hilbish = require 'hilbish'
 | 
			
		||||
local lunacolors = require 'lunacolors'
 | 
			
		||||
local bait = require 'bait'
 | 
			
		||||
local ansikit = require 'ansikit'
 | 
			
		||||
 | 
			
		||||
local unreadCount = 0
 | 
			
		||||
local running = false
 | 
			
		||||
local function doPrompt(fail)
 | 
			
		||||
	hilbish.prompt(lunacolors.format(
 | 
			
		||||
		'{blue}%u {cyan}%d ' .. (fail and '{red}' or '{green}') .. '∆ '
 | 
			
		||||
	))
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function doNotifyPrompt()
 | 
			
		||||
	if running or unreadCount == hilbish.messages.unreadCount() then return end
 | 
			
		||||
 | 
			
		||||
	local notifPrompt = string.format('• %s unread notification%s', hilbish.messages.unreadCount(), hilbish.messages.unreadCount() > 1 and 's' or '')
 | 
			
		||||
	unreadCount = hilbish.messages.unreadCount()
 | 
			
		||||
	hilbish.prompt(lunacolors.blue(notifPrompt), 'right')
 | 
			
		||||
 | 
			
		||||
	hilbish.timeout(function()
 | 
			
		||||
		hilbish.prompt('', 'right')
 | 
			
		||||
	end, 3000)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
doPrompt()
 | 
			
		||||
 | 
			
		||||
bait.catch('command.preexec', function()
 | 
			
		||||
	running = true
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
bait.catch('command.exit', function(code)
 | 
			
		||||
	running = false
 | 
			
		||||
	doPrompt(code ~= 0)
 | 
			
		||||
	doNotifyPrompt()
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
bait.catch('hilbish.vimMode', function(mode)
 | 
			
		||||
@ -22,3 +43,7 @@ bait.catch('hilbish.vimMode', function(mode)
 | 
			
		||||
		ansikit.cursorStyle(ansikit.lineCursor)
 | 
			
		||||
	end
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
bait.catch('hilbish.notification', function(notif)
 | 
			
		||||
	doNotifyPrompt()
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										32
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						@ -1,9 +1,41 @@
 | 
			
		||||
# 🎀 Changelog
 | 
			
		||||
 | 
			
		||||
## Unreleased
 | 
			
		||||
### Added
 | 
			
		||||
- Made a few additions to the sink type:
 | 
			
		||||
  - `read()` method for retrieving input (so now the `in` sink of commanders is useful)
 | 
			
		||||
  - `flush()` and `autoFlush()` related to flushing outputs
 | 
			
		||||
  - `pipe` property to check if a sink with input is a pipe (like stdin)
 | 
			
		||||
- Add fuzzy search to history search (enable via `hilbish.opts.fuzzy = true`)
 | 
			
		||||
- Show indexes on cdr list
 | 
			
		||||
- Fix doc command not displaying correct subdocs when using shorthand api doc access (`doc api hilbish.jobs` as an example)
 | 
			
		||||
- `hilbish.messages` interface (details in [#219])
 | 
			
		||||
- `hilbish.notification` signal when a message/notification is sent
 | 
			
		||||
- `notifyJobFinish` opt to send a notification when background jobs are
 | 
			
		||||
completed.
 | 
			
		||||
- Allow numbered arg substitutions in aliases.
 | 
			
		||||
  - Example: `hilbish.alias('hello', 'echo %1 says hello')` allows the user to run `hello hilbish`
 | 
			
		||||
  which will output `hilbish says hello`.
 | 
			
		||||
- Greenhouse
 | 
			
		||||
  - Greenhouse is a pager library and program. Basic usage is `greenhouse <file>`
 | 
			
		||||
  - Using this also brings enhancements to the `doc` command like easy
 | 
			
		||||
  navigation of neighboring doc files.
 | 
			
		||||
 | 
			
		||||
### Fixed
 | 
			
		||||
- Fix infinite loop when navigating history without any history. [#252](https://github.com/Rosettea/Hilbish/issues/252)
 | 
			
		||||
- Return the prefix when calling `hilbish.completions.call`. [#219](https://github.com/Rosettea/Hilbish/issues/219)
 | 
			
		||||
- Replaced `sed` in-place editing with `grep` and `mv` for compatibility with BSD utils
 | 
			
		||||
 | 
			
		||||
## [2.1.2] - 2022-04-10
 | 
			
		||||
### Removed
 | 
			
		||||
- Bad april fools code ;(
 | 
			
		||||
 | 
			
		||||
## [2.1.1] - 2022-04-01
 | 
			
		||||
### Added
 | 
			
		||||
- Validation checks for command input
 | 
			
		||||
- Improved runtime performance
 | 
			
		||||
- Validate Lua code
 | 
			
		||||
 | 
			
		||||
## [2.1.0] - 2022-02-10
 | 
			
		||||
### Added
 | 
			
		||||
- Documented custom userdata types (Job and Timer Objects)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										44
									
								
								README.md
									
									
									
									
									
								
							
							
						
						@ -1,45 +1,35 @@
 | 
			
		||||
<div align="center">
 | 
			
		||||
	<img src="./assets/hilbish-flower.png" width=128><br>
 | 
			
		||||
	<img src="./assets/hilbish-text.png" width=256><br>
 | 
			
		||||
	<blockquote>
 | 
			
		||||
	🌺 The flower shell. A comfy and nice little shell for Lua fans!
 | 
			
		||||
	</blockquote>
 | 
			
		||||
	<p align="center">
 | 
			
		||||
		<img alt="GitHub commit activity" src="https://img.shields.io/github/commit-activity/m/Rosettea/Hilbish?style=flat-square">
 | 
			
		||||
		<img alt="GitHub commits since latest release (by date)" src="https://img.shields.io/github/commits-since/Rosettea/Hilbish/latest?style=flat-square">
 | 
			
		||||
		<img alt="GitHub contributors" src="https://img.shields.io/github/contributors/Rosettea/Hilbish?style=flat-square"><br>
 | 
			
		||||
		<a href="https://github.com/Rosettea/Hilbish/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22"><img src="https://img.shields.io/github/issues/Hilbis/Hilbish/help%20wanted?style=flat-square&color=green" alt="help wanted"></a>
 | 
			
		||||
		<a href="https://github.com/Rosettea/Hilbish/blob/master/LICENSE"><img alt="GitHub license" src="https://img.shields.io/github/license/Rosettea/Hilbish?style=flat-square"></a>
 | 
			
		||||
		<a href="https://discord.gg/3PDdcQz"><img alt="Discord" src="https://img.shields.io/discord/732357621503229962?color=blue&style=flat-square"></a>
 | 
			
		||||
	</p>
 | 
			
		||||
</div>
 | 
			
		||||
<img src="./assets/hilbish-logo-and-text.png" width=512><br>
 | 
			
		||||
<blockquote>
 | 
			
		||||
🌓 The Moon-powered shell! A comfy and extensible shell for Lua fans! 🌺 ✨
 | 
			
		||||
</blockquote>
 | 
			
		||||
 | 
			
		||||
Hilbish is a extensible shell (framework). It was made to be very customizable
 | 
			
		||||
via the Lua programming language. It aims to be easy to use for the casual
 | 
			
		||||
people but powerful for those who want to tinker more with their shell,
 | 
			
		||||
the thing used to interface with most of the system.  
 | 
			
		||||
<img alt="GitHub commit activity" src="https://img.shields.io/github/commit-activity/m/Rosettea/Hilbish?style=flat-square"><img alt="GitHub commits since latest release (by date)" src="https://img.shields.io/github/commits-since/Rosettea/Hilbish/latest?style=flat-square"><img alt="GitHub contributors" src="https://img.shields.io/github/contributors/Rosettea/Hilbish?style=flat-square"><br>
 | 
			
		||||
<a href="https://github.com/Rosettea/Hilbish/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22"><img src="https://img.shields.io/github/issues/Hilbis/Hilbish/help%20wanted?style=flat-square&color=green" alt="help wanted"></a>
 | 
			
		||||
<a href="https://github.com/Rosettea/Hilbish/blob/master/LICENSE"><img alt="GitHub license" src="https://img.shields.io/github/license/Rosettea/Hilbish?style=flat-square"></a>
 | 
			
		||||
<a href="https://discord.gg/3PDdcQz"><img alt="Discord" src="https://img.shields.io/discord/732357621503229962?color=blue&style=flat-square"></a>
 | 
			
		||||
<br>
 | 
			
		||||
 | 
			
		||||
Hilbish is an extensible shell designed to be highly customizable.
 | 
			
		||||
It is configured in Lua and provides a good range of features.
 | 
			
		||||
It aims to be easy to use for anyone but powerful enough for
 | 
			
		||||
those who need it.
 | 
			
		||||
 | 
			
		||||
The motivation for choosing Lua was that its simpler and better to use
 | 
			
		||||
than old shell script. It's fine for basic interactive shell uses,
 | 
			
		||||
but that's the only place Hilbish has shell script; everything else is Lua
 | 
			
		||||
and aims to be infinitely configurable. If something isn't, open an issue!
 | 
			
		||||
 | 
			
		||||
# Table of Contents
 | 
			
		||||
- [Screenshots](#Screenshots)
 | 
			
		||||
- [Getting Hilbish](#Getting-Hilbish)
 | 
			
		||||
- [Contributing](#Contributing)
 | 
			
		||||
 | 
			
		||||
# Screenshots
 | 
			
		||||
<div align="center">
 | 
			
		||||
<img src="gallery/terminal.png"><br><br>
 | 
			
		||||
<img src="gallery/tab.png"><br><br>
 | 
			
		||||
<img src="gallery/tab.png">
 | 
			
		||||
<img src="gallery/pillprompt.png">
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
# Getting Hilbish
 | 
			
		||||
**NOTE:** Hilbish is not guaranteed to work properly on Windows, starting
 | 
			
		||||
from the 2.0 version. It will still be able to compile, but functionality
 | 
			
		||||
may be lacking.
 | 
			
		||||
may be lacking. If you want to contribute to make the situation better,
 | 
			
		||||
comment on the Windows discussion.
 | 
			
		||||
 | 
			
		||||
You can check the [install page](https://rosettea.github.io/Hilbish/install/)
 | 
			
		||||
on the website for distributed binaries from GitHub or other package repositories.
 | 
			
		||||
 | 
			
		||||
@ -13,12 +13,22 @@ vars:
 | 
			
		||||
 | 
			
		||||
tasks:
 | 
			
		||||
  default:
 | 
			
		||||
    cmds:
 | 
			
		||||
      - go build {{.GOFLAGS}}
 | 
			
		||||
    vars:
 | 
			
		||||
      GOFLAGS: '-ldflags "-s -w -X main.dataDir={{.LIBDIR}} -X main.gitCommit=$(git rev-parse --short HEAD) -X main.gitBranch=$(git rev-parse --abbrev-ref HEAD)"'
 | 
			
		||||
 | 
			
		||||
  default-nocgo:
 | 
			
		||||
    cmds:
 | 
			
		||||
      - CGO_ENABLED=0 go build {{.GOFLAGS}}
 | 
			
		||||
    vars:
 | 
			
		||||
      GOFLAGS: '-ldflags "-s -w -X main.dataDir={{.LIBDIR}} -X main.gitCommit=$(git rev-parse --short HEAD) -X main.gitBranch=$(git rev-parse --abbrev-ref HEAD)"'
 | 
			
		||||
 | 
			
		||||
  build:
 | 
			
		||||
    cmds:
 | 
			
		||||
      - go build {{.GOFLAGS}}
 | 
			
		||||
 | 
			
		||||
  build-nocgo:
 | 
			
		||||
    cmds:
 | 
			
		||||
      - CGO_ENABLED=0 go build {{.GOFLAGS}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										27
									
								
								aliases.go
									
									
									
									
									
								
							
							
						
						@ -1,6 +1,8 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
@ -46,9 +48,32 @@ func (a *aliasModule) Resolve(cmdstr string) string {
 | 
			
		||||
	a.mu.RLock()
 | 
			
		||||
	defer a.mu.RUnlock()
 | 
			
		||||
 | 
			
		||||
	args := strings.Split(cmdstr, " ")
 | 
			
		||||
	arg, _ := regexp.Compile(`[\\]?%\d+`)
 | 
			
		||||
 | 
			
		||||
	args, _ := splitInput(cmdstr)
 | 
			
		||||
	if len(args) == 0 {
 | 
			
		||||
		// this shouldnt reach but...????
 | 
			
		||||
		return cmdstr
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for a.aliases[args[0]] != "" {
 | 
			
		||||
		alias := a.aliases[args[0]]
 | 
			
		||||
		alias = arg.ReplaceAllStringFunc(alias, func(a string) string {
 | 
			
		||||
			idx, _ := strconv.Atoi(a[1:])
 | 
			
		||||
			if strings.HasPrefix(a, "\\") || idx == 0 {
 | 
			
		||||
				return strings.TrimPrefix(a, "\\")
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if idx + 1 > len(args) {
 | 
			
		||||
				return a
 | 
			
		||||
			}
 | 
			
		||||
			val := args[idx]
 | 
			
		||||
			args = cut(args, idx)
 | 
			
		||||
			cmdstr = strings.Join(args, " ")
 | 
			
		||||
 | 
			
		||||
			return val
 | 
			
		||||
		})
 | 
			
		||||
		
 | 
			
		||||
		cmdstr = alias + strings.TrimPrefix(cmdstr, args[0])
 | 
			
		||||
		cmdArgs, _ := splitInput(cmdstr)
 | 
			
		||||
		args = cmdArgs
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										13
									
								
								api.go
									
									
									
									
									
								
							
							
						
						@ -2,6 +2,7 @@
 | 
			
		||||
// The Hilbish module includes the core API, containing
 | 
			
		||||
// interfaces and functions which directly relate to shell functionality.
 | 
			
		||||
// #field ver The version of Hilbish
 | 
			
		||||
// #field goVersion The version of Go that Hilbish was compiled with
 | 
			
		||||
// #field user Username of the user
 | 
			
		||||
// #field host Hostname of the machine
 | 
			
		||||
// #field dataDir Directory for Hilbish data files, including the docs and default modules
 | 
			
		||||
@ -110,6 +111,7 @@ func hilbishLoad(rtm *rt.Runtime) (rt.Value, func()) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	util.SetFieldProtected(fakeMod, mod, "ver", rt.StringValue(getVersion()))
 | 
			
		||||
	util.SetFieldProtected(fakeMod, mod, "goVersion", rt.StringValue(runtime.Version()))
 | 
			
		||||
	util.SetFieldProtected(fakeMod, mod, "user", rt.StringValue(username))
 | 
			
		||||
	util.SetFieldProtected(fakeMod, mod, "host", rt.StringValue(host))
 | 
			
		||||
	util.SetFieldProtected(fakeMod, mod, "home", rt.StringValue(curuser.HomeDir))
 | 
			
		||||
@ -164,6 +166,9 @@ func hilbishLoad(rtm *rt.Runtime) (rt.Value, func()) {
 | 
			
		||||
	util.SetField(rtm, versionModule, "release", rt.StringValue(releaseName))
 | 
			
		||||
	mod.Set(rt.StringValue("version"), rt.TableValue(versionModule))
 | 
			
		||||
 | 
			
		||||
	pluginModule := moduleLoader(rtm)
 | 
			
		||||
	mod.Set(rt.StringValue("module"), rt.TableValue(pluginModule))
 | 
			
		||||
 | 
			
		||||
	return rt.TableValue(fakeMod), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -639,6 +644,14 @@ func hlhinter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
// 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.
 | 
			
		||||
// Note that to set a highlighter, one has to override this function.
 | 
			
		||||
// Example:
 | 
			
		||||
// ```
 | 
			
		||||
// function hilbish.highlighter(line)
 | 
			
		||||
//    return line:gsub('"%w+"', function(c) return lunacolors.green(c) end)
 | 
			
		||||
// end
 | 
			
		||||
// ```
 | 
			
		||||
// This code will highlight all double quoted strings in green.
 | 
			
		||||
// --- @param line string
 | 
			
		||||
func hlhighlighter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	return c.Next(), nil
 | 
			
		||||
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 45 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								assets/hilbish-logo-and-text.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 78 KiB  | 
| 
		 Before Width: | Height: | Size: 18 KiB  | 
@ -417,7 +417,7 @@ func main() {
 | 
			
		||||
 | 
			
		||||
			f, _ := os.Create(docPath)
 | 
			
		||||
			f.WriteString(fmt.Sprintf(header, modOrIface, modname, modu.ShortDescription))
 | 
			
		||||
			typeTag, _ := regexp.Compile(`@\w+`)
 | 
			
		||||
			typeTag, _ := regexp.Compile(`\B@\w+`)
 | 
			
		||||
			modDescription := typeTag.ReplaceAllStringFunc(strings.Replace(modu.Description, "<", `\<`, -1), func(typ string) string {
 | 
			
		||||
				typName := typ[1:]
 | 
			
		||||
				typLookup := typeTable[strings.ToLower(typName)]
 | 
			
		||||
 | 
			
		||||
@ -253,15 +253,16 @@ func callLuaCompleter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// we must keep the holy 80 cols
 | 
			
		||||
	completerReturn, err := rt.Call1(l.MainThread(),
 | 
			
		||||
	rt.FunctionValue(completecb), rt.StringValue(query),
 | 
			
		||||
	rt.StringValue(ctx), rt.TableValue(fields))
 | 
			
		||||
	cont := c.Next()
 | 
			
		||||
	err = rt.Call(l.MainThread(), rt.FunctionValue(completecb),
 | 
			
		||||
	[]rt.Value{rt.StringValue(query), rt.StringValue(ctx), rt.TableValue(fields)},
 | 
			
		||||
	cont)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return c.PushingNext1(t.Runtime, completerReturn), nil
 | 
			
		||||
	return cont, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #interface completions
 | 
			
		||||
 | 
			
		||||
@ -13,6 +13,7 @@ interfaces and functions which directly relate to shell functionality.
 | 
			
		||||
 | 
			
		||||
## Interface fields
 | 
			
		||||
- `ver`: The version of Hilbish
 | 
			
		||||
- `goVersion`: The version of Go that Hilbish was compiled with
 | 
			
		||||
- `user`: Username of the user
 | 
			
		||||
- `host`: Hostname of the machine
 | 
			
		||||
- `dataDir`: Directory for Hilbish data files, including the docs and default modules
 | 
			
		||||
@ -49,6 +50,14 @@ 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.
 | 
			
		||||
Note that to set a highlighter, one has to override this function.
 | 
			
		||||
Example:
 | 
			
		||||
```
 | 
			
		||||
function hilbish.highlighter(line)
 | 
			
		||||
   return line:gsub('"%w+"', function(c) return lunacolors.green(c) end)
 | 
			
		||||
end
 | 
			
		||||
```
 | 
			
		||||
This code will highlight all double quoted strings in green.
 | 
			
		||||
 | 
			
		||||
### hinter(line, pos)
 | 
			
		||||
The command line hint handler. It gets called on every key insert to
 | 
			
		||||
@ -109,6 +118,19 @@ A sink is a structure that has input and/or output to/from
 | 
			
		||||
a desination.
 | 
			
		||||
 | 
			
		||||
### Methods
 | 
			
		||||
#### autoFlush(auto)
 | 
			
		||||
Sets/toggles the option of automatically flushing output.
 | 
			
		||||
A call with no argument will toggle the value.
 | 
			
		||||
 | 
			
		||||
#### flush()
 | 
			
		||||
Flush writes all buffered input to the sink.
 | 
			
		||||
 | 
			
		||||
#### read() -> string
 | 
			
		||||
Reads a liine of input from the sink.
 | 
			
		||||
 | 
			
		||||
#### readAll() -> string
 | 
			
		||||
Reads all input from the sink.
 | 
			
		||||
 | 
			
		||||
#### write(str)
 | 
			
		||||
Writes data to a sink.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -21,6 +21,10 @@ Returns the text that is at the register.
 | 
			
		||||
### insert(text)
 | 
			
		||||
Inserts text into the line.
 | 
			
		||||
 | 
			
		||||
### getChar() -> string
 | 
			
		||||
Reads a keystroke from the user. This is in a format
 | 
			
		||||
of something like Ctrl-L..
 | 
			
		||||
 | 
			
		||||
### setVimRegister(register, text)
 | 
			
		||||
Sets the vim register at `register` to hold the passed text.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										53
									
								
								docs/api/hilbish/hilbish.module.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,53 @@
 | 
			
		||||
---
 | 
			
		||||
title: Interface hilbish.module
 | 
			
		||||
description: native module loading
 | 
			
		||||
layout: doc
 | 
			
		||||
menu:
 | 
			
		||||
  docs:
 | 
			
		||||
    parent: "API"
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## Introduction
 | 
			
		||||
 | 
			
		||||
The hilbish.module interface provides a function to load
 | 
			
		||||
Hilbish plugins/modules. Hilbish modules are Go-written
 | 
			
		||||
plugins (see https://pkg.go.dev/plugin) that are used to add functionality
 | 
			
		||||
to Hilbish that cannot be written in Lua for any reason.
 | 
			
		||||
 | 
			
		||||
Note that you don't ever need to use the load function that is here as
 | 
			
		||||
modules can be loaded with a `require` call like Lua C modules, and the
 | 
			
		||||
search paths can be changed with the `paths` property here.
 | 
			
		||||
 | 
			
		||||
To make a valid native module, the Go plugin has to export a Loader function
 | 
			
		||||
with a signature like so: `func(*rt.Runtime) rt.Value`.
 | 
			
		||||
 | 
			
		||||
`rt` in this case refers to the Runtime type at
 | 
			
		||||
https://pkg.go.dev/github.com/arnodel/golua@master/runtime#Runtime
 | 
			
		||||
 | 
			
		||||
Hilbish uses this package as its Lua runtime. You will need to read
 | 
			
		||||
it to use it for a native plugin.
 | 
			
		||||
 | 
			
		||||
Here is some code for an example plugin:
 | 
			
		||||
```go
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	rt "github.com/arnodel/golua/runtime"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func Loader(rtm *rt.Runtime) rt.Value {
 | 
			
		||||
	return rt.StringValue("hello world!")
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
This can be compiled with `go build -buildmode=plugin plugin.go`.
 | 
			
		||||
If you attempt to require and print the result (`print(require 'plugin')`), it will show "hello world!"
 | 
			
		||||
 | 
			
		||||
## Interface fields
 | 
			
		||||
- `paths`: A list of paths to search when loading native modules. This is in the style of Lua search paths and will be used when requiring native modules. Example: `?.so;?/?.so`
 | 
			
		||||
 | 
			
		||||
## Functions
 | 
			
		||||
### load(path)
 | 
			
		||||
Loads a module at the designated `path`.
 | 
			
		||||
It will throw if any error occurs.
 | 
			
		||||
 | 
			
		||||
@ -1,3 +1,8 @@
 | 
			
		||||
+ `command.preexec` -> input, cmdStr > Thrown before a command
 | 
			
		||||
is executed. The `input` is the user written command, while `cmdStr`
 | 
			
		||||
is what will be executed (`input` will have aliases while `cmdStr`
 | 
			
		||||
will have alias resolved input).
 | 
			
		||||
 | 
			
		||||
+ `command.exit` -> code, cmdStr > Thrown when a command exits.
 | 
			
		||||
`code` is the exit code of the command, and `cmdStr` is the command that was run.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -7,3 +7,6 @@
 | 
			
		||||
like yanking or pasting text. See `doc vim-mode actions` for more info.
 | 
			
		||||
 | 
			
		||||
+ `hilbish.cancel` > Sent when the user cancels their input with Ctrl-C.
 | 
			
		||||
 | 
			
		||||
+ `hilbish.notification` -> message > Sent when a message is
 | 
			
		||||
sent.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										11
									
								
								editor.go
									
									
									
									
									
								
							
							
						
						@ -16,6 +16,7 @@ func editorLoader(rtm *rt.Runtime) *rt.Table {
 | 
			
		||||
		"setVimRegister": {editorSetRegister, 1, false},
 | 
			
		||||
		"getVimRegister": {editorGetRegister, 2, false},
 | 
			
		||||
		"getLine": {editorGetLine, 0, false},
 | 
			
		||||
		"readChar": {editorReadChar, 0, false},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mod := rt.NewTable()
 | 
			
		||||
@ -94,3 +95,13 @@ func editorGetLine(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
 | 
			
		||||
	return c.PushingNext1(t.Runtime, rt.StringValue(string(buf))), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #interface editor
 | 
			
		||||
// getChar() -> string
 | 
			
		||||
// Reads a keystroke from the user. This is in a format
 | 
			
		||||
// of something like Ctrl-L..
 | 
			
		||||
func editorReadChar(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	buf := lr.rl.ReadChar()
 | 
			
		||||
 | 
			
		||||
	return c.PushingNext1(t.Runtime, rt.StringValue(string(buf))), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -40,6 +40,10 @@ function hilbish.editor.getVimRegister(register) end
 | 
			
		||||
--- Inserts text into the line.
 | 
			
		||||
function hilbish.editor.insert(text) end
 | 
			
		||||
 | 
			
		||||
--- Reads a keystroke from the user. This is in a format
 | 
			
		||||
--- of something like Ctrl-L..
 | 
			
		||||
function hilbish.editor.getChar() end
 | 
			
		||||
 | 
			
		||||
--- Sets the vim register at `register` to hold the passed text.
 | 
			
		||||
--- @param register string
 | 
			
		||||
--- @param text string
 | 
			
		||||
@ -79,6 +83,14 @@ function hilbish.goro(fn) end
 | 
			
		||||
--- 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.
 | 
			
		||||
--- Note that to set a highlighter, one has to override this function.
 | 
			
		||||
--- Example:
 | 
			
		||||
--- ```
 | 
			
		||||
--- function hilbish.highlighter(line)
 | 
			
		||||
---    return line:gsub('"%w+"', function(c) return lunacolors.green(c) end)
 | 
			
		||||
--- end
 | 
			
		||||
--- ```
 | 
			
		||||
--- This code will highlight all double quoted strings in green.
 | 
			
		||||
--- @param line string
 | 
			
		||||
function hilbish.highlighter(line) end
 | 
			
		||||
 | 
			
		||||
@ -180,6 +192,22 @@ function hilbish.jobs:foreground() end
 | 
			
		||||
--- @param cmd string
 | 
			
		||||
function hilbish.runner.lua(cmd) end
 | 
			
		||||
 | 
			
		||||
--- Sets/toggles the option of automatically flushing output.
 | 
			
		||||
--- A call with no argument will toggle the value.
 | 
			
		||||
--- @param auto boolean|nil
 | 
			
		||||
function hilbish:autoFlush(auto) end
 | 
			
		||||
 | 
			
		||||
--- Flush writes all buffered input to the sink.
 | 
			
		||||
function hilbish:flush() end
 | 
			
		||||
 | 
			
		||||
--- Reads a liine of input from the sink.
 | 
			
		||||
--- @returns string
 | 
			
		||||
function hilbish:read() end
 | 
			
		||||
 | 
			
		||||
--- Reads all input from the sink.
 | 
			
		||||
--- @returns string
 | 
			
		||||
function hilbish:readAll() end
 | 
			
		||||
 | 
			
		||||
--- Writes data to a sink.
 | 
			
		||||
function hilbish:write(str) end
 | 
			
		||||
 | 
			
		||||
@ -192,6 +220,10 @@ function hilbish.jobs:start() end
 | 
			
		||||
--- Stops the job from running.
 | 
			
		||||
function hilbish.jobs:stop() end
 | 
			
		||||
 | 
			
		||||
--- Loads a module at the designated `path`.
 | 
			
		||||
--- It will throw if any error occurs.
 | 
			
		||||
function hilbish.module.load(path) end
 | 
			
		||||
 | 
			
		||||
--- Runs a command in Hilbish's shell script interpreter.
 | 
			
		||||
--- This is the equivalent of using `source`.
 | 
			
		||||
--- @param cmd string
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										3
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						@ -5,9 +5,9 @@ go 1.17
 | 
			
		||||
require (
 | 
			
		||||
	github.com/arnodel/golua v0.0.0-20220221163911-dfcf252b6f86
 | 
			
		||||
	github.com/blackfireio/osinfo v1.0.3
 | 
			
		||||
	github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9
 | 
			
		||||
	github.com/maxlandon/readline v0.1.0-beta.0.20211027085530-2b76cabb8036
 | 
			
		||||
	github.com/pborman/getopt v1.1.0
 | 
			
		||||
	github.com/sahilm/fuzzy v0.1.0
 | 
			
		||||
	golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a
 | 
			
		||||
	golang.org/x/term v0.0.0-20220411215600-e5f449aeb171
 | 
			
		||||
	mvdan.cc/sh/v3 v3.5.1
 | 
			
		||||
@ -17,6 +17,7 @@ require (
 | 
			
		||||
	github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
 | 
			
		||||
	github.com/arnodel/strftime v0.1.6 // indirect
 | 
			
		||||
	github.com/evilsocket/islazy v1.10.6 // indirect
 | 
			
		||||
	github.com/kylelemons/godebug v1.1.0 // indirect
 | 
			
		||||
	github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 // indirect
 | 
			
		||||
	github.com/rivo/uniseg v0.2.0 // indirect
 | 
			
		||||
	golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 // indirect
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										20
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						@ -1,13 +1,5 @@
 | 
			
		||||
github.com/Rosettea/golua v0.0.0-20220419183026-6d22d6fec5ac h1:dtXrgjch8PQyf7C90anZUquB5U3dr8AcMGJofeuirrI=
 | 
			
		||||
github.com/Rosettea/golua v0.0.0-20220419183026-6d22d6fec5ac/go.mod h1:9jzpYPiU2is0HVGCiuIOBSXdergHUW44IEjmuN1UrIE=
 | 
			
		||||
github.com/Rosettea/golua v0.0.0-20220518005949-116371948fe3 h1:I/wWr40FFLFF9pbT3wLb1FAEZhKb/hUWE+nJ5uHBK2g=
 | 
			
		||||
github.com/Rosettea/golua v0.0.0-20220518005949-116371948fe3/go.mod h1:9jzpYPiU2is0HVGCiuIOBSXdergHUW44IEjmuN1UrIE=
 | 
			
		||||
github.com/Rosettea/golua v0.0.0-20220621002945-b05143999437 h1:6lWu4YVLeKuZ8jR9xwHONhkHBsrIbw5dpfG1gtOVw0A=
 | 
			
		||||
github.com/Rosettea/golua v0.0.0-20220621002945-b05143999437/go.mod h1:9jzpYPiU2is0HVGCiuIOBSXdergHUW44IEjmuN1UrIE=
 | 
			
		||||
github.com/Rosettea/golua v0.0.0-20221213193027-cbf6d4e4d345 h1:QNYjYDogUSiNUkffbhFSrSCtpZhofeiVYGFN2FI4wSs=
 | 
			
		||||
github.com/Rosettea/golua v0.0.0-20221213193027-cbf6d4e4d345/go.mod h1:9jzpYPiU2is0HVGCiuIOBSXdergHUW44IEjmuN1UrIE=
 | 
			
		||||
github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220306140409-795a84b00b4e h1:P2XupP8SaylWaudD1DqbWtZ3mIa8OsE9635LmR+Q+lg=
 | 
			
		||||
github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220306140409-795a84b00b4e/go.mod h1:R09vh/04ILvP2Gj8/Z9Jd0Dh0ZIvaucowMEs6abQpWs=
 | 
			
		||||
github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220524215627-dfd9a4fa219b h1:s5eDMhBk6H1BgipgLub/gv9qeyBaTuiHM0k3h2/9TSE=
 | 
			
		||||
github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220524215627-dfd9a4fa219b/go.mod h1:R09vh/04ILvP2Gj8/Z9Jd0Dh0ZIvaucowMEs6abQpWs=
 | 
			
		||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
 | 
			
		||||
@ -18,8 +10,6 @@ github.com/arnodel/strftime v0.1.6/go.mod h1:5NbK5XqYK8QpRZpqKNt4OlxLtIB8cotkLk4
 | 
			
		||||
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
 | 
			
		||||
github.com/blackfireio/osinfo v1.0.3 h1:Yk2t2GTPjBcESv6nDSWZKO87bGMQgO+Hi9OoXPpxX8c=
 | 
			
		||||
github.com/blackfireio/osinfo v1.0.3/go.mod h1:Pd987poVNmd5Wsx6PRPw4+w7kLlf9iJxoRKPtPAjOrA=
 | 
			
		||||
github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9 h1:xz6Nv3zcwO2Lila35hcb0QloCQsc38Al13RNEzWRpX4=
 | 
			
		||||
github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9/go.mod h1:2wSM9zJkl1UQEFZgSd68NfCgRz1VL1jzy/RjCg+ULrs=
 | 
			
		||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 | 
			
		||||
github.com/creack/pty v1.1.15 h1:cKRCLMj3Ddm54bKSpemfQ8AtYFBhAI2MPmdys22fBdc=
 | 
			
		||||
github.com/creack/pty v1.1.15/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
 | 
			
		||||
@ -36,6 +26,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 | 
			
		||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 | 
			
		||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 | 
			
		||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 | 
			
		||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
 | 
			
		||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
 | 
			
		||||
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
 | 
			
		||||
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
 | 
			
		||||
github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 h1:LiZB1h0GIcudcDci2bxbqI6DXV8bF8POAnArqvRrIyw=
 | 
			
		||||
@ -49,7 +41,8 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ
 | 
			
		||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
 | 
			
		||||
github.com/rogpeppe/go-internal v1.8.1-0.20210923151022-86f73c517451 h1:d1PiN4RxzIFXCJTvRkvSkKqwtRAl5ZV4lATKtQI0B7I=
 | 
			
		||||
github.com/rogpeppe/go-internal v1.8.1-0.20210923151022-86f73c517451/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
 | 
			
		||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
 | 
			
		||||
github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI=
 | 
			
		||||
github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
 | 
			
		||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 h1:w8s32wxx3sY+OjLlv9qltkLU5yvJzxjjgiHWLjdIcw4=
 | 
			
		||||
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
@ -58,18 +51,13 @@ golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7w
 | 
			
		||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210925032602-92d5a993a665/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 | 
			
		||||
golang.org/x/term v0.0.0-20210916214954-140adaaadfaf/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 | 
			
		||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
 | 
			
		||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 | 
			
		||||
golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 h1:EH1Deb8WZJ0xc0WK//leUHXcX9aLE5SymusoTmMZye8=
 | 
			
		||||
golang.org/x/term v0.0.0-20220411215600-e5f449aeb171/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 | 
			
		||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
			
		||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
 | 
			
		||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
			
		||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
 | 
			
		||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										2
									
								
								lua.go
									
									
									
									
									
								
							
							
						
						@ -68,7 +68,7 @@ func luaInit() {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Add more paths that Lua can require from
 | 
			
		||||
	err := util.DoString(l, "package.path = package.path .. " + requirePaths)
 | 
			
		||||
	_, err := util.DoString(l, "package.path = package.path .. " + requirePaths)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintln(os.Stderr, "Could not add Hilbish require paths! Libraries will be missing. This shouldn't happen.")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										8
									
								
								main.go
									
									
									
									
									
								
							
							
						
						@ -106,7 +106,7 @@ func main() {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if *verflag {
 | 
			
		||||
		fmt.Printf("Hilbish %s\n", getVersion())
 | 
			
		||||
		fmt.Printf("Hilbish %s\nCompiled with %s\n", getVersion(), runtime.Version())
 | 
			
		||||
		os.Exit(0)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -289,7 +289,7 @@ func removeDupes(slice []string) []string {
 | 
			
		||||
 | 
			
		||||
func contains(s []string, e string) bool {
 | 
			
		||||
	for _, a := range s {
 | 
			
		||||
		if a == e {
 | 
			
		||||
		if strings.ToLower(a) == strings.ToLower(e) {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@ -324,3 +324,7 @@ func getVersion() string {
 | 
			
		||||
 | 
			
		||||
	return v.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func cut(slice []string, idx int) []string {
 | 
			
		||||
	return append(slice[:idx], slice[idx + 1:]...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										92
									
								
								module.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,92 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"plugin"
 | 
			
		||||
 | 
			
		||||
	"hilbish/util"
 | 
			
		||||
 | 
			
		||||
	rt "github.com/arnodel/golua/runtime"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// #interface module
 | 
			
		||||
// native module loading
 | 
			
		||||
// #field paths A list of paths to search when loading native modules. This is in the style of Lua search paths and will be used when requiring native modules. Example: `?.so;?/?.so`
 | 
			
		||||
/*
 | 
			
		||||
The hilbish.module interface provides a function to load
 | 
			
		||||
Hilbish plugins/modules. Hilbish modules are Go-written
 | 
			
		||||
plugins (see https://pkg.go.dev/plugin) that are used to add functionality
 | 
			
		||||
to Hilbish that cannot be written in Lua for any reason.
 | 
			
		||||
 | 
			
		||||
Note that you don't ever need to use the load function that is here as
 | 
			
		||||
modules can be loaded with a `require` call like Lua C modules, and the
 | 
			
		||||
search paths can be changed with the `paths` property here.
 | 
			
		||||
 | 
			
		||||
To make a valid native module, the Go plugin has to export a Loader function
 | 
			
		||||
with a signature like so: `func(*rt.Runtime) rt.Value`.
 | 
			
		||||
 | 
			
		||||
`rt` in this case refers to the Runtime type at
 | 
			
		||||
https://pkg.go.dev/github.com/arnodel/golua@master/runtime#Runtime
 | 
			
		||||
 | 
			
		||||
Hilbish uses this package as its Lua runtime. You will need to read
 | 
			
		||||
it to use it for a native plugin.
 | 
			
		||||
 | 
			
		||||
Here is some code for an example plugin:
 | 
			
		||||
```go
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	rt "github.com/arnodel/golua/runtime"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func Loader(rtm *rt.Runtime) rt.Value {
 | 
			
		||||
	return rt.StringValue("hello world!")
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
This can be compiled with `go build -buildmode=plugin plugin.go`.
 | 
			
		||||
If you attempt to require and print the result (`print(require 'plugin')`), it will show "hello world!"
 | 
			
		||||
*/
 | 
			
		||||
func moduleLoader(rtm *rt.Runtime) *rt.Table {
 | 
			
		||||
	exports := map[string]util.LuaExport{
 | 
			
		||||
		"load": {moduleLoad, 2, false},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mod := rt.NewTable()
 | 
			
		||||
	util.SetExports(rtm, mod, exports)
 | 
			
		||||
 | 
			
		||||
	return mod
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #interface module
 | 
			
		||||
// load(path)
 | 
			
		||||
// Loads a module at the designated `path`.
 | 
			
		||||
// It will throw if any error occurs.
 | 
			
		||||
func moduleLoad(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	if err := c.CheckNArgs(1); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	path, err := c.StringArg(0)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	p, err := plugin.Open(path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	value, err := p.Lookup("Loader")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	loader, ok := value.(func(*rt.Runtime) rt.Value)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	val := loader(t.Runtime)
 | 
			
		||||
 | 
			
		||||
	return c.PushingNext1(t.Runtime, val), nil
 | 
			
		||||
}
 | 
			
		||||
@ -10,7 +10,7 @@ cdr: change directory to one which has been recently visied
 | 
			
		||||
 | 
			
		||||
usage: cdr <index>
 | 
			
		||||
 | 
			
		||||
to get a list of recent directories, use {green}{underline}cdr list{reset}]])
 | 
			
		||||
to get a list of recent directories, use {green}cdr list{reset}]])
 | 
			
		||||
		return
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,10 @@ to get a list of recent directories, use {green}{underline}cdr list{reset}]])
 | 
			
		||||
			sinks.out:writeln 'No directories have been visited.'
 | 
			
		||||
			return 1
 | 
			
		||||
		end
 | 
			
		||||
		sinks.out:writeln(table.concat(recentDirs, '\n'))
 | 
			
		||||
		for idx, d in ipairs(dirs.recentDirs) do
 | 
			
		||||
			if d:find(hilbish.home, 1, true) then d = fs.join('~', d:sub(hilbish.home:len() + 1)) end
 | 
			
		||||
			sinks.out:writeln(lunacolors.format(string.format('{cyan}%d{reset} %s', idx, d)))
 | 
			
		||||
		end
 | 
			
		||||
		return
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,9 @@
 | 
			
		||||
local ansikit = require 'ansikit'
 | 
			
		||||
local commander = require 'commander'
 | 
			
		||||
local fs = require 'fs'
 | 
			
		||||
local lunacolors = require 'lunacolors'
 | 
			
		||||
local Greenhouse = require 'nature.greenhouse'
 | 
			
		||||
local Page = require 'nature.greenhouse.page'
 | 
			
		||||
 | 
			
		||||
commander.register('doc', function(args, sinks)
 | 
			
		||||
	local moddocPath = hilbish.dataDir .. '/docs/'
 | 
			
		||||
@ -9,11 +12,6 @@ commander.register('doc', function(args, sinks)
 | 
			
		||||
		-- hilbish git
 | 
			
		||||
		moddocPath = './docs/'
 | 
			
		||||
	end
 | 
			
		||||
	local apidocHeader = [[
 | 
			
		||||
# %s
 | 
			
		||||
{grayBg}  {white}{italic}%s  {reset}
 | 
			
		||||
 | 
			
		||||
]]
 | 
			
		||||
 | 
			
		||||
	local modules = table.map(fs.readdir(moddocPath), function(f)
 | 
			
		||||
		return lunacolors.underline(lunacolors.blue(string.gsub(f, '.md', '')))
 | 
			
		||||
@ -25,47 +23,15 @@ to Hilbish.
 | 
			
		||||
 | 
			
		||||
Usage: doc <section> [subdoc]
 | 
			
		||||
Available sections: ]] .. table.concat(modules, ', ')
 | 
			
		||||
	if #args > 0 then
 | 
			
		||||
		local mod = args[1]
 | 
			
		||||
 | 
			
		||||
		local f = io.open(moddocPath .. mod .. '.md', 'rb')
 | 
			
		||||
		local funcdocs = nil
 | 
			
		||||
		local subdocName = args[2]
 | 
			
		||||
		if not f then
 | 
			
		||||
			-- assume subdir
 | 
			
		||||
			-- dataDir/docs/<mod>/<mod>.md
 | 
			
		||||
			moddocPath = moddocPath .. mod .. '/'
 | 
			
		||||
			if not subdocName then
 | 
			
		||||
				subdocName = '_index'
 | 
			
		||||
			end
 | 
			
		||||
			f = io.open(moddocPath .. subdocName .. '.md', 'rb')
 | 
			
		||||
			if not f then
 | 
			
		||||
				f = io.open(moddocPath .. subdocName:match '%w+' .. '/' .. subdocName .. '.md', 'rb')
 | 
			
		||||
			end
 | 
			
		||||
			if not f then
 | 
			
		||||
				moddocPath = moddocPath .. subdocName .. '/'
 | 
			
		||||
				subdocName = args[3] or '_index'
 | 
			
		||||
				f = io.open(moddocPath .. subdocName .. '.md', 'rb')
 | 
			
		||||
			end
 | 
			
		||||
			if not f then
 | 
			
		||||
				sinks.out:writeln('No documentation found for ' .. mod .. '.')
 | 
			
		||||
				return 1
 | 
			
		||||
			end
 | 
			
		||||
		end
 | 
			
		||||
		funcdocs = f:read '*a':gsub('-([%d]+)', '%1')
 | 
			
		||||
		local moddocs = table.filter(fs.readdir(moddocPath), function(f) return f ~= '_index.md' and f ~= 'index.md' end)
 | 
			
		||||
		local subdocs = table.map(moddocs, function(fname)
 | 
			
		||||
			return lunacolors.underline(lunacolors.blue(string.gsub(fname, '.md', '')))
 | 
			
		||||
		end)
 | 
			
		||||
		if #moddocs ~= 0 then
 | 
			
		||||
			funcdocs = funcdocs .. '\nSubdocs: ' .. table.concat(subdocs, ', ')
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		local valsStr = funcdocs:match '%-%-%-\n([^%-%-%-]+)\n'
 | 
			
		||||
	local f
 | 
			
		||||
	local function handleYamlInfo(d)
 | 
			
		||||
		local vals = {}
 | 
			
		||||
		local docs = d
 | 
			
		||||
 | 
			
		||||
		local valsStr = docs:match '%-%-%-\n([^%-%-%-]+)\n'
 | 
			
		||||
		print(valsStr)
 | 
			
		||||
		if valsStr then
 | 
			
		||||
			local _, endpos = funcdocs:find('---\n' .. valsStr .. '\n---\n\n', 1, true)
 | 
			
		||||
			funcdocs = funcdocs:sub(endpos + 1, #funcdocs)
 | 
			
		||||
			docs = docs:sub(valsStr:len() + 10, #docs)
 | 
			
		||||
 | 
			
		||||
			-- parse vals
 | 
			
		||||
			local lines = string.split(valsStr, '\n')
 | 
			
		||||
@ -78,23 +44,113 @@ Available sections: ]] .. table.concat(modules, ', ')
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
		end
 | 
			
		||||
		if mod == 'api' then
 | 
			
		||||
			funcdocs = string.format(apidocHeader, vals.title, vals.description or 'no description.') .. funcdocs
 | 
			
		||||
		end
 | 
			
		||||
		doc = funcdocs:sub(1, #funcdocs - 1)
 | 
			
		||||
		f:close()
 | 
			
		||||
 | 
			
		||||
		--docs = docs:sub(1, #docs - 1)
 | 
			
		||||
		return docs, vals
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local backtickOccurence = 0
 | 
			
		||||
	sinks.out:writeln(lunacolors.format(doc:gsub('`', function()
 | 
			
		||||
		backtickOccurence = backtickOccurence + 1
 | 
			
		||||
		if backtickOccurence % 2 == 0 then
 | 
			
		||||
			return '{reset}'
 | 
			
		||||
		else
 | 
			
		||||
			return '{underline}{green}'
 | 
			
		||||
	if #args > 0 then
 | 
			
		||||
		local mod = args[1]
 | 
			
		||||
 | 
			
		||||
		f = io.open(moddocPath .. mod .. '.md', 'rb')
 | 
			
		||||
		local funcdocs = nil
 | 
			
		||||
		local subdocName = args[2]
 | 
			
		||||
		if not f then
 | 
			
		||||
			moddocPath = moddocPath .. mod .. '/'
 | 
			
		||||
			if not subdocName then
 | 
			
		||||
				subdocName = '_index'
 | 
			
		||||
			end
 | 
			
		||||
			f = io.open(moddocPath .. subdocName .. '.md', 'rb')
 | 
			
		||||
			local oldmoddocPath = moddocPath
 | 
			
		||||
			if not f then
 | 
			
		||||
				moddocPath = moddocPath .. subdocName:match '%w+' .. '/'
 | 
			
		||||
				f = io.open(moddocPath .. subdocName .. '.md', 'rb')
 | 
			
		||||
			end
 | 
			
		||||
			if not f then
 | 
			
		||||
				moddocPath = oldmoddocPath .. subdocName .. '/'
 | 
			
		||||
				subdocName = args[3] or '_index'
 | 
			
		||||
				f = io.open(moddocPath .. subdocName .. '.md', 'rb')
 | 
			
		||||
			end
 | 
			
		||||
			if not f then
 | 
			
		||||
				sinks.out:writeln('No documentation found for ' .. mod .. '.')
 | 
			
		||||
				return 1
 | 
			
		||||
			end
 | 
			
		||||
		end
 | 
			
		||||
	end):gsub('\n#+.-\n', function(t)
 | 
			
		||||
		local signature = t:gsub('<.->(.-)</.->', '{underline}%1'):gsub('\\', '<')
 | 
			
		||||
		return '{bold}{yellow}' .. signature .. '{reset}'
 | 
			
		||||
	end)))
 | 
			
		||||
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local moddocs = table.filter(fs.readdir(moddocPath), function(f) return f ~= '_index.md' and f ~= 'index.md' end)
 | 
			
		||||
	local subdocs = table.map(moddocs, function(fname)
 | 
			
		||||
		return lunacolors.underline(lunacolors.blue(string.gsub(fname, '.md', '')))
 | 
			
		||||
	end)
 | 
			
		||||
 | 
			
		||||
	local gh = Greenhouse(sinks.out)
 | 
			
		||||
	function gh:resize()
 | 
			
		||||
		local size = terminal.size()
 | 
			
		||||
		self.region = {
 | 
			
		||||
			width = size.width,
 | 
			
		||||
			height = size.height - 3
 | 
			
		||||
		}
 | 
			
		||||
	end
 | 
			
		||||
	gh:resize()
 | 
			
		||||
 | 
			
		||||
	function gh:render()
 | 
			
		||||
		local workingPage = self.pages[self.curPage]
 | 
			
		||||
		local offset = self.offset
 | 
			
		||||
		if self.isSpecial then
 | 
			
		||||
			offset = self.specialOffset
 | 
			
		||||
			workingPage = self.specialPage
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		self.sink:write(ansikit.getCSI(self.region.height + 2 .. ';1', 'H'))
 | 
			
		||||
		if not self.isSpecial then
 | 
			
		||||
			if args[1] == 'api' then
 | 
			
		||||
				self.sink:writeln(lunacolors.reset(string.format('%s', workingPage.title)))
 | 
			
		||||
				self.sink:write(lunacolors.format(string.format('{grayBg} ↳ {white}{italic}%s {reset}', workingPage.description or 'No description.')))
 | 
			
		||||
			else
 | 
			
		||||
				self.sink:write(lunacolors.reset(string.format('Viewing doc page %s', moddocPath)))
 | 
			
		||||
			end
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
	local backtickOccurence = 0
 | 
			
		||||
	local function formatDocText(d)
 | 
			
		||||
		return lunacolors.format(d:gsub('`', function()
 | 
			
		||||
			backtickOccurence = backtickOccurence + 1
 | 
			
		||||
			if backtickOccurence % 2 == 0 then
 | 
			
		||||
				return '{reset}'
 | 
			
		||||
			else
 | 
			
		||||
				return '{underline}{green}'
 | 
			
		||||
			end
 | 
			
		||||
		end):gsub('\n#+.-\n', function(t)
 | 
			
		||||
			local signature = t:gsub('<.->(.-)</.->', '{underline}%1'):gsub('\\', '<')
 | 
			
		||||
			return '{bold}{yellow}' .. signature .. '{reset}'
 | 
			
		||||
		end))
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	local doc, vals = handleYamlInfo(#args == 0 and doc or formatDocText(f:read '*a':gsub('-([%d]+)', '%1')))
 | 
			
		||||
	if #moddocs ~= 0 and f then
 | 
			
		||||
		doc = doc .. '\nSubdocs: ' .. table.concat(subdocs, ', ') .. '\n\n'
 | 
			
		||||
	end
 | 
			
		||||
	if f then f:close() end
 | 
			
		||||
 | 
			
		||||
	local page = Page(vals.title, doc)
 | 
			
		||||
	page.description = vals.description
 | 
			
		||||
	gh:addPage(page)
 | 
			
		||||
 | 
			
		||||
	-- add subdoc pages
 | 
			
		||||
	for _, sdName in ipairs(moddocs) do
 | 
			
		||||
		local sdFile = fs.join(sdName, '_index.md')
 | 
			
		||||
		if sdName:match '.md$' then
 | 
			
		||||
			sdFile = sdName
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		local f = io.open(moddocPath .. sdFile, 'rb')
 | 
			
		||||
		local doc, vals = handleYamlInfo(f:read '*a':gsub('-([%d]+)', '%1'))
 | 
			
		||||
		local page = Page(vals.title, formatDocText(doc))
 | 
			
		||||
		page.description = vals.description
 | 
			
		||||
		gh:addPage(page)
 | 
			
		||||
	end
 | 
			
		||||
	ansikit.hideCursor()
 | 
			
		||||
	gh:initUi()
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										124
									
								
								nature/commands/greenhouse.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,124 @@
 | 
			
		||||
local ansikit = require 'ansikit'
 | 
			
		||||
local bait = require 'bait'
 | 
			
		||||
local commander = require 'commander'
 | 
			
		||||
local hilbish = require 'hilbish'
 | 
			
		||||
local lunacolors = require 'lunacolors'
 | 
			
		||||
local terminal = require 'terminal'
 | 
			
		||||
local Greenhouse = require 'nature.greenhouse'
 | 
			
		||||
local Page = require 'nature.greenhouse.page'
 | 
			
		||||
 | 
			
		||||
commander.register('greenhouse', function(args, sinks)
 | 
			
		||||
	local gh = Greenhouse(sinks.out)
 | 
			
		||||
 | 
			
		||||
	local buffer = ''
 | 
			
		||||
	local display = ''
 | 
			
		||||
	local command = false
 | 
			
		||||
	local commands = {
 | 
			
		||||
		q = function()
 | 
			
		||||
			gh.keybinds['Ctrl-D'](gh)
 | 
			
		||||
		end,
 | 
			
		||||
		['goto'] = function(args)
 | 
			
		||||
			if not args[1] then
 | 
			
		||||
				return 'nuh uh'
 | 
			
		||||
			end
 | 
			
		||||
			gh:jump(tonumber(args[1]))
 | 
			
		||||
		end
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	function gh:resize()
 | 
			
		||||
		local size = terminal.size()
 | 
			
		||||
		self.region = {
 | 
			
		||||
			width = size.width,
 | 
			
		||||
			height = size.height - 2
 | 
			
		||||
		}
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	function gh:render()
 | 
			
		||||
		local workingPage = self.pages[self.curPage]
 | 
			
		||||
		local offset = self.offset
 | 
			
		||||
		if self.isSpecial then
 | 
			
		||||
			offset = self.specialOffset
 | 
			
		||||
			workingPage = self.specialPage
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		self.sink:write(ansikit.getCSI(self.region.height + 1 .. ';1', 'H'))
 | 
			
		||||
		if not self.isSpecial then
 | 
			
		||||
			self.sink:writeln(lunacolors.format(string.format('{grayBg} ↳ Page %d%s{reset}', self.curPage, workingPage.title and ' — ' .. workingPage.title .. ' ' or '')))
 | 
			
		||||
		end
 | 
			
		||||
		self.sink:write(buffer == '' and display or buffer)
 | 
			
		||||
	end
 | 
			
		||||
	function gh:input(c)
 | 
			
		||||
		-- command handling
 | 
			
		||||
		if c == ':' and not command then
 | 
			
		||||
			command = true
 | 
			
		||||
		end
 | 
			
		||||
		if c == 'Escape' then
 | 
			
		||||
			if command then
 | 
			
		||||
				command = false
 | 
			
		||||
				buffer = ''
 | 
			
		||||
			else
 | 
			
		||||
				if self.isSpecial then gh:special() end
 | 
			
		||||
			end
 | 
			
		||||
		elseif c == 'Backspace' then
 | 
			
		||||
			buffer = buffer:sub(0, -2)
 | 
			
		||||
			if buffer == '' then
 | 
			
		||||
				command = false
 | 
			
		||||
			else
 | 
			
		||||
				goto update
 | 
			
		||||
			end
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		if command then
 | 
			
		||||
			ansikit.showCursor()
 | 
			
		||||
			if buffer:match '^:' then buffer = buffer .. c else buffer = c end
 | 
			
		||||
		else
 | 
			
		||||
			ansikit.hideCursor()
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		::update::
 | 
			
		||||
		gh:update()
 | 
			
		||||
	end
 | 
			
		||||
	gh:resize()
 | 
			
		||||
 | 
			
		||||
	gh:keybind('Enter', function(self)
 | 
			
		||||
		if self.isSpecial then
 | 
			
		||||
			self:jump(self.specialPageIdx)
 | 
			
		||||
			self:special(false)
 | 
			
		||||
		else
 | 
			
		||||
			if buffer:len() < 2 then return end
 | 
			
		||||
 | 
			
		||||
			local splitBuf = string.split(buffer, " ")
 | 
			
		||||
			local command = commands[splitBuf[1]:sub(2)]
 | 
			
		||||
			if command then
 | 
			
		||||
				table.remove(splitBuf, 1)
 | 
			
		||||
				buffer = command(splitBuf) or ''
 | 
			
		||||
			end
 | 
			
		||||
			self:update()
 | 
			
		||||
		end
 | 
			
		||||
	end)
 | 
			
		||||
 | 
			
		||||
	if sinks['in'].pipe then
 | 
			
		||||
		local page = Page('stdin', sinks['in']:readAll())
 | 
			
		||||
		gh:addPage(page)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	for _, name in ipairs(args) do
 | 
			
		||||
		local f <close> = io.open(name, 'r')
 | 
			
		||||
		if not f then
 | 
			
		||||
			sinks.err:writeln(string.format('could not open file %s', name))
 | 
			
		||||
		end
 | 
			
		||||
		local page = Page(name, f:read '*a')
 | 
			
		||||
		gh:addPage(page)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	if #gh.pages == 0 then
 | 
			
		||||
		sinks.out:writeln [[greenhouse is the Hilbish pager library and command!
 | 
			
		||||
usage: greenhouse <file>...
 | 
			
		||||
 | 
			
		||||
example: greenhouse hello.md]]
 | 
			
		||||
		return 1
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	ansikit.hideCursor()
 | 
			
		||||
	gh:initUi()
 | 
			
		||||
end)
 | 
			
		||||
							
								
								
									
										328
									
								
								nature/greenhouse/init.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,328 @@
 | 
			
		||||
-- Greenhouse is a simple text scrolling handler for terminal programs.
 | 
			
		||||
-- The idea is that it can be set a region to do its scrolling and paging
 | 
			
		||||
-- job and then the user can draw whatever outside it.
 | 
			
		||||
-- This reduces code duplication for the message viewer
 | 
			
		||||
-- and flowerbook.
 | 
			
		||||
 | 
			
		||||
local ansikit = require 'ansikit'
 | 
			
		||||
local lunacolors = require 'lunacolors'
 | 
			
		||||
local terminal = require 'terminal'
 | 
			
		||||
local Page = require 'nature.greenhouse.page'
 | 
			
		||||
local Object = require 'nature.object'
 | 
			
		||||
 | 
			
		||||
local Greenhouse = Object:extend()
 | 
			
		||||
 | 
			
		||||
function Greenhouse:new(sink)
 | 
			
		||||
	local size = terminal.size()
 | 
			
		||||
	self.region = size
 | 
			
		||||
	self.contents = nil -- or can be a table
 | 
			
		||||
	self.start = 1 -- where to start drawing from (should replace with self.region.y)
 | 
			
		||||
	self.offset = 1 -- vertical text offset
 | 
			
		||||
	self.sink = sink
 | 
			
		||||
	self.pages = {}
 | 
			
		||||
	self.curPage = 1
 | 
			
		||||
	self.keybinds = {
 | 
			
		||||
		['Up'] = function(self) self:scroll 'up' end,
 | 
			
		||||
		['Down'] = function(self) self:scroll 'down' end,
 | 
			
		||||
		['Ctrl-Left'] = self.previous,
 | 
			
		||||
		['Ctrl-Right'] = self.next,
 | 
			
		||||
		['Ctrl-N'] = function(self) self:toc(true) end,
 | 
			
		||||
		['Enter'] = function(self)
 | 
			
		||||
			if self.isSpecial then
 | 
			
		||||
				self:jump(self.specialPageIdx)
 | 
			
		||||
				self:special(false)
 | 
			
		||||
			end
 | 
			
		||||
		end
 | 
			
		||||
	}
 | 
			
		||||
	self.isSpecial = false
 | 
			
		||||
	self.specialPage = nil
 | 
			
		||||
	self.specialPageIdx = 1
 | 
			
		||||
	self.specialOffset = 1
 | 
			
		||||
 | 
			
		||||
	return self
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Greenhouse:addPage(page)
 | 
			
		||||
	table.insert(self.pages, page)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Greenhouse:updateCurrentPage(text)
 | 
			
		||||
	local page = self.pages[self.curPage]
 | 
			
		||||
	page:setText(text)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function sub(str, limit)
 | 
			
		||||
	local overhead = 0
 | 
			
		||||
	local function addOverhead(s)
 | 
			
		||||
		overhead = overhead + string.len(s)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local s = str:gsub('\x1b%[%d+;%d+;%d+;%d+;%d+%w', addOverhead)
 | 
			
		||||
     :gsub('\x1b%[%d+;%d+;%d+;%d+%w', addOverhead)
 | 
			
		||||
     :gsub('\x1b%[%d+;%d+;%d+%w',addOverhead)
 | 
			
		||||
     :gsub('\x1b%[%d+;%d+%w', addOverhead)
 | 
			
		||||
     :gsub('\x1b%[%d+%w', addOverhead)
 | 
			
		||||
 | 
			
		||||
	return s:sub(0, limit + overhead)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Greenhouse:draw()
 | 
			
		||||
	local workingPage = self.pages[self.curPage]
 | 
			
		||||
	local offset = self.offset
 | 
			
		||||
	if self.isSpecial then
 | 
			
		||||
		offset = self.specialOffset
 | 
			
		||||
		workingPage = self.specialPage
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	if workingPage.lazy and not workingPage.loaded then
 | 
			
		||||
		workingPage.initialize()
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local lines = workingPage.lines
 | 
			
		||||
	self.sink:write(ansikit.getCSI(self.start .. ';1', 'H'))
 | 
			
		||||
	self.sink:write(ansikit.getCSI(2, 'J'))
 | 
			
		||||
 | 
			
		||||
	for i = offset, offset + self.region.height - 1 do
 | 
			
		||||
		if i > #lines then break end
 | 
			
		||||
 | 
			
		||||
		local writer = self.sink.writeln
 | 
			
		||||
		if i == offset + self.region.height - 1 then writer = self.sink.write end
 | 
			
		||||
 | 
			
		||||
		writer(self.sink, sub(lines[i]:gsub('\t', '        '), self.region.width))
 | 
			
		||||
	end
 | 
			
		||||
	self:render()
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Greenhouse:render()
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Greenhouse:scroll(direction)
 | 
			
		||||
	if self.isSpecial then
 | 
			
		||||
		if direction == 'down' then
 | 
			
		||||
			self:next(true)
 | 
			
		||||
		elseif direction == 'up' then
 | 
			
		||||
			self:previous(true)
 | 
			
		||||
		end
 | 
			
		||||
		return
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local lines = self.pages[self.curPage].lines
 | 
			
		||||
 | 
			
		||||
	local oldOffset = self.offset
 | 
			
		||||
	if direction == 'down' then
 | 
			
		||||
		self.offset = math.min(self.offset + 1, math.max(1, #lines - self.region.height))
 | 
			
		||||
	elseif direction == 'up' then
 | 
			
		||||
		self.offset = math.max(self.offset - 1, 1)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	if self.offset ~= oldOffset then self:draw() end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Greenhouse:update()
 | 
			
		||||
	self:resize()
 | 
			
		||||
	if self.isSpecial then
 | 
			
		||||
		self:updateSpecial()
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	self:draw()
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
function Greenhouse:special(val)
 | 
			
		||||
	self.isSpecial = val
 | 
			
		||||
	self:update()
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Greenhouse:toggleSpecial()
 | 
			
		||||
	self:special(not self.isSpecial)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
--- This function will be called when the special page
 | 
			
		||||
--- is on and needs to be updated.
 | 
			
		||||
function Greenhouse:updateSpecial()
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Greenhouse:contents()
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Greenhouse:toc(toggle)
 | 
			
		||||
	if not self.isSpecial then
 | 
			
		||||
		self.specialPageIdx = self.curPage
 | 
			
		||||
	end
 | 
			
		||||
	if toggle then self.isSpecial = not self.isSpecial end
 | 
			
		||||
	-- Generate a special page for our table of contents
 | 
			
		||||
	local tocText = string.format([[
 | 
			
		||||
%s
 | 
			
		||||
 | 
			
		||||
]], lunacolors.cyan(lunacolors.bold '―― Table of Contents ――'))
 | 
			
		||||
 | 
			
		||||
	local genericPageCount = 1
 | 
			
		||||
	local contents = self:contents()
 | 
			
		||||
	if contents then
 | 
			
		||||
		for i, c in ipairs(contents) do
 | 
			
		||||
			local title = c.title
 | 
			
		||||
			if c.active then
 | 
			
		||||
				title = lunacolors.invert(title)
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
			tocText = tocText .. title .. '\n'
 | 
			
		||||
		end
 | 
			
		||||
	else
 | 
			
		||||
		for i, page in ipairs(self.pages) do
 | 
			
		||||
			local title = page.title
 | 
			
		||||
			if title == 'Page' then
 | 
			
		||||
				title = 'Page #' .. genericPageCount
 | 
			
		||||
				genericPageCount = genericPageCount + 1
 | 
			
		||||
			end
 | 
			
		||||
			if i == self.specialPageIdx then
 | 
			
		||||
				title = lunacolors.invert(title)
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
			tocText = tocText .. title .. '\n'
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
	self.specialPage = Page('TOC', tocText)
 | 
			
		||||
	function self:updateSpecial()
 | 
			
		||||
		self:toc()
 | 
			
		||||
	end
 | 
			
		||||
	self:draw()
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Greenhouse:resize()
 | 
			
		||||
	local size = terminal.size()
 | 
			
		||||
	self.region = size
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Greenhouse:next(special)
 | 
			
		||||
	local oldCurrent = special and self.specialPageIdx or self.curPage
 | 
			
		||||
	local pageIdx = math.min(oldCurrent + 1, #self.pages)
 | 
			
		||||
 | 
			
		||||
	if special then
 | 
			
		||||
		self.specialPageIdx = pageIdx
 | 
			
		||||
	else
 | 
			
		||||
		self.curPage = pageIdx
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	if pageIdx ~= oldCurrent then
 | 
			
		||||
		self.offset = 1
 | 
			
		||||
		self:update()
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Greenhouse:previous(special)
 | 
			
		||||
	local oldCurrent = special and self.specialPageIdx or self.curPage
 | 
			
		||||
	local pageIdx = math.max(self.curPage - 1, 1)
 | 
			
		||||
 | 
			
		||||
	if special then
 | 
			
		||||
		self.specialPageIdx = pageIdx
 | 
			
		||||
	else
 | 
			
		||||
		self.curPage = pageIdx
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	if pageIdx ~= oldCurrent then
 | 
			
		||||
		self.offset = 1
 | 
			
		||||
		self:update()
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Greenhouse:jump(idx)
 | 
			
		||||
	if idx ~= self.curPage then
 | 
			
		||||
		self.offset = 1
 | 
			
		||||
	end
 | 
			
		||||
	self.curPage = idx
 | 
			
		||||
	self:update()
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Greenhouse:keybind(key, callback)
 | 
			
		||||
	self.keybinds[key] = callback
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Greenhouse:input(char)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Greenhouse:initUi()
 | 
			
		||||
	local ansikit = require 'ansikit'
 | 
			
		||||
	local bait = require 'bait'
 | 
			
		||||
	local commander = require 'commander'
 | 
			
		||||
	local hilbish = require 'hilbish'
 | 
			
		||||
	local terminal = require 'terminal'
 | 
			
		||||
	local Page = require 'nature.greenhouse.page'
 | 
			
		||||
	local done = false
 | 
			
		||||
 | 
			
		||||
	bait.catch('signal.sigint', function()
 | 
			
		||||
		ansikit.clear()
 | 
			
		||||
		done = true
 | 
			
		||||
	end)
 | 
			
		||||
 | 
			
		||||
	bait.catch('signal.resize', function()
 | 
			
		||||
		self:update()
 | 
			
		||||
	end)
 | 
			
		||||
 | 
			
		||||
	ansikit.screenAlt()
 | 
			
		||||
	ansikit.clear(true)
 | 
			
		||||
	self:draw()
 | 
			
		||||
 | 
			
		||||
	hilbish.goro(function()
 | 
			
		||||
		while not done do
 | 
			
		||||
			local c = read()
 | 
			
		||||
			self:keybind('Ctrl-D', function()
 | 
			
		||||
				done = true
 | 
			
		||||
			end)
 | 
			
		||||
 | 
			
		||||
			if self.keybinds[c] then
 | 
			
		||||
				self.keybinds[c](self)
 | 
			
		||||
			else
 | 
			
		||||
				self:input(c)
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
	--[[
 | 
			
		||||
			if c == 27 then
 | 
			
		||||
				local c1 = read()
 | 
			
		||||
				if c1 == 91 then
 | 
			
		||||
					local c2 = read()
 | 
			
		||||
					if c2 == 66 then -- arrow down
 | 
			
		||||
						self:scroll 'down'
 | 
			
		||||
					elseif c2 == 65 then -- arrow up
 | 
			
		||||
						self:scroll 'up'
 | 
			
		||||
					end
 | 
			
		||||
 | 
			
		||||
					if c2 == 49 then
 | 
			
		||||
						local c3 = read()
 | 
			
		||||
						if c3 == 59 then
 | 
			
		||||
							local c4 = read()
 | 
			
		||||
							if c4 == 53 then
 | 
			
		||||
								local c5 = read()
 | 
			
		||||
								if c5 == 67 then
 | 
			
		||||
									self:next()
 | 
			
		||||
								elseif c5 == 68 then
 | 
			
		||||
									self:previous()
 | 
			
		||||
								end
 | 
			
		||||
							end
 | 
			
		||||
						end
 | 
			
		||||
					end
 | 
			
		||||
				end
 | 
			
		||||
				goto continue
 | 
			
		||||
			end
 | 
			
		||||
			]]--
 | 
			
		||||
 | 
			
		||||
			::continue::
 | 
			
		||||
		end
 | 
			
		||||
	end)
 | 
			
		||||
 | 
			
		||||
	while not done do
 | 
			
		||||
		--
 | 
			
		||||
	end
 | 
			
		||||
	ansikit.showCursor()
 | 
			
		||||
	ansikit.screenMain()
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function read()
 | 
			
		||||
	terminal.saveState()
 | 
			
		||||
	terminal.setRaw()
 | 
			
		||||
	local c = hilbish.editor.readChar()
 | 
			
		||||
 | 
			
		||||
	terminal.restoreState()
 | 
			
		||||
	return c
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return Greenhouse
 | 
			
		||||
							
								
								
									
										32
									
								
								nature/greenhouse/page.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,32 @@
 | 
			
		||||
local Object = require 'nature.object'
 | 
			
		||||
 | 
			
		||||
local Page = Object:extend()
 | 
			
		||||
 | 
			
		||||
function Page:new(title, text)
 | 
			
		||||
	self:setText(text)
 | 
			
		||||
	self.title = title or 'Page'
 | 
			
		||||
	self.lazy = false
 | 
			
		||||
	self.loaded = true
 | 
			
		||||
	self.children = {}
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Page:setText(text)
 | 
			
		||||
	self.lines = string.split(text, '\n')
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Page:setTitle(title)
 | 
			
		||||
	self.title = title
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Page:dynamic(initializer)
 | 
			
		||||
	self.initializer = initializer
 | 
			
		||||
	self.lazy = true
 | 
			
		||||
	self.loaded = false
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function Page:initialize()
 | 
			
		||||
	self.initializer()
 | 
			
		||||
	self.loaded = true
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return Page
 | 
			
		||||
							
								
								
									
										84
									
								
								nature/hummingbird.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,84 @@
 | 
			
		||||
local bait = require 'bait'
 | 
			
		||||
local commander = require 'commander'
 | 
			
		||||
local lunacolors = require 'lunacolors'
 | 
			
		||||
 | 
			
		||||
local M = {}
 | 
			
		||||
local counter = 0
 | 
			
		||||
local unread = 0
 | 
			
		||||
M._messages = {}
 | 
			
		||||
M.icons = {
 | 
			
		||||
	INFO = '',
 | 
			
		||||
	SUCCESS = '',
 | 
			
		||||
	WARN = '',
 | 
			
		||||
	ERROR = ''
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
hilbish.messages = {}
 | 
			
		||||
 | 
			
		||||
--- Represents a Hilbish message.
 | 
			
		||||
--- @class hilbish.message
 | 
			
		||||
--- @field icon string Unicode (preferably standard emoji) icon for the message notification.
 | 
			
		||||
--- @field title string Title of the message (like an email subject).
 | 
			
		||||
--- @field text string Contents of the message.
 | 
			
		||||
--- @field channel string Short identifier of the message. `hilbish` and `hilbish.*` is preserved for internal Hilbish messages.
 | 
			
		||||
--- @field summary string A short summary of the message.
 | 
			
		||||
--- @field read boolean Whether the full message has been read or not.
 | 
			
		||||
 | 
			
		||||
function expect(tbl, field)
 | 
			
		||||
	if not tbl[field] or tbl[field] == '' then
 | 
			
		||||
		error(string.format('expected field %s in message'))
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
--- Sends a message.
 | 
			
		||||
--- @param message hilbish.message
 | 
			
		||||
function hilbish.messages.send(message)
 | 
			
		||||
	expect(message, 'text')
 | 
			
		||||
	expect(message, 'title')
 | 
			
		||||
	counter = counter + 1
 | 
			
		||||
	unread = unread + 1
 | 
			
		||||
	message.index = counter
 | 
			
		||||
	message.read = false
 | 
			
		||||
 | 
			
		||||
	M._messages[message.index] = message
 | 
			
		||||
	bait.throw('hilbish.notification', message)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function hilbish.messages.read(idx)
 | 
			
		||||
	local msg = M._messages[idx]
 | 
			
		||||
	if msg then
 | 
			
		||||
		M._messages[idx].read = true
 | 
			
		||||
		unread = unread - 1
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function hilbish.messages.readAll(idx)
 | 
			
		||||
	for _, msg in ipairs(hilbish.messages.all()) do
 | 
			
		||||
		hilbish.messages.read(msg.index)
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function hilbish.messages.unreadCount()
 | 
			
		||||
	return unread
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function hilbish.messages.delete(idx)
 | 
			
		||||
	local msg = M._messages[idx]
 | 
			
		||||
	if not msg then
 | 
			
		||||
		error(string.format('invalid message index %d', idx or -1))
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	M._messages[idx] = nil
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function hilbish.messages.clear()
 | 
			
		||||
	for _, msg in ipairs(hilbish.messages.all()) do
 | 
			
		||||
		hilbish.messages.delete(msg.index)
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function hilbish.messages.all()
 | 
			
		||||
	return M._messages
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return M
 | 
			
		||||
@ -6,11 +6,24 @@ local fs = require 'fs'
 | 
			
		||||
package.path = package.path .. ';' .. hilbish.dataDir .. '/?/init.lua'
 | 
			
		||||
.. ';' .. hilbish.dataDir .. '/?/?.lua' .. ";" .. hilbish.dataDir .. '/?.lua'
 | 
			
		||||
 | 
			
		||||
hilbish.module.paths = '?.so;?/?.so;'
 | 
			
		||||
.. hilbish.userDir.data .. 'hilbish/libs/?/?.so'
 | 
			
		||||
.. ";" .. hilbish.userDir.data .. 'hilbish/libs/?.so'
 | 
			
		||||
 | 
			
		||||
table.insert(package.searchers, function(module)
 | 
			
		||||
	local path = package.searchpath(module, hilbish.module.paths)
 | 
			
		||||
	if not path then return nil end
 | 
			
		||||
 | 
			
		||||
	-- it didnt work normally, idk
 | 
			
		||||
	return function() return hilbish.module.load(path) end, path
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
require 'nature.commands'
 | 
			
		||||
require 'nature.completions'
 | 
			
		||||
require 'nature.opts'
 | 
			
		||||
require 'nature.vim'
 | 
			
		||||
require 'nature.runner'
 | 
			
		||||
require 'nature.hummingbird'
 | 
			
		||||
 | 
			
		||||
local shlvl = tonumber(os.getenv 'SHLVL')
 | 
			
		||||
if shlvl ~= nil then
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										59
									
								
								nature/object.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,59 @@
 | 
			
		||||
---@class nature.object
 | 
			
		||||
---@field super nature.object
 | 
			
		||||
local Object = {}
 | 
			
		||||
Object.__index = Object
 | 
			
		||||
 | 
			
		||||
---Can be overrided by child objects to implement a constructor.
 | 
			
		||||
function Object:new() end
 | 
			
		||||
 | 
			
		||||
---@return nature.object
 | 
			
		||||
function Object:extend()
 | 
			
		||||
  local cls = {}
 | 
			
		||||
  for k, v in pairs(self) do
 | 
			
		||||
    if k:find("__") == 1 then
 | 
			
		||||
      cls[k] = v
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
  cls.__index = cls
 | 
			
		||||
  cls.super = self
 | 
			
		||||
  setmetatable(cls, self)
 | 
			
		||||
  return cls
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
---Check if the object is strictly of the given type.
 | 
			
		||||
---@param T any
 | 
			
		||||
---@return boolean
 | 
			
		||||
function Object:is(T)
 | 
			
		||||
  return getmetatable(self) == T
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
---Check if the object inherits from the given type.
 | 
			
		||||
---@param T any
 | 
			
		||||
---@return boolean
 | 
			
		||||
function Object:extends(T)
 | 
			
		||||
  local mt = getmetatable(self)
 | 
			
		||||
  while mt do
 | 
			
		||||
    if mt == T then
 | 
			
		||||
      return true
 | 
			
		||||
    end
 | 
			
		||||
    mt = getmetatable(mt)
 | 
			
		||||
  end
 | 
			
		||||
  return false
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
---Metamethod to get a string representation of an object.
 | 
			
		||||
---@return string
 | 
			
		||||
function Object:__tostring()
 | 
			
		||||
  return "Object"
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
---Methamethod to allow using the object call as a constructor.
 | 
			
		||||
---@return nature.object
 | 
			
		||||
function Object:__call(...)
 | 
			
		||||
  local obj = setmetatable({}, self)
 | 
			
		||||
  obj:new(...)
 | 
			
		||||
  return obj
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
return Object
 | 
			
		||||
@ -16,7 +16,7 @@ setmetatable(hilbish.opts, {
 | 
			
		||||
 | 
			
		||||
local function setupOpt(name, default)
 | 
			
		||||
	opts[name] = default
 | 
			
		||||
	require('nature.opts.' .. name)
 | 
			
		||||
	pcall(require, 'nature.opts.' .. name)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local defaultOpts = {
 | 
			
		||||
@ -25,7 +25,9 @@ local defaultOpts = {
 | 
			
		||||
	greeting = string.format([[Welcome to {magenta}Hilbish{reset}, {cyan}%s{reset}.
 | 
			
		||||
The nice lil shell for {blue}Lua{reset} fanatics!
 | 
			
		||||
]], hilbish.user),
 | 
			
		||||
	motd = true
 | 
			
		||||
	motd = true,
 | 
			
		||||
	fuzzy = false,
 | 
			
		||||
	notifyJobFinish = true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
for optsName, default in pairs(defaultOpts) do
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										23
									
								
								nature/opts/notifyJobFinish.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,23 @@
 | 
			
		||||
local bait = require 'bait'
 | 
			
		||||
local lunacolors = require 'lunacolors'
 | 
			
		||||
 | 
			
		||||
bait.catch('job.done', function(job)
 | 
			
		||||
	if not hilbish.opts.notifyJobFinish then return end
 | 
			
		||||
	local notifText = string.format(lunacolors.format [[
 | 
			
		||||
Background job with ID#%d has exited (PID %d).
 | 
			
		||||
Command string: {bold}{yellow}%s{reset}]], job.id, job.pid, job.cmd)
 | 
			
		||||
 | 
			
		||||
	if job.stdout ~= '' then
 | 
			
		||||
		notifText = notifText .. '\n\nStandard output:\n' .. job.stdout
 | 
			
		||||
	end
 | 
			
		||||
	if job.stderr ~= '' then
 | 
			
		||||
		notifText = notifText .. '\n\nStandard error:\n' .. job.stderr
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	hilbish.messages.send {
 | 
			
		||||
		channel = 'jobNotify',
 | 
			
		||||
		title = string.format('Job ID#%d Exited', job.id),
 | 
			
		||||
		summary = string.format(lunacolors.format 'Background job with command {bold}{yellow}%s{reset} has finished running!', job.cmd),
 | 
			
		||||
		text = notifText
 | 
			
		||||
	}
 | 
			
		||||
end)
 | 
			
		||||
@ -1,5 +1,7 @@
 | 
			
		||||
package readline
 | 
			
		||||
 | 
			
		||||
import "os"
 | 
			
		||||
 | 
			
		||||
// Character codes
 | 
			
		||||
const (
 | 
			
		||||
	charCtrlA = iota + 1
 | 
			
		||||
@ -134,3 +136,57 @@ const (
 | 
			
		||||
const (
 | 
			
		||||
	seqCtermFg255 = "\033[48;5;255m"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// TODO: return whether its actually a sequence or not
 | 
			
		||||
// remedies the edge case of someone literally typing Ctrl-A for example.
 | 
			
		||||
func (rl *Instance) ReadChar() string {
 | 
			
		||||
	b := make([]byte, 1024)
 | 
			
		||||
	i, _ := os.Stdin.Read(b)
 | 
			
		||||
	r := []rune(string(b))
 | 
			
		||||
	s := string(r[:i])
 | 
			
		||||
 | 
			
		||||
	switch b[0] {
 | 
			
		||||
		case charCtrlA: return "Ctrl-A"
 | 
			
		||||
		case charCtrlB: return "Ctrl-B"
 | 
			
		||||
		case charCtrlC: return "Ctrl-C"
 | 
			
		||||
		case charEOF: return "Ctrl-D"
 | 
			
		||||
		case charCtrlE: return "Ctrl-E"
 | 
			
		||||
		case charCtrlF: return "Ctrl-F"
 | 
			
		||||
		case charCtrlG: return "Ctrl-G"
 | 
			
		||||
		case charBackspace, charBackspace2: return "Backspace"
 | 
			
		||||
		case charTab: return "Tab"
 | 
			
		||||
		case charCtrlK: return "Ctrl-K"
 | 
			
		||||
		case charCtrlL: return "Ctrl-L"
 | 
			
		||||
		case charCtrlN: return "Ctrl-N"
 | 
			
		||||
		case charCtrlO: return "Ctrl-O"
 | 
			
		||||
		case charCtrlP: return "Ctrl-P"
 | 
			
		||||
		case charCtrlQ: return "Ctrl-Q"
 | 
			
		||||
		case charCtrlR: return "Ctrl-R"
 | 
			
		||||
		case charCtrlS: return "Ctrl-S"
 | 
			
		||||
		case charCtrlT: return "Ctrl-T"
 | 
			
		||||
		case charCtrlU: return "Ctrl-U"
 | 
			
		||||
		case charCtrlV: return "Ctrl-V"
 | 
			
		||||
		case charCtrlW: return "Ctrl-W"
 | 
			
		||||
		case charCtrlX: return "Ctrl-X"
 | 
			
		||||
		case charCtrlY: return "Ctrl-Y"
 | 
			
		||||
		case charCtrlZ: return "Ctrl-Z"
 | 
			
		||||
		case '\r': fallthrough
 | 
			
		||||
		case '\n': return "Enter"
 | 
			
		||||
		case charEscape:
 | 
			
		||||
			switch s {
 | 
			
		||||
				case string(charEscape): return "Escape"
 | 
			
		||||
				case seqUp: return "Up"
 | 
			
		||||
				case seqDown: return "Down"
 | 
			
		||||
				case seqBackwards: return "Left"
 | 
			
		||||
				case seqForwards: return "Right"
 | 
			
		||||
				case seqCtrlLeftArrow: return "Ctrl-Left"
 | 
			
		||||
				case seqCtrlRightArrow: return "Ctrl-Right"
 | 
			
		||||
				case seqCtrlDelete, seqCtrlDelete2: return "Ctrl-Delete"
 | 
			
		||||
				case seqHome, seqHomeSc: return "Home"
 | 
			
		||||
				case seqEnd, seqEndSc: return "End"
 | 
			
		||||
				case seqDelete, seqDelete2: return "Delete"
 | 
			
		||||
			}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -71,10 +71,9 @@ func (g *CompletionGroup) init(rl *Instance) {
 | 
			
		||||
// The rx parameter is passed, as the shell already checked that the search pattern is valid.
 | 
			
		||||
func (g *CompletionGroup) updateTabFind(rl *Instance) {
 | 
			
		||||
 | 
			
		||||
	suggs := make([]string, 0)
 | 
			
		||||
 | 
			
		||||
	suggs := rl.Searcher(rl.search, g.Suggestions)
 | 
			
		||||
	// We perform filter right here, so we create a new completion group, and populate it with our results.
 | 
			
		||||
	for i := range g.Suggestions {
 | 
			
		||||
	/*for i := range g.Suggestions {
 | 
			
		||||
		if rl.regexSearch == nil { continue }
 | 
			
		||||
		if rl.regexSearch.MatchString(g.Suggestions[i]) {
 | 
			
		||||
			suggs = append(suggs, g.Suggestions[i])
 | 
			
		||||
@ -82,7 +81,7 @@ func (g *CompletionGroup) updateTabFind(rl *Instance) {
 | 
			
		||||
			// this is a list so lets also check the descriptions
 | 
			
		||||
			suggs = append(suggs, g.Suggestions[i])
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	}*/
 | 
			
		||||
 | 
			
		||||
	// We overwrite the group's items, (will be refreshed as soon as something is typed in the search)
 | 
			
		||||
	g.Suggestions = suggs
 | 
			
		||||
 | 
			
		||||
@ -167,8 +167,8 @@ func (rl *Instance) walkHistory(i int) {
 | 
			
		||||
	rl.updateHelpers()
 | 
			
		||||
 | 
			
		||||
	// In order to avoid having to type j/k twice each time for history navigation,
 | 
			
		||||
	// we walk once again. This only ever happens when we aren't out of bounds.
 | 
			
		||||
	if dedup && old == new {
 | 
			
		||||
	// we walk once again. This only ever happens when we aren't out of bounds and the last history item was not a empty string.
 | 
			
		||||
	if new != "" && dedup && old == new {
 | 
			
		||||
		rl.walkHistory(i)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -112,8 +112,10 @@ type Instance struct {
 | 
			
		||||
	modeAutoFind bool           // for when invoked via ^R or ^F outside of [tab]
 | 
			
		||||
	searchMode   FindMode       // Used for varying hints, and underlying functions called
 | 
			
		||||
	regexSearch  *regexp.Regexp // Holds the current search regex match
 | 
			
		||||
	search       string
 | 
			
		||||
	mainHist     bool           // Which history stdin do we want
 | 
			
		||||
	histInfo     []rune         // We store a piece of hist info, for dual history sources
 | 
			
		||||
	Searcher func(string, []string) []string
 | 
			
		||||
 | 
			
		||||
	//
 | 
			
		||||
	// History -----------------------------------------------------------------------------------
 | 
			
		||||
@ -229,6 +231,25 @@ func NewInstance() *Instance {
 | 
			
		||||
	rl.HintFormatting = "\x1b[2m"
 | 
			
		||||
	rl.evtKeyPress = make(map[string]func(string, []rune, int) *EventReturn)
 | 
			
		||||
	rl.TempDirectory = os.TempDir()
 | 
			
		||||
	rl.Searcher = func(needle string, haystack []string) []string {
 | 
			
		||||
		suggs := make([]string, 0)
 | 
			
		||||
 | 
			
		||||
		var err error
 | 
			
		||||
		rl.regexSearch, err = regexp.Compile("(?i)" + string(rl.tfLine))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			rl.RefreshPromptLog(err.Error())
 | 
			
		||||
			rl.infoText = []rune(Red("Failed to match search regexp"))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, hay := range haystack {
 | 
			
		||||
			if rl.regexSearch == nil { continue }
 | 
			
		||||
			if rl.regexSearch.MatchString(hay) {
 | 
			
		||||
				suggs = append(suggs, hay)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return suggs
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Registers
 | 
			
		||||
	rl.initRegisters()
 | 
			
		||||
 | 
			
		||||
@ -94,7 +94,7 @@ func (rl *Instance) getTabSearchCompletion() {
 | 
			
		||||
	rl.getCurrentGroup()
 | 
			
		||||
 | 
			
		||||
	// Set the info for this completion mode
 | 
			
		||||
	rl.infoText = append([]rune("Completion search: "), rl.tfLine...)
 | 
			
		||||
	rl.infoText = append([]rune("Completion search: " + UNDERLINE + BOLD), rl.tfLine...)
 | 
			
		||||
 | 
			
		||||
	for _, g := range rl.tcGroups {
 | 
			
		||||
		g.updateTabFind(rl)
 | 
			
		||||
@ -102,7 +102,7 @@ func (rl *Instance) getTabSearchCompletion() {
 | 
			
		||||
 | 
			
		||||
	// If total number of matches is zero, we directly change the info, and return
 | 
			
		||||
	if comps, _, _ := rl.getCompletionCount(); comps == 0 {
 | 
			
		||||
		rl.infoText = append(rl.infoText, []rune(DIM+RED+" ! no matches (Ctrl-G/Esc to cancel)"+RESET)...)
 | 
			
		||||
		rl.infoText = append(rl.infoText, []rune(RESET+DIM+RED+" ! no matches (Ctrl-G/Esc to cancel)"+RESET)...)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,5 @@
 | 
			
		||||
package readline
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"regexp"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// FindMode defines how the autocomplete suggestions display
 | 
			
		||||
type FindMode int
 | 
			
		||||
 | 
			
		||||
@ -30,12 +26,7 @@ func (rl *Instance) updateTabFind(r []rune) {
 | 
			
		||||
	rl.tfLine = append(rl.tfLine, r...)
 | 
			
		||||
 | 
			
		||||
	// The search regex is common to all search modes
 | 
			
		||||
	var err error
 | 
			
		||||
	rl.regexSearch, err = regexp.Compile("(?i)" + string(rl.tfLine))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		rl.RefreshPromptLog(err.Error())
 | 
			
		||||
		rl.infoText = []rune(Red("Failed to match search regexp"))
 | 
			
		||||
	}
 | 
			
		||||
	rl.search = string(rl.tfLine)
 | 
			
		||||
 | 
			
		||||
	// We update and print
 | 
			
		||||
	rl.clearHelpers()
 | 
			
		||||
 | 
			
		||||
@ -14,6 +14,7 @@ var (
 | 
			
		||||
	// effects
 | 
			
		||||
	BOLD  = "\033[1m"
 | 
			
		||||
	DIM   = "\033[2m"
 | 
			
		||||
	UNDERLINE   = "\033[4m"
 | 
			
		||||
	RESET = "\033[0m"
 | 
			
		||||
	// colors
 | 
			
		||||
	RED    = "\033[31m"
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										21
									
								
								rl.go
									
									
									
									
									
								
							
							
						
						@ -7,8 +7,9 @@ import (
 | 
			
		||||
 | 
			
		||||
	"hilbish/util"
 | 
			
		||||
 | 
			
		||||
	"github.com/maxlandon/readline"
 | 
			
		||||
	rt "github.com/arnodel/golua/runtime"
 | 
			
		||||
	"github.com/maxlandon/readline"
 | 
			
		||||
	"github.com/sahilm/fuzzy"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type lineReader struct {
 | 
			
		||||
@ -24,6 +25,24 @@ func newLineReader(prompt string, noHist bool) *lineReader {
 | 
			
		||||
		rl: rl,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	regexSearcher := rl.Searcher
 | 
			
		||||
	rl.Searcher = func(needle string, haystack []string) []string {
 | 
			
		||||
		fz, _ := util.DoString(l, "return hilbish.opts.fuzzy")
 | 
			
		||||
		fuzz, ok := fz.TryBool()
 | 
			
		||||
		if !fuzz || !ok {
 | 
			
		||||
			return regexSearcher(needle, haystack)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		matches := fuzzy.Find(needle, haystack)
 | 
			
		||||
		suggs := make([]string, 0)
 | 
			
		||||
 | 
			
		||||
		for _, match := range matches {
 | 
			
		||||
			suggs = append(suggs, match.Str)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return suggs
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// we don't mind hilbish.read rl instances having completion,
 | 
			
		||||
	// but it cant have shared history
 | 
			
		||||
	if !noHist {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										140
									
								
								sink.go
									
									
									
									
									
								
							
							
						
						@ -1,8 +1,11 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"hilbish/util"
 | 
			
		||||
 | 
			
		||||
@ -15,9 +18,11 @@ var sinkMetaKey = rt.StringValue("hshsink")
 | 
			
		||||
// A sink is a structure that has input and/or output to/from
 | 
			
		||||
// a desination.
 | 
			
		||||
type sink struct{
 | 
			
		||||
	writer io.Writer
 | 
			
		||||
	reader io.Reader
 | 
			
		||||
	writer *bufio.Writer
 | 
			
		||||
	reader *bufio.Reader
 | 
			
		||||
	file *os.File
 | 
			
		||||
	ud *rt.UserData
 | 
			
		||||
	autoFlush bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func setupSinkType(rtm *rt.Runtime) {
 | 
			
		||||
@ -25,15 +30,36 @@ func setupSinkType(rtm *rt.Runtime) {
 | 
			
		||||
 | 
			
		||||
	sinkMethods := rt.NewTable()
 | 
			
		||||
	sinkFuncs := map[string]util.LuaExport{
 | 
			
		||||
		"flush": {luaSinkFlush, 1, false},
 | 
			
		||||
		"read": {luaSinkRead, 1, false},
 | 
			
		||||
		"readAll": {luaSinkReadAll, 1, false},
 | 
			
		||||
		"autoFlush": {luaSinkAutoFlush, 2, false},
 | 
			
		||||
		"write": {luaSinkWrite, 2, false},
 | 
			
		||||
		"writeln": {luaSinkWriteln, 2, false},
 | 
			
		||||
	}
 | 
			
		||||
	util.SetExports(l, sinkMethods, sinkFuncs)
 | 
			
		||||
 | 
			
		||||
	sinkIndex := func(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
		s, _ := sinkArg(c, 0)
 | 
			
		||||
 | 
			
		||||
		arg := c.Arg(1)
 | 
			
		||||
		val := sinkMethods.Get(arg)
 | 
			
		||||
 | 
			
		||||
		if val != rt.NilValue {
 | 
			
		||||
			return c.PushingNext1(t.Runtime, val), nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		keyStr, _ := arg.TryString()
 | 
			
		||||
 | 
			
		||||
		switch keyStr {
 | 
			
		||||
			case "pipe":
 | 
			
		||||
				val = rt.BoolValue(false)
 | 
			
		||||
				if s.file != nil {
 | 
			
		||||
					fileInfo, _ := s.file.Stat();
 | 
			
		||||
					val = rt.BoolValue(fileInfo.Mode() & os.ModeCharDevice == 0)
 | 
			
		||||
				}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return c.PushingNext1(t.Runtime, val), nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -41,6 +67,57 @@ func setupSinkType(rtm *rt.Runtime) {
 | 
			
		||||
	l.SetRegistry(sinkMetaKey, rt.TableValue(sinkMeta))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// #member
 | 
			
		||||
// readAll() -> string
 | 
			
		||||
// --- @returns string
 | 
			
		||||
// Reads all input from the sink.
 | 
			
		||||
func luaSinkReadAll(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	if err := c.Check1Arg(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s, err := sinkArg(c, 0)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	lines := []string{}
 | 
			
		||||
	for {
 | 
			
		||||
		line, err := s.reader.ReadString('\n')
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if err == io.EOF {
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		lines = append(lines, line)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return c.PushingNext1(t.Runtime, rt.StringValue(strings.Join(lines, ""))), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #member
 | 
			
		||||
// read() -> string
 | 
			
		||||
// --- @returns string
 | 
			
		||||
// Reads a liine of input from the sink.
 | 
			
		||||
func luaSinkRead(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	if err := c.Check1Arg(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s, err := sinkArg(c, 0)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	str, _ := s.reader.ReadString('\n')
 | 
			
		||||
 | 
			
		||||
	return c.PushingNext1(t.Runtime, rt.StringValue(str)), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #member
 | 
			
		||||
// write(str)
 | 
			
		||||
// Writes data to a sink.
 | 
			
		||||
@ -59,6 +136,9 @@ func luaSinkWrite(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s.writer.Write([]byte(data))
 | 
			
		||||
	if s.autoFlush {
 | 
			
		||||
		s.writer.Flush()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return c.Next(), nil
 | 
			
		||||
}
 | 
			
		||||
@ -81,22 +161,74 @@ func luaSinkWriteln(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s.writer.Write([]byte(data + "\n"))
 | 
			
		||||
	if s.autoFlush {
 | 
			
		||||
		s.writer.Flush()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return c.Next(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #member
 | 
			
		||||
// flush()
 | 
			
		||||
// Flush writes all buffered input to the sink.
 | 
			
		||||
func luaSinkFlush(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	if err := c.Check1Arg(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s, err := sinkArg(c, 0)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s.writer.Flush()
 | 
			
		||||
 | 
			
		||||
	return c.Next(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #member
 | 
			
		||||
// autoFlush(auto)
 | 
			
		||||
// Sets/toggles the option of automatically flushing output.
 | 
			
		||||
// A call with no argument will toggle the value.
 | 
			
		||||
// --- @param auto boolean|nil
 | 
			
		||||
func luaSinkAutoFlush(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	s, err := sinkArg(c, 0)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	v := c.Arg(1)
 | 
			
		||||
	if v.Type() != rt.BoolType && v.Type() != rt.NilType {
 | 
			
		||||
		return nil, fmt.Errorf("#1 must be a boolean")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	value := !s.autoFlush
 | 
			
		||||
	if v.Type() == rt.BoolType {
 | 
			
		||||
		value = v.AsBool()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s.autoFlush = value
 | 
			
		||||
 | 
			
		||||
	return c.Next(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newSinkInput(r io.Reader) *sink {
 | 
			
		||||
	s := &sink{
 | 
			
		||||
		reader: r,
 | 
			
		||||
		reader: bufio.NewReader(r),
 | 
			
		||||
	}
 | 
			
		||||
	s.ud = sinkUserData(s)
 | 
			
		||||
 | 
			
		||||
	if f, ok := r.(*os.File); ok {
 | 
			
		||||
		s.file = f
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newSinkOutput(w io.Writer) *sink {
 | 
			
		||||
	s := &sink{
 | 
			
		||||
		writer: w,
 | 
			
		||||
		writer: bufio.NewWriter(w),
 | 
			
		||||
		autoFlush: true,
 | 
			
		||||
	}
 | 
			
		||||
	s.ud = sinkUserData(s)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								testplugin/testplugin.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,9 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	rt "github.com/arnodel/golua/runtime"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func Loader(rtm *rt.Runtime) rt.Value {
 | 
			
		||||
	return rt.StringValue("hello world!")
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								testplugin/testplugin.so
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -26,13 +26,14 @@ func SetFieldProtected(module, realModule *rt.Table, field string, value rt.Valu
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DoString runs the code string in the Lua runtime.
 | 
			
		||||
func DoString(rtm *rt.Runtime, code string) error {
 | 
			
		||||
func DoString(rtm *rt.Runtime, code string) (rt.Value, error) {
 | 
			
		||||
	chunk, err := rtm.CompileAndLoadLuaChunk("<string>", []byte(code), rt.TableValue(rtm.GlobalEnv()))
 | 
			
		||||
	var ret rt.Value
 | 
			
		||||
	if chunk != nil {
 | 
			
		||||
		_, err = rt.Call1(rtm.MainThread(), rt.FunctionValue(chunk))
 | 
			
		||||
		ret, err = rt.Call1(rtm.MainThread(), rt.FunctionValue(chunk))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return err
 | 
			
		||||
	return ret, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DoFile runs the contents of the file in the Lua runtime.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1
									
								
								vars.go
									
									
									
									
									
								
							
							
						
						@ -13,6 +13,7 @@ var (
 | 
			
		||||
var (
 | 
			
		||||
	ver = "v2.2.0"
 | 
			
		||||
	releaseName = "Poppy"
 | 
			
		||||
 | 
			
		||||
	gitCommit string
 | 
			
		||||
	gitBranch string
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -8,11 +8,11 @@ description: 'Something Unique. Hilbish is the new interactive shell for Lua fan
 | 
			
		||||
<div class="text-center">
 | 
			
		||||
	<h1 class="fw-light">Something Unique.</h1>
 | 
			
		||||
		<p>
 | 
			
		||||
			<strong>Hilbish</strong> is the new interactive shell for Lua fans.<br>
 | 
			
		||||
			Extensible, scriptable, configurable: All in Lua.
 | 
			
		||||
			<strong>🌺 Hilbish</strong> is the new Moon-powered interactive shell for Lua fans!<br>
 | 
			
		||||
			Extensible, scriptable, configurable: All in Lua. ✨
 | 
			
		||||
		</p>
 | 
			
		||||
	<a href="install" class="btn btn-primary">Install</a>
 | 
			
		||||
	<a href="https://github.com/Rosettea/Hilbish" class="btn btn-secondary" target="_blank">Github</a>
 | 
			
		||||
	<a href="https://github.com/Rosettea/Hilbish" class="btn btn-secondary" target="_blank">GitHub</a>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<hr>
 | 
			
		||||
@ -108,14 +108,28 @@ description: 'Something Unique. Hilbish is the new interactive shell for Lua fan
 | 
			
		||||
 | 
			
		||||
<hr>
 | 
			
		||||
 | 
			
		||||
<h1 class="fw-light">Screenshots</h1>
 | 
			
		||||
<div class="row row-cols-1 row-cols-md-2 g-4">
 | 
			
		||||
	<div class="col">
 | 
			
		||||
		<img src="/Hilbish/pillprompt.png">
 | 
			
		||||
	</div>
 | 
			
		||||
	<div class="col">
 | 
			
		||||
		<img src="/Hilbish/default.png">
 | 
			
		||||
	</div>
 | 
			
		||||
	<div class="col">
 | 
			
		||||
		<img src="/Hilbish/tab.png">
 | 
			
		||||
	</div>
 | 
			
		||||
</div>
 | 
			
		||||
<br>
 | 
			
		||||
 | 
			
		||||
<h1 class="fw-light">Why not just Lua?</h1>
 | 
			
		||||
<p>
 | 
			
		||||
	Hilbish is your interactive shell as well as a just a Lua interpreter
 | 
			
		||||
	and enhanced REPL.<br>
 | 
			
		||||
</p>
 | 
			
		||||
<ul class="list-group" style="max-width: 64em;">
 | 
			
		||||
<ul class="list-group">
 | 
			
		||||
	<li class="list-group-item"><i class="fa-solid fa-battery-full"></i> Batteries included Lua runtime that's also your user shell!</li>
 | 
			
		||||
	<li class="list-group-item"><i class="fa-solid fa-network-wired"></i> Hilbish is easily cross platform. It has OS agnostic interfaces for easy cross platform Lua code.</li>
 | 
			
		||||
	<li class="list-group-item"><i class="fa-solid fa-network-wired"></i> Provides cross-platform and OS agnostic APIs to ensure your Lua code works everywhere Hilbish does, as expected.</li>
 | 
			
		||||
</ul>
 | 
			
		||||
 | 
			
		||||
<hr>
 | 
			
		||||
@ -123,11 +137,10 @@ description: 'Something Unique. Hilbish is the new interactive shell for Lua fan
 | 
			
		||||
<h1 class="fw-light">Try It Today!</h1>
 | 
			
		||||
<p>
 | 
			
		||||
	Hilbish is known to run on the 3 major platforms (Windows, MacOS, Linux)
 | 
			
		||||
	but likely builds on other Unixes!
 | 
			
		||||
	<br>
 | 
			
		||||
	Windows doesn't work as well as it should, so if you're a Windows user,
 | 
			
		||||
	but likely builds on other Unixes! Windows doesn't work as well as it should,
 | 
			
		||||
	so if you're a Windows user,
 | 
			
		||||
	<a href="https://github.com/Rosettea/Hilbish/discussions/165">say something</a>!
 | 
			
		||||
	<ul class="list-group" style="max-width: 64em;">
 | 
			
		||||
	<ul class="list-group">
 | 
			
		||||
		<li class="list-group-item"><i class="fa-solid fa-cloud-arrow-down"></i> <a href="/Hilbish/install" style="text-decoration: none;"><strong>Download</strong></a> the binary</li>
 | 
			
		||||
		<li class="list-group-item"><i class="fa-solid fa-screwdriver-wrench"></i> <a href="https://github.com/Rosettea/Hilbish#manual-build" style="text-decoration: none;"><strong>Build</strong></a> from source</li>
 | 
			
		||||
	</ul>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										66
									
								
								website/content/blog/improving-this-website.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,66 @@
 | 
			
		||||
---
 | 
			
		||||
title: "Improving Hilbish's Branding"
 | 
			
		||||
date: 2023-04-13T22:15:31-04:00
 | 
			
		||||
draft: false
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
Happy birthday Hilbish! As of last month, Hilbish is now 2 years old.
 | 
			
		||||
Unfortunately I missed the official date, but I will still make a more
 | 
			
		||||
focused post on the date (19st).
 | 
			
		||||
 | 
			
		||||
I decided to fix up this website and Hilbish's logo, so that can
 | 
			
		||||
be thought of as something for the 2 years milestone?
 | 
			
		||||
 | 
			
		||||
# Logo
 | 
			
		||||
Hilbish's old logo was.. not that good. It definitely functioned
 | 
			
		||||
as a logo, but the yellow part of it looked ugly (sorry old logo).
 | 
			
		||||
 | 
			
		||||
<img src="https://safe.kashima.moe/4c6e9q484pcy.png" width=256>
 | 
			
		||||
<br>
 | 
			
		||||
 | 
			
		||||
You would have definitely seen the new logo, since it is currently
 | 
			
		||||
in use on the navigation bar and footer. Here it is in a bigger view:
 | 
			
		||||
	
 | 
			
		||||
<img src="https://safe.kashima.moe/oy72vpev2yi4.png" width=256>
 | 
			
		||||
<br>
 | 
			
		||||
	
 | 
			
		||||
# Website
 | 
			
		||||
Ever since this website was first made, from the release of v2.0, it has
 | 
			
		||||
been doing it's job of being a website good enough, but there were a few issues.
 | 
			
		||||
 | 
			
		||||
# Padding
 | 
			
		||||
Padding is very important! The edges of your screen need space to do nothing,
 | 
			
		||||
after all. On mobile or screens small enough, there would not be enough space
 | 
			
		||||
for the auto margin to fill, and since there was no padding besides that,
 | 
			
		||||
it means things would look a bit cramped. This was simple to fix.
 | 
			
		||||
 | 
			
		||||
Here it is before:  
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
and after:  
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
# Docs Navigation
 | 
			
		||||
On the docs page, the pages are on the left on desktop. Since
 | 
			
		||||
phones are too small to have this content on the side, it stays at the top.
 | 
			
		||||
This is a bit counter intuitive since it brings in extra scrolling
 | 
			
		||||
when navigating to every page for docs and just doesn't look that good.
 | 
			
		||||
 | 
			
		||||
A few months ago I made it collapse with the site wide navigation, but it
 | 
			
		||||
was not hidden by default. So a few improvements were made:
 | 
			
		||||
- Make the doc navigation hidden by default on mobile, just like site wide navigation
 | 
			
		||||
- Make doc navigation have the same look as site wide navigation
 | 
			
		||||
 | 
			
		||||
Here's a before:  
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
and after:  
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
Looks a lot better now.
 | 
			
		||||
 | 
			
		||||
# Other Changes
 | 
			
		||||
If you haven't noticed, I have made other changes to the website.
 | 
			
		||||
This includes:
 | 
			
		||||
- Borders! Something this simple makes the website look a lot better, especially on mobile.
 | 
			
		||||
- More padding and margin everywhere. Home, doc pages, blog post listing.
 | 
			
		||||
							
								
								
									
										38
									
								
								website/content/blog/v2.1.1-release.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,38 @@
 | 
			
		||||
---
 | 
			
		||||
title: "v2.1.1 Release"
 | 
			
		||||
date: 2023-04-01T18:15:42-04:00
 | 
			
		||||
draft: false
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
> The release with full changelogs and prebuilt binaries can be
 | 
			
		||||
seen at the [v2.1.1](https://github.com/Rosettea/Hilbish/releases/tag/v2.1.1)
 | 
			
		||||
tag.
 | 
			
		||||
 | 
			
		||||
Welcome to a fresh new release of Hilbish! Some people (or none) may be awaiting
 | 
			
		||||
the long coming v2.2 release with lots of features, but I *needed* to push
 | 
			
		||||
out this little bug fix (wink) release.
 | 
			
		||||
 | 
			
		||||
# Bug Fixes
 | 
			
		||||
## Validation checks for command input
 | 
			
		||||
When running this version, you may have noticed an odd message that sometimes
 | 
			
		||||
comes up when running commands. This is from the new TMOLI42SH
 | 
			
		||||
(The Meaning of Life is 42 String Hash) input validation scheme.
 | 
			
		||||
 | 
			
		||||
## Improved runtime code
 | 
			
		||||
Commands now have a chance of taking exactly 2-3s ~~more~~ less time of running due to
 | 
			
		||||
improvements in the code for shell runners!!!!!
 | 
			
		||||
 | 
			
		||||
## Validate lua code
 | 
			
		||||
Hilbish already threw an error when Lua code was not valid in syntax, but there was the
 | 
			
		||||
need for an extra validation scheme (called OpTTCLC - Opinion based Turing Test to Check Lua Code)
 | 
			
		||||
which results in less time wasted running invalid and TERRIBLE Lua code.
 | 
			
		||||
 | 
			
		||||
# Features
 | 
			
		||||
There is only 1 new feature in this glorious release.
 | 
			
		||||
 | 
			
		||||
## Fix your mistakes for the future
 | 
			
		||||
If you run a command that does not exist, Hilbish will say goodbye.
 | 
			
		||||
 | 
			
		||||
# Closing
 | 
			
		||||
Hope you enjoy this new release! It took a lot of effort to create this new version
 | 
			
		||||
while I was busy doing completely nothing. :)))
 | 
			
		||||
							
								
								
									
										15
									
								
								website/content/blog/v2.1.2-release.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,15 @@
 | 
			
		||||
---
 | 
			
		||||
title: "v2.1.2 Release"
 | 
			
		||||
date: 2023-04-10T12:27:41-04:00
 | 
			
		||||
draft: false
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
> The release with full changelogs and prebuilt binaries can be
 | 
			
		||||
seen at the [v2.1.2](https://github.com/Rosettea/Hilbish/releases/tag/v2.1.2)
 | 
			
		||||
tag.
 | 
			
		||||
 | 
			
		||||
This release reverts the April Fool's code additions in v2.1.1. It is
 | 
			
		||||
functionally equal to v2.1.0. Nice!
 | 
			
		||||
 | 
			
		||||
A real release will come possibly in a few days or next week, so stay tuned for
 | 
			
		||||
the good and feature-filled release of v2.2!
 | 
			
		||||
@ -5,7 +5,7 @@ weight: -40
 | 
			
		||||
menu: docs
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
Hilbish has a wide range of features to enhance the user's experience and
 | 
			
		||||
is always adding new ones. If there is something missing here or something
 | 
			
		||||
you would like to see, please [start a discussion](https://github.com/Rosettea/Hilbish/discussions)
 | 
			
		||||
Hilbish has a wide range of features to enhance the user's experience 
 | 
			
		||||
new ones are always being added. If there is something missing here or
 | 
			
		||||
something you would like to see, please [start a discussion](https://github.com/Rosettea/Hilbish/discussions)
 | 
			
		||||
or comment on any existing ones which match your request.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										39
									
								
								website/content/docs/features/notifications.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,39 @@
 | 
			
		||||
---
 | 
			
		||||
title: Notification
 | 
			
		||||
description: Get notified of shell actions.
 | 
			
		||||
layout: doc
 | 
			
		||||
menu: 
 | 
			
		||||
  docs:
 | 
			
		||||
    parent: "Features"
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
Hilbish features a simple notification system which can be
 | 
			
		||||
used by other plugins and parts of the shell to notify the user
 | 
			
		||||
of various actions. This is used via the `hilbish.message` interface.
 | 
			
		||||
 | 
			
		||||
A `message` is defined as a table with the following properties:
 | 
			
		||||
- `icon`: A unicode/emoji icon for the notification.
 | 
			
		||||
- `title`: The title of the message
 | 
			
		||||
- `text`: Message text/body
 | 
			
		||||
- `channel`: The source of the message. This should be a
 | 
			
		||||
unique and easily readable text identifier.
 | 
			
		||||
- `summary`: A short summary of the notification and message.
 | 
			
		||||
If this is not present and you are using this to display messages,
 | 
			
		||||
you should take part of the `text` instead.
 | 
			
		||||
 | 
			
		||||
The `hilbish.message` interface provides the following functions:
 | 
			
		||||
- `send(message)`: Sends a message and emits the `hilbish.notification`
 | 
			
		||||
signal. DO NOT emit the `hilbish.notification` signal directly, or
 | 
			
		||||
the message will not be stored by the message handler.
 | 
			
		||||
- `read(idx)`: Marks message at `idx` as read.
 | 
			
		||||
- `delete(idx)`: Removes message at `idx`.
 | 
			
		||||
- `readAll()`: Marks all messages as read.
 | 
			
		||||
- `clear()`: Deletes all messages.
 | 
			
		||||
 | 
			
		||||
There are a few simple use cases of this notification/messaging system.
 | 
			
		||||
It could also be used as some "inter-shell" messaging system (???) but
 | 
			
		||||
is intended to display to users.
 | 
			
		||||
 | 
			
		||||
An example is notifying users of completed jobs/commands ran in the background.
 | 
			
		||||
Any Hilbish-native command (think the upcoming Greenhouse pager) can display
 | 
			
		||||
it.
 | 
			
		||||
@ -13,5 +13,9 @@ is that it runs Lua first and then falls back to shell script.
 | 
			
		||||
 | 
			
		||||
In some cases, someone might want to switch to just shell script to avoid
 | 
			
		||||
it while interactive but still have a Lua config, or go full Lua to use
 | 
			
		||||
Hilbish as a REPL. This also allows users to add alternative languages,
 | 
			
		||||
instead of either like Fennel.
 | 
			
		||||
Hilbish as a REPL. This also allows users to add alternative languages like
 | 
			
		||||
Fennel as the interactive script runner.
 | 
			
		||||
 | 
			
		||||
Runner mode can also be used to handle specific kinds of input before
 | 
			
		||||
evaluating like normal, which is how [Link.hsh](https://github.com/TorchedSammy/Link.hsh)
 | 
			
		||||
handles links.
 | 
			
		||||
 | 
			
		||||
@ -15,9 +15,10 @@ To exit, you can either run the `exit` command or hit Ctrl+D.
 | 
			
		||||
There are a few ways to make Hilbish your default shell. A simple way is 
 | 
			
		||||
to make it your user/login shell.
 | 
			
		||||
 | 
			
		||||
{{< warning `It is not recommended to set Hilbish as your login shell. That is expected to be a 
 | 
			
		||||
POSIX compliant shell, which Hilbish is not. At most, there will just be a 
 | 
			
		||||
few variables missing in your environment` >}}
 | 
			
		||||
{{< warning `It is not recommended to set Hilbish as your login shell. That
 | 
			
		||||
is expected to be a POSIX compliant shell, which Hilbish is not. Though if
 | 
			
		||||
you still decide to do it, there will just be a few variables missing in
 | 
			
		||||
your environment` >}}
 | 
			
		||||
 | 
			
		||||
To do that, simply run `chsh -s /usr/bin/hilbish`.
 | 
			
		||||
Some distros (namely Fedora) might have `lchsh` instead, which is used like `lchsh <user>`.
 | 
			
		||||
 | 
			
		||||
@ -15,7 +15,12 @@ have breaking changes.
 | 
			
		||||
For the latest **stable release**, check here: https://github.com/Rosettea/Hilbish/releases/latest  
 | 
			
		||||
For a **development build**: https://nightly.link/Rosettea/Hilbish/workflows/build/master
 | 
			
		||||
 | 
			
		||||
## Compiling
 | 
			
		||||
To read the steps for compiling Hilbish, head over to the [GitHub repository.](https://github.com/Rosettea/Hilbish#build)
 | 
			
		||||
 | 
			
		||||
## Package Repositories
 | 
			
		||||
Methods of installing Hilbish for your Linux distro.
 | 
			
		||||
 | 
			
		||||
### Fedora (COPR)
 | 
			
		||||
An official COPR is offered to install Hilbish easily on Fedora.
 | 
			
		||||
Enable the repo:
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								website/static/default.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 58 KiB  | 
| 
		 Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 45 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								website/static/hilbish-logo-and-text.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 78 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								website/static/pillprompt.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 20 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								website/static/tab.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 18 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								website/static/terminal.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 99 KiB  | 
@ -1,31 +1,43 @@
 | 
			
		||||
{{ define "main" }}
 | 
			
		||||
<div class="container py-3 row">
 | 
			
		||||
	<div class="container" style="width: 240px;">
 | 
			
		||||
		<div class="p-3 col">
 | 
			
		||||
			<ul class="nav nav-pills mb-auto-collapse" id="navbarSupportedContent">
 | 
			
		||||
				{{ $currentPage := . }}
 | 
			
		||||
				{{ range .Site.Menus.docs.ByWeight.Reverse }}
 | 
			
		||||
					<li class="nav-item">
 | 
			
		||||
						<a href="{{ .URL }}" class="nav-link">
 | 
			
		||||
							<strong>{{ .Title }}</strong>
 | 
			
		||||
						</a>
 | 
			
		||||
					</li>
 | 
			
		||||
					{{ if .Children }}
 | 
			
		||||
						<ul style="list-style: none;">
 | 
			
		||||
							{{ range .Children }}
 | 
			
		||||
								<li class="nav-item">
 | 
			
		||||
									<a href="{{ .URL }}" class="nav-link">
 | 
			
		||||
										{{ .Title }}
 | 
			
		||||
									</a>
 | 
			
		||||
								</li>
 | 
			
		||||
	<style>
 | 
			
		||||
		@media (min-width: 768px) {
 | 
			
		||||
			.sidenav {
 | 
			
		||||
				width: 240px;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	</style>
 | 
			
		||||
<div class="row">
 | 
			
		||||
	<nav class="navbar-expand-md bg-light sidenav border-end">
 | 
			
		||||
		<div class="container">
 | 
			
		||||
			<div class="collapse navbar-collapse" id="navbarSupportedContent">
 | 
			
		||||
				<nav class="navbar navbar-expand-md bg-light sidenav col" style="margin-top: -20px;">
 | 
			
		||||
					<ul class="navbar-nav me-auto mb-2 mb-lg-0 flex-column col mt-4">
 | 
			
		||||
						{{ $currentPage := . }}
 | 
			
		||||
						{{ range .Site.Menus.docs.ByWeight.Reverse }}
 | 
			
		||||
							<li class="nav-item">
 | 
			
		||||
								<a href="{{ .URL }}" class="nav-link">
 | 
			
		||||
									<strong>{{ .Title }}</strong>
 | 
			
		||||
								</a>
 | 
			
		||||
							</li>
 | 
			
		||||
							{{ if .Children }}
 | 
			
		||||
								<ul style="list-style: none;">
 | 
			
		||||
									{{ range .Children }}
 | 
			
		||||
										<li class="nav-item">
 | 
			
		||||
											<a href="{{ .URL }}" class="nav-link">
 | 
			
		||||
												{{ .Title }}
 | 
			
		||||
											</a>
 | 
			
		||||
										</li>
 | 
			
		||||
									{{ end }}
 | 
			
		||||
								</ul>
 | 
			
		||||
							{{ end }}
 | 
			
		||||
						</ul>
 | 
			
		||||
					{{ end }}
 | 
			
		||||
				{{ end }}
 | 
			
		||||
			</ul>
 | 
			
		||||
						{{ end }}
 | 
			
		||||
					</ul>
 | 
			
		||||
				</nav>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
	<div class="p-3 col">
 | 
			
		||||
	</nav>
 | 
			
		||||
 | 
			
		||||
	<container class="col mt-2" style="padding-left: 20px; padding-right: 20px;">
 | 
			
		||||
		<div>
 | 
			
		||||
			<h1>{{ .Title }}</h1>
 | 
			
		||||
			<p><em>
 | 
			
		||||
@ -47,7 +59,7 @@
 | 
			
		||||
				Want to help improve this page? <a href="https://github.com/Rosettea/Hilbish/issues/new/choose">Create an issue.</a>
 | 
			
		||||
			</p>
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
	</container>
 | 
			
		||||
</div>
 | 
			
		||||
{{ end }}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
{{ define "main" }}
 | 
			
		||||
<main>
 | 
			
		||||
<main class="mt-4 mb-4">
 | 
			
		||||
  <div class="row row-cols-1 row-cols-md-1 g-4">
 | 
			
		||||
    {{ range where .Site.RegularPages "Section" "in" "blog" }}
 | 
			
		||||
      <div class="col d-flex justify-content-center">
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,8 @@
 | 
			
		||||
{{ define "main" }}
 | 
			
		||||
<main style="max-width: 80em; margin: auto;">
 | 
			
		||||
	{{.Content}}
 | 
			
		||||
</main>
 | 
			
		||||
<div>
 | 
			
		||||
	<main style="padding-left: 20px; padding-right: 20px; padding-top: 15px; max-width: 84em; margin: auto;">
 | 
			
		||||
		{{.Content}}
 | 
			
		||||
	</main>
 | 
			
		||||
</div>
 | 
			
		||||
{{ end }}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,24 +1,11 @@
 | 
			
		||||
<footer class="footer mt-auto mt-auto py-3 bg-light row">
 | 
			
		||||
<footer class="footer mt-auto mt-auto py-3 bg-light row border-top">
 | 
			
		||||
	<div class="col mb-3">
 | 
			
		||||
	</div>
 | 
			
		||||
 | 
			
		||||
	<div class="col mb-3">
 | 
			
		||||
		<a href="/Hilbish" class="d-flex align-items-center mb-3 link-dark text-decoration-none">
 | 
			
		||||
		<img src="/Hilbish/hilbish-flower.png" alt="" height="48" class="d-inline-block align-text-top">
 | 
			
		||||
			<img src="/Hilbish/hilbish-logo-and-text.png" alt="" height="48" class="d-inline-block align-text-top" style="margin-left: -20px">
 | 
			
		||||
		</a>
 | 
			
		||||
		<p class="text-muted">
 | 
			
		||||
			Rosettea © 2022
 | 
			
		||||
			<br>
 | 
			
		||||
			Made with <i class="fa-solid fa-heart" style="color: #f6345b;"></i>
 | 
			
		||||
		</p>
 | 
			
		||||
	</div>
 | 
			
		||||
 | 
			
		||||
	<div class="col mb-3"></div>
 | 
			
		||||
	<div class="col mb-3"></div>
 | 
			
		||||
	<div class="col mb-3"></div>
 | 
			
		||||
 | 
			
		||||
	<div class="col mb-3">
 | 
			
		||||
		<h5>Hilbish</h5>
 | 
			
		||||
		<ul class="nav flex-column">
 | 
			
		||||
			<li class="nav-item mb-2"><a href="/Hilbish" class="nav-link p-0 text-muted">Home</a></li>
 | 
			
		||||
			<li class="nav-item mb-2"><a href="/Hilbish/docs/faq" class="nav-link p-0 text-muted">FAQ</a></li>
 | 
			
		||||
@ -29,4 +16,16 @@
 | 
			
		||||
	</div>
 | 
			
		||||
 | 
			
		||||
	<div class="col mb-3"></div>
 | 
			
		||||
 | 
			
		||||
	<div class="col mb-3"></div>
 | 
			
		||||
	<div class="col mb-3"></div>
 | 
			
		||||
	<div class="col mb-3">
 | 
			
		||||
		<p class="text-muted mt-4">
 | 
			
		||||
			Rosettea © 2022-2023
 | 
			
		||||
			<br>
 | 
			
		||||
			Made with <i class="fa-solid fa-heart" style="color: #f6345b;"></i>
 | 
			
		||||
		</p>
 | 
			
		||||
	</div>
 | 
			
		||||
 | 
			
		||||
	<div class="col mb-3"></div>
 | 
			
		||||
</footer>
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,8 @@
 | 
			
		||||
<header>
 | 
			
		||||
	<nav class="navbar navbar-expand-md sticky-top bg-light">
 | 
			
		||||
	<nav class="navbar navbar-expand-md sticky-top bg-light border-bottom">
 | 
			
		||||
		<div class="container-fluid">
 | 
			
		||||
			<a class="navbar-brand" href="/Hilbish">
 | 
			
		||||
				<img src="/Hilbish/hilbish-flower.png" alt="" height="24" class="d-inline-block align-text-top">
 | 
			
		||||
				Hilbish
 | 
			
		||||
				<img src="/Hilbish/hilbish-logo-and-text.png" alt="" height="48">
 | 
			
		||||
			</a>
 | 
			
		||||
			<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
 | 
			
		||||
				<span class="navbar-toggler-icon"></span>
 | 
			
		||||
 | 
			
		||||