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