mirror of
				https://github.com/sammy-ette/Hilbish
				synced 2025-08-10 02:52:03 +00:00 
			
		
		
		
	chore: merge from master
This commit is contained in:
		
						commit
						264f6f46b6
					
				
							
								
								
									
										6
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,6 @@
 | 
			
		||||
root = true
 | 
			
		||||
 | 
			
		||||
[*]
 | 
			
		||||
charset = utf-8
 | 
			
		||||
indent_size = 4
 | 
			
		||||
indent_style = tab
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							@ -25,7 +25,7 @@ jobs:
 | 
			
		||||
      - name: Setup Go
 | 
			
		||||
        uses: actions/setup-go@v2
 | 
			
		||||
        with:
 | 
			
		||||
          go-version: '1.17.7'
 | 
			
		||||
          go-version: '1.18.8'
 | 
			
		||||
      - name: Download Task
 | 
			
		||||
        run: 'sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d'
 | 
			
		||||
      - name: Build
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										5
									
								
								.github/workflows/docs.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.github/workflows/docs.yml
									
									
									
									
										vendored
									
									
								
							@ -2,13 +2,14 @@ name: Generate docs
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches: [master]
 | 
			
		||||
    branches:
 | 
			
		||||
      - master
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  gen:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v2
 | 
			
		||||
      - uses: actions/checkout@v3
 | 
			
		||||
      - uses: actions/setup-go@v2
 | 
			
		||||
      - name: Run docgen
 | 
			
		||||
        run: go run cmd/docgen/docgen.go
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										4
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							@ -33,10 +33,14 @@ jobs:
 | 
			
		||||
    - uses: actions/checkout@v3
 | 
			
		||||
      with:
 | 
			
		||||
        submodules: true
 | 
			
		||||
        fetch-depth: 0
 | 
			
		||||
    - name: Download Task
 | 
			
		||||
      run: 'sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d'
 | 
			
		||||
    - uses: wangyoucao577/go-release-action@v1.25
 | 
			
		||||
      with:
 | 
			
		||||
        github_token: ${{ secrets.GITHUB_TOKEN }}
 | 
			
		||||
        goos: ${{ matrix.goos }}
 | 
			
		||||
        goarch: ${{ matrix.goarch }}
 | 
			
		||||
        ldflags: '-s -w'
 | 
			
		||||
        binary_name: hilbish
 | 
			
		||||
        extra_files: LICENSE README.md CHANGELOG.md .hilbishrc.lua nature libs docs emmyLuaDocs
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										47
									
								
								.github/workflows/website.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								.github/workflows/website.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,47 @@
 | 
			
		||||
name: Build website
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  - push
 | 
			
		||||
  - pull_request
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  deploy:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v3
 | 
			
		||||
        with:
 | 
			
		||||
          submodules: true
 | 
			
		||||
          fetch-depth: 0
 | 
			
		||||
 | 
			
		||||
      - name: Setup Hugo
 | 
			
		||||
        uses: peaceiris/actions-hugo@v2
 | 
			
		||||
        with:
 | 
			
		||||
          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
 | 
			
		||||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -1,6 +1,9 @@
 | 
			
		||||
*.exe
 | 
			
		||||
hilbish
 | 
			
		||||
!docs/api/hilbish
 | 
			
		||||
docgen
 | 
			
		||||
!cmd/docgen
 | 
			
		||||
 | 
			
		||||
.vim
 | 
			
		||||
petals/
 | 
			
		||||
.hugo_build.lock
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										102
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										102
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@ -1,9 +1,70 @@
 | 
			
		||||
# 🎀 Changelog
 | 
			
		||||
 | 
			
		||||
## Unreleased
 | 
			
		||||
**NOTE:** Hilbish now uses [Task] insead of Make for builds.
 | 
			
		||||
Windows support is also now at a lower tier; The only thing guaranteed is
 | 
			
		||||
Hilbish *compiling* on Windows.
 | 
			
		||||
### 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
 | 
			
		||||
- `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`.
 | 
			
		||||
 | 
			
		||||
[#219]: https://github.com/Rosettea/Hilbish/issues/219
 | 
			
		||||
### Fixed
 | 
			
		||||
- 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)
 | 
			
		||||
  - Coming with this fix is also adding the return types for some functions that were missing it
 | 
			
		||||
- Added a dedicated input and dedicated outputs for commanders (sinks - info at `doc api commander`).
 | 
			
		||||
- Local docs is used if one of Hilbish's branches is found
 | 
			
		||||
- Return 1 exit code on doc not found
 | 
			
		||||
- `hilbish.runner.getCurrent()` to get the current runner
 | 
			
		||||
- Initialize Hilbish Lua API before handling signals
 | 
			
		||||
 | 
			
		||||
### Fixed
 | 
			
		||||
- `index` or `_index` subdocs should not show up anymore
 | 
			
		||||
- `hilbish.which` not working correctly with aliases
 | 
			
		||||
- Commanders not being able to pipe with commands or any related operator.
 | 
			
		||||
- Resolve symlinks in completions
 | 
			
		||||
- Updated `runner-mode` docs
 | 
			
		||||
- Fix `hilbish.completion` functions panicking when empty input is provided
 | 
			
		||||
 | 
			
		||||
## [2.0.1] - 2022-12-28
 | 
			
		||||
### Fixed
 | 
			
		||||
- Corrected documentation for hooks, removing outdated `command.no-perm`
 | 
			
		||||
- Fixed an issue where `cd` with no args would not update the old pwd
 | 
			
		||||
- Tiny documentation enhancements for the `hilbish.timer` interface
 | 
			
		||||
 | 
			
		||||
## [2.0.0] - 2022-12-20
 | 
			
		||||
**NOTES FOR USERS/PACKAGERS UPDATING:**
 | 
			
		||||
- Hilbish now uses [Task] insead of Make for builds.
 | 
			
		||||
- The doc format has been changed from plain text to markdown.
 | 
			
		||||
**YOU MUST reinstall Hilbish to remove the duplicate, old docs.**
 | 
			
		||||
- Hilbish will by default install to **`/usr/local`** instead of just `/usr/`
 | 
			
		||||
when building via Task. This is mainly to avoid conflict of distro packages
 | 
			
		||||
and local installs, and is the correct place when building from git either way.
 | 
			
		||||
To keep Hilbish in `/usr`, you must have `PREFIX="/usr/"` when running `task build` or `task install`
 | 
			
		||||
- Windows is no longer supported. It will build and run, but **will** have problems.
 | 
			
		||||
If you want to help fix the situation, start a discussion or open an issue and contribute.
 | 
			
		||||
 | 
			
		||||
[Task]: https://taskfile.dev/#/
 | 
			
		||||
 | 
			
		||||
@ -41,7 +102,7 @@ without arguments will disown the last job.
 | 
			
		||||
fields on a job object.
 | 
			
		||||
- Documentation for jobs is now available via `doc jobs`.
 | 
			
		||||
- `hilbish.alias.resolve(cmdstr)` to resolve a command alias.
 | 
			
		||||
- `hilbish.opts` for shell options. Currently, the only opt is `autocd`.
 | 
			
		||||
- `hilbish.opts` for shell options.
 | 
			
		||||
- `hilbish.editor` interface for interacting with the line editor that
 | 
			
		||||
Hilbish uses.
 | 
			
		||||
- `hilbish.vim` interface to dynamically get/set vim registers.
 | 
			
		||||
@ -76,12 +137,19 @@ disables commands being added to history.
 | 
			
		||||
random errors introduced with the new Lua runtime (see [#197])
 | 
			
		||||
- `bait.release(name, catcher)` removes `handler` for the named `event`
 | 
			
		||||
- `exec`, `clear` and `cat` builtin commands
 | 
			
		||||
- `hilbish.cancel` hook thrown when user cancels input with Ctrl-C
 | 
			
		||||
- 1st item on history is now inserted when history search menu is opened ([#148])
 | 
			
		||||
- Documentation has been improved vastly!
 | 
			
		||||
 | 
			
		||||
[#148]: https://github.com/Rosettea/Hilbish/issues/148
 | 
			
		||||
[#197]: https://github.com/Rosettea/Hilbish/issues/197
 | 
			
		||||
 | 
			
		||||
### Changed
 | 
			
		||||
- **Breaking Change:** Upgraded to Lua 5.4.
 | 
			
		||||
This is probably one of (if not the) biggest things in this release.
 | 
			
		||||
To recap quickly on what matters (mostly):
 | 
			
		||||
  - `os.execute` returns 3 values instead of 1 (but you should be using `hilbish.run`)
 | 
			
		||||
  - I/O operations must be flushed (`io.flush()`)
 | 
			
		||||
- **Breaking Change:** MacOS config paths now match Linux.
 | 
			
		||||
- Overrides on the `hilbish` table are no longer permitted.
 | 
			
		||||
- **Breaking Change:** Runner functions are now required to return a table.
 | 
			
		||||
@ -100,6 +168,7 @@ of a dot. (ie. `job.stop()` -> `job:stop()`)
 | 
			
		||||
- All `fs` module functions which take paths now implicitly expand ~ to home.
 | 
			
		||||
- **Breaking Change:** `hilbish.greeting` has been moved to an opt (`hilbish.opts.greeting`) and is
 | 
			
		||||
always printed by default. To disable it, set the opt to false.
 | 
			
		||||
- **Breaking Change:** `command.no-perm` hook has been replaced with `command.not-executable`
 | 
			
		||||
- History is now fetched from Lua, which means users can override `hilbish.history`
 | 
			
		||||
methods to make it act how they want.
 | 
			
		||||
- `guide` has been removed. See the [website](https://rosettea.github.io/Hilbish/)
 | 
			
		||||
@ -115,7 +184,7 @@ replacing the last character.
 | 
			
		||||
- `hilbish.login` being the wrong value.
 | 
			
		||||
- Put full input in history if prompted for continued input
 | 
			
		||||
- Don't put alias expanded command in history (sound familiar?)
 | 
			
		||||
- Handle cases of stdin being nonblocking (in the case of [#130](https://github.com/Rosettea/Hilbish/issues/130))
 | 
			
		||||
- Handle cases of stdin being nonblocking (in the case of [#136](https://github.com/Rosettea/Hilbish/issues/136))
 | 
			
		||||
- Don't prompt for continued input if non interactive
 | 
			
		||||
- Don't insert unhandled control keys.
 | 
			
		||||
- Handle sh syntax error in alias
 | 
			
		||||
@ -125,11 +194,12 @@ certain color rules.
 | 
			
		||||
- Home/End keys now go to the actual start/end of the input.
 | 
			
		||||
- Input getting cut off on enter in certain cases.
 | 
			
		||||
- Go to the next line properly if input reaches end of terminal width.
 | 
			
		||||
- Cursor position with CJK characters. ([#145](https://github.com/Rosettea/Hilbish/pull/145))
 | 
			
		||||
- Files with same name as parent folder in completions getting cut off [#136](https://github.com/Rosettea/Hilbish/issues/136))
 | 
			
		||||
- Cursor position with CJK characters has been corrected ([#145](https://github.com/Rosettea/Hilbish/pull/145))
 | 
			
		||||
- Files with same name as parent folder in completions getting cut off [#130](https://github.com/Rosettea/Hilbish/issues/130))
 | 
			
		||||
- `hilbish.which` now works with commanders and aliases.
 | 
			
		||||
- Background jobs no longer take stdin so they do not interfere with shell
 | 
			
		||||
input.
 | 
			
		||||
- Full name of completion entry is used instead of being cut off
 | 
			
		||||
- Completions are fixed in cases where the query/line is an alias alone
 | 
			
		||||
where it can also resolve to the beginning of command names.
 | 
			
		||||
(reference [this commit](https://github.com/Rosettea/Hilbish/commit/2790982ad123115c6ddbc5764677fdca27668cea))
 | 
			
		||||
@ -151,6 +221,21 @@ an error of missing format variable
 | 
			
		||||
- Fix an error with sh syntax in aliases
 | 
			
		||||
- Prompt now works with east asian characters (CJK)
 | 
			
		||||
- Set back the prompt to normal after exiting the continue prompt with ctrl-d
 | 
			
		||||
- Take into account newline in input when calculating input width. Prevents
 | 
			
		||||
extra reprinting of the prompt, but input with newlines inserted is still a problem
 | 
			
		||||
- Put cursor at the end of input when exiting $EDITOR with Vim mode bind
 | 
			
		||||
- Calculate width of virtual input properly (completion candidates)
 | 
			
		||||
- Users can now tab complete files with spaces while quoted or with escaped spaces.
 | 
			
		||||
This means a query of `Files\ to\ ` with file names of `Files to tab complete` and `Files to complete`
 | 
			
		||||
will result in the files being completed.
 | 
			
		||||
- Fixed grid menu display if cell width ends up being the width of the terminal
 | 
			
		||||
- Cut off item names in grid menu if its longer than cell width
 | 
			
		||||
- Fix completion search menu disappearing
 | 
			
		||||
- Make binary completion work with bins that have spaces in the name
 | 
			
		||||
- Completion paths having duplicated characters if it's escaped
 | 
			
		||||
- Get custom completion command properly to call from Lua
 | 
			
		||||
- Put proper command on the line when using up and down arrow keys to go through command history
 | 
			
		||||
- Don't do anything if length of input rune slice is 0 ([commit for explanation](https://github.com/Rosettea/Hilbish/commit/8d40179a73fe5942707cd43f9c0463dee53eedd8))
 | 
			
		||||
 | 
			
		||||
## [2.0.0-rc1] - 2022-09-14
 | 
			
		||||
This is a pre-release version of Hilbish for testing. To see the changelog,
 | 
			
		||||
@ -580,6 +665,9 @@ This input for example will prompt for more input to complete:
 | 
			
		||||
 | 
			
		||||
First "stable" release of Hilbish.
 | 
			
		||||
 | 
			
		||||
[2.1.0]: https://github.com/Rosettea/Hilbish/compare/v2.0.1...v2.1.0
 | 
			
		||||
[2.0.1]: https://github.com/Rosettea/Hilbish/compare/v2.0.0...v2.0.1
 | 
			
		||||
[2.0.0]: https://github.com/Rosettea/Hilbish/compare/v1.2.0...v2.0.0
 | 
			
		||||
[2.0.0-rc1]: https://github.com/Rosettea/Hilbish/compare/v1.2.0...v2.0.0-rc1
 | 
			
		||||
[1.2.0]: https://github.com/Rosettea/Hilbish/compare/v1.1.4...v1.2.0
 | 
			
		||||
[1.1.0]: https://github.com/Rosettea/Hilbish/compare/v1.0.4...v1.1.0
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										2
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								LICENSE
									
									
									
									
									
								
							@ -1,6 +1,6 @@
 | 
			
		||||
MIT License
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2022 Rosettea
 | 
			
		||||
Copyright (c) 2021-2023 Rosettea
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										70
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										70
									
								
								README.md
									
									
									
									
									
								
							@ -1,77 +1,45 @@
 | 
			
		||||
<div align="center">
 | 
			
		||||
	<img src="./assets/hilbish-flower.png" width=128><br>
 | 
			
		||||
	<img src="./assets/hilbish-text.png" width=256><br>
 | 
			
		||||
<img src="./assets/hilbish-logo-and-text.png" width=512><br>
 | 
			
		||||
<blockquote>
 | 
			
		||||
	🌺 The flower shell. A comfy and nice little shell for Lua fans!
 | 
			
		||||
🌓 The Moon-powered shell! A comfy and extensible 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>
 | 
			
		||||
 | 
			
		||||
<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>
 | 
			
		||||
<br>
 | 
			
		||||
 | 
			
		||||
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.  
 | 
			
		||||
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)
 | 
			
		||||
- [Installation](#Installation)
 | 
			
		||||
  - [Prebuilt Bins](#Prebuilt-binaries)
 | 
			
		||||
  - [AUR](#AUR)
 | 
			
		||||
  - [Nixpkgs](#Nixpkgs)
 | 
			
		||||
  - [Manual Build](#Manual-Build)
 | 
			
		||||
- [Contributing](#Contributing)
 | 
			
		||||
 | 
			
		||||
# Screenshots
 | 
			
		||||
<div align="center">
 | 
			
		||||
<img src="gallery/default.png"><br><br>
 | 
			
		||||
<img src="gallery/terminal.png"><br><br>
 | 
			
		||||
<img src="gallery/tab.png">
 | 
			
		||||
<img src="gallery/pillprompt.png">
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
# Installation
 | 
			
		||||
# 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.
 | 
			
		||||
 | 
			
		||||
## Prebuilt binaries
 | 
			
		||||
Go [here](https://nightly.link/Rosettea/Hilbish/workflows/build/master) for
 | 
			
		||||
builds on the master branch.
 | 
			
		||||
You can check the [install page](https://rosettea.github.io/Hilbish/install/)
 | 
			
		||||
on the website for distributed binaries from GitHub or other package repositories.
 | 
			
		||||
Otherwise, continue reading for steps on compiling.
 | 
			
		||||
 | 
			
		||||
## AUR
 | 
			
		||||
[](https://aur.archlinux.org/packages/hilbish)  
 | 
			
		||||
Arch Linux users can install Hilbish from the AUR with the following command:  
 | 
			
		||||
```sh
 | 
			
		||||
yay -S hilbish
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
[](https://aur.archlinux.org/packages/hilbish-git)  
 | 
			
		||||
Or from the latest `master` commit with:  
 | 
			
		||||
```sh
 | 
			
		||||
yay -S hilbish-git
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Nixpkgs
 | 
			
		||||
Nix/NixOS users can install Hilbish from the central repository, nixpkgs, through the usual ways.
 | 
			
		||||
If you're new to nix you should probably read up on how to do that [here](https://nixos.wiki/wiki/Cheatsheet).
 | 
			
		||||
 | 
			
		||||
## Manual Build
 | 
			
		||||
### Prerequisites
 | 
			
		||||
## Prerequisites
 | 
			
		||||
- [Go 1.17+](https://go.dev)
 | 
			
		||||
- [Task](https://taskfile.dev/#/)
 | 
			
		||||
- [Task](https://taskfile.dev/installation/) (**Go on the hyperlink here to see Task's install method for your OS.**)
 | 
			
		||||
 | 
			
		||||
### Build
 | 
			
		||||
## Build
 | 
			
		||||
First, clone Hilbish. The recursive is required, as some Lua libraries
 | 
			
		||||
are submodules.  
 | 
			
		||||
```sh
 | 
			
		||||
 | 
			
		||||
@ -3,19 +3,20 @@
 | 
			
		||||
version: '3'
 | 
			
		||||
 | 
			
		||||
vars:
 | 
			
		||||
  PREFIX: '{{default "/usr" .PREFIX}}'
 | 
			
		||||
  PREFIX: '{{default "/usr/local" .PREFIX}}'
 | 
			
		||||
  bindir__: '{{.PREFIX}}/bin'
 | 
			
		||||
  BINDIR: '{{default .bindir__ .BINDIR}}'
 | 
			
		||||
  libdir__: '{{.PREFIX}}/share/hilbish'
 | 
			
		||||
  LIBDIR: '{{default .libdir__ .LIBDIR}}'
 | 
			
		||||
  GOFLAGS: '-ldflags "-s -w"'
 | 
			
		||||
  goflags__: '-ldflags "-s -w -X main.dataDir={{.LIBDIR}}"'
 | 
			
		||||
  GOFLAGS: '{{default .goflags__ .GOFLAGS}}'
 | 
			
		||||
 | 
			
		||||
tasks:
 | 
			
		||||
  default:
 | 
			
		||||
    cmds:
 | 
			
		||||
      - CGO_ENABLED=0 go build {{.GOFLAGS}}
 | 
			
		||||
    vars:
 | 
			
		||||
      GOFLAGS: '-ldflags "-s -w -X main.gitCommit=$(git rev-parse --short HEAD) -X main.gitBranch=$(git rev-parse --abbrev-ref HEAD)"'
 | 
			
		||||
      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:
 | 
			
		||||
@ -33,4 +34,4 @@ tasks:
 | 
			
		||||
      - rm -vrf
 | 
			
		||||
        "{{.DESTDIR}}{{.BINDIR}}/hilbish"
 | 
			
		||||
        "{{.DESTDIR}}{{.LIBDIR}}"
 | 
			
		||||
      - sed -i '/hilbish/d' /etc/shells
 | 
			
		||||
      - grep -v 'hilbish' /etc/shells > /tmp/shells.hilbish_uninstall && mv /tmp/shells.hilbish_uninstall /etc/shells
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										69
									
								
								aliases.go
									
									
									
									
									
								
							
							
						
						
									
										69
									
								
								aliases.go
									
									
									
									
									
								
							@ -1,6 +1,8 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
@ -9,46 +11,64 @@ import (
 | 
			
		||||
	rt "github.com/arnodel/golua/runtime"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var aliases *aliasHandler
 | 
			
		||||
var aliases *aliasModule
 | 
			
		||||
 | 
			
		||||
type aliasHandler struct {
 | 
			
		||||
type aliasModule struct {
 | 
			
		||||
	aliases map[string]string
 | 
			
		||||
	mu *sync.RWMutex
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// initialize aliases map
 | 
			
		||||
func newAliases() *aliasHandler {
 | 
			
		||||
	return &aliasHandler{
 | 
			
		||||
func newAliases() *aliasModule {
 | 
			
		||||
	return &aliasModule{
 | 
			
		||||
		aliases: make(map[string]string),
 | 
			
		||||
		mu: &sync.RWMutex{},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *aliasHandler) Add(alias, cmd string) {
 | 
			
		||||
func (a *aliasModule) Add(alias, cmd string) {
 | 
			
		||||
	a.mu.Lock()
 | 
			
		||||
	defer a.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	a.aliases[alias] = cmd
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *aliasHandler) All() map[string]string {
 | 
			
		||||
func (a *aliasModule) All() map[string]string {
 | 
			
		||||
	return a.aliases
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *aliasHandler) Delete(alias string) {
 | 
			
		||||
func (a *aliasModule) Delete(alias string) {
 | 
			
		||||
	a.mu.Lock()
 | 
			
		||||
	defer a.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	delete(a.aliases, alias)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *aliasHandler) Resolve(cmdstr string) string {
 | 
			
		||||
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)
 | 
			
		||||
	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
 | 
			
		||||
@ -66,7 +86,10 @@ func (a *aliasHandler) Resolve(cmdstr string) string {
 | 
			
		||||
 | 
			
		||||
// lua section
 | 
			
		||||
 | 
			
		||||
func (a *aliasHandler) Loader(rtm *rt.Runtime) *rt.Table {
 | 
			
		||||
// #interface aliases
 | 
			
		||||
// command aliasing
 | 
			
		||||
// The alias interface deals with all command aliases in Hilbish.
 | 
			
		||||
func (a *aliasModule) Loader(rtm *rt.Runtime) *rt.Table {
 | 
			
		||||
	// create a lua module with our functions
 | 
			
		||||
	hshaliasesLua := map[string]util.LuaExport{
 | 
			
		||||
		"add": util.LuaExport{hlalias, 2, false},
 | 
			
		||||
@ -81,7 +104,18 @@ func (a *aliasHandler) Loader(rtm *rt.Runtime) *rt.Table {
 | 
			
		||||
	return mod
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *aliasHandler) luaList(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
// #interface aliases
 | 
			
		||||
// add(alias, cmd)
 | 
			
		||||
// This is an alias (ha) for the `hilbish.alias` function.
 | 
			
		||||
// --- @param alias string
 | 
			
		||||
// --- @param cmd string
 | 
			
		||||
func _hlalias() {}
 | 
			
		||||
 | 
			
		||||
// #interface aliases
 | 
			
		||||
// list() -> table<string, string>
 | 
			
		||||
// Get a table of all aliases, with string keys as the alias and the value as the command.
 | 
			
		||||
// --- @returns table<string, string>
 | 
			
		||||
func (a *aliasModule) luaList(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	aliasesList := rt.NewTable()
 | 
			
		||||
	for k, v := range a.All() {
 | 
			
		||||
		aliasesList.Set(rt.StringValue(k), rt.StringValue(v))
 | 
			
		||||
@ -90,7 +124,11 @@ func (a *aliasHandler) luaList(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	return c.PushingNext1(t.Runtime, rt.TableValue(aliasesList)), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *aliasHandler) luaDelete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
// #interface aliases
 | 
			
		||||
// delete(name)
 | 
			
		||||
// Removes an alias.
 | 
			
		||||
// --- @param name string
 | 
			
		||||
func (a *aliasModule) luaDelete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	if err := c.Check1Arg(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
@ -103,7 +141,12 @@ func (a *aliasHandler) luaDelete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	return c.Next(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *aliasHandler) luaResolve(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
// #interface aliases
 | 
			
		||||
// resolve(alias) -> command (string)
 | 
			
		||||
// Tries to resolve an alias to its command.
 | 
			
		||||
// --- @param alias string
 | 
			
		||||
// --- @returns string
 | 
			
		||||
func (a *aliasModule) luaResolve(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	if err := c.Check1Arg(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										129
									
								
								api.go
									
									
									
									
									
								
							
							
						
						
									
										129
									
								
								api.go
									
									
									
									
									
								
							@ -1,6 +1,15 @@
 | 
			
		||||
// Here is the core api for the hilbi shell itself
 | 
			
		||||
// Basically, stuff about the shell itself and other functions
 | 
			
		||||
// go here.
 | 
			
		||||
// the core Hilbish API
 | 
			
		||||
// 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
 | 
			
		||||
// #field interactive Is Hilbish in an interactive shell?
 | 
			
		||||
// #field login Is Hilbish the login shell?
 | 
			
		||||
// #field vimMode Current Vim input mode of Hilbish (will be nil if not in Vim input mode)
 | 
			
		||||
// #field exitCode xit code of the last executed command
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
@ -19,7 +28,6 @@ import (
 | 
			
		||||
	rt "github.com/arnodel/golua/runtime"
 | 
			
		||||
	"github.com/arnodel/golua/lib/packagelib"
 | 
			
		||||
	"github.com/maxlandon/readline"
 | 
			
		||||
	"github.com/blackfireio/osinfo"
 | 
			
		||||
	"mvdan.cc/sh/v3/interp"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -102,78 +110,60 @@ func hilbishLoad(rtm *rt.Runtime) (rt.Value, func()) {
 | 
			
		||||
		username = strings.Split(username, "\\")[1] // for some reason Username includes the hostname on windows
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	util.SetFieldProtected(fakeMod, mod, "ver", rt.StringValue(getVersion()), "Hilbish version")
 | 
			
		||||
	util.SetFieldProtected(fakeMod, mod, "user", rt.StringValue(username), "Username of user")
 | 
			
		||||
	util.SetFieldProtected(fakeMod, mod, "host", rt.StringValue(host), "Host name of the machine")
 | 
			
		||||
	util.SetFieldProtected(fakeMod, mod, "home", rt.StringValue(curuser.HomeDir), "Home directory of the user")
 | 
			
		||||
	util.SetFieldProtected(fakeMod, mod, "dataDir", rt.StringValue(dataDir), "Directory for Hilbish's data files")
 | 
			
		||||
	util.SetFieldProtected(fakeMod, mod, "interactive", rt.BoolValue(interactive), "If this is an interactive shell")
 | 
			
		||||
	util.SetFieldProtected(fakeMod, mod, "login", rt.BoolValue(login), "Whether this is a login shell")
 | 
			
		||||
	util.SetFieldProtected(fakeMod, mod, "vimMode", rt.NilValue, "Current Vim mode of Hilbish (nil if not in Vim mode)")
 | 
			
		||||
	util.SetFieldProtected(fakeMod, mod, "exitCode", rt.IntValue(0), "Exit code of last exected command")
 | 
			
		||||
	util.Document(fakeMod, "Hilbish's core API, containing submodules and functions which relate to the shell itself.")
 | 
			
		||||
	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))
 | 
			
		||||
	util.SetFieldProtected(fakeMod, mod, "dataDir", rt.StringValue(dataDir))
 | 
			
		||||
	util.SetFieldProtected(fakeMod, mod, "interactive", rt.BoolValue(interactive))
 | 
			
		||||
	util.SetFieldProtected(fakeMod, mod, "login", rt.BoolValue(login))
 | 
			
		||||
	util.SetFieldProtected(fakeMod, mod, "vimMode", rt.NilValue)
 | 
			
		||||
	util.SetFieldProtected(fakeMod, mod, "exitCode", rt.IntValue(0))
 | 
			
		||||
 | 
			
		||||
	// hilbish.userDir table
 | 
			
		||||
	hshuser := rt.NewTable()
 | 
			
		||||
 | 
			
		||||
	util.SetField(rtm, hshuser, "config", rt.StringValue(confDir), "User's config directory")
 | 
			
		||||
	util.SetField(rtm, hshuser, "data", rt.StringValue(userDataDir), "XDG data directory")
 | 
			
		||||
	util.Document(hshuser, "User directories to store configs and/or modules.")
 | 
			
		||||
	hshuser := userDirLoader(rtm)
 | 
			
		||||
	mod.Set(rt.StringValue("userDir"), rt.TableValue(hshuser))
 | 
			
		||||
 | 
			
		||||
	// hilbish.os table
 | 
			
		||||
	hshos := rt.NewTable()
 | 
			
		||||
	info, _ := osinfo.GetOSInfo()
 | 
			
		||||
 | 
			
		||||
	util.SetField(rtm, hshos, "family", rt.StringValue(info.Family), "Family name of the current OS")
 | 
			
		||||
	util.SetField(rtm, hshos, "name", rt.StringValue(info.Name), "Pretty name of the current OS")
 | 
			
		||||
	util.SetField(rtm, hshos, "version", rt.StringValue(info.Version), "Version of the current OS")
 | 
			
		||||
	util.Document(hshos, "OS info interface")
 | 
			
		||||
	hshos := hshosLoader(rtm)
 | 
			
		||||
	mod.Set(rt.StringValue("os"), rt.TableValue(hshos))
 | 
			
		||||
 | 
			
		||||
	// hilbish.aliases table
 | 
			
		||||
	aliases = newAliases()
 | 
			
		||||
	aliasesModule := aliases.Loader(rtm)
 | 
			
		||||
	util.Document(aliasesModule, "Alias inferface for Hilbish.")
 | 
			
		||||
	mod.Set(rt.StringValue("aliases"), rt.TableValue(aliasesModule))
 | 
			
		||||
 | 
			
		||||
	// hilbish.history table
 | 
			
		||||
	historyModule := lr.Loader(rtm)
 | 
			
		||||
	mod.Set(rt.StringValue("history"), rt.TableValue(historyModule))
 | 
			
		||||
	util.Document(historyModule, "History interface for Hilbish.")
 | 
			
		||||
 | 
			
		||||
	// hilbish.completion table
 | 
			
		||||
	hshcomp := completionLoader(rtm)
 | 
			
		||||
	util.Document(hshcomp, "Completions interface for Hilbish.")
 | 
			
		||||
	mod.Set(rt.StringValue("completion"), rt.TableValue(hshcomp))
 | 
			
		||||
 | 
			
		||||
	// hilbish.runner table
 | 
			
		||||
	runnerModule := runnerModeLoader(rtm)
 | 
			
		||||
	util.Document(runnerModule, "Runner/exec interface for Hilbish.")
 | 
			
		||||
	mod.Set(rt.StringValue("runner"), rt.TableValue(runnerModule))
 | 
			
		||||
 | 
			
		||||
	// hilbish.jobs table
 | 
			
		||||
	jobs = newJobHandler()
 | 
			
		||||
	jobModule := jobs.loader(rtm)
 | 
			
		||||
	util.Document(jobModule, "(Background) job interface.")
 | 
			
		||||
	mod.Set(rt.StringValue("jobs"), rt.TableValue(jobModule))
 | 
			
		||||
 | 
			
		||||
	// hilbish.timers table
 | 
			
		||||
	timers = newTimerHandler()
 | 
			
		||||
	timerModule := timers.loader(rtm)
 | 
			
		||||
	util.Document(timerModule, "Timer interface, for control of all intervals and timeouts.")
 | 
			
		||||
	mod.Set(rt.StringValue("timers"), rt.TableValue(timerModule))
 | 
			
		||||
	timers = newTimersModule()
 | 
			
		||||
	timersModule := timers.loader(rtm)
 | 
			
		||||
	mod.Set(rt.StringValue("timers"), rt.TableValue(timersModule))
 | 
			
		||||
 | 
			
		||||
	editorModule := editorLoader(rtm)
 | 
			
		||||
	util.Document(editorModule, "")
 | 
			
		||||
	mod.Set(rt.StringValue("editor"), rt.TableValue(editorModule))
 | 
			
		||||
 | 
			
		||||
	versionModule := rt.NewTable()
 | 
			
		||||
	util.SetField(rtm, versionModule, "branch", rt.StringValue(gitBranch), "Git branch Hilbish was compiled from")
 | 
			
		||||
	util.SetField(rtm, versionModule, "full", rt.StringValue(getVersion()), "Full version info, including release name")
 | 
			
		||||
	util.SetField(rtm, versionModule, "commit", rt.StringValue(gitCommit), "Git commit Hilbish was compiled from")
 | 
			
		||||
	util.SetField(rtm, versionModule, "release", rt.StringValue(releaseName), "Release name")
 | 
			
		||||
	util.Document(versionModule, "Version info interface.")
 | 
			
		||||
	util.SetField(rtm, versionModule, "branch", rt.StringValue(gitBranch))
 | 
			
		||||
	util.SetField(rtm, versionModule, "full", rt.StringValue(getVersion()))
 | 
			
		||||
	util.SetField(rtm, versionModule, "commit", rt.StringValue(gitCommit))
 | 
			
		||||
	util.SetField(rtm, versionModule, "release", rt.StringValue(releaseName))
 | 
			
		||||
	mod.Set(rt.StringValue("version"), rt.TableValue(versionModule))
 | 
			
		||||
 | 
			
		||||
	return rt.TableValue(fakeMod), nil
 | 
			
		||||
@ -188,19 +178,21 @@ func getenv(key, fallback string) string {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func setVimMode(mode string) {
 | 
			
		||||
	util.SetField(l, hshMod, "vimMode", rt.StringValue(mode), "Current Vim mode of Hilbish (nil if not in Vim mode)")
 | 
			
		||||
	util.SetField(l, hshMod, "vimMode", rt.StringValue(mode))
 | 
			
		||||
	hooks.Emit("hilbish.vimMode", mode)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func unsetVimMode() {
 | 
			
		||||
	util.SetField(l, hshMod, "vimMode", rt.NilValue, "Current Vim mode of Hilbish (nil if not in Vim mode)")
 | 
			
		||||
	util.SetField(l, hshMod, "vimMode", rt.NilValue)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// run(cmd, returnOut) -> exitCode, stdout, stderr
 | 
			
		||||
// run(cmd, returnOut) -> exitCode (number), stdout (string), stderr (string)
 | 
			
		||||
// Runs `cmd` in Hilbish's sh interpreter.
 | 
			
		||||
// If returnOut is true, the outputs of `cmd` will be returned as the 2nd and
 | 
			
		||||
// 3rd values instead of being outputted to the terminal.
 | 
			
		||||
// --- @param cmd string
 | 
			
		||||
// --- @param returnOut boolean
 | 
			
		||||
// --- @returns number, string, string
 | 
			
		||||
func hlrun(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	if err := c.Check1Arg(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@ -241,8 +233,9 @@ func hlrun(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	return c.PushingNext(t.Runtime, rt.IntValue(int64(exitcode)), rt.StringValue(stdoutStr), rt.StringValue(stderrStr)), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// cwd()
 | 
			
		||||
// cwd() -> string
 | 
			
		||||
// Returns the current directory of the shell
 | 
			
		||||
// --- @returns string
 | 
			
		||||
func hlcwd(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	cwd, _ := os.Getwd()
 | 
			
		||||
 | 
			
		||||
@ -250,11 +243,12 @@ func hlcwd(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// read(prompt?) -> input?
 | 
			
		||||
// read(prompt) -> input (string)
 | 
			
		||||
// Read input from the user, using Hilbish's line editor/input reader.
 | 
			
		||||
// This is a separate instance from the one Hilbish actually uses.
 | 
			
		||||
// Returns `input`, will be nil if ctrl + d is pressed, or an error occurs (which shouldn't happen)
 | 
			
		||||
// --- @param prompt string
 | 
			
		||||
// --- @param prompt? string
 | 
			
		||||
// --- @returns string|nil
 | 
			
		||||
func hlread(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	luaprompt := c.Arg(0)
 | 
			
		||||
	if typ := luaprompt.Type(); typ != rt.StringType && typ != rt.NilType {
 | 
			
		||||
@ -281,7 +275,7 @@ func hlread(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
prompt(str, typ?)
 | 
			
		||||
prompt(str, typ)
 | 
			
		||||
Changes the shell prompt to `str`
 | 
			
		||||
There are a few verbs that can be used in the prompt text.
 | 
			
		||||
These will be formatted and replaced with the appropriate values.
 | 
			
		||||
@ -289,7 +283,7 @@ These will be formatted and replaced with the appropriate values.
 | 
			
		||||
`%u` - Name of current user
 | 
			
		||||
`%h` - Hostname of device
 | 
			
		||||
--- @param str string
 | 
			
		||||
--- @param typ string Type of prompt, being left or right. Left by default.
 | 
			
		||||
--- @param typ? string Type of prompt, being left or right. Left by default.
 | 
			
		||||
*/
 | 
			
		||||
func hlprompt(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	err := c.Check1Arg()
 | 
			
		||||
@ -453,12 +447,12 @@ func hlgoro(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	return c.Next(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// timeout(cb, time)
 | 
			
		||||
// Runs the `cb` function after `time` in milliseconds
 | 
			
		||||
// Returns a `timer` object (see `doc timers`).
 | 
			
		||||
// timeout(cb, time) -> @Timer
 | 
			
		||||
// Runs the `cb` function after `time` in milliseconds.
 | 
			
		||||
// This creates a timer that starts immediately.
 | 
			
		||||
// --- @param cb function
 | 
			
		||||
// --- @param time number
 | 
			
		||||
// --- @return table
 | 
			
		||||
// --- @returns Timer
 | 
			
		||||
func hltimeout(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	if err := c.CheckNArgs(2); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@ -479,12 +473,12 @@ func hltimeout(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	return c.PushingNext1(t.Runtime, rt.UserDataValue(timer.ud)), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// interval(cb, time)
 | 
			
		||||
// interval(cb, time) -> @Timer
 | 
			
		||||
// Runs the `cb` function every `time` milliseconds.
 | 
			
		||||
// Returns a `timer` object (see `doc timers`).
 | 
			
		||||
// This creates a timer that starts immediately.
 | 
			
		||||
// --- @param cb function
 | 
			
		||||
// --- @param time number
 | 
			
		||||
// --- @return table
 | 
			
		||||
// --- @return Timer
 | 
			
		||||
func hlinterval(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	if err := c.CheckNArgs(2); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@ -545,9 +539,11 @@ func hlprependPath(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	return c.Next(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// which(name)
 | 
			
		||||
// Checks if `name` is a valid command
 | 
			
		||||
// --- @param binName string
 | 
			
		||||
// which(name) -> string
 | 
			
		||||
// Checks if `name` is a valid command.
 | 
			
		||||
// Will return the path of the binary, or a basename if it's a commander.
 | 
			
		||||
// --- @param name string
 | 
			
		||||
// --- @returns string
 | 
			
		||||
func hlwhich(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	if err := c.Check1Arg(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@ -557,7 +553,10 @@ func hlwhich(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd := aliases.Resolve(name)
 | 
			
		||||
	// itll return either the original command or what was passed
 | 
			
		||||
	// if name isnt empty its not an issue
 | 
			
		||||
	alias := aliases.Resolve(name)
 | 
			
		||||
	cmd := strings.Split(alias, " ")[0]
 | 
			
		||||
 | 
			
		||||
	// check for commander
 | 
			
		||||
	if commands[cmd] != nil {
 | 
			
		||||
@ -632,7 +631,7 @@ func hlrunnerMode(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
// as the text for the hint. This is by default a shim. To set hints,
 | 
			
		||||
// override this function with your custom handler.
 | 
			
		||||
// --- @param line string
 | 
			
		||||
// --- @param pos int
 | 
			
		||||
// --- @param pos number
 | 
			
		||||
func hlhinter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	return c.Next(), nil
 | 
			
		||||
}
 | 
			
		||||
@ -642,6 +641,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
 | 
			
		||||
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 45 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								assets/hilbish-logo-and-text.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/hilbish-logo-and-text.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 78 KiB  | 
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 18 KiB  | 
@ -7,29 +7,267 @@ import (
 | 
			
		||||
	"go/doc"
 | 
			
		||||
	"go/parser"
 | 
			
		||||
	"go/token"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"os"
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type EmmyPiece struct {
 | 
			
		||||
	FuncName string
 | 
			
		||||
	Docs []string
 | 
			
		||||
var header = `---
 | 
			
		||||
title: %s %s
 | 
			
		||||
description: %s
 | 
			
		||||
layout: doc
 | 
			
		||||
menu:
 | 
			
		||||
  docs:
 | 
			
		||||
    parent: "API"
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
type emmyPiece struct {
 | 
			
		||||
	DocPiece *docPiece
 | 
			
		||||
	Annotations []string
 | 
			
		||||
	Params []string // we only need to know param name to put in function
 | 
			
		||||
	FuncName string
 | 
			
		||||
}
 | 
			
		||||
type DocPiece struct {
 | 
			
		||||
 | 
			
		||||
type module struct {
 | 
			
		||||
	Types []docPiece
 | 
			
		||||
	Docs []docPiece
 | 
			
		||||
	Fields []docPiece
 | 
			
		||||
	Properties []docPiece
 | 
			
		||||
	ShortDescription string
 | 
			
		||||
	Description string
 | 
			
		||||
	ParentModule string
 | 
			
		||||
	HasInterfaces bool
 | 
			
		||||
	HasTypes bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type docPiece struct {
 | 
			
		||||
	Doc []string
 | 
			
		||||
	FuncSig string
 | 
			
		||||
	FuncName string
 | 
			
		||||
	Interfacing string
 | 
			
		||||
	ParentModule string
 | 
			
		||||
	GoFuncName string
 | 
			
		||||
	IsInterface bool
 | 
			
		||||
	IsMember bool
 | 
			
		||||
	IsType bool
 | 
			
		||||
	Fields []docPiece
 | 
			
		||||
	Properties []docPiece
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type tag struct {
 | 
			
		||||
	id string
 | 
			
		||||
	fields []string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var docs = make(map[string]module)
 | 
			
		||||
var interfaceDocs = make(map[string]module)
 | 
			
		||||
var emmyDocs = make(map[string][]emmyPiece)
 | 
			
		||||
var typeTable = make(map[string][]string) // [0] = parentMod, [1] = interfaces
 | 
			
		||||
var prefix = map[string]string{
 | 
			
		||||
	"main": "hl",
 | 
			
		||||
	"hilbish": "hl",
 | 
			
		||||
	"fs": "f",
 | 
			
		||||
	"commander": "c",
 | 
			
		||||
	"bait": "b",
 | 
			
		||||
	"terminal": "term",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getTagsAndDocs(docs string) (map[string][]tag, []string) {
 | 
			
		||||
	pts := strings.Split(docs, "\n")
 | 
			
		||||
	parts := []string{}
 | 
			
		||||
	tags := make(map[string][]tag)
 | 
			
		||||
 | 
			
		||||
	for _, part := range pts {
 | 
			
		||||
		if strings.HasPrefix(part, "#") {
 | 
			
		||||
			tagParts := strings.Split(strings.TrimPrefix(part, "#"), " ")
 | 
			
		||||
			if tags[tagParts[0]] == nil {
 | 
			
		||||
				var id string
 | 
			
		||||
				if len(tagParts) > 1 {
 | 
			
		||||
					id = tagParts[1]
 | 
			
		||||
				}
 | 
			
		||||
				tags[tagParts[0]] = []tag{
 | 
			
		||||
					{id: id},
 | 
			
		||||
				}
 | 
			
		||||
				if len(tagParts) >= 2 {
 | 
			
		||||
					tags[tagParts[0]][0].fields = tagParts[2:]
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				fleds := []string{}
 | 
			
		||||
				if len(tagParts) >= 2 {
 | 
			
		||||
					fleds = tagParts[2:]
 | 
			
		||||
				}
 | 
			
		||||
				tags[tagParts[0]] = append(tags[tagParts[0]], tag{
 | 
			
		||||
					id: tagParts[1],
 | 
			
		||||
					fields: fleds,
 | 
			
		||||
				})
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			parts = append(parts, part)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return tags, parts
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func docPieceTag(tagName string, tags map[string][]tag) []docPiece {
 | 
			
		||||
	dps := []docPiece{}
 | 
			
		||||
	for _, tag := range tags[tagName] {
 | 
			
		||||
		dps = append(dps, docPiece{
 | 
			
		||||
			FuncName: tag.id,
 | 
			
		||||
			Doc: tag.fields,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return dps
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func setupDocType(mod string, typ *doc.Type) *docPiece {
 | 
			
		||||
	docs := strings.TrimSpace(typ.Doc)
 | 
			
		||||
	tags, doc := getTagsAndDocs(docs)
 | 
			
		||||
 | 
			
		||||
	if tags["type"] == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	inInterface := tags["interface"] != nil
 | 
			
		||||
 | 
			
		||||
	var interfaces string
 | 
			
		||||
	typeName := strings.ToUpper(string(typ.Name[0])) + typ.Name[1:]
 | 
			
		||||
	typeDoc := []string{}
 | 
			
		||||
 | 
			
		||||
	if inInterface {
 | 
			
		||||
		interfaces = tags["interface"][0].id
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fields := docPieceTag("field", tags)
 | 
			
		||||
	properties := docPieceTag("property", tags)
 | 
			
		||||
 | 
			
		||||
	for _, d := range doc {
 | 
			
		||||
		if strings.HasPrefix(d, "---") {
 | 
			
		||||
			// TODO: document types in lua
 | 
			
		||||
			/*
 | 
			
		||||
			emmyLine := strings.TrimSpace(strings.TrimPrefix(d, "---"))
 | 
			
		||||
			emmyLinePieces := strings.Split(emmyLine, " ")
 | 
			
		||||
			emmyType := emmyLinePieces[0]
 | 
			
		||||
			if emmyType == "@param" {
 | 
			
		||||
				em.Params = append(em.Params, emmyLinePieces[1])
 | 
			
		||||
			}
 | 
			
		||||
			if emmyType == "@vararg" {
 | 
			
		||||
				em.Params = append(em.Params, "...") // add vararg
 | 
			
		||||
			}
 | 
			
		||||
			em.Annotations = append(em.Annotations, d)
 | 
			
		||||
			*/
 | 
			
		||||
		} else {
 | 
			
		||||
			typeDoc = append(typeDoc, d)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var isMember bool
 | 
			
		||||
	if tags["member"] != nil {
 | 
			
		||||
		isMember = true
 | 
			
		||||
	}
 | 
			
		||||
	parentMod := mod
 | 
			
		||||
	dps := &docPiece{
 | 
			
		||||
		Doc: typeDoc,
 | 
			
		||||
		FuncName: typeName,
 | 
			
		||||
		Interfacing: interfaces,
 | 
			
		||||
		IsInterface: inInterface,
 | 
			
		||||
		IsMember: isMember,
 | 
			
		||||
		IsType: true,
 | 
			
		||||
		ParentModule: parentMod,
 | 
			
		||||
		Fields: fields,
 | 
			
		||||
		Properties: properties,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	typeTable[strings.ToLower(typeName)] = []string{parentMod, interfaces}
 | 
			
		||||
 | 
			
		||||
	return dps
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func setupDoc(mod string, fun *doc.Func) *docPiece {
 | 
			
		||||
	docs := strings.TrimSpace(fun.Doc)
 | 
			
		||||
	tags, parts := getTagsAndDocs(docs)
 | 
			
		||||
 | 
			
		||||
	// i couldnt fit this into the condition below for some reason so here's a goto!
 | 
			
		||||
	if tags["member"] != nil {
 | 
			
		||||
		goto start
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!strings.HasPrefix(fun.Name, prefix[mod]) && tags["interface"] == nil) || (strings.ToLower(fun.Name) == "loader" && tags["interface"] == nil) {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
start:
 | 
			
		||||
	inInterface := tags["interface"] != nil
 | 
			
		||||
	var interfaces string
 | 
			
		||||
	funcsig := parts[0]
 | 
			
		||||
	doc := parts[1:]
 | 
			
		||||
	funcName := strings.TrimPrefix(fun.Name, prefix[mod])
 | 
			
		||||
	funcdoc := []string{}
 | 
			
		||||
 | 
			
		||||
	if inInterface {
 | 
			
		||||
		interfaces = tags["interface"][0].id
 | 
			
		||||
		funcName = interfaces + "." + strings.Split(funcsig, "(")[0]
 | 
			
		||||
	}
 | 
			
		||||
	em := emmyPiece{FuncName: funcName}
 | 
			
		||||
 | 
			
		||||
	fields := docPieceTag("field", tags)
 | 
			
		||||
	properties := docPieceTag("property", tags)
 | 
			
		||||
 | 
			
		||||
	for _, d := range doc {
 | 
			
		||||
		if strings.HasPrefix(d, "---") {
 | 
			
		||||
			emmyLine := strings.TrimSpace(strings.TrimPrefix(d, "---"))
 | 
			
		||||
			emmyLinePieces := strings.Split(emmyLine, " ")
 | 
			
		||||
			emmyType := emmyLinePieces[0]
 | 
			
		||||
			if emmyType == "@param" {
 | 
			
		||||
				em.Params = append(em.Params, emmyLinePieces[1])
 | 
			
		||||
			}
 | 
			
		||||
			if emmyType == "@vararg" {
 | 
			
		||||
				em.Params = append(em.Params, "...") // add vararg
 | 
			
		||||
			}
 | 
			
		||||
			em.Annotations = append(em.Annotations, d)
 | 
			
		||||
		} else {
 | 
			
		||||
			funcdoc = append(funcdoc, d)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var isMember bool
 | 
			
		||||
	if tags["member"] != nil {
 | 
			
		||||
		isMember = true
 | 
			
		||||
	}
 | 
			
		||||
	var parentMod string
 | 
			
		||||
	if inInterface {
 | 
			
		||||
		parentMod = mod
 | 
			
		||||
	}
 | 
			
		||||
	dps := &docPiece{
 | 
			
		||||
		Doc: funcdoc,
 | 
			
		||||
		FuncSig: funcsig,
 | 
			
		||||
		FuncName: funcName,
 | 
			
		||||
		Interfacing: interfaces,
 | 
			
		||||
		GoFuncName: strings.ToLower(fun.Name),
 | 
			
		||||
		IsInterface: inInterface,
 | 
			
		||||
		IsMember: isMember,
 | 
			
		||||
		ParentModule: parentMod,
 | 
			
		||||
		Fields: fields,
 | 
			
		||||
		Properties: properties,
 | 
			
		||||
	}
 | 
			
		||||
	if strings.HasSuffix(dps.GoFuncName, strings.ToLower("loader")) {
 | 
			
		||||
		dps.Doc = parts
 | 
			
		||||
	}
 | 
			
		||||
	em.DocPiece = dps
 | 
			
		||||
			
 | 
			
		||||
	emmyDocs[mod] = append(emmyDocs[mod], em)
 | 
			
		||||
	return dps
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// feel free to clean this up
 | 
			
		||||
// it works, dont really care about the code
 | 
			
		||||
func main() {
 | 
			
		||||
	fset := token.NewFileSet()
 | 
			
		||||
	os.Mkdir("docs", 0777)
 | 
			
		||||
	os.Mkdir("docs/api", 0777)
 | 
			
		||||
	os.Mkdir("emmyLuaDocs", 0777)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	dirs := []string{"./"}
 | 
			
		||||
	filepath.Walk("golibs/", func (path string, info os.FileInfo, err error) error {
 | 
			
		||||
		if !info.IsDir() {
 | 
			
		||||
@ -51,94 +289,182 @@ func main() {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	prefix := map[string]string{
 | 
			
		||||
		"hilbish": "hl",
 | 
			
		||||
		"fs": "f",
 | 
			
		||||
		"commander": "c",
 | 
			
		||||
		"bait": "b",
 | 
			
		||||
		"terminal": "term",
 | 
			
		||||
	}
 | 
			
		||||
	docs := make(map[string][]DocPiece)
 | 
			
		||||
	emmyDocs := make(map[string][]EmmyPiece)
 | 
			
		||||
 | 
			
		||||
	interfaceModules := make(map[string]*module)
 | 
			
		||||
	for l, f := range pkgs {
 | 
			
		||||
		p := doc.New(f, "./", doc.AllDecls)
 | 
			
		||||
		for _, t := range p.Funcs {
 | 
			
		||||
		pieces := []docPiece{}
 | 
			
		||||
		typePieces := []docPiece{}
 | 
			
		||||
		mod := l
 | 
			
		||||
			if strings.HasPrefix(t.Name, "hl") { mod = "hilbish" }
 | 
			
		||||
			if !strings.HasPrefix(t.Name, prefix[mod]) || t.Name == "Loader" { continue }
 | 
			
		||||
			parts := strings.Split(strings.TrimSpace(t.Doc), "\n")
 | 
			
		||||
			funcsig := parts[0]
 | 
			
		||||
			doc := parts[1:]
 | 
			
		||||
			funcdoc := []string{}
 | 
			
		||||
			em := EmmyPiece{FuncName: strings.TrimPrefix(t.Name, prefix[mod])}
 | 
			
		||||
			for _, d := range doc {
 | 
			
		||||
				if strings.HasPrefix(d, "---") {
 | 
			
		||||
					emmyLine := strings.TrimSpace(strings.TrimPrefix(d, "---"))
 | 
			
		||||
					emmyLinePieces := strings.Split(emmyLine, " ")
 | 
			
		||||
					emmyType := emmyLinePieces[0]
 | 
			
		||||
					if emmyType == "@param" {
 | 
			
		||||
						em.Params = append(em.Params, emmyLinePieces[1])
 | 
			
		||||
					}
 | 
			
		||||
					if emmyType == "@vararg" {
 | 
			
		||||
						em.Params = append(em.Params, "...") // add vararg
 | 
			
		||||
					}
 | 
			
		||||
					em.Docs = append(em.Docs, d)
 | 
			
		||||
				} else {
 | 
			
		||||
					funcdoc = append(funcdoc, d)
 | 
			
		||||
		if mod == "main" {
 | 
			
		||||
			mod = "hilbish"
 | 
			
		||||
		}
 | 
			
		||||
		var hasInterfaces bool
 | 
			
		||||
		for _, t := range p.Funcs {
 | 
			
		||||
			piece := setupDoc(mod, t)
 | 
			
		||||
			if piece == nil {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			dps := DocPiece{
 | 
			
		||||
				Doc: funcdoc,
 | 
			
		||||
				FuncSig: funcsig,
 | 
			
		||||
				FuncName: strings.TrimPrefix(t.Name, prefix[mod]),
 | 
			
		||||
			pieces = append(pieces, *piece)
 | 
			
		||||
			if piece.IsInterface {
 | 
			
		||||
				hasInterfaces = true
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
			docs[mod] = append(docs[mod], dps)
 | 
			
		||||
			emmyDocs[mod] = append(emmyDocs[mod], em)
 | 
			
		||||
		}
 | 
			
		||||
		for _, t := range p.Types {
 | 
			
		||||
			for _, m := range t.Methods {
 | 
			
		||||
				if !strings.HasPrefix(m.Name, prefix[l]) || m.Name == "Loader" { continue }
 | 
			
		||||
				parts := strings.Split(strings.TrimSpace(m.Doc), "\n")
 | 
			
		||||
				funcsig := parts[0]
 | 
			
		||||
				doc := parts[1:]
 | 
			
		||||
				funcdoc := []string{}
 | 
			
		||||
				em := EmmyPiece{FuncName: strings.TrimPrefix(m.Name, prefix[l])}
 | 
			
		||||
				for _, d := range doc {
 | 
			
		||||
					if strings.HasPrefix(d, "---") {
 | 
			
		||||
						emmyLine := strings.TrimSpace(strings.TrimPrefix(d, "---"))
 | 
			
		||||
						emmyLinePieces := strings.Split(emmyLine, " ")
 | 
			
		||||
						emmyType := emmyLinePieces[0]
 | 
			
		||||
						if emmyType == "@param" {
 | 
			
		||||
							em.Params = append(em.Params, emmyLinePieces[1])
 | 
			
		||||
			typePiece := setupDocType(mod, t)
 | 
			
		||||
			if typePiece != nil {
 | 
			
		||||
				typePieces = append(typePieces, *typePiece)
 | 
			
		||||
				if typePiece.IsInterface {
 | 
			
		||||
					hasInterfaces = true
 | 
			
		||||
				}
 | 
			
		||||
						if emmyType == "@vararg" {
 | 
			
		||||
							em.Params = append(em.Params, "...") // add vararg
 | 
			
		||||
						}
 | 
			
		||||
						em.Docs = append(em.Docs, d)
 | 
			
		||||
					} else {
 | 
			
		||||
						funcdoc = append(funcdoc, d)
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				dps := DocPiece{
 | 
			
		||||
					Doc: funcdoc,
 | 
			
		||||
					FuncSig: funcsig,
 | 
			
		||||
					FuncName: strings.TrimPrefix(m.Name, prefix[l]),
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
				docs[l] = append(docs[l], dps)
 | 
			
		||||
				emmyDocs[l] = append(emmyDocs[l], em)
 | 
			
		||||
			for _, m := range t.Methods {
 | 
			
		||||
				piece := setupDoc(mod, m)
 | 
			
		||||
				if piece == nil {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				pieces = append(pieces, *piece)
 | 
			
		||||
				if piece.IsInterface {
 | 
			
		||||
					hasInterfaces = true
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		tags, descParts := getTagsAndDocs(strings.TrimSpace(p.Doc))
 | 
			
		||||
		shortDesc := descParts[0]
 | 
			
		||||
		desc := descParts[1:]
 | 
			
		||||
		filteredPieces := []docPiece{}
 | 
			
		||||
		filteredTypePieces := []docPiece{}
 | 
			
		||||
		for _, piece := range pieces {
 | 
			
		||||
			if !piece.IsInterface {
 | 
			
		||||
				filteredPieces = append(filteredPieces, piece)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			modname := piece.ParentModule + "." + piece.Interfacing
 | 
			
		||||
			if interfaceModules[modname] == nil {
 | 
			
		||||
				interfaceModules[modname] = &module{
 | 
			
		||||
					ParentModule: piece.ParentModule,
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if strings.HasSuffix(piece.GoFuncName, strings.ToLower("loader")) {
 | 
			
		||||
				shortDesc := piece.Doc[0]
 | 
			
		||||
				desc := piece.Doc[1:]
 | 
			
		||||
				interfaceModules[modname].ShortDescription = shortDesc
 | 
			
		||||
				interfaceModules[modname].Description = strings.Join(desc, "\n")
 | 
			
		||||
				interfaceModules[modname].Fields = piece.Fields
 | 
			
		||||
				interfaceModules[modname].Properties = piece.Properties
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			interfaceModules[modname].Docs = append(interfaceModules[modname].Docs, piece)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, piece := range typePieces {
 | 
			
		||||
			if !piece.IsInterface {
 | 
			
		||||
				filteredTypePieces = append(filteredTypePieces, piece)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			modname := piece.ParentModule + "." + piece.Interfacing
 | 
			
		||||
			if interfaceModules[modname] == nil {
 | 
			
		||||
				interfaceModules[modname] = &module{
 | 
			
		||||
					ParentModule: piece.ParentModule,
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			interfaceModules[modname].Types = append(interfaceModules[modname].Types, piece)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		docs[mod] = module{
 | 
			
		||||
			Types: filteredTypePieces,
 | 
			
		||||
			Docs: filteredPieces,
 | 
			
		||||
			ShortDescription: shortDesc,
 | 
			
		||||
			Description: strings.Join(desc, "\n"),
 | 
			
		||||
			HasInterfaces: hasInterfaces,
 | 
			
		||||
			Properties: docPieceTag("property", tags),
 | 
			
		||||
			Fields: docPieceTag("field", tags),
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for key, mod := range interfaceModules {
 | 
			
		||||
		docs[key] = *mod
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var wg sync.WaitGroup
 | 
			
		||||
	wg.Add(len(docs) * 2)
 | 
			
		||||
 | 
			
		||||
	for mod, v := range docs {
 | 
			
		||||
		if mod == "main" { continue }
 | 
			
		||||
		f, _ := os.Create("docs/" + mod + ".txt")
 | 
			
		||||
		for _, dps := range v {
 | 
			
		||||
			f.WriteString(dps.FuncSig + " > ")
 | 
			
		||||
		docPath := "docs/api/" + mod + ".md"
 | 
			
		||||
		if v.HasInterfaces {
 | 
			
		||||
			os.Mkdir("docs/api/" + mod, 0777)
 | 
			
		||||
			os.Remove(docPath) // remove old doc path if it exists
 | 
			
		||||
			docPath = "docs/api/" + mod + "/_index.md"
 | 
			
		||||
		}
 | 
			
		||||
		if v.ParentModule != "" {
 | 
			
		||||
			docPath = "docs/api/" + v.ParentModule + "/" + mod + ".md"
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		go func(modname, docPath string, modu module) {
 | 
			
		||||
			defer wg.Done()
 | 
			
		||||
			modOrIface := "Module"
 | 
			
		||||
			if modu.ParentModule != "" {
 | 
			
		||||
				modOrIface = "Interface"
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			f, _ := os.Create(docPath)
 | 
			
		||||
			f.WriteString(fmt.Sprintf(header, modOrIface, modname, modu.ShortDescription))
 | 
			
		||||
			typeTag, _ := regexp.Compile(`@\w+`)
 | 
			
		||||
			modDescription := typeTag.ReplaceAllStringFunc(strings.Replace(modu.Description, "<", `\<`, -1), func(typ string) string {
 | 
			
		||||
				typName := typ[1:]
 | 
			
		||||
				typLookup := typeTable[strings.ToLower(typName)]
 | 
			
		||||
				ifaces := typLookup[0] + "." + typLookup[1] + "/"
 | 
			
		||||
				if typLookup[1] == "" {
 | 
			
		||||
					ifaces = ""
 | 
			
		||||
				}
 | 
			
		||||
				linkedTyp := fmt.Sprintf("/Hilbish/docs/api/%s/%s#%s", typLookup[0], ifaces, strings.ToLower(typName))
 | 
			
		||||
				return fmt.Sprintf(`<a href="%s" style="text-decoration: none;">%s</a>`, linkedTyp, typName)
 | 
			
		||||
			})
 | 
			
		||||
			f.WriteString(fmt.Sprintf("## Introduction\n%s\n\n", modDescription))
 | 
			
		||||
			if len(modu.Fields) != 0 {
 | 
			
		||||
				f.WriteString("## Interface fields\n")
 | 
			
		||||
				for _, dps := range modu.Fields {
 | 
			
		||||
					f.WriteString(fmt.Sprintf("- `%s`: ", dps.FuncName))
 | 
			
		||||
					f.WriteString(strings.Join(dps.Doc, " "))
 | 
			
		||||
					f.WriteString("\n")
 | 
			
		||||
				}
 | 
			
		||||
				f.WriteString("\n")
 | 
			
		||||
			}
 | 
			
		||||
			if len(modu.Properties) != 0 {
 | 
			
		||||
				f.WriteString("## Object properties\n")
 | 
			
		||||
				for _, dps := range modu.Properties {
 | 
			
		||||
					f.WriteString(fmt.Sprintf("- `%s`: ", dps.FuncName))
 | 
			
		||||
					f.WriteString(strings.Join(dps.Doc, " "))
 | 
			
		||||
					f.WriteString("\n")
 | 
			
		||||
				}
 | 
			
		||||
				f.WriteString("\n")
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if len(modu.Docs) != 0 {
 | 
			
		||||
				f.WriteString("## Functions\n")
 | 
			
		||||
				for _, dps := range modu.Docs {
 | 
			
		||||
					if dps.IsMember {
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
					htmlSig := typeTag.ReplaceAllStringFunc(strings.Replace(dps.FuncSig, "<", `\<`, -1), func(typ string) string {
 | 
			
		||||
						typName := typ[1:]
 | 
			
		||||
						typLookup := typeTable[strings.ToLower(typName)]
 | 
			
		||||
						ifaces := typLookup[0] + "." + typLookup[1] + "/"
 | 
			
		||||
						if typLookup[1] == "" {
 | 
			
		||||
							ifaces = ""
 | 
			
		||||
						}
 | 
			
		||||
						linkedTyp := fmt.Sprintf("/Hilbish/docs/api/%s/%s#%s", typLookup[0], ifaces, strings.ToLower(typName))
 | 
			
		||||
						return fmt.Sprintf(`<a href="%s" style="text-decoration: none;">%s</a>`, linkedTyp, typName)
 | 
			
		||||
					})
 | 
			
		||||
					f.WriteString(fmt.Sprintf("### %s\n", htmlSig))
 | 
			
		||||
					for _, doc := range dps.Doc {
 | 
			
		||||
						if !strings.HasPrefix(doc, "---") {
 | 
			
		||||
							f.WriteString(doc + "\n")
 | 
			
		||||
@ -148,23 +474,81 @@ func main() {
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
	for mod, v := range emmyDocs {
 | 
			
		||||
		if mod == "main" { continue }
 | 
			
		||||
		f, _ := os.Create("emmyLuaDocs/" + mod + ".lua")
 | 
			
		||||
		f.WriteString("--- @meta\n\nlocal " + mod + " = {}\n\n")
 | 
			
		||||
		for _, em := range v {
 | 
			
		||||
			var funcdocs []string
 | 
			
		||||
			for _, dps := range docs[mod] {
 | 
			
		||||
				if dps.FuncName == em.FuncName {
 | 
			
		||||
					funcdocs = dps.Doc
 | 
			
		||||
			if len(modu.Types) != 0 {
 | 
			
		||||
				f.WriteString("## Types\n")
 | 
			
		||||
				for _, dps := range modu.Types {
 | 
			
		||||
					f.WriteString(fmt.Sprintf("## %s\n", dps.FuncName))
 | 
			
		||||
					for _, doc := range dps.Doc {
 | 
			
		||||
						if !strings.HasPrefix(doc, "---") {
 | 
			
		||||
							f.WriteString(doc + "\n")
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
			f.WriteString("--- " + strings.Join(funcdocs, "\n--- ") + "\n")
 | 
			
		||||
			if len(em.Docs) != 0 {
 | 
			
		||||
				f.WriteString(strings.Join(em.Docs, "\n") + "\n")
 | 
			
		||||
			}
 | 
			
		||||
			f.WriteString("function " + mod + "." + em.FuncName + "(" + strings.Join(em.Params, ", ") + ") end\n\n")
 | 
			
		||||
		}
 | 
			
		||||
		f.WriteString("return " + mod + "\n")
 | 
			
		||||
					if len(dps.Properties) != 0 {
 | 
			
		||||
						f.WriteString("### Properties\n")
 | 
			
		||||
						for _, dps := range dps.Properties {
 | 
			
		||||
							f.WriteString(fmt.Sprintf("- `%s`: ", dps.FuncName))
 | 
			
		||||
							f.WriteString(strings.Join(dps.Doc, " "))
 | 
			
		||||
							f.WriteString("\n")
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
					f.WriteString("\n")
 | 
			
		||||
					f.WriteString("### Methods\n")
 | 
			
		||||
					for _, dps := range modu.Docs {
 | 
			
		||||
						if !dps.IsMember {
 | 
			
		||||
							continue
 | 
			
		||||
						}
 | 
			
		||||
						htmlSig := typeTag.ReplaceAllStringFunc(strings.Replace(dps.FuncSig, "<", `\<`, -1), func(typ string) string {
 | 
			
		||||
							typName := regexp.MustCompile(`\w+`).FindString(typ[1:])
 | 
			
		||||
							typLookup := typeTable[strings.ToLower(typName)]
 | 
			
		||||
							fmt.Printf("%+q, \n", typLookup)
 | 
			
		||||
							linkedTyp := fmt.Sprintf("/Hilbish/docs/api/%s/%s/#%s", typLookup[0], typLookup[0] + "." + typLookup[1], strings.ToLower(typName))
 | 
			
		||||
							return fmt.Sprintf(`<a href="#%s" style="text-decoration: none;">%s</a>`, linkedTyp, typName)
 | 
			
		||||
						})
 | 
			
		||||
						f.WriteString(fmt.Sprintf("#### %s\n", htmlSig))
 | 
			
		||||
						for _, doc := range dps.Doc {
 | 
			
		||||
							if !strings.HasPrefix(doc, "---") {
 | 
			
		||||
								f.WriteString(doc + "\n")
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
						f.WriteString("\n")
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}(mod, docPath, v)
 | 
			
		||||
 | 
			
		||||
		go func(md, modname string, modu module) {
 | 
			
		||||
			defer wg.Done()
 | 
			
		||||
 | 
			
		||||
			if modu.ParentModule != "" {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			ff, _ := os.Create("emmyLuaDocs/" + modname + ".lua")
 | 
			
		||||
			ff.WriteString("--- @meta\n\nlocal " + modname + " = {}\n\n")
 | 
			
		||||
			for _, em := range emmyDocs[modname] {
 | 
			
		||||
				if strings.HasSuffix(em.DocPiece.GoFuncName, strings.ToLower("loader")) {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				dps := em.DocPiece
 | 
			
		||||
				funcdocs := dps.Doc
 | 
			
		||||
				ff.WriteString("--- " + strings.Join(funcdocs, "\n--- ") + "\n")
 | 
			
		||||
				if len(em.Annotations) != 0 {
 | 
			
		||||
					ff.WriteString(strings.Join(em.Annotations, "\n") + "\n")
 | 
			
		||||
				}
 | 
			
		||||
				accessor := "."
 | 
			
		||||
				if dps.IsMember {
 | 
			
		||||
					accessor = ":"
 | 
			
		||||
				}
 | 
			
		||||
				signature := strings.Split(dps.FuncSig, " ->")[0]
 | 
			
		||||
				var intrface string
 | 
			
		||||
				if dps.IsInterface {
 | 
			
		||||
					intrface = "." + dps.Interfacing
 | 
			
		||||
				}
 | 
			
		||||
				ff.WriteString("function " + modname + intrface + accessor + signature + " end\n\n")
 | 
			
		||||
			}
 | 
			
		||||
			ff.WriteString("return " + modname + "\n")
 | 
			
		||||
		}(mod, mod, v)
 | 
			
		||||
	}
 | 
			
		||||
	wg.Wait()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										122
									
								
								complete.go
									
									
									
									
									
								
							
							
						
						
									
										122
									
								
								complete.go
									
									
									
									
									
								
							@ -11,15 +11,49 @@ import (
 | 
			
		||||
	rt "github.com/arnodel/golua/runtime"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func splitQuote(str string) []string {
 | 
			
		||||
var charEscapeMap = []string{
 | 
			
		||||
	"\"", "\\\"",
 | 
			
		||||
	"'", "\\'",
 | 
			
		||||
	"`", "\\`",
 | 
			
		||||
	" ", "\\ ",
 | 
			
		||||
	"(", "\\(",
 | 
			
		||||
	")", "\\)",
 | 
			
		||||
	"[", "\\[",
 | 
			
		||||
	"]", "\\]",
 | 
			
		||||
	"$", "\\$",
 | 
			
		||||
	"&", "\\&",
 | 
			
		||||
	"*", "\\*",
 | 
			
		||||
	">", "\\>",
 | 
			
		||||
	"<", "\\<",
 | 
			
		||||
	"|", "\\|",
 | 
			
		||||
}
 | 
			
		||||
var charEscapeMapInvert = invert(charEscapeMap)
 | 
			
		||||
var escapeReplaer = strings.NewReplacer(charEscapeMap...)
 | 
			
		||||
var escapeInvertReplaer = strings.NewReplacer(charEscapeMapInvert...)
 | 
			
		||||
 | 
			
		||||
func invert(m []string) []string {
 | 
			
		||||
	newM := make([]string, len(charEscapeMap))
 | 
			
		||||
	for i := range m {
 | 
			
		||||
		if (i + 1) % 2 == 0 {
 | 
			
		||||
			newM[i] = m[i - 1]
 | 
			
		||||
			newM[i - 1] = m[i]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return newM
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func splitForFile(str string) []string {
 | 
			
		||||
	split := []string{}
 | 
			
		||||
	sb := &strings.Builder{}
 | 
			
		||||
	quoted := false
 | 
			
		||||
 | 
			
		||||
	for _, r := range str {
 | 
			
		||||
	for i, r := range str {
 | 
			
		||||
		if r == '"' {
 | 
			
		||||
			quoted = !quoted
 | 
			
		||||
			sb.WriteRune(r)
 | 
			
		||||
		} else if r == ' ' && str[i - 1] == '\\' {
 | 
			
		||||
			sb.WriteRune(r)
 | 
			
		||||
		} else if !quoted && r == ' ' {
 | 
			
		||||
			split = append(split, sb.String())
 | 
			
		||||
			sb.Reset()
 | 
			
		||||
@ -39,12 +73,22 @@ func splitQuote(str string) []string {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func fileComplete(query, ctx string, fields []string) ([]string, string) {
 | 
			
		||||
	q := splitQuote(ctx)
 | 
			
		||||
	q := splitForFile(ctx)
 | 
			
		||||
	path := ""
 | 
			
		||||
	if len(q) != 0 {
 | 
			
		||||
		path = q[len(q) - 1]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return matchPath(q[len(q) - 1])
 | 
			
		||||
	return matchPath(path)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func binaryComplete(query, ctx string, fields []string) ([]string, string) {
 | 
			
		||||
	q := splitForFile(ctx)
 | 
			
		||||
	query = ""
 | 
			
		||||
	if len(q) != 0 {
 | 
			
		||||
		query = q[len(q) - 1]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var completions []string
 | 
			
		||||
 | 
			
		||||
	prefixes := []string{"./", "../", "/", "~/"}
 | 
			
		||||
@ -54,7 +98,7 @@ func binaryComplete(query, ctx string, fields []string) ([]string, string) {
 | 
			
		||||
			if len(fileCompletions) != 0 {
 | 
			
		||||
				for _, f := range fileCompletions {
 | 
			
		||||
					fullPath, _ := filepath.Abs(util.ExpandHome(query + strings.TrimPrefix(f, filePref)))
 | 
			
		||||
					if err := findExecutable(fullPath, false, true); err != nil {
 | 
			
		||||
					if err := findExecutable(escapeInvertReplaer.Replace(fullPath), false, true); err != nil {
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
					completions = append(completions, f)
 | 
			
		||||
@ -66,7 +110,6 @@ func binaryComplete(query, ctx string, fields []string) ([]string, string) {
 | 
			
		||||
 | 
			
		||||
	// filter out executables, but in path
 | 
			
		||||
	for _, dir := range filepath.SplitList(os.Getenv("PATH")) {
 | 
			
		||||
		// print dir to stderr for debugging
 | 
			
		||||
		// search for an executable which matches our query string
 | 
			
		||||
		if matches, err := filepath.Glob(filepath.Join(dir, query + "*")); err == nil {
 | 
			
		||||
			// get basename from matches
 | 
			
		||||
@ -102,6 +145,7 @@ func matchPath(query string) ([]string, string) {
 | 
			
		||||
	var entries []string
 | 
			
		||||
	var baseName string
 | 
			
		||||
 | 
			
		||||
	query = escapeInvertReplaer.Replace(query)
 | 
			
		||||
	path, _ := filepath.Abs(util.ExpandHome(filepath.Dir(query)))
 | 
			
		||||
	if string(query) == "" {
 | 
			
		||||
		// filepath base below would give us "."
 | 
			
		||||
@ -112,7 +156,16 @@ func matchPath(query string) ([]string, string) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	files, _ := os.ReadDir(path)
 | 
			
		||||
	for _, file := range files {
 | 
			
		||||
	for _, entry := range files {
 | 
			
		||||
		// should we handle errors here?
 | 
			
		||||
		file, err := entry.Info()
 | 
			
		||||
		if err == nil && file.Mode() & os.ModeSymlink != 0 {
 | 
			
		||||
			path, err := filepath.EvalSymlinks(filepath.Join(path, file.Name()))
 | 
			
		||||
			if err == nil {
 | 
			
		||||
				file, err = os.Lstat(path)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if strings.HasPrefix(file.Name(), baseName) {
 | 
			
		||||
			entry := file.Name()
 | 
			
		||||
			if file.IsDir() {
 | 
			
		||||
@ -124,32 +177,20 @@ func matchPath(query string) ([]string, string) {
 | 
			
		||||
			entries = append(entries, entry)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if !strings.HasPrefix(oldQuery, "\"") {
 | 
			
		||||
		baseName = escapeFilename(baseName)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return entries, baseName
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func escapeFilename(fname string) string {
 | 
			
		||||
	args := []string{
 | 
			
		||||
		"\"", "\\\"",
 | 
			
		||||
		"'", "\\'",
 | 
			
		||||
		"`", "\\`",
 | 
			
		||||
		" ", "\\ ",
 | 
			
		||||
		"(", "\\(",
 | 
			
		||||
		")", "\\)",
 | 
			
		||||
		"[", "\\[",
 | 
			
		||||
		"]", "\\]",
 | 
			
		||||
		"$", "\\$",
 | 
			
		||||
		"&", "\\&",
 | 
			
		||||
		"*", "\\*",
 | 
			
		||||
		">", "\\>",
 | 
			
		||||
		"<", "\\<",
 | 
			
		||||
		"|", "\\|",
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r := strings.NewReplacer(args...)
 | 
			
		||||
	return r.Replace(fname)
 | 
			
		||||
	return escapeReplaer.Replace(fname)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #interface completions
 | 
			
		||||
// tab completions
 | 
			
		||||
// The completions interface deals with tab completions.
 | 
			
		||||
func completionLoader(rtm *rt.Runtime) *rt.Table {
 | 
			
		||||
	exports := map[string]util.LuaExport{
 | 
			
		||||
		"files": {luaFileComplete, 3, false},
 | 
			
		||||
@ -164,11 +205,26 @@ func completionLoader(rtm *rt.Runtime) *rt.Table {
 | 
			
		||||
	return mod
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// left as a shim, might doc in the same way as hilbish functions
 | 
			
		||||
// #interface completions
 | 
			
		||||
// handler(line, pos)
 | 
			
		||||
// The handler function is the callback for tab completion in Hilbish.
 | 
			
		||||
// You can check the completions doc for more info.
 | 
			
		||||
// --- @param line string
 | 
			
		||||
// --- @param pos string
 | 
			
		||||
func completionHandler(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	return c.Next(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #interface completions
 | 
			
		||||
// call(name, query, ctx, fields) -> completionGroups (table), prefix (string)
 | 
			
		||||
// Calls a completer function. This is mainly used to call
 | 
			
		||||
// a command completer, which will have a `name` in the form
 | 
			
		||||
// of `command.name`, example: `command.git`.
 | 
			
		||||
// You can check `doc completions` for info on the `completionGroups` return value.
 | 
			
		||||
// --- @param name string
 | 
			
		||||
// --- @param query string
 | 
			
		||||
// --- @param ctx string
 | 
			
		||||
// --- @param fields table
 | 
			
		||||
func callLuaCompleter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	if err := c.CheckNArgs(4); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@ -208,6 +264,12 @@ func callLuaCompleter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	return c.PushingNext1(t.Runtime, completerReturn), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #interface completions
 | 
			
		||||
// files(query, ctx, fields) -> entries (table), prefix (string)
 | 
			
		||||
// Returns file completion candidates based on the provided query.
 | 
			
		||||
// --- @param query string
 | 
			
		||||
// --- @param ctx string
 | 
			
		||||
// --- @param fields table
 | 
			
		||||
func luaFileComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	query, ctx, fds, err := getCompleteParams(t, c)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@ -224,6 +286,12 @@ func luaFileComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	return c.PushingNext(t.Runtime, rt.TableValue(luaComps), rt.StringValue(pfx)), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #interface completions
 | 
			
		||||
// bins(query, ctx, fields) -> entries (table), prefix (string)
 | 
			
		||||
// Returns binary/executale completion candidates based on the provided query.
 | 
			
		||||
// --- @param query string
 | 
			
		||||
// --- @param ctx string
 | 
			
		||||
// --- @param fields table
 | 
			
		||||
func luaBinaryComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	query, ctx, fds, err := getCompleteParams(t, c)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								docs/api/_index.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								docs/api/_index.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
			
		||||
---
 | 
			
		||||
title: API
 | 
			
		||||
layout: doc
 | 
			
		||||
weight: -50
 | 
			
		||||
menu: docs
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
Welcome to the API documentation for Hilbish. This documents Lua functions
 | 
			
		||||
provided by Hilbish.
 | 
			
		||||
							
								
								
									
										34
									
								
								docs/api/bait.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								docs/api/bait.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,34 @@
 | 
			
		||||
---
 | 
			
		||||
title: Module bait
 | 
			
		||||
description: the event emitter
 | 
			
		||||
layout: doc
 | 
			
		||||
menu:
 | 
			
		||||
  docs:
 | 
			
		||||
    parent: "API"
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## Introduction
 | 
			
		||||
Bait is the event emitter for Hilbish. Why name it bait? Why not.
 | 
			
		||||
It throws hooks that you can catch. This is what you will use if
 | 
			
		||||
you want to listen in on hooks to know when certain things have
 | 
			
		||||
happened, like when you've changed directory, a command has failed,
 | 
			
		||||
etc. To find all available hooks thrown by Hilbish, see doc hooks.
 | 
			
		||||
 | 
			
		||||
## Functions
 | 
			
		||||
### catch(name, cb)
 | 
			
		||||
Catches a hook with `name`. Runs the `cb` when it is thrown
 | 
			
		||||
 | 
			
		||||
### catchOnce(name, cb)
 | 
			
		||||
Same as catch, but only runs the `cb` once and then removes the hook
 | 
			
		||||
 | 
			
		||||
### hooks(name) -> table
 | 
			
		||||
Returns a table with hooks (callback functions) on the event with `name`.
 | 
			
		||||
 | 
			
		||||
### release(name, catcher)
 | 
			
		||||
Removes the `catcher` for the event with `name`.
 | 
			
		||||
For this to work, `catcher` has to be the same function used to catch
 | 
			
		||||
an event, like one saved to a variable.
 | 
			
		||||
 | 
			
		||||
### throw(name, ...args)
 | 
			
		||||
Throws a hook with `name` with the provided `args`
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										46
									
								
								docs/api/commander.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								docs/api/commander.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,46 @@
 | 
			
		||||
---
 | 
			
		||||
title: Module commander
 | 
			
		||||
description: library for custom commands
 | 
			
		||||
layout: doc
 | 
			
		||||
menu:
 | 
			
		||||
  docs:
 | 
			
		||||
    parent: "API"
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## Introduction
 | 
			
		||||
 | 
			
		||||
Commander is a library for writing custom commands in Lua.
 | 
			
		||||
In order to make it easier to write commands for Hilbish,
 | 
			
		||||
not require separate scripts and to be able to use in a config,
 | 
			
		||||
the Commander library exists. This is like a very simple wrapper
 | 
			
		||||
that works with Hilbish for writing commands. Example:
 | 
			
		||||
 | 
			
		||||
```lua
 | 
			
		||||
local commander = require 'commander'
 | 
			
		||||
 | 
			
		||||
commander.register('hello', function(args, sinks)
 | 
			
		||||
	sinks.out:writeln 'Hello world!'
 | 
			
		||||
end)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
In this example, a command with the name of `hello` is created
 | 
			
		||||
that will print `Hello world!` to output. One question you may
 | 
			
		||||
have is: What is the `sinks` parameter?
 | 
			
		||||
 | 
			
		||||
The `sinks` parameter is a table with 3 keys: `in`, `out`,
 | 
			
		||||
and `err`. The values of these is a <a href="/Hilbish/docs/api/hilbish/#sink" style="text-decoration: none;">Sink</a>.
 | 
			
		||||
 | 
			
		||||
- `in` is the standard input. You can read from this sink
 | 
			
		||||
to get user input. (**This is currently unimplemented.**)
 | 
			
		||||
- `out` is standard output. This is usually where text meant for
 | 
			
		||||
output should go.
 | 
			
		||||
- `err` is standard error. This sink is for writing errors, as the
 | 
			
		||||
name would suggest.
 | 
			
		||||
 | 
			
		||||
## Functions
 | 
			
		||||
### deregister(name)
 | 
			
		||||
Deregisters any command registered with `name`
 | 
			
		||||
 | 
			
		||||
### register(name, cb)
 | 
			
		||||
Register a command with `name` that runs `cb` when ran
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										51
									
								
								docs/api/fs.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								docs/api/fs.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,51 @@
 | 
			
		||||
---
 | 
			
		||||
title: Module fs
 | 
			
		||||
description: filesystem interaction and functionality library
 | 
			
		||||
layout: doc
 | 
			
		||||
menu:
 | 
			
		||||
  docs:
 | 
			
		||||
    parent: "API"
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## Introduction
 | 
			
		||||
The fs module provides easy and simple access to filesystem functions
 | 
			
		||||
and other things, and acts an addition to the Lua standard library's
 | 
			
		||||
I/O and filesystem functions.
 | 
			
		||||
 | 
			
		||||
## Functions
 | 
			
		||||
### abs(path) -> string
 | 
			
		||||
Gives an absolute version of `path`.
 | 
			
		||||
 | 
			
		||||
### basename(path) -> string
 | 
			
		||||
Gives the basename of `path`. For the rules,
 | 
			
		||||
see Go's filepath.Base
 | 
			
		||||
 | 
			
		||||
### cd(dir)
 | 
			
		||||
Changes directory to `dir`
 | 
			
		||||
 | 
			
		||||
### dir(path) -> string
 | 
			
		||||
Returns the directory part of `path`. For the rules, see Go's
 | 
			
		||||
filepath.Dir
 | 
			
		||||
 | 
			
		||||
### glob(pattern) -> matches (table)
 | 
			
		||||
Glob all files and directories that match the pattern.
 | 
			
		||||
For the rules, see Go's filepath.Glob
 | 
			
		||||
 | 
			
		||||
### join(...) -> string
 | 
			
		||||
Takes paths and joins them together with the OS's
 | 
			
		||||
directory separator (forward or backward slash).
 | 
			
		||||
 | 
			
		||||
### mkdir(name, recursive)
 | 
			
		||||
Makes a directory called `name`. If `recursive` is true, it will create its parent directories.
 | 
			
		||||
 | 
			
		||||
### readdir(dir) -> {}
 | 
			
		||||
Returns a table of files in `dir`.
 | 
			
		||||
 | 
			
		||||
### stat(path) -> {}
 | 
			
		||||
Returns a table of info about the `path`.
 | 
			
		||||
It contains the following keys:
 | 
			
		||||
name (string) - Name of the path
 | 
			
		||||
size (number) - Size of the path
 | 
			
		||||
mode (string) - Permission mode in an octal format string (with leading 0)
 | 
			
		||||
isDir (boolean) - If the path is a directory
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										136
									
								
								docs/api/hilbish/_index.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								docs/api/hilbish/_index.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,136 @@
 | 
			
		||||
---
 | 
			
		||||
title: Module hilbish
 | 
			
		||||
description: the core Hilbish API
 | 
			
		||||
layout: doc
 | 
			
		||||
menu:
 | 
			
		||||
  docs:
 | 
			
		||||
    parent: "API"
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## Introduction
 | 
			
		||||
The Hilbish module includes the core API, containing
 | 
			
		||||
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
 | 
			
		||||
- `interactive`: Is Hilbish in an interactive shell?
 | 
			
		||||
- `login`: Is Hilbish the login shell?
 | 
			
		||||
- `vimMode`: Current Vim input mode of Hilbish (will be nil if not in Vim input mode)
 | 
			
		||||
- `exitCode`: xit code of the last executed command
 | 
			
		||||
 | 
			
		||||
## Functions
 | 
			
		||||
### alias(cmd, orig)
 | 
			
		||||
Sets an alias of `cmd` to `orig`
 | 
			
		||||
 | 
			
		||||
### appendPath(dir)
 | 
			
		||||
Appends `dir` to $PATH
 | 
			
		||||
 | 
			
		||||
### complete(scope, cb)
 | 
			
		||||
Registers a completion handler for `scope`.
 | 
			
		||||
A `scope` is currently only expected to be `command.<cmd>`,
 | 
			
		||||
replacing <cmd> with the name of the command (for example `command.git`).
 | 
			
		||||
`cb` must be a function that returns a table of "completion groups."
 | 
			
		||||
Check `doc completions` for more information.
 | 
			
		||||
 | 
			
		||||
### cwd() -> string
 | 
			
		||||
Returns the current directory of the shell
 | 
			
		||||
 | 
			
		||||
### exec(cmd)
 | 
			
		||||
Replaces running hilbish with `cmd`
 | 
			
		||||
 | 
			
		||||
### goro(fn)
 | 
			
		||||
Puts `fn` in a goroutine
 | 
			
		||||
 | 
			
		||||
### highlighter(line)
 | 
			
		||||
Line highlighter handler. This is mainly for syntax highlighting, but in
 | 
			
		||||
reality could set the input of the prompt to *display* anything. The
 | 
			
		||||
callback is passed the current line and is expected to return a line that
 | 
			
		||||
will be used as the input display.
 | 
			
		||||
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
 | 
			
		||||
determine what text to use as an inline hint. It is passed the current
 | 
			
		||||
line and cursor position. It is expected to return a string which is used
 | 
			
		||||
as the text for the hint. This is by default a shim. To set hints,
 | 
			
		||||
override this function with your custom handler.
 | 
			
		||||
 | 
			
		||||
### inputMode(mode)
 | 
			
		||||
Sets the input mode for Hilbish's line reader. Accepts either emacs or vim
 | 
			
		||||
 | 
			
		||||
### interval(cb, time) -> <a href="/Hilbish/docs/api/hilbish/hilbish.timers/#timer" style="text-decoration: none;">Timer</a>
 | 
			
		||||
Runs the `cb` function every `time` milliseconds.
 | 
			
		||||
This creates a timer that starts immediately.
 | 
			
		||||
 | 
			
		||||
### multiprompt(str)
 | 
			
		||||
Changes the continued line prompt to `str`
 | 
			
		||||
 | 
			
		||||
### prependPath(dir)
 | 
			
		||||
Prepends `dir` to $PATH
 | 
			
		||||
 | 
			
		||||
### prompt(str, typ)
 | 
			
		||||
Changes the shell prompt to `str`
 | 
			
		||||
There are a few verbs that can be used in the prompt text.
 | 
			
		||||
These will be formatted and replaced with the appropriate values.
 | 
			
		||||
`%d` - Current working directory
 | 
			
		||||
`%u` - Name of current user
 | 
			
		||||
`%h` - Hostname of device
 | 
			
		||||
 | 
			
		||||
### read(prompt) -> input (string)
 | 
			
		||||
Read input from the user, using Hilbish's line editor/input reader.
 | 
			
		||||
This is a separate instance from the one Hilbish actually uses.
 | 
			
		||||
Returns `input`, will be nil if ctrl + d is pressed, or an error occurs (which shouldn't happen)
 | 
			
		||||
 | 
			
		||||
### run(cmd, returnOut) -> exitCode (number), stdout (string), stderr (string)
 | 
			
		||||
Runs `cmd` in Hilbish's sh interpreter.
 | 
			
		||||
If returnOut is true, the outputs of `cmd` will be returned as the 2nd and
 | 
			
		||||
3rd values instead of being outputted to the terminal.
 | 
			
		||||
 | 
			
		||||
### runnerMode(mode)
 | 
			
		||||
Sets the execution/runner mode for interactive Hilbish. This determines whether
 | 
			
		||||
Hilbish wll try to run input as Lua and/or sh or only do one of either.
 | 
			
		||||
Accepted values for mode are hybrid (the default), hybridRev (sh first then Lua),
 | 
			
		||||
sh, and lua. It also accepts a function, to which if it is passed one
 | 
			
		||||
will call it to execute user input instead.
 | 
			
		||||
 | 
			
		||||
### timeout(cb, time) -> <a href="/Hilbish/docs/api/hilbish/hilbish.timers/#timer" style="text-decoration: none;">Timer</a>
 | 
			
		||||
Runs the `cb` function after `time` in milliseconds.
 | 
			
		||||
This creates a timer that starts immediately.
 | 
			
		||||
 | 
			
		||||
### which(name) -> string
 | 
			
		||||
Checks if `name` is a valid command.
 | 
			
		||||
Will return the path of the binary, or a basename if it's a commander.
 | 
			
		||||
 | 
			
		||||
## Types
 | 
			
		||||
## Sink
 | 
			
		||||
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 input from the sink.
 | 
			
		||||
 | 
			
		||||
#### write(str)
 | 
			
		||||
Writes data to a sink.
 | 
			
		||||
 | 
			
		||||
#### writeln(str)
 | 
			
		||||
Writes data to a sink with a newline at the end.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										25
									
								
								docs/api/hilbish/hilbish.aliases.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								docs/api/hilbish/hilbish.aliases.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,25 @@
 | 
			
		||||
---
 | 
			
		||||
title: Interface hilbish.aliases
 | 
			
		||||
description: command aliasing
 | 
			
		||||
layout: doc
 | 
			
		||||
menu:
 | 
			
		||||
  docs:
 | 
			
		||||
    parent: "API"
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## Introduction
 | 
			
		||||
The alias interface deals with all command aliases in Hilbish.
 | 
			
		||||
 | 
			
		||||
## Functions
 | 
			
		||||
### add(alias, cmd)
 | 
			
		||||
This is an alias (ha) for the `hilbish.alias` function.
 | 
			
		||||
 | 
			
		||||
### delete(name)
 | 
			
		||||
Removes an alias.
 | 
			
		||||
 | 
			
		||||
### list() -> table\<string, string>
 | 
			
		||||
Get a table of all aliases, with string keys as the alias and the value as the command.
 | 
			
		||||
 | 
			
		||||
### resolve(alias) -> command (string)
 | 
			
		||||
Tries to resolve an alias to its command.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										29
									
								
								docs/api/hilbish/hilbish.completions.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								docs/api/hilbish/hilbish.completions.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,29 @@
 | 
			
		||||
---
 | 
			
		||||
title: Interface hilbish.completions
 | 
			
		||||
description: tab completions
 | 
			
		||||
layout: doc
 | 
			
		||||
menu:
 | 
			
		||||
  docs:
 | 
			
		||||
    parent: "API"
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## Introduction
 | 
			
		||||
The completions interface deals with tab completions.
 | 
			
		||||
 | 
			
		||||
## Functions
 | 
			
		||||
### call(name, query, ctx, fields) -> completionGroups (table), prefix (string)
 | 
			
		||||
Calls a completer function. This is mainly used to call
 | 
			
		||||
a command completer, which will have a `name` in the form
 | 
			
		||||
of `command.name`, example: `command.git`.
 | 
			
		||||
You can check `doc completions` for info on the `completionGroups` return value.
 | 
			
		||||
 | 
			
		||||
### handler(line, pos)
 | 
			
		||||
The handler function is the callback for tab completion in Hilbish.
 | 
			
		||||
You can check the completions doc for more info.
 | 
			
		||||
 | 
			
		||||
### bins(query, ctx, fields) -> entries (table), prefix (string)
 | 
			
		||||
Returns binary/executale completion candidates based on the provided query.
 | 
			
		||||
 | 
			
		||||
### files(query, ctx, fields) -> entries (table), prefix (string)
 | 
			
		||||
Returns file completion candidates based on the provided query.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										26
									
								
								docs/api/hilbish/hilbish.editor.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								docs/api/hilbish/hilbish.editor.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
			
		||||
---
 | 
			
		||||
title: Interface hilbish.editor
 | 
			
		||||
description: interactions for Hilbish's line reader
 | 
			
		||||
layout: doc
 | 
			
		||||
menu:
 | 
			
		||||
  docs:
 | 
			
		||||
    parent: "API"
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## Introduction
 | 
			
		||||
The hilbish.editor interface provides functions to
 | 
			
		||||
directly interact with the line editor in use.
 | 
			
		||||
 | 
			
		||||
## Functions
 | 
			
		||||
### getLine() -> string
 | 
			
		||||
Returns the current input line.
 | 
			
		||||
 | 
			
		||||
### getVimRegister(register) -> string
 | 
			
		||||
Returns the text that is at the register.
 | 
			
		||||
 | 
			
		||||
### insert(text)
 | 
			
		||||
Inserts text into the line.
 | 
			
		||||
 | 
			
		||||
### setVimRegister(register, text)
 | 
			
		||||
Sets the vim register at `register` to hold the passed text.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										30
									
								
								docs/api/hilbish/hilbish.history.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								docs/api/hilbish/hilbish.history.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,30 @@
 | 
			
		||||
---
 | 
			
		||||
title: Interface hilbish.history
 | 
			
		||||
description: command history
 | 
			
		||||
layout: doc
 | 
			
		||||
menu:
 | 
			
		||||
  docs:
 | 
			
		||||
    parent: "API"
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## Introduction
 | 
			
		||||
The history interface deals with command history.
 | 
			
		||||
This includes the ability to override functions to change the main
 | 
			
		||||
method of saving history.
 | 
			
		||||
 | 
			
		||||
## Functions
 | 
			
		||||
### add(cmd)
 | 
			
		||||
Adds a command to the history.
 | 
			
		||||
 | 
			
		||||
### all() -> table
 | 
			
		||||
Retrieves all history.
 | 
			
		||||
 | 
			
		||||
### clear()
 | 
			
		||||
Deletes all commands from the history.
 | 
			
		||||
 | 
			
		||||
### get(idx)
 | 
			
		||||
Retrieves a command from the history based on the `idx`.
 | 
			
		||||
 | 
			
		||||
### size() -> number
 | 
			
		||||
Returns the amount of commands in the history.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										58
									
								
								docs/api/hilbish/hilbish.jobs.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								docs/api/hilbish/hilbish.jobs.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,58 @@
 | 
			
		||||
---
 | 
			
		||||
title: Interface hilbish.jobs
 | 
			
		||||
description: background job management
 | 
			
		||||
layout: doc
 | 
			
		||||
menu:
 | 
			
		||||
  docs:
 | 
			
		||||
    parent: "API"
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## Introduction
 | 
			
		||||
 | 
			
		||||
Manage interactive jobs in Hilbish via Lua.
 | 
			
		||||
 | 
			
		||||
Jobs are the name of background tasks/commands. A job can be started via
 | 
			
		||||
interactive usage or with the functions defined below for use in external runners.
 | 
			
		||||
 | 
			
		||||
## Functions
 | 
			
		||||
### add(cmdstr, args, execPath)
 | 
			
		||||
Adds a new job to the job table. Note that this does not immediately run it.
 | 
			
		||||
 | 
			
		||||
### all() -> table\<<a href="/Hilbish/docs/api/hilbish/hilbish.jobs/#job" style="text-decoration: none;">Job</a>>
 | 
			
		||||
Returns a table of all job objects.
 | 
			
		||||
 | 
			
		||||
### disown(id)
 | 
			
		||||
Disowns a job. This deletes it from the job table.
 | 
			
		||||
 | 
			
		||||
### get(id) -> <a href="/Hilbish/docs/api/hilbish/hilbish.jobs/#job" style="text-decoration: none;">Job</a>
 | 
			
		||||
Get a job object via its ID.
 | 
			
		||||
 | 
			
		||||
### last() -> <a href="/Hilbish/docs/api/hilbish/hilbish.jobs/#job" style="text-decoration: none;">Job</a>
 | 
			
		||||
Returns the last added job from the table.
 | 
			
		||||
 | 
			
		||||
## Types
 | 
			
		||||
## Job
 | 
			
		||||
The Job type describes a Hilbish job.
 | 
			
		||||
### Properties
 | 
			
		||||
- `cmd`: The user entered command string for the job.
 | 
			
		||||
- `running`: Whether the job is running or not.
 | 
			
		||||
- `id`: The ID of the job in the job table
 | 
			
		||||
- `pid`: The Process ID
 | 
			
		||||
- `exitCode`: The last exit code of the job.
 | 
			
		||||
- `stdout`: The standard output of the job. This just means the normal logs of the process.
 | 
			
		||||
- `stderr`: The standard error stream of the process. This (usually) includes error messages of the job.
 | 
			
		||||
 | 
			
		||||
### Methods
 | 
			
		||||
#### background()
 | 
			
		||||
Puts a job in the background. This acts the same as initially running a job.
 | 
			
		||||
 | 
			
		||||
#### foreground()
 | 
			
		||||
Puts a job in the foreground. This will cause it to run like it was
 | 
			
		||||
executed normally and wait for it to complete.
 | 
			
		||||
 | 
			
		||||
#### start()
 | 
			
		||||
Starts running the job.
 | 
			
		||||
 | 
			
		||||
#### stop()
 | 
			
		||||
Stops the job from running.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										19
									
								
								docs/api/hilbish/hilbish.os.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								docs/api/hilbish/hilbish.os.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
			
		||||
---
 | 
			
		||||
title: Interface hilbish.os
 | 
			
		||||
description: OS Info
 | 
			
		||||
layout: doc
 | 
			
		||||
menu:
 | 
			
		||||
  docs:
 | 
			
		||||
    parent: "API"
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## Introduction
 | 
			
		||||
The `os` interface provides simple text information properties about
 | 
			
		||||
the current OS on the systen. This mainly includes the name and
 | 
			
		||||
version.
 | 
			
		||||
 | 
			
		||||
## Interface fields
 | 
			
		||||
- `family`: Family name of the current OS
 | 
			
		||||
- `name`: Pretty name of the current OS
 | 
			
		||||
- `version`: Version of the current OS
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										31
									
								
								docs/api/hilbish/hilbish.runner.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								docs/api/hilbish/hilbish.runner.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,31 @@
 | 
			
		||||
---
 | 
			
		||||
title: Interface hilbish.runner
 | 
			
		||||
description: interactive command runner customization
 | 
			
		||||
layout: doc
 | 
			
		||||
menu:
 | 
			
		||||
  docs:
 | 
			
		||||
    parent: "API"
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## Introduction
 | 
			
		||||
The runner interface contains functions that allow the user to change
 | 
			
		||||
how Hilbish interprets interactive input.
 | 
			
		||||
Users can add and change the default runner for interactive input to any
 | 
			
		||||
language or script of their choosing. A good example is using it to
 | 
			
		||||
write command in Fennel.
 | 
			
		||||
 | 
			
		||||
## Functions
 | 
			
		||||
### setMode(cb)
 | 
			
		||||
This is the same as the `hilbish.runnerMode` function. It takes a callback,
 | 
			
		||||
which will be used to execute all interactive input.
 | 
			
		||||
In normal cases, neither callbacks should be overrided by the user,
 | 
			
		||||
as the higher level functions listed below this will handle it.
 | 
			
		||||
 | 
			
		||||
### lua(cmd)
 | 
			
		||||
Evaluates `cmd` as Lua input. This is the same as using `dofile`
 | 
			
		||||
or `load`, but is appropriated for the runner interface.
 | 
			
		||||
 | 
			
		||||
### sh(cmd)
 | 
			
		||||
Runs a command in Hilbish's shell script interpreter.
 | 
			
		||||
This is the equivalent of using `source`.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										59
									
								
								docs/api/hilbish/hilbish.timers.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								docs/api/hilbish/hilbish.timers.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,59 @@
 | 
			
		||||
---
 | 
			
		||||
title: Interface hilbish.timers
 | 
			
		||||
description: timeout and interval API
 | 
			
		||||
layout: doc
 | 
			
		||||
menu:
 | 
			
		||||
  docs:
 | 
			
		||||
    parent: "API"
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## Introduction
 | 
			
		||||
 | 
			
		||||
If you ever want to run a piece of code on a timed interval, or want to wait
 | 
			
		||||
a few seconds, you don't have to rely on timing tricks, as Hilbish has a
 | 
			
		||||
timer API to set intervals and timeouts.
 | 
			
		||||
 | 
			
		||||
These are the simple functions `hilbish.interval` and `hilbish.timeout` (doc
 | 
			
		||||
accessible with `doc hilbish`). But if you want slightly more control over
 | 
			
		||||
them, there is the `hilbish.timers` interface. It allows you to get
 | 
			
		||||
a timer via ID and control them.
 | 
			
		||||
 | 
			
		||||
All functions documented with the `Timer` type refer to a Timer object.
 | 
			
		||||
 | 
			
		||||
An example of usage:
 | 
			
		||||
```
 | 
			
		||||
local t = hilbish.timers.create(hilbish.timers.TIMEOUT, 5000, function()
 | 
			
		||||
	print 'hello!'
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
t:start()
 | 
			
		||||
print(t.running) // true
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Interface fields
 | 
			
		||||
- `INTERVAL`: Constant for an interval timer type
 | 
			
		||||
- `TIMEOUT`: Constant for a timeout timer type
 | 
			
		||||
 | 
			
		||||
## Functions
 | 
			
		||||
### create(type, time, callback) -> <a href="/Hilbish/docs/api/hilbish/hilbish.timers/#timer" style="text-decoration: none;">Timer</a>
 | 
			
		||||
Creates a timer that runs based on the specified `time` in milliseconds.
 | 
			
		||||
The `type` can either be `hilbish.timers.INTERVAL` or `hilbish.timers.TIMEOUT`
 | 
			
		||||
 | 
			
		||||
### get(id) -> <a href="/Hilbish/docs/api/hilbish/hilbish.timers/#timer" style="text-decoration: none;">Timer</a>
 | 
			
		||||
Retrieves a timer via its ID.
 | 
			
		||||
 | 
			
		||||
## Types
 | 
			
		||||
## Timer
 | 
			
		||||
The Job type describes a Hilbish timer.
 | 
			
		||||
### Properties
 | 
			
		||||
- `type`: What type of timer it is
 | 
			
		||||
- `running`: If the timer is running
 | 
			
		||||
- `duration`: The duration in milliseconds that the timer will run
 | 
			
		||||
 | 
			
		||||
### Methods
 | 
			
		||||
#### start()
 | 
			
		||||
Starts a timer.
 | 
			
		||||
 | 
			
		||||
#### stop()
 | 
			
		||||
Stops a timer.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										18
									
								
								docs/api/hilbish/hilbish.userDir.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								docs/api/hilbish/hilbish.userDir.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,18 @@
 | 
			
		||||
---
 | 
			
		||||
title: Interface hilbish.userDir
 | 
			
		||||
description: user-related directories
 | 
			
		||||
layout: doc
 | 
			
		||||
menu:
 | 
			
		||||
  docs:
 | 
			
		||||
    parent: "API"
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## Introduction
 | 
			
		||||
This interface just contains properties to know about certain user directories.
 | 
			
		||||
It is equivalent to XDG on Linux and gets the user's preferred directories
 | 
			
		||||
for configs and data.
 | 
			
		||||
 | 
			
		||||
## Interface fields
 | 
			
		||||
- `config`: The user's config directory
 | 
			
		||||
- `data`: The user's directory for program data
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										26
									
								
								docs/api/terminal.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								docs/api/terminal.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
			
		||||
---
 | 
			
		||||
title: Module terminal
 | 
			
		||||
description: low level terminal library
 | 
			
		||||
layout: doc
 | 
			
		||||
menu:
 | 
			
		||||
  docs:
 | 
			
		||||
    parent: "API"
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## Introduction
 | 
			
		||||
The terminal library is a simple and lower level library for certain terminal interactions.
 | 
			
		||||
 | 
			
		||||
## Functions
 | 
			
		||||
### restoreState()
 | 
			
		||||
Restores the last saved state of the terminal
 | 
			
		||||
 | 
			
		||||
### saveState()
 | 
			
		||||
Saves the current state of the terminal
 | 
			
		||||
 | 
			
		||||
### setRaw()
 | 
			
		||||
Puts the terminal in raw mode
 | 
			
		||||
 | 
			
		||||
### size()
 | 
			
		||||
Gets the dimensions of the terminal. Returns a table with `width` and `height`
 | 
			
		||||
Note: this is not the size in relation to the dimensions of the display
 | 
			
		||||
 | 
			
		||||
@ -1,12 +0,0 @@
 | 
			
		||||
catch(name, cb) > Catches a hook with `name`. Runs the `cb` when it is thrown
 | 
			
		||||
 | 
			
		||||
catchOnce(name, cb) > Same as catch, but only runs the `cb` once and then removes the hook
 | 
			
		||||
 | 
			
		||||
hooks(name) -> {cb, cb...} > Returns a table with hooks on the event with `name`.
 | 
			
		||||
 | 
			
		||||
release(name, catcher) > Removes the `catcher` for the event with `name`
 | 
			
		||||
For this to work, `catcher` has to be the same function used to catch
 | 
			
		||||
an event, like one saved to a variable.
 | 
			
		||||
 | 
			
		||||
throw(name, ...args) > Throws a hook with `name` with the provided `args`
 | 
			
		||||
 | 
			
		||||
@ -1,4 +0,0 @@
 | 
			
		||||
deregister(name) > Deregisters any command registered with `name`
 | 
			
		||||
 | 
			
		||||
register(name, cb) > Register a command with `name` that runs `cb` when ran
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										56
									
								
								docs/completions.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								docs/completions.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,56 @@
 | 
			
		||||
Hilbish has a pretty good completion system. It has a nice looking
 | 
			
		||||
menu, with 2 types of menus: grid (like file completions) or
 | 
			
		||||
list.
 | 
			
		||||
 | 
			
		||||
Like most parts of Hilbish, it's made to be extensible and
 | 
			
		||||
customizable. The default handler for completions in general can
 | 
			
		||||
be overwritten to provide more advanced completions if needed.
 | 
			
		||||
 | 
			
		||||
# Completion Handler
 | 
			
		||||
By default, it provides 3 things: for the first argument,
 | 
			
		||||
binaries (with a plain name requested to complete, those in
 | 
			
		||||
$PATH), files, or command completions. With the default 
 | 
			
		||||
completion handler, it will try to run a handler for the 
 | 
			
		||||
command or fallback to file completions.
 | 
			
		||||
 | 
			
		||||
To overwrite it, just assign a function to
 | 
			
		||||
`hilbish.completion.handler` like so:
 | 
			
		||||
function hilbish.completion.handler(line, pos)
 | 
			
		||||
	-- do things
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
It is passed 2 arguments, the entire line, and the current
 | 
			
		||||
cursor position. The functions in the completion interface
 | 
			
		||||
take 3 arguments: query, ctx, and fields.
 | 
			
		||||
 | 
			
		||||
- The `query`, which what the user is currently trying to complete
 | 
			
		||||
- `ctx`, being just the entire line
 | 
			
		||||
- `fields` being a table of arguments. It's just `ctx` split up,
 | 
			
		||||
delimited by spaces.
 | 
			
		||||
 | 
			
		||||
It's expected to return 2 things: a table of completion groups, and
 | 
			
		||||
a prefix. A completion group is defined as a table with 2 keys:
 | 
			
		||||
`items` and `type`.
 | 
			
		||||
 | 
			
		||||
- The `items` field is just a table of items to use for completions.
 | 
			
		||||
- The `type` is for the completion menu type, being either `grid` or
 | 
			
		||||
`list`.
 | 
			
		||||
 | 
			
		||||
The prefix is what all the completions start with. It should be empty
 | 
			
		||||
if the user doesn't have a query. If the beginning of the completion
 | 
			
		||||
item does not match the prefix, it will be replaced and fixed
 | 
			
		||||
properly in the line. It is case sensitive.
 | 
			
		||||
 | 
			
		||||
If you want to overwrite the functionality of the general completion
 | 
			
		||||
handler, or make your command completion have files as well
 | 
			
		||||
(and filter them), then there is the `files` function, which is
 | 
			
		||||
mentioned below.
 | 
			
		||||
 | 
			
		||||
# Completion Interface
 | 
			
		||||
## Functions
 | 
			
		||||
- `files(query, ctx, fields)` -> table, prefix: get file completions,
 | 
			
		||||
based on the user's query.
 | 
			
		||||
- `bins(query, ctx, fields)` -> table, prefix: get binary/executable
 | 
			
		||||
completions, based on user query.
 | 
			
		||||
- `call(scope, query, ctx, fields)` -> table, prefix: call a completion
 | 
			
		||||
handler with `scope`, usually being in the form of `command.<name>`
 | 
			
		||||
@ -1,44 +0,0 @@
 | 
			
		||||
Hilbish has a pretty good completion system. It has a nice looking menu,
 | 
			
		||||
with 2 types of menus: grid (like file completions) or list.
 | 
			
		||||
 | 
			
		||||
Like most parts of Hilbish, it's made to be extensible and customizable.
 | 
			
		||||
The default handler for completions in general can be overwritten to provide
 | 
			
		||||
more advanced completions if needed.
 | 
			
		||||
 | 
			
		||||
# Completion Handler
 | 
			
		||||
By default, it provides 3 things: for the first argument, binaries (with a
 | 
			
		||||
plain name requested to complete, those in $PATH), files, or command
 | 
			
		||||
completions. With the default completion handler, it will try to run a
 | 
			
		||||
handler for the command or fallback to file completions.
 | 
			
		||||
 | 
			
		||||
To overwrite it, just assign a function to `hilbish.completion.handler`
 | 
			
		||||
like so:
 | 
			
		||||
function hilbish.completion.handler(line, pos)
 | 
			
		||||
	-- do things
 | 
			
		||||
end
 | 
			
		||||
It is passed 2 arguments, the entire line, and the current cursor position.
 | 
			
		||||
The functions in the completion interface take 3 arguments: query, ctx,
 | 
			
		||||
and fields. The `query`, which what the user is currently trying to complete,
 | 
			
		||||
`ctx`, being just the entire line, and `fields` being a table of arguments.
 | 
			
		||||
It's just `ctx` split up, delimited by spaces.
 | 
			
		||||
It's expected to return 2 things: a table of completion groups, and a prefix.
 | 
			
		||||
A completion group is defined as a table with 2 keys: `items` and `type`.
 | 
			
		||||
The `items` field is just a table of items to use for completions.
 | 
			
		||||
The `type` is for the completion menu type, being either `grid` or `list`.
 | 
			
		||||
The prefix is what all the completions start with. It should be empty
 | 
			
		||||
if the user doesn't have a query. If the beginning of the completion
 | 
			
		||||
item does not match the prefix, it will be replaced and fixed properly
 | 
			
		||||
in the line. It is case sensitive.
 | 
			
		||||
 | 
			
		||||
If you want to overwrite the functionality of the general completion handler,
 | 
			
		||||
or make your command completion have files as well (and filter them),
 | 
			
		||||
then there is the `files` function, which is mentioned below.
 | 
			
		||||
 | 
			
		||||
# Completion Interface
 | 
			
		||||
## Functions
 | 
			
		||||
- `files(query, ctx, fields)` -> table, prefix: get file completions, based
 | 
			
		||||
on the user's query.
 | 
			
		||||
- `bins(query, ctx, fields)` -> table, prefix: get binary/executable
 | 
			
		||||
completions, based on user query.
 | 
			
		||||
- `call(scope, query, ctx, fields)` -> table, prefix: call a completion handler
 | 
			
		||||
with `scope`, usually being in the form of `command.<name>`
 | 
			
		||||
							
								
								
									
										22
									
								
								docs/fs.txt
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								docs/fs.txt
									
									
									
									
									
								
							@ -1,22 +0,0 @@
 | 
			
		||||
abs(path) > Gives an absolute version of `path`.
 | 
			
		||||
 | 
			
		||||
basename(path) > Gives the basename of `path`. For the rules,
 | 
			
		||||
see Go's filepath.Base
 | 
			
		||||
 | 
			
		||||
cd(dir) > Changes directory to `dir`
 | 
			
		||||
 | 
			
		||||
dir(path) > Returns the directory part of `path`. For the rules, see Go's
 | 
			
		||||
filepath.Dir
 | 
			
		||||
 | 
			
		||||
glob(pattern) > Glob all files and directories that match the pattern.
 | 
			
		||||
For the rules, see Go's filepath.Glob
 | 
			
		||||
 | 
			
		||||
join(paths...) > Takes paths and joins them together with the OS's
 | 
			
		||||
directory separator (forward or backward slash).
 | 
			
		||||
 | 
			
		||||
mkdir(name, recursive) > Makes a directory called `name`. If `recursive` is true, it will create its parent directories.
 | 
			
		||||
 | 
			
		||||
readdir(dir) > Returns a table of files in `dir`
 | 
			
		||||
 | 
			
		||||
stat(path) > Returns info about `path`
 | 
			
		||||
 | 
			
		||||
@ -1,62 +0,0 @@
 | 
			
		||||
alias(cmd, orig) > Sets an alias of `cmd` to `orig`
 | 
			
		||||
 | 
			
		||||
appendPath(dir) > Appends `dir` to $PATH
 | 
			
		||||
 | 
			
		||||
complete(scope, cb) > Registers a completion handler for `scope`.
 | 
			
		||||
A `scope` is currently only expected to be `command.<cmd>`,
 | 
			
		||||
replacing <cmd> with the name of the command (for example `command.git`).
 | 
			
		||||
`cb` must be a function that returns a table of "completion groups."
 | 
			
		||||
Check `doc completions` for more information.
 | 
			
		||||
 | 
			
		||||
cwd() > Returns the current directory of the shell
 | 
			
		||||
 | 
			
		||||
exec(cmd) > Replaces running hilbish with `cmd`
 | 
			
		||||
 | 
			
		||||
goro(fn) > Puts `fn` in a goroutine
 | 
			
		||||
 | 
			
		||||
highlighter(line) > Line highlighter handler. This is mainly for syntax highlighting, but in
 | 
			
		||||
reality could set the input of the prompt to *display* anything. The
 | 
			
		||||
callback is passed the current line and is expected to return a line that
 | 
			
		||||
will be used as the input display.
 | 
			
		||||
 | 
			
		||||
hinter(line, pos) > The command line hint handler. It gets called on every key insert to
 | 
			
		||||
determine what text to use as an inline hint. It is passed the current
 | 
			
		||||
line and cursor position. It is expected to return a string which is used
 | 
			
		||||
as the text for the hint. This is by default a shim. To set hints,
 | 
			
		||||
override this function with your custom handler.
 | 
			
		||||
 | 
			
		||||
inputMode(mode) > Sets the input mode for Hilbish's line reader. Accepts either emacs or vim
 | 
			
		||||
 | 
			
		||||
interval(cb, time) > Runs the `cb` function every `time` milliseconds.
 | 
			
		||||
Returns a `timer` object (see `doc timers`).
 | 
			
		||||
 | 
			
		||||
multiprompt(str) > Changes the continued line prompt to `str`
 | 
			
		||||
 | 
			
		||||
prependPath(dir) > Prepends `dir` to $PATH
 | 
			
		||||
 | 
			
		||||
prompt(str, typ?) > Changes the shell prompt to `str`
 | 
			
		||||
There are a few verbs that can be used in the prompt text.
 | 
			
		||||
These will be formatted and replaced with the appropriate values.
 | 
			
		||||
`%d` - Current working directory
 | 
			
		||||
`%u` - Name of current user
 | 
			
		||||
`%h` - Hostname of device
 | 
			
		||||
 | 
			
		||||
read(prompt?) -> input? > Read input from the user, using Hilbish's line editor/input reader.
 | 
			
		||||
This is a separate instance from the one Hilbish actually uses.
 | 
			
		||||
Returns `input`, will be nil if ctrl + d is pressed, or an error occurs (which shouldn't happen)
 | 
			
		||||
 | 
			
		||||
run(cmd, returnOut) -> exitCode, stdout, stderr > Runs `cmd` in Hilbish's sh interpreter.
 | 
			
		||||
If returnOut is true, the outputs of `cmd` will be returned as the 2nd and
 | 
			
		||||
3rd values instead of being outputted to the terminal.
 | 
			
		||||
 | 
			
		||||
runnerMode(mode) > Sets the execution/runner mode for interactive Hilbish. This determines whether
 | 
			
		||||
Hilbish wll try to run input as Lua and/or sh or only do one of either.
 | 
			
		||||
Accepted values for mode are hybrid (the default), hybridRev (sh first then Lua),
 | 
			
		||||
sh, and lua. It also accepts a function, to which if it is passed one
 | 
			
		||||
will call it to execute user input instead.
 | 
			
		||||
 | 
			
		||||
timeout(cb, time) > Runs the `cb` function after `time` in milliseconds
 | 
			
		||||
Returns a `timer` object (see `doc timers`).
 | 
			
		||||
 | 
			
		||||
which(name) > Checks if `name` is a valid command
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										12
									
								
								docs/hooks/command.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								docs/hooks/command.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
			
		||||
+ `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.
 | 
			
		||||
 | 
			
		||||
+ `command.not-found` -> cmdStr > Thrown when a command is not found.
 | 
			
		||||
 | 
			
		||||
+ `command.not-executable` -> cmdStr > Thrown when Hilbish attempts to run a file
 | 
			
		||||
that is not executable.
 | 
			
		||||
@ -1,7 +0,0 @@
 | 
			
		||||
+ `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.
 | 
			
		||||
 | 
			
		||||
+ `command.not-found` -> cmdStr > Thrown when a command is not found.
 | 
			
		||||
 | 
			
		||||
+ `command.no-perm` -> cmdStr > Thrown when Hilbish attempts to execute a file but
 | 
			
		||||
has no permission.
 | 
			
		||||
@ -5,3 +5,8 @@
 | 
			
		||||
 | 
			
		||||
+ `hilbish.vimAction` -> actionName, args > Sent when the user does a "vim action," being something
 | 
			
		||||
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.
 | 
			
		||||
@ -38,8 +38,22 @@ The exit code has to be a number, it will be 0 otherwise and the error can be
 | 
			
		||||
These are the "low level" functions for the `hilbish.runner` interface.
 | 
			
		||||
 | 
			
		||||
+ setMode(mode) > The same as `hilbish.runnerMode`
 | 
			
		||||
+ sh(input) -> input, code, err > Runs `input` in Hilbish's sh interpreter
 | 
			
		||||
+ lua(input) -> input, code, err > Evals `input` as Lua code
 | 
			
		||||
+ sh(input) -> table > Runs `input` in Hilbish's sh interpreter
 | 
			
		||||
+ lua(input) -> table > Evals `input` as Lua code
 | 
			
		||||
 | 
			
		||||
The table value that runners return can have at least 4 values:
 | 
			
		||||
+ input (string): The full input text.
 | 
			
		||||
+ exitCode (number): Exit code (usually from a command)
 | 
			
		||||
+ continue (boolean): Whether to prompt the user for more input
 | 
			
		||||
(in the case of incomplete syntax)
 | 
			
		||||
+ err (string): A string that represents an error from the runner.
 | 
			
		||||
This should only be set when, for example, there is a syntax error.
 | 
			
		||||
It can be set to a few special values for Hilbish to throw the right
 | 
			
		||||
hooks and have a better looking message.
 | 
			
		||||
 | 
			
		||||
+ `<command>: not-found` will throw a `command.not-found` hook
 | 
			
		||||
based on what `<command>` is.
 | 
			
		||||
+ `<command>: not-executable` will throw a `command.not-executable` hook.
 | 
			
		||||
 | 
			
		||||
The others here are defined in Lua and have EmmyLua documentation.
 | 
			
		||||
These functions should be preferred over the previous ones.
 | 
			
		||||
@ -1,9 +0,0 @@
 | 
			
		||||
restoreState() > Restores the last saved state of the terminal
 | 
			
		||||
 | 
			
		||||
saveState() > Saves the current state of the terminal
 | 
			
		||||
 | 
			
		||||
setRaw() > Puts the terminal in raw mode
 | 
			
		||||
 | 
			
		||||
size() > Gets the dimensions of the terminal. Returns a table with `width` and `height`
 | 
			
		||||
Note: this is not the size in relation to the dimensions of the display
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1
									
								
								docs/timers.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								docs/timers.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
This has been moved to the `hilbish.timers` API doc (accessible by `doc api hilbish.timers`)
 | 
			
		||||
@ -1,38 +0,0 @@
 | 
			
		||||
If you ever want to run a piece of code on a timed interval, or want to wait
 | 
			
		||||
a few seconds, you don't have to rely on timing tricks, as Hilbish has a
 | 
			
		||||
timer API to set intervals and timeouts.
 | 
			
		||||
 | 
			
		||||
These are the simple functions `hilbish.interval` and `hilbish.timeout` (doc
 | 
			
		||||
accessible with `doc hilbish`). But if you want slightly more control over
 | 
			
		||||
them, there is the `hilbish.timers` interface. It allows you to get
 | 
			
		||||
a timer via ID.
 | 
			
		||||
 | 
			
		||||
# Timer Interface
 | 
			
		||||
## Functions
 | 
			
		||||
- `get(id)` -> timer: get a timer via its id
 | 
			
		||||
- `create(type, ms, callback)` -> timer: creates a timer, adding it to the timer pool.
 | 
			
		||||
`type` is the type of timer it will be. 0 is an interval, 1 is a timeout.
 | 
			
		||||
`ms` is the time it will run for in seconds. callback is the function called
 | 
			
		||||
when the timer is triggered.
 | 
			
		||||
 | 
			
		||||
# Timer Object
 | 
			
		||||
All those previously mentioned functions return a `timer` object, to which
 | 
			
		||||
you can stop and start a timer again.
 | 
			
		||||
 | 
			
		||||
An example of usage:
 | 
			
		||||
local t = hilbish.timers.create(1, 5000, function()
 | 
			
		||||
	print 'hello!'
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
t:stop()
 | 
			
		||||
print(t.running, t.duration, t.type)
 | 
			
		||||
t:start()
 | 
			
		||||
 | 
			
		||||
## Properties
 | 
			
		||||
- `duration`: amount of time the timer runs for in milliseconds
 | 
			
		||||
- `running`: whether the timer is running or not
 | 
			
		||||
- `type`: the type of timer (0 is interval, 1 is timeout)
 | 
			
		||||
 | 
			
		||||
## Functions
 | 
			
		||||
- `stop()`: stops the timer. returns an error if it's already stopped
 | 
			
		||||
- `start()`: starts the timer. returns an error if it's already started
 | 
			
		||||
							
								
								
									
										19
									
								
								editor.go
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								editor.go
									
									
									
									
									
								
							@ -6,6 +6,10 @@ import (
 | 
			
		||||
	rt "github.com/arnodel/golua/runtime"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// #interface editor
 | 
			
		||||
// interactions for Hilbish's line reader
 | 
			
		||||
// The hilbish.editor interface provides functions to
 | 
			
		||||
// directly interact with the line editor in use.
 | 
			
		||||
func editorLoader(rtm *rt.Runtime) *rt.Table {
 | 
			
		||||
	exports := map[string]util.LuaExport{
 | 
			
		||||
		"insert": {editorInsert, 1, false},
 | 
			
		||||
@ -20,6 +24,9 @@ func editorLoader(rtm *rt.Runtime) *rt.Table {
 | 
			
		||||
	return mod
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #interface editor
 | 
			
		||||
// insert(text)
 | 
			
		||||
// Inserts text into the line.
 | 
			
		||||
func editorInsert(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	if err := c.Check1Arg(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@ -35,6 +42,11 @@ func editorInsert(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	return c.Next(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #interface editor
 | 
			
		||||
// setVimRegister(register, text)
 | 
			
		||||
// Sets the vim register at `register` to hold the passed text.
 | 
			
		||||
// --- @param register string
 | 
			
		||||
// --- @param text string
 | 
			
		||||
func editorSetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	if err := c.Check1Arg(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@ -55,6 +67,10 @@ func editorSetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	return c.Next(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #interface editor
 | 
			
		||||
// getVimRegister(register) -> string
 | 
			
		||||
// Returns the text that is at the register.
 | 
			
		||||
// --- @param register string
 | 
			
		||||
func editorGetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	if err := c.Check1Arg(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@ -70,6 +86,9 @@ func editorGetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	return c.PushingNext1(t.Runtime, rt.StringValue(string(buf))), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #interface editor
 | 
			
		||||
// getLine() -> string
 | 
			
		||||
// Returns the current input line.
 | 
			
		||||
func editorGetLine(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	buf := lr.rl.GetLine()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -12,17 +12,21 @@ function bait.catch(name, cb) end
 | 
			
		||||
--- @param cb function
 | 
			
		||||
function bait.catchOnce(name, cb) end
 | 
			
		||||
 | 
			
		||||
--- Returns a table with hooks on the event with `name`.
 | 
			
		||||
function bait.hooks() end
 | 
			
		||||
--- Returns a table with hooks (callback functions) on the event with `name`.
 | 
			
		||||
--- @param name string
 | 
			
		||||
--- @returns table<function>
 | 
			
		||||
function bait.hooks(name) end
 | 
			
		||||
 | 
			
		||||
--- Removes the `catcher` for the event with `name`
 | 
			
		||||
--- Removes the `catcher` for the event with `name`.
 | 
			
		||||
--- For this to work, `catcher` has to be the same function used to catch
 | 
			
		||||
--- an event, like one saved to a variable.
 | 
			
		||||
function bait.release() end
 | 
			
		||||
--- @param name string
 | 
			
		||||
--- @param catcher function
 | 
			
		||||
function bait.release(name, catcher) end
 | 
			
		||||
 | 
			
		||||
--- Throws a hook with `name` with the provided `args`
 | 
			
		||||
--- @param name string
 | 
			
		||||
--- @vararg any
 | 
			
		||||
function bait.throw(name, ...) end
 | 
			
		||||
function bait.throw(name, ...args) end
 | 
			
		||||
 | 
			
		||||
return bait
 | 
			
		||||
 | 
			
		||||
@ -4,11 +4,13 @@ local fs = {}
 | 
			
		||||
 | 
			
		||||
--- Gives an absolute version of `path`.
 | 
			
		||||
--- @param path string
 | 
			
		||||
--- @returns string
 | 
			
		||||
function fs.abs(path) end
 | 
			
		||||
 | 
			
		||||
--- Gives the basename of `path`. For the rules,
 | 
			
		||||
--- see Go's filepath.Base
 | 
			
		||||
function fs.basename() end
 | 
			
		||||
--- @returns string
 | 
			
		||||
function fs.basename(path) end
 | 
			
		||||
 | 
			
		||||
--- Changes directory to `dir`
 | 
			
		||||
--- @param dir string
 | 
			
		||||
@ -16,28 +18,40 @@ function fs.cd(dir) end
 | 
			
		||||
 | 
			
		||||
--- Returns the directory part of `path`. For the rules, see Go's
 | 
			
		||||
--- filepath.Dir
 | 
			
		||||
function fs.dir() end
 | 
			
		||||
--- @param path string
 | 
			
		||||
--- @returns string
 | 
			
		||||
function fs.dir(path) end
 | 
			
		||||
 | 
			
		||||
--- Glob all files and directories that match the pattern.
 | 
			
		||||
--- For the rules, see Go's filepath.Glob
 | 
			
		||||
function fs.glob() end
 | 
			
		||||
--- @param pattern string
 | 
			
		||||
--- @returns table
 | 
			
		||||
function fs.glob(pattern) end
 | 
			
		||||
 | 
			
		||||
--- Takes paths and joins them together with the OS's
 | 
			
		||||
--- directory separator (forward or backward slash).
 | 
			
		||||
function fs.join() end
 | 
			
		||||
--- @vararg string
 | 
			
		||||
--- @returns string
 | 
			
		||||
function fs.join(...) end
 | 
			
		||||
 | 
			
		||||
--- Makes a directory called `name`. If `recursive` is true, it will create its parent directories.
 | 
			
		||||
--- @param name string
 | 
			
		||||
--- @param recursive boolean
 | 
			
		||||
function fs.mkdir(name, recursive) end
 | 
			
		||||
 | 
			
		||||
--- Returns a table of files in `dir`
 | 
			
		||||
--- Returns a table of files in `dir`.
 | 
			
		||||
--- @param dir string
 | 
			
		||||
--- @return table
 | 
			
		||||
function fs.readdir(dir) end
 | 
			
		||||
 | 
			
		||||
--- Returns info about `path`
 | 
			
		||||
--- Returns a table of info about the `path`.
 | 
			
		||||
--- It contains the following keys:
 | 
			
		||||
--- name (string) - Name of the path
 | 
			
		||||
--- size (number) - Size of the path
 | 
			
		||||
--- mode (string) - Permission mode in an octal format string (with leading 0)
 | 
			
		||||
--- isDir (boolean) - If the path is a directory
 | 
			
		||||
--- @param path string
 | 
			
		||||
--- @returns table
 | 
			
		||||
function fs.stat(path) end
 | 
			
		||||
 | 
			
		||||
return fs
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,49 @@
 | 
			
		||||
 | 
			
		||||
local hilbish = {}
 | 
			
		||||
 | 
			
		||||
--- This is an alias (ha) for the `hilbish.alias` function.
 | 
			
		||||
--- @param alias string
 | 
			
		||||
--- @param cmd string
 | 
			
		||||
function hilbish.aliases.add(alias, cmd) end
 | 
			
		||||
 | 
			
		||||
--- This is the same as the `hilbish.runnerMode` function. It takes a callback,
 | 
			
		||||
--- which will be used to execute all interactive input.
 | 
			
		||||
--- In normal cases, neither callbacks should be overrided by the user,
 | 
			
		||||
--- as the higher level functions listed below this will handle it.
 | 
			
		||||
--- @param cb function
 | 
			
		||||
function hilbish.runner.setMode(cb) end
 | 
			
		||||
 | 
			
		||||
--- Calls a completer function. This is mainly used to call
 | 
			
		||||
--- a command completer, which will have a `name` in the form
 | 
			
		||||
--- of `command.name`, example: `command.git`.
 | 
			
		||||
--- You can check `doc completions` for info on the `completionGroups` return value.
 | 
			
		||||
--- @param name string
 | 
			
		||||
--- @param query string
 | 
			
		||||
--- @param ctx string
 | 
			
		||||
--- @param fields table
 | 
			
		||||
function hilbish.completions.call(name, query, ctx, fields) end
 | 
			
		||||
 | 
			
		||||
--- The handler function is the callback for tab completion in Hilbish.
 | 
			
		||||
--- You can check the completions doc for more info.
 | 
			
		||||
--- @param line string
 | 
			
		||||
--- @param pos string
 | 
			
		||||
function hilbish.completions.handler(line, pos) end
 | 
			
		||||
 | 
			
		||||
--- Returns the current input line.
 | 
			
		||||
function hilbish.editor.getLine() end
 | 
			
		||||
 | 
			
		||||
--- Returns the text that is at the register.
 | 
			
		||||
--- @param register string
 | 
			
		||||
function hilbish.editor.getVimRegister(register) end
 | 
			
		||||
 | 
			
		||||
--- Inserts text into the line.
 | 
			
		||||
function hilbish.editor.insert(text) end
 | 
			
		||||
 | 
			
		||||
--- Sets the vim register at `register` to hold the passed text.
 | 
			
		||||
--- @param register string
 | 
			
		||||
--- @param text string
 | 
			
		||||
function hilbish.editor.setVimRegister(register, text) end
 | 
			
		||||
 | 
			
		||||
--- Sets an alias of `cmd` to `orig`
 | 
			
		||||
--- @param cmd string
 | 
			
		||||
--- @param orig string
 | 
			
		||||
@ -21,6 +64,7 @@ function hilbish.appendPath(dir) end
 | 
			
		||||
function hilbish.complete(scope, cb) end
 | 
			
		||||
 | 
			
		||||
--- Returns the current directory of the shell
 | 
			
		||||
--- @returns string
 | 
			
		||||
function hilbish.cwd() end
 | 
			
		||||
 | 
			
		||||
--- Replaces running hilbish with `cmd`
 | 
			
		||||
@ -35,6 +79,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
 | 
			
		||||
 | 
			
		||||
@ -44,7 +96,7 @@ function hilbish.highlighter(line) end
 | 
			
		||||
--- as the text for the hint. This is by default a shim. To set hints,
 | 
			
		||||
--- override this function with your custom handler.
 | 
			
		||||
--- @param line string
 | 
			
		||||
--- @param pos int
 | 
			
		||||
--- @param pos number
 | 
			
		||||
function hilbish.hinter(line, pos) end
 | 
			
		||||
 | 
			
		||||
--- Sets the input mode for Hilbish's line reader. Accepts either emacs or vim
 | 
			
		||||
@ -52,10 +104,10 @@ function hilbish.hinter(line, pos) end
 | 
			
		||||
function hilbish.inputMode(mode) end
 | 
			
		||||
 | 
			
		||||
--- Runs the `cb` function every `time` milliseconds.
 | 
			
		||||
--- Returns a `timer` object (see `doc timers`).
 | 
			
		||||
--- This creates a timer that starts immediately.
 | 
			
		||||
--- @param cb function
 | 
			
		||||
--- @param time number
 | 
			
		||||
--- @return table
 | 
			
		||||
--- @return Timer
 | 
			
		||||
function hilbish.interval(cb, time) end
 | 
			
		||||
 | 
			
		||||
--- Changes the continued line prompt to `str`
 | 
			
		||||
@ -73,20 +125,23 @@ function hilbish.prependPath(dir) end
 | 
			
		||||
--- `%u` - Name of current user
 | 
			
		||||
--- `%h` - Hostname of device
 | 
			
		||||
--- @param str string
 | 
			
		||||
--- @param typ string Type of prompt, being left or right. Left by default.
 | 
			
		||||
--- @param typ? string Type of prompt, being left or right. Left by default.
 | 
			
		||||
function hilbish.prompt(str, typ) end
 | 
			
		||||
 | 
			
		||||
--- Read input from the user, using Hilbish's line editor/input reader.
 | 
			
		||||
--- This is a separate instance from the one Hilbish actually uses.
 | 
			
		||||
--- Returns `input`, will be nil if ctrl + d is pressed, or an error occurs (which shouldn't happen)
 | 
			
		||||
--- @param prompt string
 | 
			
		||||
--- @param prompt? string
 | 
			
		||||
--- @returns string|nil
 | 
			
		||||
function hilbish.read(prompt) end
 | 
			
		||||
 | 
			
		||||
--- Runs `cmd` in Hilbish's sh interpreter.
 | 
			
		||||
--- If returnOut is true, the outputs of `cmd` will be returned as the 2nd and
 | 
			
		||||
--- 3rd values instead of being outputted to the terminal.
 | 
			
		||||
--- @param cmd string
 | 
			
		||||
function hilbish.run(cmd) end
 | 
			
		||||
--- @param returnOut boolean
 | 
			
		||||
--- @returns number, string, string
 | 
			
		||||
function hilbish.run(cmd, returnOut) end
 | 
			
		||||
 | 
			
		||||
--- Sets the execution/runner mode for interactive Hilbish. This determines whether
 | 
			
		||||
--- Hilbish wll try to run input as Lua and/or sh or only do one of either.
 | 
			
		||||
@ -96,15 +151,143 @@ function hilbish.run(cmd) end
 | 
			
		||||
--- @param mode string|function
 | 
			
		||||
function hilbish.runnerMode(mode) end
 | 
			
		||||
 | 
			
		||||
--- Runs the `cb` function after `time` in milliseconds
 | 
			
		||||
--- Returns a `timer` object (see `doc timers`).
 | 
			
		||||
--- Runs the `cb` function after `time` in milliseconds.
 | 
			
		||||
--- This creates a timer that starts immediately.
 | 
			
		||||
--- @param cb function
 | 
			
		||||
--- @param time number
 | 
			
		||||
--- @return table
 | 
			
		||||
--- @returns Timer
 | 
			
		||||
function hilbish.timeout(cb, time) end
 | 
			
		||||
 | 
			
		||||
--- Checks if `name` is a valid command
 | 
			
		||||
--- @param binName string
 | 
			
		||||
function hilbish.which(binName) end
 | 
			
		||||
--- Checks if `name` is a valid command.
 | 
			
		||||
--- Will return the path of the binary, or a basename if it's a commander.
 | 
			
		||||
--- @param name string
 | 
			
		||||
--- @returns string
 | 
			
		||||
function hilbish.which(name) end
 | 
			
		||||
 | 
			
		||||
--- Puts a job in the background. This acts the same as initially running a job.
 | 
			
		||||
function hilbish.jobs:background() end
 | 
			
		||||
 | 
			
		||||
--- Returns binary/executale completion candidates based on the provided query.
 | 
			
		||||
--- @param query string
 | 
			
		||||
--- @param ctx string
 | 
			
		||||
--- @param fields table
 | 
			
		||||
function hilbish.completions.bins(query, ctx, fields) end
 | 
			
		||||
 | 
			
		||||
--- Returns file completion candidates based on the provided query.
 | 
			
		||||
--- @param query string
 | 
			
		||||
--- @param ctx string
 | 
			
		||||
--- @param fields table
 | 
			
		||||
function hilbish.completions.files(query, ctx, fields) end
 | 
			
		||||
 | 
			
		||||
--- Puts a job in the foreground. This will cause it to run like it was
 | 
			
		||||
--- executed normally and wait for it to complete.
 | 
			
		||||
function hilbish.jobs:foreground() end
 | 
			
		||||
 | 
			
		||||
--- Evaluates `cmd` as Lua input. This is the same as using `dofile`
 | 
			
		||||
--- or `load`, but is appropriated for the runner interface.
 | 
			
		||||
--- @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 input from the sink.
 | 
			
		||||
--- @returns string
 | 
			
		||||
function hilbish:read() end
 | 
			
		||||
 | 
			
		||||
--- Writes data to a sink.
 | 
			
		||||
function hilbish:write(str) end
 | 
			
		||||
 | 
			
		||||
--- Writes data to a sink with a newline at the end.
 | 
			
		||||
function hilbish:writeln(str) end
 | 
			
		||||
 | 
			
		||||
--- Starts running the job.
 | 
			
		||||
function hilbish.jobs:start() end
 | 
			
		||||
 | 
			
		||||
--- Stops the job from running.
 | 
			
		||||
function hilbish.jobs:stop() end
 | 
			
		||||
 | 
			
		||||
--- Runs a command in Hilbish's shell script interpreter.
 | 
			
		||||
--- This is the equivalent of using `source`.
 | 
			
		||||
--- @param cmd string
 | 
			
		||||
function hilbish.runner.sh(cmd) end
 | 
			
		||||
 | 
			
		||||
--- Starts a timer.
 | 
			
		||||
function hilbish.timers:start() end
 | 
			
		||||
 | 
			
		||||
--- Stops a timer.
 | 
			
		||||
function hilbish.timers:stop() end
 | 
			
		||||
 | 
			
		||||
--- Removes an alias.
 | 
			
		||||
--- @param name string
 | 
			
		||||
function hilbish.aliases.delete(name) end
 | 
			
		||||
 | 
			
		||||
--- Get a table of all aliases, with string keys as the alias and the value as the command.
 | 
			
		||||
--- @returns table<string, string>
 | 
			
		||||
function hilbish.aliases.list() end
 | 
			
		||||
 | 
			
		||||
--- Tries to resolve an alias to its command.
 | 
			
		||||
--- @param alias string
 | 
			
		||||
--- @returns string
 | 
			
		||||
function hilbish.aliases.resolve(alias) end
 | 
			
		||||
 | 
			
		||||
--- Adds a new job to the job table. Note that this does not immediately run it.
 | 
			
		||||
--- @param cmdstr string
 | 
			
		||||
--- @param args table
 | 
			
		||||
--- @param execPath string
 | 
			
		||||
function hilbish.jobs.add(cmdstr, args, execPath) end
 | 
			
		||||
 | 
			
		||||
--- Returns a table of all job objects.
 | 
			
		||||
--- @returns table<Job>
 | 
			
		||||
function hilbish.jobs.all() end
 | 
			
		||||
 | 
			
		||||
--- Disowns a job. This deletes it from the job table.
 | 
			
		||||
--- @param id number
 | 
			
		||||
function hilbish.jobs.disown(id) end
 | 
			
		||||
 | 
			
		||||
--- Get a job object via its ID.
 | 
			
		||||
--- @param id number
 | 
			
		||||
--- @returns Job
 | 
			
		||||
function hilbish.jobs.get(id) end
 | 
			
		||||
 | 
			
		||||
--- Returns the last added job from the table.
 | 
			
		||||
--- @returns Job
 | 
			
		||||
function hilbish.jobs.last() end
 | 
			
		||||
 | 
			
		||||
--- Adds a command to the history.
 | 
			
		||||
--- @param cmd string
 | 
			
		||||
function hilbish.history.add(cmd) end
 | 
			
		||||
 | 
			
		||||
--- Retrieves all history.
 | 
			
		||||
--- @returns table
 | 
			
		||||
function hilbish.history.all() end
 | 
			
		||||
 | 
			
		||||
--- Deletes all commands from the history.
 | 
			
		||||
function hilbish.history.clear() end
 | 
			
		||||
 | 
			
		||||
--- Retrieves a command from the history based on the `idx`.
 | 
			
		||||
--- @param idx number
 | 
			
		||||
function hilbish.history.get(idx) end
 | 
			
		||||
 | 
			
		||||
--- Returns the amount of commands in the history.
 | 
			
		||||
--- @returns number
 | 
			
		||||
function hilbish.history.size() end
 | 
			
		||||
 | 
			
		||||
--- Creates a timer that runs based on the specified `time` in milliseconds.
 | 
			
		||||
--- The `type` can either be `hilbish.timers.INTERVAL` or `hilbish.timers.TIMEOUT`
 | 
			
		||||
--- @param type number
 | 
			
		||||
--- @param time number
 | 
			
		||||
--- @param callback function
 | 
			
		||||
function hilbish.timers.create(type, time, callback) end
 | 
			
		||||
 | 
			
		||||
--- Retrieves a timer via its ID.
 | 
			
		||||
--- @param id number
 | 
			
		||||
--- @returns Timer
 | 
			
		||||
function hilbish.timers.get(id) end
 | 
			
		||||
 | 
			
		||||
return hilbish
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										19
									
								
								exec.go
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								exec.go
									
									
									
									
									
								
							@ -141,10 +141,10 @@ func runInput(input string, priv bool) {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if exErr, ok := isExecError(err); ok {
 | 
			
		||||
			hooks.Emit("command." + exErr.typ, exErr.cmd)
 | 
			
		||||
			err = exErr.sprint()
 | 
			
		||||
		}
 | 
			
		||||
		} else {
 | 
			
		||||
			fmt.Fprintln(os.Stderr, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	cmdFinish(exitCode, input, priv)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -321,8 +321,18 @@ func execHandle(bg bool) interp.ExecHandlerFunc {
 | 
			
		||||
			luacmdArgs.Set(rt.IntValue(int64(i + 1)), rt.StringValue(str))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		hc := interp.HandlerCtx(ctx)
 | 
			
		||||
		if commands[args[0]] != nil {
 | 
			
		||||
			luaexitcode, err := rt.Call1(l.MainThread(), rt.FunctionValue(commands[args[0]]), rt.TableValue(luacmdArgs))
 | 
			
		||||
			stdin := newSinkInput(hc.Stdin)
 | 
			
		||||
			stdout := newSinkOutput(hc.Stdout)
 | 
			
		||||
			stderr := newSinkOutput(hc.Stderr)
 | 
			
		||||
 | 
			
		||||
			sinks := rt.NewTable()
 | 
			
		||||
			sinks.Set(rt.StringValue("in"), rt.UserDataValue(stdin.ud))
 | 
			
		||||
			sinks.Set(rt.StringValue("out"), rt.UserDataValue(stdout.ud))
 | 
			
		||||
			sinks.Set(rt.StringValue("err"), rt.UserDataValue(stderr.ud))
 | 
			
		||||
 | 
			
		||||
			luaexitcode, err := rt.Call1(l.MainThread(), rt.FunctionValue(commands[args[0]]), rt.TableValue(luacmdArgs), rt.TableValue(sinks))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				fmt.Fprintln(os.Stderr, "Error in command:\n" + err.Error())
 | 
			
		||||
				return interp.NewExitStatus(1)
 | 
			
		||||
@ -362,7 +372,6 @@ func execHandle(bg bool) interp.ExecHandlerFunc {
 | 
			
		||||
		killTimeout := 2 * time.Second
 | 
			
		||||
		// from here is basically copy-paste of the default exec handler from
 | 
			
		||||
		// sh/interp but with our job handling
 | 
			
		||||
		hc := interp.HandlerCtx(ctx)
 | 
			
		||||
		path, err := interp.LookPathDir(hc.Dir, hc.Env, args[0])
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Fprintln(hc.Stderr, err)
 | 
			
		||||
@ -550,7 +559,7 @@ func splitInput(input string) ([]string, string) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func cmdFinish(code uint8, cmdstr string, private bool) {
 | 
			
		||||
	util.SetField(l, hshMod, "exitCode", rt.IntValue(int64(code)), "Exit code of last exected command")
 | 
			
		||||
	util.SetField(l, hshMod, "exitCode", rt.IntValue(int64(code)))
 | 
			
		||||
	// using AsValue (to convert to lua type) on an interface which is an int
 | 
			
		||||
	// results in it being unknown in lua .... ????
 | 
			
		||||
	// so we allow the hook handler to take lua runtime Values
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								gallery/tab.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								gallery/tab.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 18 KiB  | 
							
								
								
									
										3
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								go.mod
									
									
									
									
									
								
							@ -8,6 +8,7 @@ require (
 | 
			
		||||
	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
 | 
			
		||||
@ -29,4 +30,4 @@ replace github.com/maxlandon/readline => ./readline
 | 
			
		||||
 | 
			
		||||
replace layeh.com/gopher-luar => github.com/layeh/gopher-luar v1.0.10
 | 
			
		||||
 | 
			
		||||
replace github.com/arnodel/golua => github.com/Rosettea/golua v0.0.0-20220518005949-116371948fe3
 | 
			
		||||
replace github.com/arnodel/golua => github.com/Rosettea/golua v0.0.0-20221213193027-cbf6d4e4d345
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										6
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								go.sum
									
									
									
									
									
								
							@ -2,6 +2,10 @@ github.com/Rosettea/golua v0.0.0-20220419183026-6d22d6fec5ac h1:dtXrgjch8PQyf7C9
 | 
			
		||||
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=
 | 
			
		||||
@ -45,6 +49,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=
 | 
			
		||||
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 h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
 | 
			
		||||
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=
 | 
			
		||||
 | 
			
		||||
@ -1,3 +1,9 @@
 | 
			
		||||
// the event emitter
 | 
			
		||||
// Bait is the event emitter for Hilbish. Why name it bait? Why not.
 | 
			
		||||
// It throws hooks that you can catch. This is what you will use if
 | 
			
		||||
// you want to listen in on hooks to know when certain things have
 | 
			
		||||
// happened, like when you've changed directory, a command has failed,
 | 
			
		||||
// etc. To find all available hooks thrown by Hilbish, see doc hooks.
 | 
			
		||||
package bait
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
@ -198,15 +204,6 @@ func (b *Bait) loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
 | 
			
		||||
	mod := rt.NewTable()
 | 
			
		||||
	util.SetExports(rtm, mod, exports)
 | 
			
		||||
 | 
			
		||||
	util.Document(mod,
 | 
			
		||||
`Bait is the event emitter for Hilbish. Why name it bait?
 | 
			
		||||
Because it throws hooks that you can catch (emits events
 | 
			
		||||
that you can listen to) and because why not, fun naming
 | 
			
		||||
is fun. This is what you will use if you want to listen
 | 
			
		||||
in on hooks to know when certain things have happened,
 | 
			
		||||
like when you've changed directory, a command has
 | 
			
		||||
failed, etc. To find all available hooks, see doc hooks.`)
 | 
			
		||||
 | 
			
		||||
	return rt.TableValue(mod), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -283,9 +280,11 @@ func (b *Bait) bcatchOnce(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// release(name, catcher)
 | 
			
		||||
// Removes the `catcher` for the event with `name`
 | 
			
		||||
// Removes the `catcher` for the event with `name`.
 | 
			
		||||
// For this to work, `catcher` has to be the same function used to catch
 | 
			
		||||
// an event, like one saved to a variable.
 | 
			
		||||
// --- @param name string
 | 
			
		||||
// --- @param catcher function
 | 
			
		||||
func (b *Bait) brelease(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	name, catcher, err := util.HandleStrCallback(t, c)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@ -297,8 +296,10 @@ func (b *Bait) brelease(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	return c.Next(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// hooks(name) -> {cb, cb...}
 | 
			
		||||
// Returns a table with hooks on the event with `name`.
 | 
			
		||||
// hooks(name) -> table
 | 
			
		||||
// Returns a table with hooks (callback functions) on the event with `name`.
 | 
			
		||||
// --- @param name string
 | 
			
		||||
// --- @returns table<function>
 | 
			
		||||
func (b *Bait) bhooks(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	if err := c.Check1Arg(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
 | 
			
		||||
@ -1,3 +1,33 @@
 | 
			
		||||
// library for custom commands
 | 
			
		||||
/*
 | 
			
		||||
Commander is a library for writing custom commands in Lua.
 | 
			
		||||
In order to make it easier to write commands for Hilbish,
 | 
			
		||||
not require separate scripts and to be able to use in a config,
 | 
			
		||||
the Commander library exists. This is like a very simple wrapper
 | 
			
		||||
that works with Hilbish for writing commands. Example:
 | 
			
		||||
 | 
			
		||||
```lua
 | 
			
		||||
local commander = require 'commander'
 | 
			
		||||
 | 
			
		||||
commander.register('hello', function(args, sinks)
 | 
			
		||||
	sinks.out:writeln 'Hello world!'
 | 
			
		||||
end)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
In this example, a command with the name of `hello` is created
 | 
			
		||||
that will print `Hello world!` to output. One question you may
 | 
			
		||||
have is: What is the `sinks` parameter?
 | 
			
		||||
 | 
			
		||||
The `sinks` parameter is a table with 3 keys: `in`, `out`,
 | 
			
		||||
and `err`. The values of these is a @Sink.
 | 
			
		||||
 | 
			
		||||
- `in` is the standard input. You can read from this sink
 | 
			
		||||
to get user input. (**This is currently unimplemented.**)
 | 
			
		||||
- `out` is standard output. This is usually where text meant for
 | 
			
		||||
output should go.
 | 
			
		||||
- `err` is standard error. This sink is for writing errors, as the
 | 
			
		||||
name would suggest.
 | 
			
		||||
*/
 | 
			
		||||
package commander
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
@ -32,7 +62,6 @@ func (c *Commander) loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
 | 
			
		||||
	}
 | 
			
		||||
	mod := rt.NewTable()
 | 
			
		||||
	util.SetExports(rtm, mod, exports)
 | 
			
		||||
	util.Document(mod, "Commander is Hilbish's custom command library, a way to write commands in Lua.")
 | 
			
		||||
 | 
			
		||||
	return rt.TableValue(mod), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,3 +1,7 @@
 | 
			
		||||
// filesystem interaction and functionality library
 | 
			
		||||
// The fs module provides easy and simple access to filesystem functions
 | 
			
		||||
// and other things, and acts an addition to the Lua standard library's
 | 
			
		||||
// I/O and filesystem functions.
 | 
			
		||||
package fs
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
@ -35,10 +39,6 @@ func loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
 | 
			
		||||
	mod.Set(rt.StringValue("pathSep"), rt.StringValue(string(os.PathSeparator)))
 | 
			
		||||
	mod.Set(rt.StringValue("pathListSep"), rt.StringValue(string(os.PathListSeparator)))
 | 
			
		||||
 | 
			
		||||
	util.Document(mod, `The fs module provides easy and simple access to
 | 
			
		||||
filesystem functions and other things, and acts an
 | 
			
		||||
addition to the Lua standard library's I/O and filesystem functions.`)
 | 
			
		||||
 | 
			
		||||
	return rt.TableValue(mod), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -93,9 +93,15 @@ func fmkdir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	return c.Next(), err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// stat(path)
 | 
			
		||||
// Returns info about `path`
 | 
			
		||||
// stat(path) -> {}
 | 
			
		||||
// Returns a table of info about the `path`.
 | 
			
		||||
// It contains the following keys:
 | 
			
		||||
// name (string) - Name of the path
 | 
			
		||||
// size (number) - Size of the path
 | 
			
		||||
// mode (string) - Permission mode in an octal format string (with leading 0)
 | 
			
		||||
// isDir (boolean) - If the path is a directory
 | 
			
		||||
// --- @param path string
 | 
			
		||||
// --- @returns table
 | 
			
		||||
func fstat(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	if err := c.Check1Arg(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@ -119,8 +125,8 @@ func fstat(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	return c.PushingNext1(t.Runtime, rt.TableValue(statTbl)), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readdir(dir)
 | 
			
		||||
// Returns a table of files in `dir`
 | 
			
		||||
// readdir(dir) -> {}
 | 
			
		||||
// Returns a table of files in `dir`.
 | 
			
		||||
// --- @param dir string
 | 
			
		||||
// --- @return table
 | 
			
		||||
func freaddir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
@ -145,9 +151,10 @@ func freaddir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	return c.PushingNext1(t.Runtime, rt.TableValue(names)), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// abs(path)
 | 
			
		||||
// abs(path) -> string
 | 
			
		||||
// Gives an absolute version of `path`.
 | 
			
		||||
// --- @param path string
 | 
			
		||||
// --- @returns string
 | 
			
		||||
func fabs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	path, err := c.StringArg(0)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@ -163,9 +170,10 @@ func fabs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	return c.PushingNext1(t.Runtime, rt.StringValue(abspath)), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// basename(path)
 | 
			
		||||
// basename(path) -> string
 | 
			
		||||
// Gives the basename of `path`. For the rules,
 | 
			
		||||
// see Go's filepath.Base
 | 
			
		||||
// --- @returns string
 | 
			
		||||
func fbasename(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	if err := c.Check1Arg(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@ -178,9 +186,11 @@ func fbasename(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	return c.PushingNext(t.Runtime, rt.StringValue(filepath.Base(path))), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// dir(path)
 | 
			
		||||
// dir(path) -> string
 | 
			
		||||
// Returns the directory part of `path`. For the rules, see Go's
 | 
			
		||||
// filepath.Dir
 | 
			
		||||
// --- @param path string
 | 
			
		||||
// --- @returns string
 | 
			
		||||
func fdir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	if err := c.Check1Arg(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@ -193,9 +203,11 @@ func fdir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	return c.PushingNext(t.Runtime, rt.StringValue(filepath.Dir(path))), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// glob(pattern)
 | 
			
		||||
// glob(pattern) -> matches (table)
 | 
			
		||||
// Glob all files and directories that match the pattern.
 | 
			
		||||
// For the rules, see Go's filepath.Glob
 | 
			
		||||
// --- @param pattern string
 | 
			
		||||
// --- @returns table
 | 
			
		||||
func fglob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	if err := c.Check1Arg(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@ -219,9 +231,11 @@ func fglob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	return c.PushingNext(t.Runtime, rt.TableValue(luaMatches)), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// join(paths...)
 | 
			
		||||
// join(...) -> string
 | 
			
		||||
// Takes paths and joins them together with the OS's
 | 
			
		||||
// directory separator (forward or backward slash).
 | 
			
		||||
// --- @vararg string
 | 
			
		||||
// --- @returns string
 | 
			
		||||
func fjoin(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	strs := make([]string, len(c.Etc()))
 | 
			
		||||
	for i, v := range c.Etc() {
 | 
			
		||||
 | 
			
		||||
@ -1,3 +1,5 @@
 | 
			
		||||
// low level terminal library
 | 
			
		||||
// The terminal library is a simple and lower level library for certain terminal interactions.
 | 
			
		||||
package terminal
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
@ -26,7 +28,6 @@ func loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
 | 
			
		||||
 | 
			
		||||
	mod := rt.NewTable()
 | 
			
		||||
	util.SetExports(rtm, mod, exports)
 | 
			
		||||
	util.Document(mod, "The terminal library is a simple and lower level library for certain terminal interactions.")
 | 
			
		||||
 | 
			
		||||
	return rt.TableValue(mod), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										59
									
								
								hilbish-git.spec
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								hilbish-git.spec
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,59 @@
 | 
			
		||||
%global _missing_build_ids_terminate_build 0
 | 
			
		||||
%global debug_package %{nil}
 | 
			
		||||
 | 
			
		||||
Name: hilbish-git
 | 
			
		||||
Version: {{{ git_tag_version }}}.{{{ git_short_hash }}}
 | 
			
		||||
Release: 1%{?dist}
 | 
			
		||||
Summary: The flower shell. A comfy and nice little shell for Lua fans!
 | 
			
		||||
License: MIT
 | 
			
		||||
 | 
			
		||||
Source: {{{ git_dir_pack }}}
 | 
			
		||||
BuildRequires: git golang go-task
 | 
			
		||||
Requires: inspect succulent lunacolors
 | 
			
		||||
 | 
			
		||||
Url: https://github.com/Rosettea/Hilbish
 | 
			
		||||
VCS: {{{ git_dir_vcs }}}
 | 
			
		||||
 | 
			
		||||
%description
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
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!
 | 
			
		||||
 | 
			
		||||
%prep
 | 
			
		||||
{{{ git_dir_setup_macro }}}
 | 
			
		||||
sed -i '\|/etc/shells|d' Taskfile.yaml
 | 
			
		||||
 | 
			
		||||
%build
 | 
			
		||||
go-task
 | 
			
		||||
 | 
			
		||||
%install
 | 
			
		||||
go-task install PREFIX=%{buildroot}/usr BINDIR=%{buildroot}/%{_bindir}
 | 
			
		||||
 | 
			
		||||
%post
 | 
			
		||||
if [ "$1" = 1 ]; then
 | 
			
		||||
	if [ ! -f %{_sysconfdir}/shells ] ; then
 | 
			
		||||
		echo "%{_bindir}/hilbish" > %{_sysconfdir}/shells
 | 
			
		||||
		echo "/bin/hilbish" >> %{_sysconfdir}/shells
 | 
			
		||||
	else
 | 
			
		||||
		grep -q "^%{_bindir}/hilbish$" %{_sysconfdir}/shells || echo "%{_bindir}/hilbish" >> %{_sysconfdir}/shells
 | 
			
		||||
		grep -q "^/bin/hilbish$" %{_sysconfdir}/shells || echo "/bin/hilbish" >> %{_sysconfdir}/shells
 | 
			
		||||
	fi
 | 
			
		||||
fi
 | 
			
		||||
 
 | 
			
		||||
%postun
 | 
			
		||||
if [ "$1" = 0 ] && [ -f %{_sysconfdir}/shells ] ; then
 | 
			
		||||
	sed -i '\!^%{_bindir}/hilbish$!d' %{_sysconfdir}/shells
 | 
			
		||||
	sed -i '\!^/bin/hilbish$!d' %{_sysconfdir}/shells
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
%files
 | 
			
		||||
%doc README.md
 | 
			
		||||
%license LICENSE
 | 
			
		||||
%{_bindir}/hilbish
 | 
			
		||||
%{_datadir}/hilbish
 | 
			
		||||
@ -73,13 +73,13 @@ func newFileHistory(path string) *fileHistory {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	itms := []string{""}
 | 
			
		||||
	lines := strings.Split(string(data), "\n")
 | 
			
		||||
	itms := make([]string, len(lines) - 1)
 | 
			
		||||
	for i, l := range lines {
 | 
			
		||||
		if i == len(lines) - 1 {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		itms = append(itms, l)
 | 
			
		||||
		itms[i] = l
 | 
			
		||||
	}
 | 
			
		||||
	f, err := os.OpenFile(path, os.O_APPEND | os.O_WRONLY | os.O_CREATE, 0755)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										57
									
								
								job.go
									
									
									
									
									
								
							
							
						
						
									
										57
									
								
								job.go
									
									
									
									
									
								
							@ -18,6 +18,16 @@ import (
 | 
			
		||||
var jobs *jobHandler
 | 
			
		||||
var jobMetaKey = rt.StringValue("hshjob")
 | 
			
		||||
 | 
			
		||||
// #type
 | 
			
		||||
// #interface jobs
 | 
			
		||||
// #property cmd The user entered command string for the job.
 | 
			
		||||
// #property running Whether the job is running or not.
 | 
			
		||||
// #property id The ID of the job in the job table
 | 
			
		||||
// #property pid The Process ID
 | 
			
		||||
// #property exitCode The last exit code of the job.
 | 
			
		||||
// #property stdout The standard output of the job. This just means the normal logs of the process.
 | 
			
		||||
// #property stderr The standard error stream of the process. This (usually) includes error messages of the job.
 | 
			
		||||
// The Job type describes a Hilbish job.
 | 
			
		||||
type job struct {
 | 
			
		||||
	cmd string
 | 
			
		||||
	running bool
 | 
			
		||||
@ -110,6 +120,10 @@ func (j *job) getProc() *os.Process {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #interface jobs
 | 
			
		||||
// #member
 | 
			
		||||
// start()
 | 
			
		||||
// Starts running the job.
 | 
			
		||||
func luaStartJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	if err := c.Check1Arg(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@ -130,6 +144,10 @@ func luaStartJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	return c.Next(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #interface jobs
 | 
			
		||||
// #member
 | 
			
		||||
// stop()
 | 
			
		||||
// Stops the job from running.
 | 
			
		||||
func luaStopJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	if err := c.Check1Arg(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@ -148,6 +166,11 @@ func luaStopJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	return c.Next(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #interface jobs
 | 
			
		||||
// #member
 | 
			
		||||
// foreground()
 | 
			
		||||
// Puts a job in the foreground. This will cause it to run like it was
 | 
			
		||||
// executed normally and wait for it to complete.
 | 
			
		||||
func luaForegroundJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	if err := c.Check1Arg(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@ -180,6 +203,10 @@ func luaForegroundJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	return c.Next(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #interface jobs
 | 
			
		||||
// #member
 | 
			
		||||
// background()
 | 
			
		||||
// Puts a job in the background. This acts the same as initially running a job.
 | 
			
		||||
func luaBackgroundJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	if err := c.Check1Arg(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@ -276,6 +303,13 @@ func (j *jobHandler) stopAll() {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #interface jobs
 | 
			
		||||
// background job management
 | 
			
		||||
/*
 | 
			
		||||
Manage interactive jobs in Hilbish via Lua.
 | 
			
		||||
 | 
			
		||||
Jobs are the name of background tasks/commands. A job can be started via
 | 
			
		||||
interactive usage or with the functions defined below for use in external runners. */
 | 
			
		||||
func (j *jobHandler) loader(rtm *rt.Runtime) *rt.Table {
 | 
			
		||||
	jobMethods := rt.NewTable()
 | 
			
		||||
	jFuncs := map[string]util.LuaExport{
 | 
			
		||||
@ -353,6 +387,11 @@ func jobUserData(j *job) *rt.UserData {
 | 
			
		||||
	return rt.NewUserData(j, jobMeta.AsTable())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #interface jobs
 | 
			
		||||
// get(id) -> @Job
 | 
			
		||||
// Get a job object via its ID.
 | 
			
		||||
// --- @param id number
 | 
			
		||||
// --- @returns Job
 | 
			
		||||
func (j *jobHandler) luaGetJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	j.mu.RLock()
 | 
			
		||||
	defer j.mu.RUnlock()
 | 
			
		||||
@ -373,6 +412,12 @@ func (j *jobHandler) luaGetJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	return c.PushingNext(t.Runtime, rt.UserDataValue(job.ud)), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #interface jobs
 | 
			
		||||
// add(cmdstr, args, execPath)
 | 
			
		||||
// Adds a new job to the job table. Note that this does not immediately run it.
 | 
			
		||||
// --- @param cmdstr string
 | 
			
		||||
// --- @param args table
 | 
			
		||||
// --- @param execPath string
 | 
			
		||||
func (j *jobHandler) luaAddJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	if err := c.CheckNArgs(3); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@ -402,6 +447,10 @@ func (j *jobHandler) luaAddJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	return c.PushingNext1(t.Runtime, rt.UserDataValue(jb.ud)), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #interface jobs
 | 
			
		||||
// all() -> table<@Job>
 | 
			
		||||
// Returns a table of all job objects.
 | 
			
		||||
// --- @returns table<Job>
 | 
			
		||||
func (j *jobHandler) luaAllJobs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	j.mu.RLock()
 | 
			
		||||
	defer j.mu.RUnlock()
 | 
			
		||||
@ -414,6 +463,10 @@ func (j *jobHandler) luaAllJobs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	return c.PushingNext1(t.Runtime, rt.TableValue(jobTbl)), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #interface jobs
 | 
			
		||||
// disown(id)
 | 
			
		||||
// Disowns a job. This deletes it from the job table.
 | 
			
		||||
// --- @param id number
 | 
			
		||||
func (j *jobHandler) luaDisownJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	if err := c.Check1Arg(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@ -431,6 +484,10 @@ func (j *jobHandler) luaDisownJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	return c.Next(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #interface jobs
 | 
			
		||||
// last() -> @Job
 | 
			
		||||
// Returns the last added job from the table.
 | 
			
		||||
// --- @returns Job
 | 
			
		||||
func (j *jobHandler) luaLastJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	j.mu.RLock()
 | 
			
		||||
	defer j.mu.RUnlock()
 | 
			
		||||
 | 
			
		||||
@ -1 +1 @@
 | 
			
		||||
Subproject commit 8467b87dd8d49c68b4100b2d129d5f071544b8cf
 | 
			
		||||
Subproject commit 34a57c964590f89aa065188a588c7b38aff99c28
 | 
			
		||||
							
								
								
									
										3
									
								
								lua.go
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								lua.go
									
									
									
									
									
								
							@ -23,6 +23,7 @@ func luaInit() {
 | 
			
		||||
		MessageHandler: debuglib.Traceback,
 | 
			
		||||
	})
 | 
			
		||||
	lib.LoadAll(l)
 | 
			
		||||
	setupSinkType(l)
 | 
			
		||||
 | 
			
		||||
	lib.LoadLibs(l, hilbishLoader)
 | 
			
		||||
	// yes this is stupid, i know
 | 
			
		||||
@ -67,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.")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										19
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								main.go
									
									
									
									
									
								
							@ -109,7 +109,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)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -118,9 +118,11 @@ func main() {
 | 
			
		||||
		os.Setenv("SHELL", os.Args[0])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	go handleSignals()
 | 
			
		||||
	lr = newLineReader("", false)
 | 
			
		||||
	luaInit()
 | 
			
		||||
 | 
			
		||||
	go handleSignals()
 | 
			
		||||
 | 
			
		||||
	// If user's config doesn't exixt,
 | 
			
		||||
	if _, err := os.Stat(defaultConfPath); os.IsNotExist(err) && *configflag == defaultConfPath {
 | 
			
		||||
		// Read default from current directory
 | 
			
		||||
@ -184,11 +186,14 @@ input:
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if err != readline.CtrlC {
 | 
			
		||||
			if err == readline.CtrlC {
 | 
			
		||||
				fmt.Println("^C")
 | 
			
		||||
				hooks.Emit("hilbish.cancel")
 | 
			
		||||
			} else {
 | 
			
		||||
				// If we get a completely random error, print
 | 
			
		||||
				fmt.Fprintln(os.Stderr, err)
 | 
			
		||||
			}
 | 
			
		||||
			fmt.Println("^C")
 | 
			
		||||
			// TODO: Halt if any other error occurs
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		var priv bool
 | 
			
		||||
@ -287,7 +292,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
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@ -322,3 +327,7 @@ func getVersion() string {
 | 
			
		||||
 | 
			
		||||
	return v.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func cut(slice []string, idx int) []string {
 | 
			
		||||
	return append(slice[:idx], slice[idx + 1:]...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,15 +1,15 @@
 | 
			
		||||
local commander = require 'commander'
 | 
			
		||||
 | 
			
		||||
commander.register('bg', function()
 | 
			
		||||
commander.register('bg', function(_, sinks)
 | 
			
		||||
	local job = hilbish.jobs.last()
 | 
			
		||||
	if not job then
 | 
			
		||||
		print 'bg: no last job'
 | 
			
		||||
		sinks.out:writeln 'bg: no last job'
 | 
			
		||||
		return 1
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local err = job.background()
 | 
			
		||||
	local err = job:background()
 | 
			
		||||
	if err then
 | 
			
		||||
		print('bg: ' .. err)
 | 
			
		||||
		sinks.out:writeln('bg: ' .. err)
 | 
			
		||||
		return 2
 | 
			
		||||
	end
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,11 @@
 | 
			
		||||
local commander = require 'commander'
 | 
			
		||||
local fs = require 'fs'
 | 
			
		||||
 | 
			
		||||
commander.register('cat', function(args)
 | 
			
		||||
commander.register('cat', function(args, sinks)
 | 
			
		||||
	local exit = 0
 | 
			
		||||
 | 
			
		||||
	if #args == 0 then
 | 
			
		||||
		print [[
 | 
			
		||||
		sinks.out:writeln [[
 | 
			
		||||
usage: cat [file]...]]
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
@ -13,11 +13,11 @@ usage: cat [file]...]]
 | 
			
		||||
		local f = io.open(fName)
 | 
			
		||||
		if f == nil then
 | 
			
		||||
			exit = 1
 | 
			
		||||
			print(string.format('cat: %s: no such file or directory', fName))
 | 
			
		||||
			sinks.out:writeln(string.format('cat: %s: no such file or directory', fName))
 | 
			
		||||
			goto continue
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		io.write(f:read '*a')
 | 
			
		||||
		sinks.out:writeln(f:read '*a')
 | 
			
		||||
		::continue::
 | 
			
		||||
	end
 | 
			
		||||
	io.flush()
 | 
			
		||||
 | 
			
		||||
@ -4,32 +4,25 @@ local fs = require 'fs'
 | 
			
		||||
local dirs = require 'nature.dirs'
 | 
			
		||||
 | 
			
		||||
dirs.old = hilbish.cwd()
 | 
			
		||||
commander.register('cd', function (args)
 | 
			
		||||
commander.register('cd', function (args, sinks)
 | 
			
		||||
	if #args > 1 then
 | 
			
		||||
		print("cd: too many arguments")
 | 
			
		||||
		sinks.out:writeln("cd: too many arguments")
 | 
			
		||||
		return 1
 | 
			
		||||
	elseif #args > 0 then
 | 
			
		||||
		local path = args[1]:gsub('$%$','\0'):gsub('${([%w_]+)}', os.getenv)
 | 
			
		||||
		:gsub('$([%w_]+)', os.getenv):gsub('%z','$'):gsub('^%s*(.-)%s*$', '%1')
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local path = args[1] and args[1] or hilbish.home
 | 
			
		||||
	if path == '-' then
 | 
			
		||||
		path = dirs.old
 | 
			
		||||
            print(path)
 | 
			
		||||
		sinks.out:writeln(path)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	dirs.setOld(hilbish.cwd())
 | 
			
		||||
	dirs.push(path)
 | 
			
		||||
 | 
			
		||||
	local ok, err = pcall(function() fs.cd(path) end)
 | 
			
		||||
	if not ok then
 | 
			
		||||
			print(err)
 | 
			
		||||
		sinks.out:writeln(err)
 | 
			
		||||
		return 1
 | 
			
		||||
	end
 | 
			
		||||
	bait.throw('cd', path)
 | 
			
		||||
 | 
			
		||||
		return
 | 
			
		||||
	end
 | 
			
		||||
	fs.cd(hilbish.home)
 | 
			
		||||
	bait.throw('cd', hilbish.home)
 | 
			
		||||
 | 
			
		||||
	dirs.push(hilbish.home)
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
@ -3,35 +3,38 @@ local fs = require 'fs'
 | 
			
		||||
local lunacolors = require 'lunacolors'
 | 
			
		||||
local dirs = require 'nature.dirs'
 | 
			
		||||
 | 
			
		||||
commander.register('cdr', function(args)
 | 
			
		||||
commander.register('cdr', function(args, sinks)
 | 
			
		||||
	if not args[1] then
 | 
			
		||||
		print(lunacolors.format [[
 | 
			
		||||
		sinks.out:writeln(lunacolors.format [[
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
	if args[1] == 'list' then
 | 
			
		||||
		local recentDirs = dirs.recentDirs
 | 
			
		||||
		if #recentDirs == 0 then
 | 
			
		||||
			print 'No directories have been visited.'
 | 
			
		||||
			sinks.out:writeln 'No directories have been visited.'
 | 
			
		||||
			return 1
 | 
			
		||||
		end
 | 
			
		||||
		print(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
 | 
			
		||||
 | 
			
		||||
	local index = tonumber(args[1])
 | 
			
		||||
	if not index then
 | 
			
		||||
		print(string.format('Received %s as index, which isn\'t a number.', index))
 | 
			
		||||
		sinks.out:writeln(string.format('Received %s as index, which isn\'t a number.', index))
 | 
			
		||||
		return 1
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	if not dirs.recent(index) then
 | 
			
		||||
		print(string.format('No recent directory found at index %s.', index))
 | 
			
		||||
		sinks.out:writeln(string.format('No recent directory found at index %s.', index))
 | 
			
		||||
		return 1
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,8 @@
 | 
			
		||||
local commander = require 'commander'
 | 
			
		||||
 | 
			
		||||
commander.register('disown', function(args)
 | 
			
		||||
commander.register('disown', function(args, sinks)
 | 
			
		||||
	if #hilbish.jobs.all() == 0 then
 | 
			
		||||
		print 'disown: no current job'
 | 
			
		||||
		sinks.out:writeln 'disown: no current job'
 | 
			
		||||
		return 1
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,7 @@ commander.register('disown', function(args)
 | 
			
		||||
	if #args < 0 then
 | 
			
		||||
		id = tonumber(args[1])
 | 
			
		||||
		if not id then
 | 
			
		||||
			print 'disown: invalid id for job'
 | 
			
		||||
			sinks.out:writeln 'disown: invalid id for job'
 | 
			
		||||
			return 1
 | 
			
		||||
		end
 | 
			
		||||
	else
 | 
			
		||||
@ -19,7 +19,7 @@ commander.register('disown', function(args)
 | 
			
		||||
 | 
			
		||||
	local ok = pcall(hilbish.jobs.disown, id)
 | 
			
		||||
	if not ok then
 | 
			
		||||
		print 'disown: job does not exist'
 | 
			
		||||
		sinks.out:writeln 'disown: job does not exist'
 | 
			
		||||
		return 2
 | 
			
		||||
	end
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
@ -2,94 +2,99 @@ local commander = require 'commander'
 | 
			
		||||
local fs = require 'fs'
 | 
			
		||||
local lunacolors = require 'lunacolors'
 | 
			
		||||
 | 
			
		||||
commander.register('doc', function(args)
 | 
			
		||||
commander.register('doc', function(args, sinks)
 | 
			
		||||
	local moddocPath = hilbish.dataDir .. '/docs/'
 | 
			
		||||
	local modDocFormat = [[
 | 
			
		||||
%s
 | 
			
		||||
%s
 | 
			
		||||
# Functions
 | 
			
		||||
	local stat = pcall(fs.stat, '.git/refs/heads/extended-job-api')
 | 
			
		||||
	if stat then
 | 
			
		||||
		-- 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', '')))
 | 
			
		||||
	end)
 | 
			
		||||
	local doc = [[
 | 
			
		||||
Welcome to Hilbish's documentation viewer! Here you can find
 | 
			
		||||
documentation for builtin functions and other things related
 | 
			
		||||
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 .. '.txt', 'rb')
 | 
			
		||||
		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>.txt
 | 
			
		||||
			-- dataDir/docs/<mod>/<mod>.md
 | 
			
		||||
			moddocPath = moddocPath .. mod .. '/'
 | 
			
		||||
			local subdocName = args[2]
 | 
			
		||||
			if not subdocName then
 | 
			
		||||
				subdocName = 'index'
 | 
			
		||||
				subdocName = '_index'
 | 
			
		||||
			end
 | 
			
		||||
			f = io.open(moddocPath .. subdocName .. '.txt', 'rb')
 | 
			
		||||
			f = io.open(moddocPath .. subdocName .. '.md', 'rb')
 | 
			
		||||
			if not f then
 | 
			
		||||
				print('No documentation found for ' .. mod .. '.')
 | 
			
		||||
				return
 | 
			
		||||
				f = io.open(moddocPath .. subdocName:match '%w+' .. '/' .. subdocName .. '.md', 'rb')
 | 
			
		||||
			end
 | 
			
		||||
			funcdocs = f:read '*a'
 | 
			
		||||
			local moddocs = table.filter(fs.readdir(moddocPath), function(f) return f ~= 'index.txt' 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, '.txt', '')))
 | 
			
		||||
			return lunacolors.underline(lunacolors.blue(string.gsub(fname, '.md', '')))
 | 
			
		||||
		end)
 | 
			
		||||
			if subdocName == 'index' then
 | 
			
		||||
		if #moddocs ~= 0 then
 | 
			
		||||
			funcdocs = funcdocs .. '\nSubdocs: ' .. table.concat(subdocs, ', ')
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		local valsStr = funcdocs:match '%-%-%-\n([^%-%-%-]+)\n'
 | 
			
		||||
		local vals = {}
 | 
			
		||||
		if valsStr then
 | 
			
		||||
			local _, endpos = funcdocs:find('---\n' .. valsStr .. '\n---\n\n', 1, true)
 | 
			
		||||
			funcdocs = funcdocs:sub(endpos + 1, #funcdocs)
 | 
			
		||||
 | 
			
		||||
			-- parse vals
 | 
			
		||||
			local lines = string.split(valsStr, '\n')
 | 
			
		||||
			for _, line in ipairs(lines) do
 | 
			
		||||
				local key = line:match '(%w+): '
 | 
			
		||||
				local val = line:match '^%w+: (.-)$'
 | 
			
		||||
 | 
			
		||||
				if key then
 | 
			
		||||
					vals[key] = val
 | 
			
		||||
				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()
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
		if not funcdocs then
 | 
			
		||||
			funcdocs = f:read '*a'
 | 
			
		||||
		end
 | 
			
		||||
		local desc = ''
 | 
			
		||||
		local ok = pcall(require, mod)
 | 
			
		||||
	local backtickOccurence = 0
 | 
			
		||||
		local formattedFuncs = lunacolors.format(funcdocs:sub(1, #funcdocs - 1):gsub('`', function()
 | 
			
		||||
	sinks.out:writeln(lunacolors.format(doc:gsub('`', function()
 | 
			
		||||
		backtickOccurence = backtickOccurence + 1
 | 
			
		||||
		if backtickOccurence % 2 == 0 then
 | 
			
		||||
			return '{reset}'
 | 
			
		||||
		else
 | 
			
		||||
			return '{underline}{green}'
 | 
			
		||||
		end
 | 
			
		||||
		end))
 | 
			
		||||
 | 
			
		||||
		if ok then
 | 
			
		||||
			local props = {}
 | 
			
		||||
			local propstr = ''
 | 
			
		||||
			local modDesc = ''
 | 
			
		||||
			local modmt = getmetatable(require(mod))
 | 
			
		||||
			if modmt then
 | 
			
		||||
				modDesc = modmt.__doc
 | 
			
		||||
				if modmt.__docProp then
 | 
			
		||||
					-- not all modules have docs for properties
 | 
			
		||||
					props = table.map(modmt.__docProp, function(v, k)
 | 
			
		||||
						return lunacolors.underline(lunacolors.blue(k)) .. ' > ' .. v
 | 
			
		||||
					end)
 | 
			
		||||
				end
 | 
			
		||||
				if #props > 0 then
 | 
			
		||||
					propstr = '\n# Properties\n' .. table.concat(props, '\n') .. '\n'
 | 
			
		||||
				end
 | 
			
		||||
				desc = string.format(modDocFormat, modDesc, propstr)
 | 
			
		||||
			end
 | 
			
		||||
		end
 | 
			
		||||
		print(desc .. formattedFuncs)
 | 
			
		||||
		f:close()
 | 
			
		||||
 | 
			
		||||
		return
 | 
			
		||||
	end
 | 
			
		||||
	local modules = table.map(fs.readdir(moddocPath), function(f)
 | 
			
		||||
		return lunacolors.underline(lunacolors.blue(string.gsub(f, '.txt', '')))
 | 
			
		||||
	end)
 | 
			
		||||
 | 
			
		||||
	io.write [[
 | 
			
		||||
Welcome to Hilbish's doc tool! Here you can find documentation for builtin
 | 
			
		||||
functions and other things.
 | 
			
		||||
 | 
			
		||||
Usage: doc <section> [subdoc]
 | 
			
		||||
A section is a module or a literal section and a subdoc is a subsection for it.
 | 
			
		||||
 | 
			
		||||
Available sections: ]]
 | 
			
		||||
	io.flush()
 | 
			
		||||
 | 
			
		||||
	print(table.concat(modules, ', '))
 | 
			
		||||
	end):gsub('\n#+.-\n', function(t)
 | 
			
		||||
		local signature = t:gsub('<.->(.-)</.->', '{underline}%1'):gsub('\\', '<')
 | 
			
		||||
		return '{bold}{yellow}' .. signature .. '{reset}'
 | 
			
		||||
	end)))
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
@ -1,15 +1,15 @@
 | 
			
		||||
local commander = require 'commander'
 | 
			
		||||
 | 
			
		||||
commander.register('fg', function()
 | 
			
		||||
commander.register('fg', function(_, sinks)
 | 
			
		||||
	local job = hilbish.jobs.last()
 | 
			
		||||
	if not job then
 | 
			
		||||
		print 'fg: no last job'
 | 
			
		||||
		sinks.out:writeln 'fg: no last job'
 | 
			
		||||
		return 1
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local err = job.foreground() -- waits for job; blocks
 | 
			
		||||
	local err = job:foreground() -- waits for job; blocks
 | 
			
		||||
	if err then
 | 
			
		||||
		print('fg: ' .. err)
 | 
			
		||||
		sinks.out:writeln('fg: ' .. err)
 | 
			
		||||
		return 2
 | 
			
		||||
	end
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
@ -24,7 +24,7 @@ function hilbish.completion.handler(line, pos)
 | 
			
		||||
		return {compGroup}, pfx
 | 
			
		||||
	else
 | 
			
		||||
		local ok, compGroups, pfx = pcall(hilbish.completion.call,
 | 
			
		||||
		'command.' .. #fields[1], query, ctx, fields)
 | 
			
		||||
		'command.' .. fields[1], query, ctx, fields)
 | 
			
		||||
		if ok then
 | 
			
		||||
			return compGroups, pfx
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										84
									
								
								nature/hummingbird.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										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
 | 
			
		||||
@ -11,6 +11,7 @@ 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
 | 
			
		||||
@ -67,5 +68,13 @@ do
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
bait.catch('error', function(event, handler, err)
 | 
			
		||||
	bait.release(event, handler)
 | 
			
		||||
	print(string.format('Encountered an error in %s handler\n%s', event, err:sub(8)))
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
bait.catch('command.not-found', function(cmd)
 | 
			
		||||
	print(string.format('hilbish: %s not found', cmd))
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
bait.catch('command.not-executable', function(cmd)
 | 
			
		||||
	print(string.format('hilbish: %s: not executable', cmd))
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -2,8 +2,8 @@ local bait = require 'bait'
 | 
			
		||||
local lunacolors = require 'lunacolors'
 | 
			
		||||
 | 
			
		||||
hilbish.motd = [[
 | 
			
		||||
Hilbish 2.0 is a {red}major{reset} update! If your config doesn't work
 | 
			
		||||
anymore, that will definitely be why! A MOTD, very message, much day.
 | 
			
		||||
1000 commits on the Hilbish repository brings us to {cyan}Version 2.1!{reset}
 | 
			
		||||
Docs, docs, docs... At least builtins work with pipes now.
 | 
			
		||||
]]
 | 
			
		||||
 | 
			
		||||
bait.catch('hilbish.init', function()
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										23
									
								
								nature/opts/notifyJobFinish.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										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,3 +1,4 @@
 | 
			
		||||
--- hilbish.runner
 | 
			
		||||
local currentRunner = 'hybrid'
 | 
			
		||||
local runners = {}
 | 
			
		||||
 | 
			
		||||
@ -74,6 +75,12 @@ function hilbish.runner.setCurrent(name)
 | 
			
		||||
	hilbish.runner.setMode(r.run)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
--- Returns the current runner by name.
 | 
			
		||||
--- @returns string
 | 
			
		||||
function hilbish.runner.getCurrent()
 | 
			
		||||
	return currentRunner
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
hilbish.runner.add('hybrid', function(input)
 | 
			
		||||
	local cmdStr = hilbish.aliases.resolve(input)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										27
									
								
								os.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								os.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,27 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"hilbish/util"
 | 
			
		||||
 | 
			
		||||
	rt "github.com/arnodel/golua/runtime"
 | 
			
		||||
	"github.com/blackfireio/osinfo"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// #interface os
 | 
			
		||||
// OS Info
 | 
			
		||||
// The `os` interface provides simple text information properties about
 | 
			
		||||
// the current OS on the systen. This mainly includes the name and
 | 
			
		||||
// version.
 | 
			
		||||
// #field family Family name of the current OS
 | 
			
		||||
// #field name Pretty name of the current OS
 | 
			
		||||
// #field version Version of the current OS
 | 
			
		||||
func hshosLoader(rtm *rt.Runtime) *rt.Table {
 | 
			
		||||
	info, _ := osinfo.GetOSInfo()
 | 
			
		||||
	mod := rt.NewTable()
 | 
			
		||||
 | 
			
		||||
	util.SetField(rtm, mod, "family", rt.StringValue(info.Family))
 | 
			
		||||
	util.SetField(rtm, mod, "name", rt.StringValue(info.Name))
 | 
			
		||||
	util.SetField(rtm, mod, "version", rt.StringValue(info.Version))
 | 
			
		||||
 | 
			
		||||
	return mod
 | 
			
		||||
}
 | 
			
		||||
@ -4,6 +4,7 @@ import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"github.com/rivo/uniseg"
 | 
			
		||||
)	
 | 
			
		||||
 | 
			
		||||
// initGrid - Grid display details. Called each time we want to be sure to have
 | 
			
		||||
@ -13,8 +14,8 @@ func (g *CompletionGroup) initGrid(rl *Instance) {
 | 
			
		||||
	// Compute size of each completion item box
 | 
			
		||||
	tcMaxLength := 1
 | 
			
		||||
	for i := range g.Suggestions {
 | 
			
		||||
		if len(g.Suggestions[i]) > tcMaxLength {
 | 
			
		||||
			tcMaxLength = len([]rune(g.Suggestions[i]))
 | 
			
		||||
		if uniseg.GraphemeClusterCount(g.Suggestions[i]) > tcMaxLength {
 | 
			
		||||
			tcMaxLength = uniseg.GraphemeClusterCount(g.Suggestions[i])
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -103,7 +104,7 @@ func (g *CompletionGroup) writeGrid(rl *Instance) (comp string) {
 | 
			
		||||
		rl.tcUsedY++
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cellWidth := strconv.Itoa((GetTermWidth() / g.tcMaxX) - 2)
 | 
			
		||||
	cellWidth := strconv.Itoa((GetTermWidth() / g.tcMaxX) - 4)
 | 
			
		||||
	x := 0
 | 
			
		||||
	y := 1
 | 
			
		||||
 | 
			
		||||
@ -124,7 +125,15 @@ func (g *CompletionGroup) writeGrid(rl *Instance) (comp string) {
 | 
			
		||||
			comp += seqInvert
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		comp += fmt.Sprintf("%-"+cellWidth+"s %s", fmtEscape(g.Suggestions[i]), seqReset)
 | 
			
		||||
		sugg := g.Suggestions[i]
 | 
			
		||||
		if len(sugg) > GetTermWidth() {
 | 
			
		||||
			sugg = sugg[:GetTermWidth() - 4] + "..."
 | 
			
		||||
		}
 | 
			
		||||
		formatStr := "%-"+cellWidth+"s%s "
 | 
			
		||||
		if g.tcMaxX == 1 {
 | 
			
		||||
			formatStr = "%s%s"
 | 
			
		||||
		}
 | 
			
		||||
		comp += fmt.Sprintf(formatStr, fmtEscape(sugg), seqReset)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Always add a newline to the group if the end if not punctuated with one
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -123,23 +123,20 @@ func (rl *Instance) walkHistory(i int) {
 | 
			
		||||
 | 
			
		||||
	// When we are exiting the current line buffer to move around
 | 
			
		||||
	// the history, we make buffer the current line
 | 
			
		||||
	if rl.histPos == 0 && (rl.histPos+i) == 1 {
 | 
			
		||||
	if rl.histOffset == 0 && rl.histOffset + i == 1 {
 | 
			
		||||
		rl.lineBuf = string(rl.line)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch rl.histPos + i {
 | 
			
		||||
	case 0, history.Len() + 1:
 | 
			
		||||
		rl.histPos = 0
 | 
			
		||||
	rl.histOffset += i
 | 
			
		||||
	if rl.histOffset == 0 {
 | 
			
		||||
		rl.line = []rune(rl.lineBuf)
 | 
			
		||||
		rl.pos = len(rl.lineBuf)
 | 
			
		||||
		return
 | 
			
		||||
	case -1:
 | 
			
		||||
		rl.histPos = 0
 | 
			
		||||
		rl.lineBuf = string(rl.line)
 | 
			
		||||
	default:
 | 
			
		||||
	} else if rl.histOffset <= -1 {
 | 
			
		||||
		rl.histOffset = 0
 | 
			
		||||
	} else {
 | 
			
		||||
		dedup = true
 | 
			
		||||
		old = string(rl.line)
 | 
			
		||||
		new, err = history.GetLine(history.Len() - rl.histPos - 1)
 | 
			
		||||
		new, err = history.GetLine(history.Len() - rl.histOffset)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			rl.resetHelpers()
 | 
			
		||||
			print("\r\n" + err.Error() + "\r\n")
 | 
			
		||||
@ -148,7 +145,6 @@ func (rl *Instance) walkHistory(i int) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		rl.clearLine()
 | 
			
		||||
		rl.histPos += i
 | 
			
		||||
		rl.line = []rune(new)
 | 
			
		||||
		rl.pos = len(rl.line)
 | 
			
		||||
		if rl.pos > 0 {
 | 
			
		||||
 | 
			
		||||
@ -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 -----------------------------------------------------------------------------------
 | 
			
		||||
@ -134,6 +136,7 @@ type Instance struct {
 | 
			
		||||
	// history operating params
 | 
			
		||||
	lineBuf    string
 | 
			
		||||
	histPos    int
 | 
			
		||||
	histOffset int
 | 
			
		||||
	histNavIdx int // Used for quick history navigation.
 | 
			
		||||
 | 
			
		||||
	//
 | 
			
		||||
@ -228,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()
 | 
			
		||||
 | 
			
		||||
@ -49,7 +49,7 @@ func (rl *Instance) Readline() (string, error) {
 | 
			
		||||
 | 
			
		||||
	// History Init
 | 
			
		||||
	// We need this set to the last command, so that we can access it quickly
 | 
			
		||||
	rl.histPos = 0
 | 
			
		||||
	rl.histOffset = 0
 | 
			
		||||
	rl.viUndoHistory = []undoItem{{line: "", pos: 0}}
 | 
			
		||||
 | 
			
		||||
	// Multisplit
 | 
			
		||||
@ -238,7 +238,9 @@ func (rl *Instance) Readline() (string, error) {
 | 
			
		||||
 | 
			
		||||
			// Normal completion search does only refresh the search pattern and the comps
 | 
			
		||||
			if rl.modeTabFind || rl.modeAutoFind {
 | 
			
		||||
				rl.resetVirtualComp(false)
 | 
			
		||||
				rl.backspaceTabFind()
 | 
			
		||||
				rl.renderHelpers()
 | 
			
		||||
				rl.viUndoSkipAppend = true
 | 
			
		||||
			} else {
 | 
			
		||||
				// Always cancel any virtual completion
 | 
			
		||||
@ -331,6 +333,8 @@ func (rl *Instance) Readline() (string, error) {
 | 
			
		||||
 | 
			
		||||
			rl.modeTabFind = true
 | 
			
		||||
			rl.updateTabFind([]rune{})
 | 
			
		||||
			rl.updateVirtualComp()
 | 
			
		||||
			rl.renderHelpers()
 | 
			
		||||
			rl.viUndoSkipAppend = true
 | 
			
		||||
 | 
			
		||||
		// Tab Completion & Completion Search ---------------------------------------------------------------
 | 
			
		||||
@ -484,7 +488,10 @@ func (rl *Instance) Readline() (string, error) {
 | 
			
		||||
				if string(r[:i]) != seqShiftTab &&
 | 
			
		||||
					string(r[:i]) != seqForwards && string(r[:i]) != seqBackwards &&
 | 
			
		||||
					string(r[:i]) != seqUp && string(r[:i]) != seqDown {
 | 
			
		||||
					rl.resetVirtualComp(false)
 | 
			
		||||
					// basically only applies except on 1st ctrl r open
 | 
			
		||||
					// so if we have not explicitly selected something
 | 
			
		||||
					// (tabCompletionSelect is false) drop virtual completion
 | 
			
		||||
					rl.resetVirtualComp(!rl.tabCompletionSelect)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
@ -517,7 +524,9 @@ func (rl *Instance) Readline() (string, error) {
 | 
			
		||||
			if rl.modeAutoFind || rl.modeTabFind {
 | 
			
		||||
				rl.resetVirtualComp(false)
 | 
			
		||||
				rl.updateTabFind(r[:i])
 | 
			
		||||
				rl.renderHelpers()
 | 
			
		||||
				rl.viUndoSkipAppend = true
 | 
			
		||||
				continue
 | 
			
		||||
			} else {
 | 
			
		||||
				rl.resetVirtualComp(false)
 | 
			
		||||
				rl.editorInput(r[:i])
 | 
			
		||||
@ -537,6 +546,10 @@ func (rl *Instance) Readline() (string, error) {
 | 
			
		||||
// entry readline is currently configured for and then update the line entries
 | 
			
		||||
// accordingly.
 | 
			
		||||
func (rl *Instance) editorInput(r []rune) {
 | 
			
		||||
	if len(r) == 0 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch rl.modeViMode {
 | 
			
		||||
	case VimKeys:
 | 
			
		||||
		rl.vi(r[0])
 | 
			
		||||
@ -604,6 +617,7 @@ func (rl *Instance) escapeSeq(r []rune) {
 | 
			
		||||
	case string(charEscape):
 | 
			
		||||
		switch {
 | 
			
		||||
		case rl.modeAutoFind:
 | 
			
		||||
			rl.resetVirtualComp(true)
 | 
			
		||||
			rl.resetTabFind()
 | 
			
		||||
			rl.clearHelpers()
 | 
			
		||||
			rl.resetTabCompletion()
 | 
			
		||||
@ -611,6 +625,7 @@ func (rl *Instance) escapeSeq(r []rune) {
 | 
			
		||||
			rl.renderHelpers()
 | 
			
		||||
 | 
			
		||||
		case rl.modeTabFind:
 | 
			
		||||
			rl.resetVirtualComp(true)
 | 
			
		||||
			rl.resetTabFind()
 | 
			
		||||
			rl.resetTabCompletion()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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"
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										46
									
								
								rl.go
									
									
									
									
									
								
							
							
						
						
									
										46
									
								
								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 {
 | 
			
		||||
@ -225,7 +244,11 @@ func (lr *lineReader) Resize() {
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// lua module
 | 
			
		||||
// #interface history
 | 
			
		||||
// command history
 | 
			
		||||
// The history interface deals with command history. 
 | 
			
		||||
// This includes the ability to override functions to change the main
 | 
			
		||||
// method of saving history.
 | 
			
		||||
func (lr *lineReader) Loader(rtm *rt.Runtime) *rt.Table {
 | 
			
		||||
	lrLua := map[string]util.LuaExport{
 | 
			
		||||
		"add": {lr.luaAddHistory, 1, false},
 | 
			
		||||
@ -241,6 +264,10 @@ func (lr *lineReader) Loader(rtm *rt.Runtime) *rt.Table {
 | 
			
		||||
	return mod
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #interface history
 | 
			
		||||
// add(cmd)
 | 
			
		||||
// Adds a command to the history.
 | 
			
		||||
// --- @param cmd string
 | 
			
		||||
func (lr *lineReader) luaAddHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	if err := c.Check1Arg(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@ -254,10 +281,18 @@ func (lr *lineReader) luaAddHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error)
 | 
			
		||||
	return c.Next(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #interface history
 | 
			
		||||
// size() -> number
 | 
			
		||||
// Returns the amount of commands in the history.
 | 
			
		||||
// --- @returns number
 | 
			
		||||
func (lr *lineReader) luaSize(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	return c.PushingNext1(t.Runtime, rt.IntValue(int64(lr.fileHist.Len()))), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #interface history
 | 
			
		||||
// get(idx)
 | 
			
		||||
// Retrieves a command from the history based on the `idx`.
 | 
			
		||||
// --- @param idx number
 | 
			
		||||
func (lr *lineReader) luaGetHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	if err := c.Check1Arg(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@ -272,6 +307,10 @@ func (lr *lineReader) luaGetHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error)
 | 
			
		||||
	return c.PushingNext1(t.Runtime, rt.StringValue(cmd)), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #interface history
 | 
			
		||||
// all() -> table
 | 
			
		||||
// Retrieves all history.
 | 
			
		||||
// --- @returns table
 | 
			
		||||
func (lr *lineReader) luaAllHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	tbl := rt.NewTable()
 | 
			
		||||
	size := lr.fileHist.Len()
 | 
			
		||||
@ -284,6 +323,9 @@ func (lr *lineReader) luaAllHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error)
 | 
			
		||||
	return c.PushingNext1(t.Runtime, rt.TableValue(tbl)), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #interface history
 | 
			
		||||
// clear()
 | 
			
		||||
// Deletes all commands from the history.
 | 
			
		||||
func (lr *lineReader) luaClearHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	lr.fileHist.clear()
 | 
			
		||||
	return c.Next(), nil
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										2
									
								
								rpkg.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								rpkg.conf
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,2 @@
 | 
			
		||||
[rpkg]
 | 
			
		||||
user_macros = "${git_props:root}/rpkg.macros"
 | 
			
		||||
							
								
								
									
										25
									
								
								rpkg.macros
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								rpkg.macros
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,25 @@
 | 
			
		||||
function git_short_hash {
 | 
			
		||||
    short_hash="$(cached git_short_hash)"
 | 
			
		||||
 | 
			
		||||
    if [ -z "$short_hash" ]; then
 | 
			
		||||
        short_hash="$(git rev-parse --short HEAD)"
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    output "$short_hash"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function git_tag_version {
 | 
			
		||||
    tag="$(cached git_tag_version)"
 | 
			
		||||
 | 
			
		||||
    if [ -z "$tag" ]; then
 | 
			
		||||
        tag="$(git describe --tags --abbrev=0)"
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    # Remove the potential prefix of `v`
 | 
			
		||||
    if [[ $tag =~ ^v[0-9].* ]]; then
 | 
			
		||||
        tag="${tag:1}"
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    tag="${tag/"-"/"."}"
 | 
			
		||||
    output "$tag"
 | 
			
		||||
}
 | 
			
		||||
@ -6,6 +6,13 @@ import (
 | 
			
		||||
	rt "github.com/arnodel/golua/runtime"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// #interface runner
 | 
			
		||||
// interactive command runner customization
 | 
			
		||||
// The runner interface contains functions that allow the user to change
 | 
			
		||||
// how Hilbish interprets interactive input.
 | 
			
		||||
// Users can add and change the default runner for interactive input to any
 | 
			
		||||
// language or script of their choosing. A good example is using it to
 | 
			
		||||
// write command in Fennel.
 | 
			
		||||
func runnerModeLoader(rtm *rt.Runtime) *rt.Table {
 | 
			
		||||
	exports := map[string]util.LuaExport{
 | 
			
		||||
		"sh": {shRunner, 1, false},
 | 
			
		||||
@ -19,6 +26,20 @@ func runnerModeLoader(rtm *rt.Runtime) *rt.Table {
 | 
			
		||||
	return mod
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #interface runner
 | 
			
		||||
// setMode(cb)
 | 
			
		||||
// This is the same as the `hilbish.runnerMode` function. It takes a callback,
 | 
			
		||||
// which will be used to execute all interactive input.
 | 
			
		||||
// In normal cases, neither callbacks should be overrided by the user,
 | 
			
		||||
// as the higher level functions listed below this will handle it.
 | 
			
		||||
// --- @param cb function
 | 
			
		||||
func _runnerMode() {}
 | 
			
		||||
 | 
			
		||||
// #interface runner
 | 
			
		||||
// sh(cmd)
 | 
			
		||||
// Runs a command in Hilbish's shell script interpreter.
 | 
			
		||||
// This is the equivalent of using `source`.
 | 
			
		||||
// --- @param cmd string
 | 
			
		||||
func shRunner(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	if err := c.Check1Arg(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@ -42,6 +63,11 @@ func shRunner(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	return c.PushingNext(t.Runtime, rt.TableValue(runnerRet)), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #interface runner
 | 
			
		||||
// lua(cmd)
 | 
			
		||||
// Evaluates `cmd` as Lua input. This is the same as using `dofile`
 | 
			
		||||
// or `load`, but is appropriated for the runner interface.
 | 
			
		||||
// --- @param cmd string
 | 
			
		||||
func luaRunner(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	if err := c.Check1Arg(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										226
									
								
								sink.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										226
									
								
								sink.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,226 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"hilbish/util"
 | 
			
		||||
 | 
			
		||||
	rt "github.com/arnodel/golua/runtime"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var sinkMetaKey = rt.StringValue("hshsink")
 | 
			
		||||
 | 
			
		||||
// #type
 | 
			
		||||
// A sink is a structure that has input and/or output to/from
 | 
			
		||||
// a desination.
 | 
			
		||||
type sink struct{
 | 
			
		||||
	writer *bufio.Writer
 | 
			
		||||
	reader *bufio.Reader
 | 
			
		||||
	file *os.File
 | 
			
		||||
	ud *rt.UserData
 | 
			
		||||
	autoFlush bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func setupSinkType(rtm *rt.Runtime) {
 | 
			
		||||
	sinkMeta := rt.NewTable()
 | 
			
		||||
 | 
			
		||||
	sinkMethods := rt.NewTable()
 | 
			
		||||
	sinkFuncs := map[string]util.LuaExport{
 | 
			
		||||
		"flush": {luaSinkFlush, 1, false},
 | 
			
		||||
		"read": {luaSinkRead, 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
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sinkMeta.Set(rt.StringValue("__index"), rt.FunctionValue(rt.NewGoFunction(sinkIndex, "__index", 2, false)))
 | 
			
		||||
	l.SetRegistry(sinkMetaKey, rt.TableValue(sinkMeta))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #member
 | 
			
		||||
// read() -> string
 | 
			
		||||
// --- @returns string
 | 
			
		||||
// Reads 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.
 | 
			
		||||
func luaSinkWrite(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	if err := c.CheckNArgs(2); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s, err := sinkArg(c, 0)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	data, err := c.StringArg(1)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s.writer.Write([]byte(data))
 | 
			
		||||
	if s.autoFlush {
 | 
			
		||||
		s.writer.Flush()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return c.Next(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #member
 | 
			
		||||
// writeln(str)
 | 
			
		||||
// Writes data to a sink with a newline at the end.
 | 
			
		||||
func luaSinkWriteln(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 | 
			
		||||
	if err := c.CheckNArgs(2); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s, err := sinkArg(c, 0)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	data, err := c.StringArg(1)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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: 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: bufio.NewWriter(w),
 | 
			
		||||
		autoFlush: true,
 | 
			
		||||
	}
 | 
			
		||||
	s.ud = sinkUserData(s)
 | 
			
		||||
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func sinkArg(c *rt.GoCont, arg int) (*sink, error) {
 | 
			
		||||
	s, ok := valueToSink(c.Arg(arg))
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, fmt.Errorf("#%d must be a sink", arg + 1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return s, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func valueToSink(val rt.Value) (*sink, bool) {
 | 
			
		||||
	u, ok := val.TryUserData()
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s, ok := u.Value().(*sink)
 | 
			
		||||
	return s, ok
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func sinkUserData(s *sink) *rt.UserData {
 | 
			
		||||
	sinkMeta := l.Registry(sinkMetaKey)
 | 
			
		||||
	return rt.NewUserData(s, sinkMeta.AsTable())
 | 
			
		||||
}
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user