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 |       - name: Setup Go | ||||||
|         uses: actions/setup-go@v2 |         uses: actions/setup-go@v2 | ||||||
|         with: |         with: | ||||||
|           go-version: '1.17.7' |           go-version: '1.18.8' | ||||||
|       - name: Download Task |       - name: Download Task | ||||||
|         run: 'sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d' |         run: 'sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d' | ||||||
|       - name: Build |       - name: Build | ||||||
|  | |||||||
							
								
								
									
										5
									
								
								.github/workflows/docs.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.github/workflows/docs.yml
									
									
									
									
										vendored
									
									
								
							| @ -2,13 +2,14 @@ name: Generate docs | |||||||
| 
 | 
 | ||||||
| on: | on: | ||||||
|   push: |   push: | ||||||
|     branches: [master] |     branches: | ||||||
|  |       - master | ||||||
| 
 | 
 | ||||||
| jobs: | jobs: | ||||||
|   gen: |   gen: | ||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     steps: |     steps: | ||||||
|       - uses: actions/checkout@v2 |       - uses: actions/checkout@v3 | ||||||
|       - uses: actions/setup-go@v2 |       - uses: actions/setup-go@v2 | ||||||
|       - name: Run docgen |       - name: Run docgen | ||||||
|         run: go run cmd/docgen/docgen.go |         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 |     - uses: actions/checkout@v3 | ||||||
|       with: |       with: | ||||||
|         submodules: true |         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 |     - uses: wangyoucao577/go-release-action@v1.25 | ||||||
|       with: |       with: | ||||||
|         github_token: ${{ secrets.GITHUB_TOKEN }} |         github_token: ${{ secrets.GITHUB_TOKEN }} | ||||||
|         goos: ${{ matrix.goos }} |         goos: ${{ matrix.goos }} | ||||||
|         goarch: ${{ matrix.goarch }} |         goarch: ${{ matrix.goarch }} | ||||||
|  |         ldflags: '-s -w' | ||||||
|         binary_name: hilbish |         binary_name: hilbish | ||||||
|         extra_files: LICENSE README.md CHANGELOG.md .hilbishrc.lua nature libs docs emmyLuaDocs |         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 | *.exe | ||||||
| hilbish | hilbish | ||||||
|  | !docs/api/hilbish | ||||||
| docgen | docgen | ||||||
|  | !cmd/docgen | ||||||
| 
 | 
 | ||||||
| .vim | .vim | ||||||
| petals/ | petals/ | ||||||
|  | .hugo_build.lock | ||||||
|  | |||||||
| @ -1,18 +1,39 @@ | |||||||
| -- Default Hilbish config | -- Default Hilbish config | ||||||
|  | local hilbish = require 'hilbish' | ||||||
| local lunacolors = require 'lunacolors' | local lunacolors = require 'lunacolors' | ||||||
| local bait = require 'bait' | local bait = require 'bait' | ||||||
| local ansikit = require 'ansikit' | local ansikit = require 'ansikit' | ||||||
| 
 | 
 | ||||||
|  | local unreadCount = 0 | ||||||
|  | local running = false | ||||||
| local function doPrompt(fail) | local function doPrompt(fail) | ||||||
| 	hilbish.prompt(lunacolors.format( | 	hilbish.prompt(lunacolors.format( | ||||||
| 		'{blue}%u {cyan}%d ' .. (fail and '{red}' or '{green}') .. '∆ ' | 		'{blue}%u {cyan}%d ' .. (fail and '{red}' or '{green}') .. '∆ ' | ||||||
| 	)) | 	)) | ||||||
| end | 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() | doPrompt() | ||||||
| 
 | 
 | ||||||
|  | bait.catch('command.preexec', function() | ||||||
|  | 	running = true | ||||||
|  | end) | ||||||
|  | 
 | ||||||
| bait.catch('command.exit', function(code) | bait.catch('command.exit', function(code) | ||||||
|  | 	running = false | ||||||
| 	doPrompt(code ~= 0) | 	doPrompt(code ~= 0) | ||||||
|  | 	doNotifyPrompt() | ||||||
| end) | end) | ||||||
| 
 | 
 | ||||||
| bait.catch('hilbish.vimMode', function(mode) | bait.catch('hilbish.vimMode', function(mode) | ||||||
| @ -22,3 +43,7 @@ bait.catch('hilbish.vimMode', function(mode) | |||||||
| 		ansikit.cursorStyle(ansikit.lineCursor) | 		ansikit.cursorStyle(ansikit.lineCursor) | ||||||
| 	end | 	end | ||||||
| end) | end) | ||||||
|  | 
 | ||||||
|  | bait.catch('hilbish.notification', function(notif) | ||||||
|  | 	doNotifyPrompt() | ||||||
|  | end) | ||||||
|  | |||||||
							
								
								
									
										102
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										102
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @ -1,9 +1,70 @@ | |||||||
| # 🎀 Changelog | # 🎀 Changelog | ||||||
| 
 | 
 | ||||||
| ## Unreleased | ## Unreleased | ||||||
| **NOTE:** Hilbish now uses [Task] insead of Make for builds. | ### Added | ||||||
| Windows support is also now at a lower tier; The only thing guaranteed is | - Made a few additions to the sink type: | ||||||
| Hilbish *compiling* on Windows. |   - `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/#/ | [Task]: https://taskfile.dev/#/ | ||||||
| 
 | 
 | ||||||
| @ -41,7 +102,7 @@ without arguments will disown the last job. | |||||||
| fields on a job object. | fields on a job object. | ||||||
| - Documentation for jobs is now available via `doc jobs`. | - Documentation for jobs is now available via `doc jobs`. | ||||||
| - `hilbish.alias.resolve(cmdstr)` to resolve a command alias. | - `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.editor` interface for interacting with the line editor that | ||||||
| Hilbish uses. | Hilbish uses. | ||||||
| - `hilbish.vim` interface to dynamically get/set vim registers. | - `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]) | random errors introduced with the new Lua runtime (see [#197]) | ||||||
| - `bait.release(name, catcher)` removes `handler` for the named `event` | - `bait.release(name, catcher)` removes `handler` for the named `event` | ||||||
| - `exec`, `clear` and `cat` builtin commands | - `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 | [#197]: https://github.com/Rosettea/Hilbish/issues/197 | ||||||
| 
 | 
 | ||||||
| ### Changed | ### Changed | ||||||
| - **Breaking Change:** Upgraded to Lua 5.4. | - **Breaking Change:** Upgraded to Lua 5.4. | ||||||
| This is probably one of (if not the) biggest things in this release. | 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. | - **Breaking Change:** MacOS config paths now match Linux. | ||||||
| - Overrides on the `hilbish` table are no longer permitted. | - Overrides on the `hilbish` table are no longer permitted. | ||||||
| - **Breaking Change:** Runner functions are now required to return a table. | - **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. | - 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 | - **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. | 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` | - History is now fetched from Lua, which means users can override `hilbish.history` | ||||||
| methods to make it act how they want. | methods to make it act how they want. | ||||||
| - `guide` has been removed. See the [website](https://rosettea.github.io/Hilbish/) | - `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. | - `hilbish.login` being the wrong value. | ||||||
| - Put full input in history if prompted for continued input | - Put full input in history if prompted for continued input | ||||||
| - Don't put alias expanded command in history (sound familiar?) | - 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 prompt for continued input if non interactive | ||||||
| - Don't insert unhandled control keys. | - Don't insert unhandled control keys. | ||||||
| - Handle sh syntax error in alias | - 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. | - Home/End keys now go to the actual start/end of the input. | ||||||
| - Input getting cut off on enter in certain cases. | - Input getting cut off on enter in certain cases. | ||||||
| - Go to the next line properly if input reaches end of terminal width. | - 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)) | - 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 [#136](https://github.com/Rosettea/Hilbish/issues/136)) | - 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. | - `hilbish.which` now works with commanders and aliases. | ||||||
| - Background jobs no longer take stdin so they do not interfere with shell | - Background jobs no longer take stdin so they do not interfere with shell | ||||||
| input. | 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 | - Completions are fixed in cases where the query/line is an alias alone | ||||||
| where it can also resolve to the beginning of command names. | where it can also resolve to the beginning of command names. | ||||||
| (reference [this commit](https://github.com/Rosettea/Hilbish/commit/2790982ad123115c6ddbc5764677fdca27668cea)) | (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 | - Fix an error with sh syntax in aliases | ||||||
| - Prompt now works with east asian characters (CJK) | - Prompt now works with east asian characters (CJK) | ||||||
| - Set back the prompt to normal after exiting the continue prompt with ctrl-d | - 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 | ## [2.0.0-rc1] - 2022-09-14 | ||||||
| This is a pre-release version of Hilbish for testing. To see the changelog, | 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. | 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 | [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.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 | [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 | MIT License | ||||||
| 
 | 
 | ||||||
| Copyright (c) 2022 Rosettea | Copyright (c) 2021-2023 Rosettea | ||||||
| 
 | 
 | ||||||
| Permission is hereby granted, free of charge, to any person obtaining a copy | Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
| of this software and associated documentation files (the "Software"), to deal | of this software and associated documentation files (the "Software"), to deal | ||||||
|  | |||||||
							
								
								
									
										80
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										80
									
								
								README.md
									
									
									
									
									
								
							| @ -1,77 +1,45 @@ | |||||||
| <div align="center"> | <img src="./assets/hilbish-logo-and-text.png" width=512><br> | ||||||
| 	<img src="./assets/hilbish-flower.png" width=128><br> | <blockquote> | ||||||
| 	<img src="./assets/hilbish-text.png" width=256><br> | 🌓 The Moon-powered shell! A comfy and extensible shell for Lua fans! 🌺 ✨ | ||||||
| 	<blockquote> | </blockquote> | ||||||
| 	🌺 The flower shell. A comfy and nice little shell for Lua fans! |  | ||||||
| 	</blockquote> |  | ||||||
| 	<p align="center"> |  | ||||||
| 		<img alt="GitHub commit activity" src="https://img.shields.io/github/commit-activity/m/Rosettea/Hilbish?style=flat-square"> |  | ||||||
| 		<img alt="GitHub commits since latest release (by date)" src="https://img.shields.io/github/commits-since/Rosettea/Hilbish/latest?style=flat-square"> |  | ||||||
| 		<img alt="GitHub contributors" src="https://img.shields.io/github/contributors/Rosettea/Hilbish?style=flat-square"><br> |  | ||||||
| 		<a href="https://github.com/Rosettea/Hilbish/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22"><img src="https://img.shields.io/github/issues/Hilbis/Hilbish/help%20wanted?style=flat-square&color=green" alt="help wanted"></a> |  | ||||||
| 		<a href="https://github.com/Rosettea/Hilbish/blob/master/LICENSE"><img alt="GitHub license" src="https://img.shields.io/github/license/Rosettea/Hilbish?style=flat-square"></a> |  | ||||||
| 		<a href="https://discord.gg/3PDdcQz"><img alt="Discord" src="https://img.shields.io/discord/732357621503229962?color=blue&style=flat-square"></a> |  | ||||||
| 	</p> |  | ||||||
| </div> |  | ||||||
| 
 | 
 | ||||||
| Hilbish is a extensible shell (framework). It was made to be very customizable | <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> | ||||||
| via the Lua programming language. It aims to be easy to use for the casual | <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> | ||||||
| people but powerful for those who want to tinker more with their shell, | <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> | ||||||
| the thing used to interface with most of the system.   | <a href="https://discord.gg/3PDdcQz"><img alt="Discord" src="https://img.shields.io/discord/732357621503229962?color=blue&style=flat-square"></a> | ||||||
|  | <br> | ||||||
|  | 
 | ||||||
|  | Hilbish is an extensible shell designed to be highly customizable. | ||||||
|  | It is configured in Lua and provides a good range of features. | ||||||
|  | It aims to be easy to use for anyone but powerful enough for | ||||||
|  | those who need it. | ||||||
| 
 | 
 | ||||||
| The motivation for choosing Lua was that its simpler and better to use | 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, | 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 | 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! | 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 | # Screenshots | ||||||
| <div align="center"> | <div align="center"> | ||||||
| <img src="gallery/default.png"><br><br> | <img src="gallery/tab.png"> | ||||||
| <img src="gallery/terminal.png"><br><br> |  | ||||||
| <img src="gallery/pillprompt.png"> | <img src="gallery/pillprompt.png"> | ||||||
| </div> | </div> | ||||||
| 
 | 
 | ||||||
| # Installation | # Getting Hilbish | ||||||
| **NOTE:** Hilbish is not guaranteed to work properly on Windows, starting | **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 | 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 | You can check the [install page](https://rosettea.github.io/Hilbish/install/) | ||||||
| Go [here](https://nightly.link/Rosettea/Hilbish/workflows/build/master) for | on the website for distributed binaries from GitHub or other package repositories. | ||||||
| builds on the master branch. | Otherwise, continue reading for steps on compiling. | ||||||
| 
 | 
 | ||||||
| ## AUR | ## Prerequisites | ||||||
| [](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 |  | ||||||
| - [Go 1.17+](https://go.dev) | - [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 | First, clone Hilbish. The recursive is required, as some Lua libraries | ||||||
| are submodules.   | are submodules.   | ||||||
| ```sh | ```sh | ||||||
|  | |||||||
| @ -3,19 +3,20 @@ | |||||||
| version: '3' | version: '3' | ||||||
| 
 | 
 | ||||||
| vars: | vars: | ||||||
|   PREFIX: '{{default "/usr" .PREFIX}}' |   PREFIX: '{{default "/usr/local" .PREFIX}}' | ||||||
|   bindir__: '{{.PREFIX}}/bin' |   bindir__: '{{.PREFIX}}/bin' | ||||||
|   BINDIR: '{{default .bindir__ .BINDIR}}' |   BINDIR: '{{default .bindir__ .BINDIR}}' | ||||||
|   libdir__: '{{.PREFIX}}/share/hilbish' |   libdir__: '{{.PREFIX}}/share/hilbish' | ||||||
|   LIBDIR: '{{default .libdir__ .LIBDIR}}' |   LIBDIR: '{{default .libdir__ .LIBDIR}}' | ||||||
|   GOFLAGS: '-ldflags "-s -w"' |   goflags__: '-ldflags "-s -w -X main.dataDir={{.LIBDIR}}"' | ||||||
|  |   GOFLAGS: '{{default .goflags__ .GOFLAGS}}' | ||||||
| 
 | 
 | ||||||
| tasks: | tasks: | ||||||
|   default: |   default: | ||||||
|     cmds: |     cmds: | ||||||
|       - CGO_ENABLED=0 go build {{.GOFLAGS}} |       - CGO_ENABLED=0 go build {{.GOFLAGS}} | ||||||
|     vars: |     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: |   build: | ||||||
|     cmds: |     cmds: | ||||||
| @ -33,4 +34,4 @@ tasks: | |||||||
|       - rm -vrf |       - rm -vrf | ||||||
|         "{{.DESTDIR}}{{.BINDIR}}/hilbish" |         "{{.DESTDIR}}{{.BINDIR}}/hilbish" | ||||||
|         "{{.DESTDIR}}{{.LIBDIR}}" |         "{{.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 | package main | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"regexp" | ||||||
|  | 	"strconv" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"sync" | 	"sync" | ||||||
| 
 | 
 | ||||||
| @ -9,46 +11,64 @@ import ( | |||||||
| 	rt "github.com/arnodel/golua/runtime" | 	rt "github.com/arnodel/golua/runtime" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var aliases *aliasHandler | var aliases *aliasModule | ||||||
| 
 | 
 | ||||||
| type aliasHandler struct { | type aliasModule struct { | ||||||
| 	aliases map[string]string | 	aliases map[string]string | ||||||
| 	mu *sync.RWMutex | 	mu *sync.RWMutex | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // initialize aliases map | // initialize aliases map | ||||||
| func newAliases() *aliasHandler { | func newAliases() *aliasModule { | ||||||
| 	return &aliasHandler{ | 	return &aliasModule{ | ||||||
| 		aliases: make(map[string]string), | 		aliases: make(map[string]string), | ||||||
| 		mu: &sync.RWMutex{}, | 		mu: &sync.RWMutex{}, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a *aliasHandler) Add(alias, cmd string) { | func (a *aliasModule) Add(alias, cmd string) { | ||||||
| 	a.mu.Lock() | 	a.mu.Lock() | ||||||
| 	defer a.mu.Unlock() | 	defer a.mu.Unlock() | ||||||
| 
 | 
 | ||||||
| 	a.aliases[alias] = cmd | 	a.aliases[alias] = cmd | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a *aliasHandler) All() map[string]string { | func (a *aliasModule) All() map[string]string { | ||||||
| 	return a.aliases | 	return a.aliases | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a *aliasHandler) Delete(alias string) { | func (a *aliasModule) Delete(alias string) { | ||||||
| 	a.mu.Lock() | 	a.mu.Lock() | ||||||
| 	defer a.mu.Unlock() | 	defer a.mu.Unlock() | ||||||
| 
 | 
 | ||||||
| 	delete(a.aliases, alias) | 	delete(a.aliases, alias) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a *aliasHandler) Resolve(cmdstr string) string { | func (a *aliasModule) Resolve(cmdstr string) string { | ||||||
| 	a.mu.RLock() | 	a.mu.RLock() | ||||||
| 	defer a.mu.RUnlock() | 	defer a.mu.RUnlock() | ||||||
| 
 | 
 | ||||||
| 	args := strings.Split(cmdstr, " ") | 	arg, _ := regexp.Compile(`[\\]?%\d+`) | ||||||
|  | 
 | ||||||
|  | 	args, _ := splitInput(cmdstr) | ||||||
| 	for a.aliases[args[0]] != "" { | 	for a.aliases[args[0]] != "" { | ||||||
| 		alias := 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]) | 		cmdstr = alias + strings.TrimPrefix(cmdstr, args[0]) | ||||||
| 		cmdArgs, _ := splitInput(cmdstr) | 		cmdArgs, _ := splitInput(cmdstr) | ||||||
| 		args = cmdArgs | 		args = cmdArgs | ||||||
| @ -66,7 +86,10 @@ func (a *aliasHandler) Resolve(cmdstr string) string { | |||||||
| 
 | 
 | ||||||
| // lua section | // 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 | 	// create a lua module with our functions | ||||||
| 	hshaliasesLua := map[string]util.LuaExport{ | 	hshaliasesLua := map[string]util.LuaExport{ | ||||||
| 		"add": util.LuaExport{hlalias, 2, false}, | 		"add": util.LuaExport{hlalias, 2, false}, | ||||||
| @ -81,7 +104,18 @@ func (a *aliasHandler) Loader(rtm *rt.Runtime) *rt.Table { | |||||||
| 	return mod | 	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() | 	aliasesList := rt.NewTable() | ||||||
| 	for k, v := range a.All() { | 	for k, v := range a.All() { | ||||||
| 		aliasesList.Set(rt.StringValue(k), rt.StringValue(v)) | 		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 | 	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 { | 	if err := c.Check1Arg(); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @ -103,7 +141,12 @@ func (a *aliasHandler) luaDelete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | |||||||
| 	return c.Next(), nil | 	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 { | 	if err := c.Check1Arg(); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  | |||||||
							
								
								
									
										129
									
								
								api.go
									
									
									
									
									
								
							
							
						
						
									
										129
									
								
								api.go
									
									
									
									
									
								
							| @ -1,6 +1,15 @@ | |||||||
| // Here is the core api for the hilbi shell itself | // the core Hilbish API | ||||||
| // Basically, stuff about the shell itself and other functions | // The Hilbish module includes the core API, containing | ||||||
| // go here. | // 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 | package main | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| @ -19,7 +28,6 @@ import ( | |||||||
| 	rt "github.com/arnodel/golua/runtime" | 	rt "github.com/arnodel/golua/runtime" | ||||||
| 	"github.com/arnodel/golua/lib/packagelib" | 	"github.com/arnodel/golua/lib/packagelib" | ||||||
| 	"github.com/maxlandon/readline" | 	"github.com/maxlandon/readline" | ||||||
| 	"github.com/blackfireio/osinfo" |  | ||||||
| 	"mvdan.cc/sh/v3/interp" | 	"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 | 		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, "ver", rt.StringValue(getVersion())) | ||||||
| 	util.SetFieldProtected(fakeMod, mod, "user", rt.StringValue(username), "Username of user") | 	util.SetFieldProtected(fakeMod, mod, "goVersion", rt.StringValue(runtime.Version())) | ||||||
| 	util.SetFieldProtected(fakeMod, mod, "host", rt.StringValue(host), "Host name of the machine") | 	util.SetFieldProtected(fakeMod, mod, "user", rt.StringValue(username)) | ||||||
| 	util.SetFieldProtected(fakeMod, mod, "home", rt.StringValue(curuser.HomeDir), "Home directory of the user") | 	util.SetFieldProtected(fakeMod, mod, "host", rt.StringValue(host)) | ||||||
| 	util.SetFieldProtected(fakeMod, mod, "dataDir", rt.StringValue(dataDir), "Directory for Hilbish's data files") | 	util.SetFieldProtected(fakeMod, mod, "home", rt.StringValue(curuser.HomeDir)) | ||||||
| 	util.SetFieldProtected(fakeMod, mod, "interactive", rt.BoolValue(interactive), "If this is an interactive shell") | 	util.SetFieldProtected(fakeMod, mod, "dataDir", rt.StringValue(dataDir)) | ||||||
| 	util.SetFieldProtected(fakeMod, mod, "login", rt.BoolValue(login), "Whether this is a login shell") | 	util.SetFieldProtected(fakeMod, mod, "interactive", rt.BoolValue(interactive)) | ||||||
| 	util.SetFieldProtected(fakeMod, mod, "vimMode", rt.NilValue, "Current Vim mode of Hilbish (nil if not in Vim mode)") | 	util.SetFieldProtected(fakeMod, mod, "login", rt.BoolValue(login)) | ||||||
| 	util.SetFieldProtected(fakeMod, mod, "exitCode", rt.IntValue(0), "Exit code of last exected command") | 	util.SetFieldProtected(fakeMod, mod, "vimMode", rt.NilValue) | ||||||
| 	util.Document(fakeMod, "Hilbish's core API, containing submodules and functions which relate to the shell itself.") | 	util.SetFieldProtected(fakeMod, mod, "exitCode", rt.IntValue(0)) | ||||||
| 
 | 
 | ||||||
| 	// hilbish.userDir table | 	// hilbish.userDir table | ||||||
| 	hshuser := rt.NewTable() | 	hshuser := userDirLoader(rtm) | ||||||
| 
 |  | ||||||
| 	util.SetField(rtm, hshuser, "config", rt.StringValue(confDir), "User's config directory") |  | ||||||
| 	util.SetField(rtm, hshuser, "data", rt.StringValue(userDataDir), "XDG data directory") |  | ||||||
| 	util.Document(hshuser, "User directories to store configs and/or modules.") |  | ||||||
| 	mod.Set(rt.StringValue("userDir"), rt.TableValue(hshuser)) | 	mod.Set(rt.StringValue("userDir"), rt.TableValue(hshuser)) | ||||||
| 
 | 
 | ||||||
| 	// hilbish.os table | 	// hilbish.os table | ||||||
| 	hshos := rt.NewTable() | 	hshos := hshosLoader(rtm) | ||||||
| 	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") |  | ||||||
| 	mod.Set(rt.StringValue("os"), rt.TableValue(hshos)) | 	mod.Set(rt.StringValue("os"), rt.TableValue(hshos)) | ||||||
| 
 | 
 | ||||||
| 	// hilbish.aliases table | 	// hilbish.aliases table | ||||||
| 	aliases = newAliases() | 	aliases = newAliases() | ||||||
| 	aliasesModule := aliases.Loader(rtm) | 	aliasesModule := aliases.Loader(rtm) | ||||||
| 	util.Document(aliasesModule, "Alias inferface for Hilbish.") |  | ||||||
| 	mod.Set(rt.StringValue("aliases"), rt.TableValue(aliasesModule)) | 	mod.Set(rt.StringValue("aliases"), rt.TableValue(aliasesModule)) | ||||||
| 
 | 
 | ||||||
| 	// hilbish.history table | 	// hilbish.history table | ||||||
| 	historyModule := lr.Loader(rtm) | 	historyModule := lr.Loader(rtm) | ||||||
| 	mod.Set(rt.StringValue("history"), rt.TableValue(historyModule)) | 	mod.Set(rt.StringValue("history"), rt.TableValue(historyModule)) | ||||||
| 	util.Document(historyModule, "History interface for Hilbish.") |  | ||||||
| 
 | 
 | ||||||
| 	// hilbish.completion table | 	// hilbish.completion table | ||||||
| 	hshcomp := completionLoader(rtm) | 	hshcomp := completionLoader(rtm) | ||||||
| 	util.Document(hshcomp, "Completions interface for Hilbish.") |  | ||||||
| 	mod.Set(rt.StringValue("completion"), rt.TableValue(hshcomp)) | 	mod.Set(rt.StringValue("completion"), rt.TableValue(hshcomp)) | ||||||
| 
 | 
 | ||||||
| 	// hilbish.runner table | 	// hilbish.runner table | ||||||
| 	runnerModule := runnerModeLoader(rtm) | 	runnerModule := runnerModeLoader(rtm) | ||||||
| 	util.Document(runnerModule, "Runner/exec interface for Hilbish.") |  | ||||||
| 	mod.Set(rt.StringValue("runner"), rt.TableValue(runnerModule)) | 	mod.Set(rt.StringValue("runner"), rt.TableValue(runnerModule)) | ||||||
| 
 | 
 | ||||||
| 	// hilbish.jobs table | 	// hilbish.jobs table | ||||||
| 	jobs = newJobHandler() | 	jobs = newJobHandler() | ||||||
| 	jobModule := jobs.loader(rtm) | 	jobModule := jobs.loader(rtm) | ||||||
| 	util.Document(jobModule, "(Background) job interface.") |  | ||||||
| 	mod.Set(rt.StringValue("jobs"), rt.TableValue(jobModule)) | 	mod.Set(rt.StringValue("jobs"), rt.TableValue(jobModule)) | ||||||
| 
 | 
 | ||||||
| 	// hilbish.timers table | 	// hilbish.timers table | ||||||
| 	timers = newTimerHandler() | 	timers = newTimersModule() | ||||||
| 	timerModule := timers.loader(rtm) | 	timersModule := timers.loader(rtm) | ||||||
| 	util.Document(timerModule, "Timer interface, for control of all intervals and timeouts.") | 	mod.Set(rt.StringValue("timers"), rt.TableValue(timersModule)) | ||||||
| 	mod.Set(rt.StringValue("timers"), rt.TableValue(timerModule)) |  | ||||||
| 
 | 
 | ||||||
| 	editorModule := editorLoader(rtm) | 	editorModule := editorLoader(rtm) | ||||||
| 	util.Document(editorModule, "") |  | ||||||
| 	mod.Set(rt.StringValue("editor"), rt.TableValue(editorModule)) | 	mod.Set(rt.StringValue("editor"), rt.TableValue(editorModule)) | ||||||
| 
 | 
 | ||||||
| 	versionModule := rt.NewTable() | 	versionModule := rt.NewTable() | ||||||
| 	util.SetField(rtm, versionModule, "branch", rt.StringValue(gitBranch), "Git branch Hilbish was compiled from") | 	util.SetField(rtm, versionModule, "branch", rt.StringValue(gitBranch)) | ||||||
| 	util.SetField(rtm, versionModule, "full", rt.StringValue(getVersion()), "Full version info, including release name") | 	util.SetField(rtm, versionModule, "full", rt.StringValue(getVersion())) | ||||||
| 	util.SetField(rtm, versionModule, "commit", rt.StringValue(gitCommit), "Git commit Hilbish was compiled from") | 	util.SetField(rtm, versionModule, "commit", rt.StringValue(gitCommit)) | ||||||
| 	util.SetField(rtm, versionModule, "release", rt.StringValue(releaseName), "Release name") | 	util.SetField(rtm, versionModule, "release", rt.StringValue(releaseName)) | ||||||
| 	util.Document(versionModule, "Version info interface.") |  | ||||||
| 	mod.Set(rt.StringValue("version"), rt.TableValue(versionModule)) | 	mod.Set(rt.StringValue("version"), rt.TableValue(versionModule)) | ||||||
| 
 | 
 | ||||||
| 	return rt.TableValue(fakeMod), nil | 	return rt.TableValue(fakeMod), nil | ||||||
| @ -188,19 +178,21 @@ func getenv(key, fallback string) string { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func setVimMode(mode 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) | 	hooks.Emit("hilbish.vimMode", mode) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func unsetVimMode() { | 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. | // Runs `cmd` in Hilbish's sh interpreter. | ||||||
| // If returnOut is true, the outputs of `cmd` will be returned as the 2nd and | // If returnOut is true, the outputs of `cmd` will be returned as the 2nd and | ||||||
| // 3rd values instead of being outputted to the terminal. | // 3rd values instead of being outputted to the terminal. | ||||||
| // --- @param cmd string | // --- @param cmd string | ||||||
|  | // --- @param returnOut boolean | ||||||
|  | // --- @returns number, string, string | ||||||
| func hlrun(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | func hlrun(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	if err := c.Check1Arg(); err != nil { | 	if err := c.Check1Arg(); err != nil { | ||||||
| 		return nil, err | 		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 | 	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 the current directory of the shell | ||||||
|  | // --- @returns string | ||||||
| func hlcwd(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | func hlcwd(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	cwd, _ := os.Getwd() | 	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. | // Read input from the user, using Hilbish's line editor/input reader. | ||||||
| // This is a separate instance from the one Hilbish actually uses. | // 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) | // 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) { | func hlread(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	luaprompt := c.Arg(0) | 	luaprompt := c.Arg(0) | ||||||
| 	if typ := luaprompt.Type(); typ != rt.StringType && typ != rt.NilType { | 	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` | Changes the shell prompt to `str` | ||||||
| There are a few verbs that can be used in the prompt text. | There are a few verbs that can be used in the prompt text. | ||||||
| These will be formatted and replaced with the appropriate values. | 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 | `%u` - Name of current user | ||||||
| `%h` - Hostname of device | `%h` - Hostname of device | ||||||
| --- @param str string | --- @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) { | func hlprompt(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	err := c.Check1Arg() | 	err := c.Check1Arg() | ||||||
| @ -453,12 +447,12 @@ func hlgoro(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | |||||||
| 	return c.Next(), nil | 	return c.Next(), nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // timeout(cb, time) | // timeout(cb, time) -> @Timer | ||||||
| // Runs the `cb` function after `time` in milliseconds | // Runs the `cb` function after `time` in milliseconds. | ||||||
| // Returns a `timer` object (see `doc timers`). | // This creates a timer that starts immediately. | ||||||
| // --- @param cb function | // --- @param cb function | ||||||
| // --- @param time number | // --- @param time number | ||||||
| // --- @return table | // --- @returns Timer | ||||||
| func hltimeout(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | func hltimeout(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	if err := c.CheckNArgs(2); err != nil { | 	if err := c.CheckNArgs(2); err != nil { | ||||||
| 		return nil, err | 		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 | 	return c.PushingNext1(t.Runtime, rt.UserDataValue(timer.ud)), nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // interval(cb, time) | // interval(cb, time) -> @Timer | ||||||
| // Runs the `cb` function every `time` milliseconds. | // 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 cb function | ||||||
| // --- @param time number | // --- @param time number | ||||||
| // --- @return table | // --- @return Timer | ||||||
| func hlinterval(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | func hlinterval(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	if err := c.CheckNArgs(2); err != nil { | 	if err := c.CheckNArgs(2); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| @ -545,9 +539,11 @@ func hlprependPath(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | |||||||
| 	return c.Next(), nil | 	return c.Next(), nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // which(name) | // which(name) -> string | ||||||
| // Checks if `name` is a valid command | // Checks if `name` is a valid command. | ||||||
| // --- @param binName string | // 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) { | func hlwhich(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	if err := c.Check1Arg(); err != nil { | 	if err := c.Check1Arg(); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| @ -557,7 +553,10 @@ func hlwhich(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | |||||||
| 		return nil, err | 		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 | 	// check for commander | ||||||
| 	if commands[cmd] != nil { | 	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, | // as the text for the hint. This is by default a shim. To set hints, | ||||||
| // override this function with your custom handler. | // override this function with your custom handler. | ||||||
| // --- @param line string | // --- @param line string | ||||||
| // --- @param pos int | // --- @param pos number | ||||||
| func hlhinter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | func hlhinter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	return c.Next(), nil | 	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 | // 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 | // callback is passed the current line and is expected to return a line that | ||||||
| // will be used as the input display. | // 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 | // --- @param line string | ||||||
| func hlhighlighter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | func hlhighlighter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	return c.Next(), nil | 	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,28 +7,266 @@ import ( | |||||||
| 	"go/doc" | 	"go/doc" | ||||||
| 	"go/parser" | 	"go/parser" | ||||||
| 	"go/token" | 	"go/token" | ||||||
|  | 	"regexp" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"os" | 	"os" | ||||||
|  | 	"sync" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type EmmyPiece struct { | var header = `--- | ||||||
| 	FuncName string | title: %s %s | ||||||
| 	Docs []string | 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 | 	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 | 	Doc []string | ||||||
| 	FuncSig string | 	FuncSig string | ||||||
| 	FuncName 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() { | func main() { | ||||||
| 	fset := token.NewFileSet() | 	fset := token.NewFileSet() | ||||||
| 	os.Mkdir("docs", 0777) | 	os.Mkdir("docs", 0777) | ||||||
|  | 	os.Mkdir("docs/api", 0777) | ||||||
| 	os.Mkdir("emmyLuaDocs", 0777) | 	os.Mkdir("emmyLuaDocs", 0777) | ||||||
| 	 |  | ||||||
| 
 | 
 | ||||||
| 	dirs := []string{"./"} | 	dirs := []string{"./"} | ||||||
| 	filepath.Walk("golibs/", func (path string, info os.FileInfo, err error) error { | 	filepath.Walk("golibs/", func (path string, info os.FileInfo, err error) error { | ||||||
| @ -51,120 +289,266 @@ func main() { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	prefix := map[string]string{ | 	interfaceModules := make(map[string]*module) | ||||||
| 		"hilbish": "hl", |  | ||||||
| 		"fs": "f", |  | ||||||
| 		"commander": "c", |  | ||||||
| 		"bait": "b", |  | ||||||
| 		"terminal": "term", |  | ||||||
| 	} |  | ||||||
| 	docs := make(map[string][]DocPiece) |  | ||||||
| 	emmyDocs := make(map[string][]EmmyPiece) |  | ||||||
| 
 |  | ||||||
| 	for l, f := range pkgs { | 	for l, f := range pkgs { | ||||||
| 		p := doc.New(f, "./", doc.AllDecls) | 		p := doc.New(f, "./", doc.AllDecls) | ||||||
|  | 		pieces := []docPiece{} | ||||||
|  | 		typePieces := []docPiece{} | ||||||
|  | 		mod := l | ||||||
|  | 		if mod == "main" { | ||||||
|  | 			mod = "hilbish" | ||||||
|  | 		} | ||||||
|  | 		var hasInterfaces bool | ||||||
| 		for _, t := range p.Funcs { | 		for _, t := range p.Funcs { | ||||||
| 			mod := l | 			piece := setupDoc(mod, t) | ||||||
| 			if strings.HasPrefix(t.Name, "hl") { mod = "hilbish" } | 			if piece == nil { | ||||||
| 			if !strings.HasPrefix(t.Name, prefix[mod]) || t.Name == "Loader" { continue } | 				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) |  | ||||||
| 				} |  | ||||||
| 			} | 			} | ||||||
| 			 | 
 | ||||||
| 			dps := DocPiece{ | 			pieces = append(pieces, *piece) | ||||||
| 				Doc: funcdoc, | 			if piece.IsInterface { | ||||||
| 				FuncSig: funcsig, | 				hasInterfaces = true | ||||||
| 				FuncName: strings.TrimPrefix(t.Name, prefix[mod]), |  | ||||||
| 			} | 			} | ||||||
| 			 |  | ||||||
| 			docs[mod] = append(docs[mod], dps) |  | ||||||
| 			emmyDocs[mod] = append(emmyDocs[mod], em) |  | ||||||
| 		} | 		} | ||||||
| 		for _, t := range p.Types { | 		for _, t := range p.Types { | ||||||
| 			for _, m := range t.Methods { | 			typePiece := setupDocType(mod, t) | ||||||
| 				if !strings.HasPrefix(m.Name, prefix[l]) || m.Name == "Loader" { continue } | 			if typePiece != nil { | ||||||
| 				parts := strings.Split(strings.TrimSpace(m.Doc), "\n") | 				typePieces = append(typePieces, *typePiece) | ||||||
| 				funcsig := parts[0] | 				if typePiece.IsInterface { | ||||||
| 				doc := parts[1:] | 					hasInterfaces = true | ||||||
| 				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]) |  | ||||||
| 						} |  | ||||||
| 						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, | 			for _, m := range t.Methods { | ||||||
| 					FuncName: strings.TrimPrefix(m.Name, prefix[l]), | 				piece := setupDoc(mod, m) | ||||||
|  | 				if piece == nil { | ||||||
|  | 					continue | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
| 				docs[l] = append(docs[l], dps) | 				pieces = append(pieces, *piece) | ||||||
| 				emmyDocs[l] = append(emmyDocs[l], em) | 				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 { | 	for mod, v := range docs { | ||||||
| 		if mod == "main" { continue } | 		docPath := "docs/api/" + mod + ".md" | ||||||
| 		f, _ := os.Create("docs/" + mod + ".txt") | 		if v.HasInterfaces { | ||||||
| 		for _, dps := range v { | 			os.Mkdir("docs/api/" + mod, 0777) | ||||||
| 			f.WriteString(dps.FuncSig + " > ") | 			os.Remove(docPath) // remove old doc path if it exists | ||||||
| 			for _, doc := range dps.Doc { | 			docPath = "docs/api/" + mod + "/_index.md" | ||||||
| 				if !strings.HasPrefix(doc, "---") { | 		} | ||||||
| 					f.WriteString(doc + "\n") | 		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") | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 					f.WriteString("\n") | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 			f.WriteString("\n") | 
 | ||||||
| 		} | 			if len(modu.Types) != 0 { | ||||||
| 	} | 				f.WriteString("## Types\n") | ||||||
| 	 | 				for _, dps := range modu.Types { | ||||||
| 	for mod, v := range emmyDocs { | 					f.WriteString(fmt.Sprintf("## %s\n", dps.FuncName)) | ||||||
| 		if mod == "main" { continue } | 					for _, doc := range dps.Doc { | ||||||
| 		f, _ := os.Create("emmyLuaDocs/" + mod + ".lua") | 						if !strings.HasPrefix(doc, "---") { | ||||||
| 		f.WriteString("--- @meta\n\nlocal " + mod + " = {}\n\n") | 							f.WriteString(doc + "\n") | ||||||
| 		for _, em := range v { | 						} | ||||||
| 			var funcdocs []string | 					} | ||||||
| 			for _, dps := range docs[mod] { | 					if len(dps.Properties) != 0 { | ||||||
| 				if dps.FuncName == em.FuncName { | 						f.WriteString("### Properties\n") | ||||||
| 					funcdocs = dps.Doc | 						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") | ||||||
|  | 					} | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 			f.WriteString("--- " + strings.Join(funcdocs, "\n--- ") + "\n") | 		}(mod, docPath, v) | ||||||
| 			if len(em.Docs) != 0 { | 
 | ||||||
| 				f.WriteString(strings.Join(em.Docs, "\n") + "\n") | 		go func(md, modname string, modu module) { | ||||||
|  | 			defer wg.Done() | ||||||
|  | 
 | ||||||
|  | 			if modu.ParentModule != "" { | ||||||
|  | 				return | ||||||
| 			} | 			} | ||||||
| 			f.WriteString("function " + mod + "." + em.FuncName + "(" + strings.Join(em.Params, ", ") + ") end\n\n") | 
 | ||||||
| 		} | 			ff, _ := os.Create("emmyLuaDocs/" + modname + ".lua") | ||||||
| 		f.WriteString("return " + mod + "\n") | 			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" | 	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{} | 	split := []string{} | ||||||
| 	sb := &strings.Builder{} | 	sb := &strings.Builder{} | ||||||
| 	quoted := false | 	quoted := false | ||||||
| 
 | 
 | ||||||
| 	for _, r := range str { | 	for i, r := range str { | ||||||
| 		if r == '"' { | 		if r == '"' { | ||||||
| 			quoted = !quoted | 			quoted = !quoted | ||||||
| 			sb.WriteRune(r) | 			sb.WriteRune(r) | ||||||
|  | 		} else if r == ' ' && str[i - 1] == '\\' { | ||||||
|  | 			sb.WriteRune(r) | ||||||
| 		} else if !quoted && r == ' ' { | 		} else if !quoted && r == ' ' { | ||||||
| 			split = append(split, sb.String()) | 			split = append(split, sb.String()) | ||||||
| 			sb.Reset() | 			sb.Reset() | ||||||
| @ -39,12 +73,22 @@ func splitQuote(str string) []string { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func fileComplete(query, ctx string, fields []string) ([]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) { | 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 | 	var completions []string | ||||||
| 
 | 
 | ||||||
| 	prefixes := []string{"./", "../", "/", "~/"} | 	prefixes := []string{"./", "../", "/", "~/"} | ||||||
| @ -54,7 +98,7 @@ func binaryComplete(query, ctx string, fields []string) ([]string, string) { | |||||||
| 			if len(fileCompletions) != 0 { | 			if len(fileCompletions) != 0 { | ||||||
| 				for _, f := range fileCompletions { | 				for _, f := range fileCompletions { | ||||||
| 					fullPath, _ := filepath.Abs(util.ExpandHome(query + strings.TrimPrefix(f, filePref))) | 					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 | 						continue | ||||||
| 					} | 					} | ||||||
| 					completions = append(completions, f) | 					completions = append(completions, f) | ||||||
| @ -66,7 +110,6 @@ func binaryComplete(query, ctx string, fields []string) ([]string, string) { | |||||||
| 
 | 
 | ||||||
| 	// filter out executables, but in path | 	// filter out executables, but in path | ||||||
| 	for _, dir := range filepath.SplitList(os.Getenv("PATH")) { | 	for _, dir := range filepath.SplitList(os.Getenv("PATH")) { | ||||||
| 		// print dir to stderr for debugging |  | ||||||
| 		// search for an executable which matches our query string | 		// search for an executable which matches our query string | ||||||
| 		if matches, err := filepath.Glob(filepath.Join(dir, query + "*")); err == nil { | 		if matches, err := filepath.Glob(filepath.Join(dir, query + "*")); err == nil { | ||||||
| 			// get basename from matches | 			// get basename from matches | ||||||
| @ -102,6 +145,7 @@ func matchPath(query string) ([]string, string) { | |||||||
| 	var entries []string | 	var entries []string | ||||||
| 	var baseName string | 	var baseName string | ||||||
| 
 | 
 | ||||||
|  | 	query = escapeInvertReplaer.Replace(query) | ||||||
| 	path, _ := filepath.Abs(util.ExpandHome(filepath.Dir(query))) | 	path, _ := filepath.Abs(util.ExpandHome(filepath.Dir(query))) | ||||||
| 	if string(query) == "" { | 	if string(query) == "" { | ||||||
| 		// filepath base below would give us "." | 		// filepath base below would give us "." | ||||||
| @ -112,7 +156,16 @@ func matchPath(query string) ([]string, string) { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	files, _ := os.ReadDir(path) | 	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) { | 		if strings.HasPrefix(file.Name(), baseName) { | ||||||
| 			entry := file.Name() | 			entry := file.Name() | ||||||
| 			if file.IsDir() { | 			if file.IsDir() { | ||||||
| @ -124,32 +177,20 @@ func matchPath(query string) ([]string, string) { | |||||||
| 			entries = append(entries, entry) | 			entries = append(entries, entry) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | 	if !strings.HasPrefix(oldQuery, "\"") { | ||||||
|  | 		baseName = escapeFilename(baseName) | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	return entries, baseName | 	return entries, baseName | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func escapeFilename(fname string) string { | func escapeFilename(fname string) string { | ||||||
| 	args := []string{ | 	return escapeReplaer.Replace(fname) | ||||||
| 		"\"", "\\\"", |  | ||||||
| 		"'", "\\'", |  | ||||||
| 		"`", "\\`", |  | ||||||
| 		" ", "\\ ", |  | ||||||
| 		"(", "\\(", |  | ||||||
| 		")", "\\)", |  | ||||||
| 		"[", "\\[", |  | ||||||
| 		"]", "\\]", |  | ||||||
| 		"$", "\\$", |  | ||||||
| 		"&", "\\&", |  | ||||||
| 		"*", "\\*", |  | ||||||
| 		">", "\\>", |  | ||||||
| 		"<", "\\<", |  | ||||||
| 		"|", "\\|", |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	r := strings.NewReplacer(args...) |  | ||||||
| 	return r.Replace(fname) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // #interface completions | ||||||
|  | // tab completions | ||||||
|  | // The completions interface deals with tab completions. | ||||||
| func completionLoader(rtm *rt.Runtime) *rt.Table { | func completionLoader(rtm *rt.Runtime) *rt.Table { | ||||||
| 	exports := map[string]util.LuaExport{ | 	exports := map[string]util.LuaExport{ | ||||||
| 		"files": {luaFileComplete, 3, false}, | 		"files": {luaFileComplete, 3, false}, | ||||||
| @ -164,11 +205,26 @@ func completionLoader(rtm *rt.Runtime) *rt.Table { | |||||||
| 	return mod | 	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) { | func completionHandler(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	return c.Next(), nil | 	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) { | func callLuaCompleter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	if err := c.CheckNArgs(4); err != nil { | 	if err := c.CheckNArgs(4); err != nil { | ||||||
| 		return nil, err | 		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 | 	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) { | func luaFileComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	query, ctx, fds, err := getCompleteParams(t, c) | 	query, ctx, fds, err := getCompleteParams(t, c) | ||||||
| 	if err != nil { | 	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 | 	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) { | func luaBinaryComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	query, ctx, fds, err := getCompleteParams(t, c) | 	query, ctx, fds, err := getCompleteParams(t, c) | ||||||
| 	if err != nil { | 	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 | + `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. | 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. | These are the "low level" functions for the `hilbish.runner` interface. | ||||||
| 
 | 
 | ||||||
| + setMode(mode) > The same as `hilbish.runnerMode` | + setMode(mode) > The same as `hilbish.runnerMode` | ||||||
| + sh(input) -> input, code, err > Runs `input` in Hilbish's sh interpreter | + sh(input) -> table > Runs `input` in Hilbish's sh interpreter | ||||||
| + lua(input) -> input, code, err > Evals `input` as Lua code | + 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. | The others here are defined in Lua and have EmmyLua documentation. | ||||||
| These functions should be preferred over the previous ones. | 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" | 	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 { | func editorLoader(rtm *rt.Runtime) *rt.Table { | ||||||
| 	exports := map[string]util.LuaExport{ | 	exports := map[string]util.LuaExport{ | ||||||
| 		"insert": {editorInsert, 1, false}, | 		"insert": {editorInsert, 1, false}, | ||||||
| @ -20,6 +24,9 @@ func editorLoader(rtm *rt.Runtime) *rt.Table { | |||||||
| 	return mod | 	return mod | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // #interface editor | ||||||
|  | // insert(text) | ||||||
|  | // Inserts text into the line. | ||||||
| func editorInsert(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | func editorInsert(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	if err := c.Check1Arg(); err != nil { | 	if err := c.Check1Arg(); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| @ -35,6 +42,11 @@ func editorInsert(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | |||||||
| 	return c.Next(), nil | 	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) { | func editorSetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	if err := c.Check1Arg(); err != nil { | 	if err := c.Check1Arg(); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| @ -55,6 +67,10 @@ func editorSetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | |||||||
| 	return c.Next(), nil | 	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) { | func editorGetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	if err := c.Check1Arg(); err != nil { | 	if err := c.Check1Arg(); err != nil { | ||||||
| 		return nil, err | 		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 | 	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) { | func editorGetLine(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	buf := lr.rl.GetLine() | 	buf := lr.rl.GetLine() | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -12,17 +12,21 @@ function bait.catch(name, cb) end | |||||||
| --- @param cb function | --- @param cb function | ||||||
| function bait.catchOnce(name, cb) end | function bait.catchOnce(name, cb) end | ||||||
| 
 | 
 | ||||||
| --- Returns a table with hooks on the event with `name`. | --- Returns a table with hooks (callback functions) on the event with `name`. | ||||||
| function bait.hooks() end | --- @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 | --- For this to work, `catcher` has to be the same function used to catch | ||||||
| --- an event, like one saved to a variable. | --- 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` | --- Throws a hook with `name` with the provided `args` | ||||||
| --- @param name string | --- @param name string | ||||||
| --- @vararg any | --- @vararg any | ||||||
| function bait.throw(name, ...) end | function bait.throw(name, ...args) end | ||||||
| 
 | 
 | ||||||
| return bait | return bait | ||||||
|  | |||||||
| @ -4,11 +4,13 @@ local fs = {} | |||||||
| 
 | 
 | ||||||
| --- Gives an absolute version of `path`. | --- Gives an absolute version of `path`. | ||||||
| --- @param path string | --- @param path string | ||||||
|  | --- @returns string | ||||||
| function fs.abs(path) end | function fs.abs(path) end | ||||||
| 
 | 
 | ||||||
| --- Gives the basename of `path`. For the rules, | --- Gives the basename of `path`. For the rules, | ||||||
| --- see Go's filepath.Base | --- see Go's filepath.Base | ||||||
| function fs.basename() end | --- @returns string | ||||||
|  | function fs.basename(path) end | ||||||
| 
 | 
 | ||||||
| --- Changes directory to `dir` | --- Changes directory to `dir` | ||||||
| --- @param dir string | --- @param dir string | ||||||
| @ -16,28 +18,40 @@ function fs.cd(dir) end | |||||||
| 
 | 
 | ||||||
| --- Returns the directory part of `path`. For the rules, see Go's | --- Returns the directory part of `path`. For the rules, see Go's | ||||||
| --- filepath.Dir | --- 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. | --- Glob all files and directories that match the pattern. | ||||||
| --- For the rules, see Go's filepath.Glob | --- 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 | --- Takes paths and joins them together with the OS's | ||||||
| --- directory separator (forward or backward slash). | --- 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. | --- Makes a directory called `name`. If `recursive` is true, it will create its parent directories. | ||||||
| --- @param name string | --- @param name string | ||||||
| --- @param recursive boolean | --- @param recursive boolean | ||||||
| function fs.mkdir(name, recursive) end | function fs.mkdir(name, recursive) end | ||||||
| 
 | 
 | ||||||
| --- Returns a table of files in `dir` | --- Returns a table of files in `dir`. | ||||||
| --- @param dir string | --- @param dir string | ||||||
| --- @return table | --- @return table | ||||||
| function fs.readdir(dir) end | 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 | --- @param path string | ||||||
|  | --- @returns table | ||||||
| function fs.stat(path) end | function fs.stat(path) end | ||||||
| 
 | 
 | ||||||
| return fs | return fs | ||||||
|  | |||||||
| @ -2,6 +2,49 @@ | |||||||
| 
 | 
 | ||||||
| local hilbish = {} | 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` | --- Sets an alias of `cmd` to `orig` | ||||||
| --- @param cmd string | --- @param cmd string | ||||||
| --- @param orig string | --- @param orig string | ||||||
| @ -21,6 +64,7 @@ function hilbish.appendPath(dir) end | |||||||
| function hilbish.complete(scope, cb) end | function hilbish.complete(scope, cb) end | ||||||
| 
 | 
 | ||||||
| --- Returns the current directory of the shell | --- Returns the current directory of the shell | ||||||
|  | --- @returns string | ||||||
| function hilbish.cwd() end | function hilbish.cwd() end | ||||||
| 
 | 
 | ||||||
| --- Replaces running hilbish with `cmd` | --- 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 | --- 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 | --- callback is passed the current line and is expected to return a line that | ||||||
| --- will be used as the input display. | --- 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 | --- @param line string | ||||||
| function hilbish.highlighter(line) end | 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, | --- as the text for the hint. This is by default a shim. To set hints, | ||||||
| --- override this function with your custom handler. | --- override this function with your custom handler. | ||||||
| --- @param line string | --- @param line string | ||||||
| --- @param pos int | --- @param pos number | ||||||
| function hilbish.hinter(line, pos) end | function hilbish.hinter(line, pos) end | ||||||
| 
 | 
 | ||||||
| --- Sets the input mode for Hilbish's line reader. Accepts either emacs or vim | --- 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 | function hilbish.inputMode(mode) end | ||||||
| 
 | 
 | ||||||
| --- Runs the `cb` function every `time` milliseconds. | --- 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 cb function | ||||||
| --- @param time number | --- @param time number | ||||||
| --- @return table | --- @return Timer | ||||||
| function hilbish.interval(cb, time) end | function hilbish.interval(cb, time) end | ||||||
| 
 | 
 | ||||||
| --- Changes the continued line prompt to `str` | --- Changes the continued line prompt to `str` | ||||||
| @ -73,20 +125,23 @@ function hilbish.prependPath(dir) end | |||||||
| --- `%u` - Name of current user | --- `%u` - Name of current user | ||||||
| --- `%h` - Hostname of device | --- `%h` - Hostname of device | ||||||
| --- @param str string | --- @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 | function hilbish.prompt(str, typ) end | ||||||
| 
 | 
 | ||||||
| --- Read input from the user, using Hilbish's line editor/input reader. | --- Read input from the user, using Hilbish's line editor/input reader. | ||||||
| --- This is a separate instance from the one Hilbish actually uses. | --- 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) | --- 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 | function hilbish.read(prompt) end | ||||||
| 
 | 
 | ||||||
| --- Runs `cmd` in Hilbish's sh interpreter. | --- Runs `cmd` in Hilbish's sh interpreter. | ||||||
| --- If returnOut is true, the outputs of `cmd` will be returned as the 2nd and | --- If returnOut is true, the outputs of `cmd` will be returned as the 2nd and | ||||||
| --- 3rd values instead of being outputted to the terminal. | --- 3rd values instead of being outputted to the terminal. | ||||||
| --- @param cmd string | --- @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 | --- 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. | --- 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 | --- @param mode string|function | ||||||
| function hilbish.runnerMode(mode) end | function hilbish.runnerMode(mode) end | ||||||
| 
 | 
 | ||||||
| --- Runs the `cb` function after `time` in milliseconds | --- Runs the `cb` function after `time` in milliseconds. | ||||||
| --- Returns a `timer` object (see `doc timers`). | --- This creates a timer that starts immediately. | ||||||
| --- @param cb function | --- @param cb function | ||||||
| --- @param time number | --- @param time number | ||||||
| --- @return table | --- @returns Timer | ||||||
| function hilbish.timeout(cb, time) end | function hilbish.timeout(cb, time) end | ||||||
| 
 | 
 | ||||||
| --- Checks if `name` is a valid command | --- Checks if `name` is a valid command. | ||||||
| --- @param binName string | --- Will return the path of the binary, or a basename if it's a commander. | ||||||
| function hilbish.which(binName) end | --- @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 | return hilbish | ||||||
|  | |||||||
							
								
								
									
										19
									
								
								exec.go
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								exec.go
									
									
									
									
									
								
							| @ -141,9 +141,9 @@ func runInput(input string, priv bool) { | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if exErr, ok := isExecError(err); ok { | 		if exErr, ok := isExecError(err); ok { | ||||||
| 			hooks.Emit("command." + exErr.typ, exErr.cmd) | 			hooks.Emit("command." + exErr.typ, exErr.cmd) | ||||||
| 			err = exErr.sprint() | 		} else { | ||||||
|  | 			fmt.Fprintln(os.Stderr, err) | ||||||
| 		} | 		} | ||||||
| 		fmt.Fprintln(os.Stderr, err) |  | ||||||
| 	} | 	} | ||||||
| 	cmdFinish(exitCode, input, priv) | 	cmdFinish(exitCode, input, priv) | ||||||
| } | } | ||||||
| @ -321,8 +321,18 @@ func execHandle(bg bool) interp.ExecHandlerFunc { | |||||||
| 			luacmdArgs.Set(rt.IntValue(int64(i + 1)), rt.StringValue(str)) | 			luacmdArgs.Set(rt.IntValue(int64(i + 1)), rt.StringValue(str)) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		hc := interp.HandlerCtx(ctx) | ||||||
| 		if commands[args[0]] != nil { | 		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 { | 			if err != nil { | ||||||
| 				fmt.Fprintln(os.Stderr, "Error in command:\n" + err.Error()) | 				fmt.Fprintln(os.Stderr, "Error in command:\n" + err.Error()) | ||||||
| 				return interp.NewExitStatus(1) | 				return interp.NewExitStatus(1) | ||||||
| @ -362,7 +372,6 @@ func execHandle(bg bool) interp.ExecHandlerFunc { | |||||||
| 		killTimeout := 2 * time.Second | 		killTimeout := 2 * time.Second | ||||||
| 		// from here is basically copy-paste of the default exec handler from | 		// from here is basically copy-paste of the default exec handler from | ||||||
| 		// sh/interp but with our job handling | 		// sh/interp but with our job handling | ||||||
| 		hc := interp.HandlerCtx(ctx) |  | ||||||
| 		path, err := interp.LookPathDir(hc.Dir, hc.Env, args[0]) | 		path, err := interp.LookPathDir(hc.Dir, hc.Env, args[0]) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			fmt.Fprintln(hc.Stderr, err) | 			fmt.Fprintln(hc.Stderr, err) | ||||||
| @ -550,7 +559,7 @@ func splitInput(input string) ([]string, string) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func cmdFinish(code uint8, cmdstr string, private bool) { | 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 | 	// using AsValue (to convert to lua type) on an interface which is an int | ||||||
| 	// results in it being unknown in lua .... ???? | 	// results in it being unknown in lua .... ???? | ||||||
| 	// so we allow the hook handler to take lua runtime Values | 	// 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/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9 | ||||||
| 	github.com/maxlandon/readline v0.1.0-beta.0.20211027085530-2b76cabb8036 | 	github.com/maxlandon/readline v0.1.0-beta.0.20211027085530-2b76cabb8036 | ||||||
| 	github.com/pborman/getopt v1.1.0 | 	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/sys v0.0.0-20220520151302-bc2c85ada10a | ||||||
| 	golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 | 	golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 | ||||||
| 	mvdan.cc/sh/v3 v3.5.1 | 	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 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-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 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-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 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.20220306140409-795a84b00b4e/go.mod h1:R09vh/04ILvP2Gj8/Z9Jd0Dh0ZIvaucowMEs6abQpWs= | ||||||
| github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220524215627-dfd9a4fa219b h1:s5eDMhBk6H1BgipgLub/gv9qeyBaTuiHM0k3h2/9TSE= | github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220524215627-dfd9a4fa219b 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.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 h1:d1PiN4RxzIFXCJTvRkvSkKqwtRAl5ZV4lATKtQI0B7I= | ||||||
| github.com/rogpeppe/go-internal v1.8.1-0.20210923151022-86f73c517451/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= | 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 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-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||||
| golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 h1:w8s32wxx3sY+OjLlv9qltkLU5yvJzxjjgiHWLjdIcw4= | golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 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 | package bait | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| @ -198,15 +204,6 @@ func (b *Bait) loaderFunc(rtm *rt.Runtime) (rt.Value, func()) { | |||||||
| 	mod := rt.NewTable() | 	mod := rt.NewTable() | ||||||
| 	util.SetExports(rtm, mod, exports) | 	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 | 	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) | // 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 | // For this to work, `catcher` has to be the same function used to catch | ||||||
| // an event, like one saved to a variable. | // 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) { | func (b *Bait) brelease(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	name, catcher, err := util.HandleStrCallback(t, c) | 	name, catcher, err := util.HandleStrCallback(t, c) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @ -297,8 +296,10 @@ func (b *Bait) brelease(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | |||||||
| 	return c.Next(), nil | 	return c.Next(), nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // hooks(name) -> {cb, cb...} | // hooks(name) -> table | ||||||
| // Returns a table with hooks on the event with `name`. | // 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) { | func (b *Bait) bhooks(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	if err := c.Check1Arg(); err != nil { | 	if err := c.Check1Arg(); err != nil { | ||||||
| 		return nil, err | 		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 | package commander | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| @ -32,7 +62,6 @@ func (c *Commander) loaderFunc(rtm *rt.Runtime) (rt.Value, func()) { | |||||||
| 	} | 	} | ||||||
| 	mod := rt.NewTable() | 	mod := rt.NewTable() | ||||||
| 	util.SetExports(rtm, mod, exports) | 	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 | 	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 | package fs | ||||||
| 
 | 
 | ||||||
| import ( | 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("pathSep"), rt.StringValue(string(os.PathSeparator))) | ||||||
| 	mod.Set(rt.StringValue("pathListSep"), rt.StringValue(string(os.PathListSeparator))) | 	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 | 	return rt.TableValue(mod), nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -93,9 +93,15 @@ func fmkdir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | |||||||
| 	return c.Next(), err | 	return c.Next(), err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // stat(path) | // stat(path) -> {} | ||||||
| // 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 | // --- @param path string | ||||||
|  | // --- @returns table | ||||||
| func fstat(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | func fstat(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	if err := c.Check1Arg(); err != nil { | 	if err := c.Check1Arg(); err != nil { | ||||||
| 		return nil, err | 		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 | 	return c.PushingNext1(t.Runtime, rt.TableValue(statTbl)), nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // readdir(dir) | // readdir(dir) -> {} | ||||||
| // Returns a table of files in `dir` | // Returns a table of files in `dir`. | ||||||
| // --- @param dir string | // --- @param dir string | ||||||
| // --- @return table | // --- @return table | ||||||
| func freaddir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | 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 | 	return c.PushingNext1(t.Runtime, rt.TableValue(names)), nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // abs(path) | // abs(path) -> string | ||||||
| // Gives an absolute version of `path`. | // Gives an absolute version of `path`. | ||||||
| // --- @param path string | // --- @param path string | ||||||
|  | // --- @returns string | ||||||
| func fabs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | func fabs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	path, err := c.StringArg(0) | 	path, err := c.StringArg(0) | ||||||
| 	if err != nil { | 	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 | 	return c.PushingNext1(t.Runtime, rt.StringValue(abspath)), nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // basename(path) | // basename(path) -> string | ||||||
| // Gives the basename of `path`. For the rules, | // Gives the basename of `path`. For the rules, | ||||||
| // see Go's filepath.Base | // see Go's filepath.Base | ||||||
|  | // --- @returns string | ||||||
| func fbasename(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | func fbasename(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	if err := c.Check1Arg(); err != nil { | 	if err := c.Check1Arg(); err != nil { | ||||||
| 		return nil, err | 		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 | 	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 | // Returns the directory part of `path`. For the rules, see Go's | ||||||
| // filepath.Dir | // filepath.Dir | ||||||
|  | // --- @param path string | ||||||
|  | // --- @returns string | ||||||
| func fdir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | func fdir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	if err := c.Check1Arg(); err != nil { | 	if err := c.Check1Arg(); err != nil { | ||||||
| 		return nil, err | 		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 | 	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. | // Glob all files and directories that match the pattern. | ||||||
| // For the rules, see Go's filepath.Glob | // For the rules, see Go's filepath.Glob | ||||||
|  | // --- @param pattern string | ||||||
|  | // --- @returns table | ||||||
| func fglob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | func fglob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	if err := c.Check1Arg(); err != nil { | 	if err := c.Check1Arg(); err != nil { | ||||||
| 		return nil, err | 		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 | 	return c.PushingNext(t.Runtime, rt.TableValue(luaMatches)), nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // join(paths...) | // join(...) -> string | ||||||
| // Takes paths and joins them together with the OS's | // Takes paths and joins them together with the OS's | ||||||
| // directory separator (forward or backward slash). | // directory separator (forward or backward slash). | ||||||
|  | // --- @vararg string | ||||||
|  | // --- @returns string | ||||||
| func fjoin(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | func fjoin(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	strs := make([]string, len(c.Etc())) | 	strs := make([]string, len(c.Etc())) | ||||||
| 	for i, v := range 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 | package terminal | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| @ -26,7 +28,6 @@ func loaderFunc(rtm *rt.Runtime) (rt.Value, func()) { | |||||||
| 
 | 
 | ||||||
| 	mod := rt.NewTable() | 	mod := rt.NewTable() | ||||||
| 	util.SetExports(rtm, mod, exports) | 	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 | 	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") | 	lines := strings.Split(string(data), "\n") | ||||||
|  | 	itms := make([]string, len(lines) - 1) | ||||||
| 	for i, l := range lines { | 	for i, l := range lines { | ||||||
| 		if i == len(lines) - 1 { | 		if i == len(lines) - 1 { | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
| 		itms = append(itms, l) | 		itms[i] = l | ||||||
| 	} | 	} | ||||||
| 	f, err := os.OpenFile(path, os.O_APPEND | os.O_WRONLY | os.O_CREATE, 0755) | 	f, err := os.OpenFile(path, os.O_APPEND | os.O_WRONLY | os.O_CREATE, 0755) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|  | |||||||
							
								
								
									
										57
									
								
								job.go
									
									
									
									
									
								
							
							
						
						
									
										57
									
								
								job.go
									
									
									
									
									
								
							| @ -18,6 +18,16 @@ import ( | |||||||
| var jobs *jobHandler | var jobs *jobHandler | ||||||
| var jobMetaKey = rt.StringValue("hshjob") | 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 { | type job struct { | ||||||
| 	cmd string | 	cmd string | ||||||
| 	running bool | 	running bool | ||||||
| @ -110,6 +120,10 @@ func (j *job) getProc() *os.Process { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // #interface jobs | ||||||
|  | // #member | ||||||
|  | // start() | ||||||
|  | // Starts running the job. | ||||||
| func luaStartJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | func luaStartJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	if err := c.Check1Arg(); err != nil { | 	if err := c.Check1Arg(); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| @ -130,6 +144,10 @@ func luaStartJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | |||||||
| 	return c.Next(), nil | 	return c.Next(), nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // #interface jobs | ||||||
|  | // #member | ||||||
|  | // stop() | ||||||
|  | // Stops the job from running. | ||||||
| func luaStopJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | func luaStopJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	if err := c.Check1Arg(); err != nil { | 	if err := c.Check1Arg(); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| @ -148,6 +166,11 @@ func luaStopJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | |||||||
| 	return c.Next(), nil | 	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) { | func luaForegroundJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	if err := c.Check1Arg(); err != nil { | 	if err := c.Check1Arg(); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| @ -180,6 +203,10 @@ func luaForegroundJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | |||||||
| 	return c.Next(), nil | 	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) { | func luaBackgroundJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	if err := c.Check1Arg(); err != nil { | 	if err := c.Check1Arg(); err != nil { | ||||||
| 		return nil, err | 		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 { | func (j *jobHandler) loader(rtm *rt.Runtime) *rt.Table { | ||||||
| 	jobMethods := rt.NewTable() | 	jobMethods := rt.NewTable() | ||||||
| 	jFuncs := map[string]util.LuaExport{ | 	jFuncs := map[string]util.LuaExport{ | ||||||
| @ -353,6 +387,11 @@ func jobUserData(j *job) *rt.UserData { | |||||||
| 	return rt.NewUserData(j, jobMeta.AsTable()) | 	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) { | func (j *jobHandler) luaGetJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	j.mu.RLock() | 	j.mu.RLock() | ||||||
| 	defer j.mu.RUnlock() | 	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 | 	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) { | func (j *jobHandler) luaAddJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	if err := c.CheckNArgs(3); err != nil { | 	if err := c.CheckNArgs(3); err != nil { | ||||||
| 		return nil, err | 		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 | 	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) { | func (j *jobHandler) luaAllJobs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	j.mu.RLock() | 	j.mu.RLock() | ||||||
| 	defer j.mu.RUnlock() | 	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 | 	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) { | func (j *jobHandler) luaDisownJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	if err := c.Check1Arg(); err != nil { | 	if err := c.Check1Arg(); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| @ -431,6 +484,10 @@ func (j *jobHandler) luaDisownJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | |||||||
| 	return c.Next(), nil | 	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) { | func (j *jobHandler) luaLastJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	j.mu.RLock() | 	j.mu.RLock() | ||||||
| 	defer j.mu.RUnlock() | 	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, | 		MessageHandler: debuglib.Traceback, | ||||||
| 	}) | 	}) | ||||||
| 	lib.LoadAll(l) | 	lib.LoadAll(l) | ||||||
|  | 	setupSinkType(l) | ||||||
| 
 | 
 | ||||||
| 	lib.LoadLibs(l, hilbishLoader) | 	lib.LoadLibs(l, hilbishLoader) | ||||||
| 	// yes this is stupid, i know | 	// yes this is stupid, i know | ||||||
| @ -67,7 +68,7 @@ func luaInit() { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Add more paths that Lua can require from | 	// 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 { | 	if err != nil { | ||||||
| 		fmt.Fprintln(os.Stderr, "Could not add Hilbish require paths! Libraries will be missing. This shouldn't happen.") | 		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 { | 	if *verflag { | ||||||
| 		fmt.Printf("Hilbish %s\n", getVersion()) | 		fmt.Printf("Hilbish %s\nCompiled with %s\n", getVersion(), runtime.Version()) | ||||||
| 		os.Exit(0) | 		os.Exit(0) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -118,9 +118,11 @@ func main() { | |||||||
| 		os.Setenv("SHELL", os.Args[0]) | 		os.Setenv("SHELL", os.Args[0]) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	go handleSignals() |  | ||||||
| 	lr = newLineReader("", false) | 	lr = newLineReader("", false) | ||||||
| 	luaInit() | 	luaInit() | ||||||
|  | 
 | ||||||
|  | 	go handleSignals() | ||||||
|  | 
 | ||||||
| 	// If user's config doesn't exixt, | 	// If user's config doesn't exixt, | ||||||
| 	if _, err := os.Stat(defaultConfPath); os.IsNotExist(err) && *configflag == defaultConfPath { | 	if _, err := os.Stat(defaultConfPath); os.IsNotExist(err) && *configflag == defaultConfPath { | ||||||
| 		// Read default from current directory | 		// Read default from current directory | ||||||
| @ -184,11 +186,14 @@ input: | |||||||
| 			break | 			break | ||||||
| 		} | 		} | ||||||
| 		if err != nil { | 		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 | 				// If we get a completely random error, print | ||||||
| 				fmt.Fprintln(os.Stderr, err) | 				fmt.Fprintln(os.Stderr, err) | ||||||
| 			} | 			} | ||||||
| 			fmt.Println("^C") | 			// TODO: Halt if any other error occurs | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
| 		var priv bool | 		var priv bool | ||||||
| @ -287,7 +292,7 @@ func removeDupes(slice []string) []string { | |||||||
| 
 | 
 | ||||||
| func contains(s []string, e string) bool { | func contains(s []string, e string) bool { | ||||||
| 	for _, a := range s { | 	for _, a := range s { | ||||||
| 		if a == e { | 		if strings.ToLower(a) == strings.ToLower(e) { | ||||||
| 			return true | 			return true | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @ -322,3 +327,7 @@ func getVersion() string { | |||||||
| 
 | 
 | ||||||
| 	return v.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' | local commander = require 'commander' | ||||||
| 
 | 
 | ||||||
| commander.register('bg', function() | commander.register('bg', function(_, sinks) | ||||||
| 	local job = hilbish.jobs.last() | 	local job = hilbish.jobs.last() | ||||||
| 	if not job then | 	if not job then | ||||||
| 		print 'bg: no last job' | 		sinks.out:writeln 'bg: no last job' | ||||||
| 		return 1 | 		return 1 | ||||||
| 	end | 	end | ||||||
| 
 | 
 | ||||||
| 	local err = job.background() | 	local err = job:background() | ||||||
| 	if err then | 	if err then | ||||||
| 		print('bg: ' .. err) | 		sinks.out:writeln('bg: ' .. err) | ||||||
| 		return 2 | 		return 2 | ||||||
| 	end | 	end | ||||||
| end) | end) | ||||||
|  | |||||||
| @ -1,11 +1,11 @@ | |||||||
| local commander = require 'commander' | local commander = require 'commander' | ||||||
| local fs = require 'fs' | local fs = require 'fs' | ||||||
| 
 | 
 | ||||||
| commander.register('cat', function(args) | commander.register('cat', function(args, sinks) | ||||||
| 	local exit = 0 | 	local exit = 0 | ||||||
| 
 | 
 | ||||||
| 	if #args == 0 then | 	if #args == 0 then | ||||||
| 		print [[ | 		sinks.out:writeln [[ | ||||||
| usage: cat [file]...]] | usage: cat [file]...]] | ||||||
| 	end | 	end | ||||||
| 
 | 
 | ||||||
| @ -13,11 +13,11 @@ usage: cat [file]...]] | |||||||
| 		local f = io.open(fName) | 		local f = io.open(fName) | ||||||
| 		if f == nil then | 		if f == nil then | ||||||
| 			exit = 1 | 			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 | 			goto continue | ||||||
| 		end | 		end | ||||||
| 
 | 
 | ||||||
| 		io.write(f:read '*a') | 		sinks.out:writeln(f:read '*a') | ||||||
| 		::continue:: | 		::continue:: | ||||||
| 	end | 	end | ||||||
| 	io.flush() | 	io.flush() | ||||||
|  | |||||||
| @ -4,32 +4,25 @@ local fs = require 'fs' | |||||||
| local dirs = require 'nature.dirs' | local dirs = require 'nature.dirs' | ||||||
| 
 | 
 | ||||||
| dirs.old = hilbish.cwd() | dirs.old = hilbish.cwd() | ||||||
| commander.register('cd', function (args) | commander.register('cd', function (args, sinks) | ||||||
| 	if #args > 1 then | 	if #args > 1 then | ||||||
| 		print("cd: too many arguments") | 		sinks.out:writeln("cd: too many arguments") | ||||||
| 		return 1 | 		return 1 | ||||||
| 	elseif #args > 0 then |  | ||||||
| 		local path = args[1]:gsub('$%$','\0'):gsub('${([%w_]+)}', os.getenv) |  | ||||||
| 		:gsub('$([%w_]+)', os.getenv):gsub('%z','$'):gsub('^%s*(.-)%s*$', '%1') |  | ||||||
| 
 |  | ||||||
|         if path == '-' then |  | ||||||
| 			path = dirs.old |  | ||||||
|             print(path) |  | ||||||
|         end |  | ||||||
|         dirs.setOld(hilbish.cwd()) |  | ||||||
| 		dirs.push(path) |  | ||||||
| 
 |  | ||||||
| 		local ok, err = pcall(function() fs.cd(path) end) |  | ||||||
| 		if not ok then |  | ||||||
| 			print(err) |  | ||||||
| 			return 1 |  | ||||||
| 		end |  | ||||||
| 		bait.throw('cd', path) |  | ||||||
| 
 |  | ||||||
| 		return |  | ||||||
| 	end | 	end | ||||||
| 	fs.cd(hilbish.home) |  | ||||||
| 	bait.throw('cd', hilbish.home) |  | ||||||
| 
 | 
 | ||||||
| 	dirs.push(hilbish.home) | 	local path = args[1] and args[1] or hilbish.home | ||||||
|  | 	if path == '-' then | ||||||
|  | 		path = dirs.old | ||||||
|  | 		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 | ||||||
|  | 		sinks.out:writeln(err) | ||||||
|  | 		return 1 | ||||||
|  | 	end | ||||||
|  | 	bait.throw('cd', path) | ||||||
| end) | end) | ||||||
|  | |||||||
| @ -3,35 +3,38 @@ local fs = require 'fs' | |||||||
| local lunacolors = require 'lunacolors' | local lunacolors = require 'lunacolors' | ||||||
| local dirs = require 'nature.dirs' | local dirs = require 'nature.dirs' | ||||||
| 
 | 
 | ||||||
| commander.register('cdr', function(args) | commander.register('cdr', function(args, sinks) | ||||||
| 	if not args[1] then | 	if not args[1] then | ||||||
| 		print(lunacolors.format [[ | 		sinks.out:writeln(lunacolors.format [[ | ||||||
| cdr: change directory to one which has been recently visied | cdr: change directory to one which has been recently visied | ||||||
| 
 | 
 | ||||||
| usage: cdr <index> | 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 | 		return | ||||||
| 	end | 	end | ||||||
| 
 | 
 | ||||||
| 	if args[1] == 'list' then | 	if args[1] == 'list' then | ||||||
| 		local recentDirs = dirs.recentDirs | 		local recentDirs = dirs.recentDirs | ||||||
| 		if #recentDirs == 0 then | 		if #recentDirs == 0 then | ||||||
| 			print 'No directories have been visited.' | 			sinks.out:writeln 'No directories have been visited.' | ||||||
| 			return 1 | 			return 1 | ||||||
| 		end | 		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 | 		return | ||||||
| 	end | 	end | ||||||
| 
 | 
 | ||||||
| 	local index = tonumber(args[1]) | 	local index = tonumber(args[1]) | ||||||
| 	if not index then | 	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 | 		return 1 | ||||||
| 	end | 	end | ||||||
| 
 | 
 | ||||||
| 	if not dirs.recent(index) then | 	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 | 		return 1 | ||||||
| 	end | 	end | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,8 +1,8 @@ | |||||||
| local commander = require 'commander' | local commander = require 'commander' | ||||||
| 
 | 
 | ||||||
| commander.register('disown', function(args) | commander.register('disown', function(args, sinks) | ||||||
| 	if #hilbish.jobs.all() == 0 then | 	if #hilbish.jobs.all() == 0 then | ||||||
| 		print 'disown: no current job' | 		sinks.out:writeln 'disown: no current job' | ||||||
| 		return 1 | 		return 1 | ||||||
| 	end | 	end | ||||||
| 
 | 
 | ||||||
| @ -10,7 +10,7 @@ commander.register('disown', function(args) | |||||||
| 	if #args < 0 then | 	if #args < 0 then | ||||||
| 		id = tonumber(args[1]) | 		id = tonumber(args[1]) | ||||||
| 		if not id then | 		if not id then | ||||||
| 			print 'disown: invalid id for job' | 			sinks.out:writeln 'disown: invalid id for job' | ||||||
| 			return 1 | 			return 1 | ||||||
| 		end | 		end | ||||||
| 	else | 	else | ||||||
| @ -19,7 +19,7 @@ commander.register('disown', function(args) | |||||||
| 
 | 
 | ||||||
| 	local ok = pcall(hilbish.jobs.disown, id) | 	local ok = pcall(hilbish.jobs.disown, id) | ||||||
| 	if not ok then | 	if not ok then | ||||||
| 		print 'disown: job does not exist' | 		sinks.out:writeln 'disown: job does not exist' | ||||||
| 		return 2 | 		return 2 | ||||||
| 	end | 	end | ||||||
| end) | end) | ||||||
|  | |||||||
| @ -2,94 +2,99 @@ local commander = require 'commander' | |||||||
| local fs = require 'fs' | local fs = require 'fs' | ||||||
| local lunacolors = require 'lunacolors' | local lunacolors = require 'lunacolors' | ||||||
| 
 | 
 | ||||||
| commander.register('doc', function(args) | commander.register('doc', function(args, sinks) | ||||||
| 	local moddocPath = hilbish.dataDir .. '/docs/' | 	local moddocPath = hilbish.dataDir .. '/docs/' | ||||||
| 	local modDocFormat = [[ | 	local stat = pcall(fs.stat, '.git/refs/heads/extended-job-api') | ||||||
| %s | 	if stat then | ||||||
| %s | 		-- hilbish git | ||||||
| # Functions | 		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 | 	if #args > 0 then | ||||||
| 		local mod = args[1] | 		local mod = args[1] | ||||||
| 
 | 
 | ||||||
| 		local f = io.open(moddocPath .. mod .. '.txt', 'rb') | 		local f = io.open(moddocPath .. mod .. '.md', 'rb') | ||||||
| 		local funcdocs = nil | 		local funcdocs = nil | ||||||
|  | 		local subdocName = args[2] | ||||||
| 		if not f then | 		if not f then | ||||||
| 			-- assume subdir | 			-- assume subdir | ||||||
| 			-- dataDir/docs/<mod>/<mod>.txt | 			-- dataDir/docs/<mod>/<mod>.md | ||||||
| 			moddocPath = moddocPath .. mod .. '/' | 			moddocPath = moddocPath .. mod .. '/' | ||||||
| 			local subdocName = args[2] |  | ||||||
| 			if not subdocName then | 			if not subdocName then | ||||||
| 				subdocName = 'index' | 				subdocName = '_index' | ||||||
| 			end | 			end | ||||||
| 			f = io.open(moddocPath .. subdocName .. '.txt', 'rb') | 			f = io.open(moddocPath .. subdocName .. '.md', 'rb') | ||||||
| 			if not f then | 			if not f then | ||||||
| 				print('No documentation found for ' .. mod .. '.') | 				f = io.open(moddocPath .. subdocName:match '%w+' .. '/' .. subdocName .. '.md', 'rb') | ||||||
| 				return |  | ||||||
| 			end | 			end | ||||||
| 			funcdocs = f:read '*a' | 			if not f then | ||||||
| 			local moddocs = table.filter(fs.readdir(moddocPath), function(f) return f ~= 'index.txt' end) | 				moddocPath = moddocPath .. subdocName .. '/' | ||||||
| 			local subdocs = table.map(moddocs, function(fname) | 				subdocName = args[3] or '_index' | ||||||
| 				return lunacolors.underline(lunacolors.blue(string.gsub(fname, '.txt', ''))) | 				f = io.open(moddocPath .. subdocName .. '.md', 'rb') | ||||||
| 			end) |  | ||||||
| 			if subdocName == 'index' then |  | ||||||
| 				funcdocs = funcdocs .. '\nSubdocs: ' .. table.concat(subdocs, ', ') |  | ||||||
| 			end | 			end | ||||||
|  | 			if not f then | ||||||
|  | 				sinks.out:writeln('No documentation found for ' .. mod .. '.') | ||||||
|  | 				return 1 | ||||||
|  | 			end | ||||||
|  | 		end | ||||||
|  | 		funcdocs = f:read '*a':gsub('-([%d]+)', '%1') | ||||||
|  | 		local moddocs = table.filter(fs.readdir(moddocPath), function(f) return f ~= '_index.md' and f ~= 'index.md' end) | ||||||
|  | 		local subdocs = table.map(moddocs, function(fname) | ||||||
|  | 			return lunacolors.underline(lunacolors.blue(string.gsub(fname, '.md', ''))) | ||||||
|  | 		end) | ||||||
|  | 		if #moddocs ~= 0 then | ||||||
|  | 			funcdocs = funcdocs .. '\nSubdocs: ' .. table.concat(subdocs, ', ') | ||||||
| 		end | 		end | ||||||
| 
 | 
 | ||||||
| 		if not funcdocs then | 		local valsStr = funcdocs:match '%-%-%-\n([^%-%-%-]+)\n' | ||||||
| 			funcdocs = f:read '*a' | 		local vals = {} | ||||||
| 		end | 		if valsStr then | ||||||
| 		local desc = '' | 			local _, endpos = funcdocs:find('---\n' .. valsStr .. '\n---\n\n', 1, true) | ||||||
| 		local ok = pcall(require, mod) | 			funcdocs = funcdocs:sub(endpos + 1, #funcdocs) | ||||||
| 		local backtickOccurence = 0 |  | ||||||
| 		local formattedFuncs = lunacolors.format(funcdocs:sub(1, #funcdocs - 1):gsub('`', function() |  | ||||||
| 			backtickOccurence = backtickOccurence + 1 |  | ||||||
| 			if backtickOccurence % 2 == 0 then |  | ||||||
| 				return '{reset}' |  | ||||||
| 			else |  | ||||||
| 				return '{underline}{green}' |  | ||||||
| 			end |  | ||||||
| 		end)) |  | ||||||
| 
 | 
 | ||||||
| 		if ok then | 			-- parse vals | ||||||
| 			local props = {} | 			local lines = string.split(valsStr, '\n') | ||||||
| 			local propstr = '' | 			for _, line in ipairs(lines) do | ||||||
| 			local modDesc = '' | 				local key = line:match '(%w+): ' | ||||||
| 			local modmt = getmetatable(require(mod)) | 				local val = line:match '^%w+: (.-)$' | ||||||
| 			if modmt then | 
 | ||||||
| 				modDesc = modmt.__doc | 				if key then | ||||||
| 				if modmt.__docProp then | 					vals[key] = val | ||||||
| 					-- not all modules have docs for properties |  | ||||||
| 					props = table.map(modmt.__docProp, function(v, k) |  | ||||||
| 						return lunacolors.underline(lunacolors.blue(k)) .. ' > ' .. v |  | ||||||
| 					end) |  | ||||||
| 				end | 				end | ||||||
| 				if #props > 0 then |  | ||||||
| 					propstr = '\n# Properties\n' .. table.concat(props, '\n') .. '\n' |  | ||||||
| 				end |  | ||||||
| 				desc = string.format(modDocFormat, modDesc, propstr) |  | ||||||
| 			end | 			end | ||||||
| 		end | 		end | ||||||
| 		print(desc .. formattedFuncs) | 		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() | 		f:close() | ||||||
| 
 |  | ||||||
| 		return |  | ||||||
| 	end | 	end | ||||||
| 	local modules = table.map(fs.readdir(moddocPath), function(f) |  | ||||||
| 		return lunacolors.underline(lunacolors.blue(string.gsub(f, '.txt', ''))) |  | ||||||
| 	end) |  | ||||||
| 
 | 
 | ||||||
| 	io.write [[ | 	local backtickOccurence = 0 | ||||||
| Welcome to Hilbish's doc tool! Here you can find documentation for builtin | 	sinks.out:writeln(lunacolors.format(doc:gsub('`', function() | ||||||
| functions and other things. | 		backtickOccurence = backtickOccurence + 1 | ||||||
| 
 | 		if backtickOccurence % 2 == 0 then | ||||||
| Usage: doc <section> [subdoc] | 			return '{reset}' | ||||||
| A section is a module or a literal section and a subdoc is a subsection for it. | 		else | ||||||
| 
 | 			return '{underline}{green}' | ||||||
| Available sections: ]] | 		end | ||||||
| 	io.flush() | 	end):gsub('\n#+.-\n', function(t) | ||||||
| 
 | 		local signature = t:gsub('<.->(.-)</.->', '{underline}%1'):gsub('\\', '<') | ||||||
| 	print(table.concat(modules, ', ')) | 		return '{bold}{yellow}' .. signature .. '{reset}' | ||||||
|  | 	end))) | ||||||
| end) | end) | ||||||
|  | |||||||
| @ -1,15 +1,15 @@ | |||||||
| local commander = require 'commander' | local commander = require 'commander' | ||||||
| 
 | 
 | ||||||
| commander.register('fg', function() | commander.register('fg', function(_, sinks) | ||||||
| 	local job = hilbish.jobs.last() | 	local job = hilbish.jobs.last() | ||||||
| 	if not job then | 	if not job then | ||||||
| 		print 'fg: no last job' | 		sinks.out:writeln 'fg: no last job' | ||||||
| 		return 1 | 		return 1 | ||||||
| 	end | 	end | ||||||
| 
 | 
 | ||||||
| 	local err = job.foreground() -- waits for job; blocks | 	local err = job:foreground() -- waits for job; blocks | ||||||
| 	if err then | 	if err then | ||||||
| 		print('fg: ' .. err) | 		sinks.out:writeln('fg: ' .. err) | ||||||
| 		return 2 | 		return 2 | ||||||
| 	end | 	end | ||||||
| end) | end) | ||||||
|  | |||||||
| @ -24,7 +24,7 @@ function hilbish.completion.handler(line, pos) | |||||||
| 		return {compGroup}, pfx | 		return {compGroup}, pfx | ||||||
| 	else | 	else | ||||||
| 		local ok, compGroups, pfx = pcall(hilbish.completion.call, | 		local ok, compGroups, pfx = pcall(hilbish.completion.call, | ||||||
| 		'command.' .. #fields[1], query, ctx, fields) | 		'command.' .. fields[1], query, ctx, fields) | ||||||
| 		if ok then | 		if ok then | ||||||
| 			return compGroups, pfx | 			return compGroups, pfx | ||||||
| 		end | 		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.opts' | ||||||
| require 'nature.vim' | require 'nature.vim' | ||||||
| require 'nature.runner' | require 'nature.runner' | ||||||
|  | require 'nature.hummingbird' | ||||||
| 
 | 
 | ||||||
| local shlvl = tonumber(os.getenv 'SHLVL') | local shlvl = tonumber(os.getenv 'SHLVL') | ||||||
| if shlvl ~= nil then | if shlvl ~= nil then | ||||||
| @ -67,5 +68,13 @@ do | |||||||
| end | end | ||||||
| 
 | 
 | ||||||
| bait.catch('error', function(event, handler, err) | 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) | end) | ||||||
|  | |||||||
| @ -16,7 +16,7 @@ setmetatable(hilbish.opts, { | |||||||
| 
 | 
 | ||||||
| local function setupOpt(name, default) | local function setupOpt(name, default) | ||||||
| 	opts[name] = default | 	opts[name] = default | ||||||
| 	require('nature.opts.' .. name) | 	pcall(require, 'nature.opts.' .. name) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| local defaultOpts = { | local defaultOpts = { | ||||||
| @ -25,7 +25,9 @@ local defaultOpts = { | |||||||
| 	greeting = string.format([[Welcome to {magenta}Hilbish{reset}, {cyan}%s{reset}. | 	greeting = string.format([[Welcome to {magenta}Hilbish{reset}, {cyan}%s{reset}. | ||||||
| The nice lil shell for {blue}Lua{reset} fanatics! | The nice lil shell for {blue}Lua{reset} fanatics! | ||||||
| ]], hilbish.user), | ]], hilbish.user), | ||||||
| 	motd = true | 	motd = true, | ||||||
|  | 	fuzzy = false, | ||||||
|  | 	notifyJobFinish = true | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| for optsName, default in pairs(defaultOpts) do | for optsName, default in pairs(defaultOpts) do | ||||||
|  | |||||||
| @ -2,8 +2,8 @@ local bait = require 'bait' | |||||||
| local lunacolors = require 'lunacolors' | local lunacolors = require 'lunacolors' | ||||||
| 
 | 
 | ||||||
| hilbish.motd = [[ | hilbish.motd = [[ | ||||||
| Hilbish 2.0 is a {red}major{reset} update! If your config doesn't work | 1000 commits on the Hilbish repository brings us to {cyan}Version 2.1!{reset} | ||||||
| anymore, that will definitely be why! A MOTD, very message, much day. | Docs, docs, docs... At least builtins work with pipes now. | ||||||
| ]] | ]] | ||||||
| 
 | 
 | ||||||
| bait.catch('hilbish.init', function() | 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 currentRunner = 'hybrid' | ||||||
| local runners = {} | local runners = {} | ||||||
| 
 | 
 | ||||||
| @ -74,6 +75,12 @@ function hilbish.runner.setCurrent(name) | |||||||
| 	hilbish.runner.setMode(r.run) | 	hilbish.runner.setMode(r.run) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
|  | --- Returns the current runner by name. | ||||||
|  | --- @returns string | ||||||
|  | function hilbish.runner.getCurrent() | ||||||
|  | 	return currentRunner | ||||||
|  | end | ||||||
|  | 
 | ||||||
| hilbish.runner.add('hybrid', function(input) | hilbish.runner.add('hybrid', function(input) | ||||||
| 	local cmdStr = hilbish.aliases.resolve(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,7 +4,8 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"strings" | 	"strings" | ||||||
| ) | 	"github.com/rivo/uniseg" | ||||||
|  | )	 | ||||||
| 
 | 
 | ||||||
| // initGrid - Grid display details. Called each time we want to be sure to have | // initGrid - Grid display details. Called each time we want to be sure to have | ||||||
| // a working completion group either immediately, or later on. Generally defered. | // a working completion group either immediately, or later on. Generally defered. | ||||||
| @ -13,8 +14,8 @@ func (g *CompletionGroup) initGrid(rl *Instance) { | |||||||
| 	// Compute size of each completion item box | 	// Compute size of each completion item box | ||||||
| 	tcMaxLength := 1 | 	tcMaxLength := 1 | ||||||
| 	for i := range g.Suggestions { | 	for i := range g.Suggestions { | ||||||
| 		if len(g.Suggestions[i]) > tcMaxLength { | 		if uniseg.GraphemeClusterCount(g.Suggestions[i]) > tcMaxLength { | ||||||
| 			tcMaxLength = len([]rune(g.Suggestions[i])) | 			tcMaxLength = uniseg.GraphemeClusterCount(g.Suggestions[i]) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -103,7 +104,7 @@ func (g *CompletionGroup) writeGrid(rl *Instance) (comp string) { | |||||||
| 		rl.tcUsedY++ | 		rl.tcUsedY++ | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	cellWidth := strconv.Itoa((GetTermWidth() / g.tcMaxX) - 2) | 	cellWidth := strconv.Itoa((GetTermWidth() / g.tcMaxX) - 4) | ||||||
| 	x := 0 | 	x := 0 | ||||||
| 	y := 1 | 	y := 1 | ||||||
| 
 | 
 | ||||||
| @ -124,7 +125,15 @@ func (g *CompletionGroup) writeGrid(rl *Instance) (comp string) { | |||||||
| 			comp += seqInvert | 			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 | 	// 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. | // The rx parameter is passed, as the shell already checked that the search pattern is valid. | ||||||
| func (g *CompletionGroup) updateTabFind(rl *Instance) { | 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. | 	// 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 == nil { continue } | ||||||
| 		if rl.regexSearch.MatchString(g.Suggestions[i]) { | 		if rl.regexSearch.MatchString(g.Suggestions[i]) { | ||||||
| 			suggs = append(suggs, 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 | 			// this is a list so lets also check the descriptions | ||||||
| 			suggs = append(suggs, g.Suggestions[i]) | 			suggs = append(suggs, g.Suggestions[i]) | ||||||
| 		} | 		} | ||||||
| 	} | 	}*/ | ||||||
| 
 | 
 | ||||||
| 	// We overwrite the group's items, (will be refreshed as soon as something is typed in the search) | 	// We overwrite the group's items, (will be refreshed as soon as something is typed in the search) | ||||||
| 	g.Suggestions = suggs | 	g.Suggestions = suggs | ||||||
|  | |||||||
| @ -123,23 +123,20 @@ func (rl *Instance) walkHistory(i int) { | |||||||
| 
 | 
 | ||||||
| 	// When we are exiting the current line buffer to move around | 	// When we are exiting the current line buffer to move around | ||||||
| 	// the history, we make buffer the current line | 	// 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) | 		rl.lineBuf = string(rl.line) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	switch rl.histPos + i { | 	rl.histOffset += i | ||||||
| 	case 0, history.Len() + 1: | 	if rl.histOffset == 0 { | ||||||
| 		rl.histPos = 0 |  | ||||||
| 		rl.line = []rune(rl.lineBuf) | 		rl.line = []rune(rl.lineBuf) | ||||||
| 		rl.pos = len(rl.lineBuf) | 		rl.pos = len(rl.lineBuf) | ||||||
| 		return | 	} else if rl.histOffset <= -1 { | ||||||
| 	case -1: | 		rl.histOffset = 0 | ||||||
| 		rl.histPos = 0 | 	} else { | ||||||
| 		rl.lineBuf = string(rl.line) |  | ||||||
| 	default: |  | ||||||
| 		dedup = true | 		dedup = true | ||||||
| 		old = string(rl.line) | 		old = string(rl.line) | ||||||
| 		new, err = history.GetLine(history.Len() - rl.histPos - 1) | 		new, err = history.GetLine(history.Len() - rl.histOffset) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			rl.resetHelpers() | 			rl.resetHelpers() | ||||||
| 			print("\r\n" + err.Error() + "\r\n") | 			print("\r\n" + err.Error() + "\r\n") | ||||||
| @ -148,7 +145,6 @@ func (rl *Instance) walkHistory(i int) { | |||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		rl.clearLine() | 		rl.clearLine() | ||||||
| 		rl.histPos += i |  | ||||||
| 		rl.line = []rune(new) | 		rl.line = []rune(new) | ||||||
| 		rl.pos = len(rl.line) | 		rl.pos = len(rl.line) | ||||||
| 		if rl.pos > 0 { | 		if rl.pos > 0 { | ||||||
|  | |||||||
| @ -112,8 +112,10 @@ type Instance struct { | |||||||
| 	modeAutoFind bool           // for when invoked via ^R or ^F outside of [tab] | 	modeAutoFind bool           // for when invoked via ^R or ^F outside of [tab] | ||||||
| 	searchMode   FindMode       // Used for varying hints, and underlying functions called | 	searchMode   FindMode       // Used for varying hints, and underlying functions called | ||||||
| 	regexSearch  *regexp.Regexp // Holds the current search regex match | 	regexSearch  *regexp.Regexp // Holds the current search regex match | ||||||
|  | 	search       string | ||||||
| 	mainHist     bool           // Which history stdin do we want | 	mainHist     bool           // Which history stdin do we want | ||||||
| 	histInfo     []rune         // We store a piece of hist info, for dual history sources | 	histInfo     []rune         // We store a piece of hist info, for dual history sources | ||||||
|  | 	Searcher func(string, []string) []string | ||||||
| 
 | 
 | ||||||
| 	// | 	// | ||||||
| 	// History ----------------------------------------------------------------------------------- | 	// History ----------------------------------------------------------------------------------- | ||||||
| @ -134,6 +136,7 @@ type Instance struct { | |||||||
| 	// history operating params | 	// history operating params | ||||||
| 	lineBuf    string | 	lineBuf    string | ||||||
| 	histPos    int | 	histPos    int | ||||||
|  | 	histOffset int | ||||||
| 	histNavIdx int // Used for quick history navigation. | 	histNavIdx int // Used for quick history navigation. | ||||||
| 
 | 
 | ||||||
| 	// | 	// | ||||||
| @ -228,6 +231,25 @@ func NewInstance() *Instance { | |||||||
| 	rl.HintFormatting = "\x1b[2m" | 	rl.HintFormatting = "\x1b[2m" | ||||||
| 	rl.evtKeyPress = make(map[string]func(string, []rune, int) *EventReturn) | 	rl.evtKeyPress = make(map[string]func(string, []rune, int) *EventReturn) | ||||||
| 	rl.TempDirectory = os.TempDir() | 	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 | 	// Registers | ||||||
| 	rl.initRegisters() | 	rl.initRegisters() | ||||||
|  | |||||||
| @ -49,7 +49,7 @@ func (rl *Instance) Readline() (string, error) { | |||||||
| 
 | 
 | ||||||
| 	// History Init | 	// History Init | ||||||
| 	// We need this set to the last command, so that we can access it quickly | 	// 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}} | 	rl.viUndoHistory = []undoItem{{line: "", pos: 0}} | ||||||
| 
 | 
 | ||||||
| 	// Multisplit | 	// Multisplit | ||||||
| @ -238,7 +238,9 @@ func (rl *Instance) Readline() (string, error) { | |||||||
| 
 | 
 | ||||||
| 			// Normal completion search does only refresh the search pattern and the comps | 			// Normal completion search does only refresh the search pattern and the comps | ||||||
| 			if rl.modeTabFind || rl.modeAutoFind { | 			if rl.modeTabFind || rl.modeAutoFind { | ||||||
|  | 				rl.resetVirtualComp(false) | ||||||
| 				rl.backspaceTabFind() | 				rl.backspaceTabFind() | ||||||
|  | 				rl.renderHelpers() | ||||||
| 				rl.viUndoSkipAppend = true | 				rl.viUndoSkipAppend = true | ||||||
| 			} else { | 			} else { | ||||||
| 				// Always cancel any virtual completion | 				// Always cancel any virtual completion | ||||||
| @ -331,6 +333,8 @@ func (rl *Instance) Readline() (string, error) { | |||||||
| 
 | 
 | ||||||
| 			rl.modeTabFind = true | 			rl.modeTabFind = true | ||||||
| 			rl.updateTabFind([]rune{}) | 			rl.updateTabFind([]rune{}) | ||||||
|  | 			rl.updateVirtualComp() | ||||||
|  | 			rl.renderHelpers() | ||||||
| 			rl.viUndoSkipAppend = true | 			rl.viUndoSkipAppend = true | ||||||
| 
 | 
 | ||||||
| 		// Tab Completion & Completion Search --------------------------------------------------------------- | 		// Tab Completion & Completion Search --------------------------------------------------------------- | ||||||
| @ -484,7 +488,10 @@ func (rl *Instance) Readline() (string, error) { | |||||||
| 				if string(r[:i]) != seqShiftTab && | 				if string(r[:i]) != seqShiftTab && | ||||||
| 					string(r[:i]) != seqForwards && string(r[:i]) != seqBackwards && | 					string(r[:i]) != seqForwards && string(r[:i]) != seqBackwards && | ||||||
| 					string(r[:i]) != seqUp && string(r[:i]) != seqDown { | 					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 { | 			if rl.modeAutoFind || rl.modeTabFind { | ||||||
| 				rl.resetVirtualComp(false) | 				rl.resetVirtualComp(false) | ||||||
| 				rl.updateTabFind(r[:i]) | 				rl.updateTabFind(r[:i]) | ||||||
|  | 				rl.renderHelpers() | ||||||
| 				rl.viUndoSkipAppend = true | 				rl.viUndoSkipAppend = true | ||||||
|  | 				continue | ||||||
| 			} else { | 			} else { | ||||||
| 				rl.resetVirtualComp(false) | 				rl.resetVirtualComp(false) | ||||||
| 				rl.editorInput(r[:i]) | 				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 | // entry readline is currently configured for and then update the line entries | ||||||
| // accordingly. | // accordingly. | ||||||
| func (rl *Instance) editorInput(r []rune) { | func (rl *Instance) editorInput(r []rune) { | ||||||
|  | 	if len(r) == 0 { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	switch rl.modeViMode { | 	switch rl.modeViMode { | ||||||
| 	case VimKeys: | 	case VimKeys: | ||||||
| 		rl.vi(r[0]) | 		rl.vi(r[0]) | ||||||
| @ -604,6 +617,7 @@ func (rl *Instance) escapeSeq(r []rune) { | |||||||
| 	case string(charEscape): | 	case string(charEscape): | ||||||
| 		switch { | 		switch { | ||||||
| 		case rl.modeAutoFind: | 		case rl.modeAutoFind: | ||||||
|  | 			rl.resetVirtualComp(true) | ||||||
| 			rl.resetTabFind() | 			rl.resetTabFind() | ||||||
| 			rl.clearHelpers() | 			rl.clearHelpers() | ||||||
| 			rl.resetTabCompletion() | 			rl.resetTabCompletion() | ||||||
| @ -611,6 +625,7 @@ func (rl *Instance) escapeSeq(r []rune) { | |||||||
| 			rl.renderHelpers() | 			rl.renderHelpers() | ||||||
| 
 | 
 | ||||||
| 		case rl.modeTabFind: | 		case rl.modeTabFind: | ||||||
|  | 			rl.resetVirtualComp(true) | ||||||
| 			rl.resetTabFind() | 			rl.resetTabFind() | ||||||
| 			rl.resetTabCompletion() | 			rl.resetTabCompletion() | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -94,7 +94,7 @@ func (rl *Instance) getTabSearchCompletion() { | |||||||
| 	rl.getCurrentGroup() | 	rl.getCurrentGroup() | ||||||
| 
 | 
 | ||||||
| 	// Set the info for this completion mode | 	// 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 { | 	for _, g := range rl.tcGroups { | ||||||
| 		g.updateTabFind(rl) | 		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 total number of matches is zero, we directly change the info, and return | ||||||
| 	if comps, _, _ := rl.getCompletionCount(); comps == 0 { | 	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 | package readline | ||||||
| 
 | 
 | ||||||
| import ( |  | ||||||
| 	"regexp" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // FindMode defines how the autocomplete suggestions display | // FindMode defines how the autocomplete suggestions display | ||||||
| type FindMode int | type FindMode int | ||||||
| 
 | 
 | ||||||
| @ -30,12 +26,7 @@ func (rl *Instance) updateTabFind(r []rune) { | |||||||
| 	rl.tfLine = append(rl.tfLine, r...) | 	rl.tfLine = append(rl.tfLine, r...) | ||||||
| 
 | 
 | ||||||
| 	// The search regex is common to all search modes | 	// The search regex is common to all search modes | ||||||
| 	var err error | 	rl.search = string(rl.tfLine) | ||||||
| 	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")) |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	// We update and print | 	// We update and print | ||||||
| 	rl.clearHelpers() | 	rl.clearHelpers() | ||||||
|  | |||||||
| @ -14,6 +14,7 @@ var ( | |||||||
| 	// effects | 	// effects | ||||||
| 	BOLD  = "\033[1m" | 	BOLD  = "\033[1m" | ||||||
| 	DIM   = "\033[2m" | 	DIM   = "\033[2m" | ||||||
|  | 	UNDERLINE   = "\033[4m" | ||||||
| 	RESET = "\033[0m" | 	RESET = "\033[0m" | ||||||
| 	// colors | 	// colors | ||||||
| 	RED    = "\033[31m" | 	RED    = "\033[31m" | ||||||
|  | |||||||
							
								
								
									
										46
									
								
								rl.go
									
									
									
									
									
								
							
							
						
						
									
										46
									
								
								rl.go
									
									
									
									
									
								
							| @ -7,8 +7,9 @@ import ( | |||||||
| 
 | 
 | ||||||
| 	"hilbish/util" | 	"hilbish/util" | ||||||
| 
 | 
 | ||||||
| 	"github.com/maxlandon/readline" |  | ||||||
| 	rt "github.com/arnodel/golua/runtime" | 	rt "github.com/arnodel/golua/runtime" | ||||||
|  | 	"github.com/maxlandon/readline" | ||||||
|  | 	"github.com/sahilm/fuzzy" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type lineReader struct { | type lineReader struct { | ||||||
| @ -24,6 +25,24 @@ func newLineReader(prompt string, noHist bool) *lineReader { | |||||||
| 		rl: rl, | 		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, | 	// we don't mind hilbish.read rl instances having completion, | ||||||
| 	// but it cant have shared history | 	// but it cant have shared history | ||||||
| 	if !noHist { | 	if !noHist { | ||||||
| @ -225,7 +244,11 @@ func (lr *lineReader) Resize() { | |||||||
| 	return | 	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 { | func (lr *lineReader) Loader(rtm *rt.Runtime) *rt.Table { | ||||||
| 	lrLua := map[string]util.LuaExport{ | 	lrLua := map[string]util.LuaExport{ | ||||||
| 		"add": {lr.luaAddHistory, 1, false}, | 		"add": {lr.luaAddHistory, 1, false}, | ||||||
| @ -241,6 +264,10 @@ func (lr *lineReader) Loader(rtm *rt.Runtime) *rt.Table { | |||||||
| 	return mod | 	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) { | func (lr *lineReader) luaAddHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	if err := c.Check1Arg(); err != nil { | 	if err := c.Check1Arg(); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| @ -254,10 +281,18 @@ func (lr *lineReader) luaAddHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) | |||||||
| 	return c.Next(), nil | 	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) { | 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 | 	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) { | func (lr *lineReader) luaGetHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	if err := c.Check1Arg(); err != nil { | 	if err := c.Check1Arg(); err != nil { | ||||||
| 		return nil, err | 		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 | 	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) { | func (lr *lineReader) luaAllHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	tbl := rt.NewTable() | 	tbl := rt.NewTable() | ||||||
| 	size := lr.fileHist.Len() | 	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 | 	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) { | func (lr *lineReader) luaClearHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	lr.fileHist.clear() | 	lr.fileHist.clear() | ||||||
| 	return c.Next(), nil | 	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" | 	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 { | func runnerModeLoader(rtm *rt.Runtime) *rt.Table { | ||||||
| 	exports := map[string]util.LuaExport{ | 	exports := map[string]util.LuaExport{ | ||||||
| 		"sh": {shRunner, 1, false}, | 		"sh": {shRunner, 1, false}, | ||||||
| @ -19,6 +26,20 @@ func runnerModeLoader(rtm *rt.Runtime) *rt.Table { | |||||||
| 	return mod | 	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) { | func shRunner(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	if err := c.Check1Arg(); err != nil { | 	if err := c.Check1Arg(); err != nil { | ||||||
| 		return nil, err | 		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 | 	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) { | func luaRunner(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	if err := c.Check1Arg(); err != nil { | 	if err := c.Check1Arg(); err != nil { | ||||||
| 		return nil, err | 		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