chore: add plugins
This commit is contained in:
parent
57cf519bbe
commit
b1fbce5b3c
25
nu_plugin_clipboard/.github/workflows/publish_crate.yaml
vendored
Normal file
25
nu_plugin_clipboard/.github/workflows/publish_crate.yaml
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
name: Publish Crate
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Rust toolchain
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
override: true
|
||||||
|
|
||||||
|
- name: Publish to crates.io
|
||||||
|
uses: katyo/publish-crates@v2
|
||||||
|
with:
|
||||||
|
registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
||||||
|
env:
|
||||||
|
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
3
nu_plugin_clipboard/.gitignore
vendored
Normal file
3
nu_plugin_clipboard/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
/target
|
||||||
|
.vscode
|
||||||
|
.idea/
|
2316
nu_plugin_clipboard/Cargo.lock
generated
Normal file
2316
nu_plugin_clipboard/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
32
nu_plugin_clipboard/Cargo.toml
Normal file
32
nu_plugin_clipboard/Cargo.toml
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
[package]
|
||||||
|
name = "nu_plugin_clipboard"
|
||||||
|
license = "MIT"
|
||||||
|
authors = [
|
||||||
|
"Motalleb Fallahnezhad <fmotalleb@gmail.com>",
|
||||||
|
]
|
||||||
|
keywords = [
|
||||||
|
"nushell",
|
||||||
|
"clipboard",
|
||||||
|
"plugin",
|
||||||
|
]
|
||||||
|
homepage = "https://github.com/FMotalleb/nu_plugin_clipboard"
|
||||||
|
repository = "https://github.com/FMotalleb/nu_plugin_clipboard"
|
||||||
|
description = "A nushell plugin to copy text into clipboard or get text from it."
|
||||||
|
version = "0.105.2"
|
||||||
|
edition = "2024"
|
||||||
|
readme = "README.md"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
nu-plugin = { version = "0.105.2", path = "../nushell/crates/nu-plugin" }
|
||||||
|
nu-protocol = { version = "0.105.2", features = ["plugin"] , path = "../nushell/crates/nu-protocol" }
|
||||||
|
arboard = { version = "3.5.0", default-features = false }
|
||||||
|
nu-json = { path = "../nushell/crates/nu-json", version = "0.105.2" }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = [
|
||||||
|
]
|
||||||
|
use-wayland = [
|
||||||
|
"arboard/wayland-data-control",
|
||||||
|
]
|
||||||
|
debug = [
|
||||||
|
]
|
7
nu_plugin_clipboard/LICENSE
Normal file
7
nu_plugin_clipboard/LICENSE
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Copyright 2023 "Motalleb Fallahnezhad"
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
95
nu_plugin_clipboard/README.md
Normal file
95
nu_plugin_clipboard/README.md
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
# 📋 nu_plugin_clipboard
|
||||||
|
|
||||||
|
A [nushell](https://www.nushell.sh/) plugin for interacting with the clipboard, allowing you to copy/paste text, objects, and tables.
|
||||||
|
|
||||||
|
## ✨ Features
|
||||||
|
|
||||||
|
- **`clipboard copy`**: Copies input text to the clipboard.
|
||||||
|
- **Daemon Behavior:** Since version **0.105.2**, using env variables will try to detect display server.
|
||||||
|
This config will override this behavior, if you need to override this please report and issue:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$env.config.plugins.clipboard.NO_DAEMON = true
|
||||||
|
```
|
||||||
|
|
||||||
|
- To make this setting permanent, add it to your `config env`.
|
||||||
|
|
||||||
|
- **`clipboard paste`**: Retrieves the current clipboard content.
|
||||||
|
|
||||||
|
## ⚠️ Important
|
||||||
|
|
||||||
|
If you face the error `Error: × Clipboard Error: The clipboard contents were not available in the requested format...`
|
||||||
|
Try disabling the daemon mode, as mentioned in [#20](https://github.com/FMotalleb/nu_plugin_clipboard/issues/20).
|
||||||
|
|
||||||
|
## 📌 Usage Examples
|
||||||
|
|
||||||
|
### Copying a string (supports only strings for now)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
echo "test value" | clipboard copy
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using clipboard content
|
||||||
|
|
||||||
|
```bash
|
||||||
|
clipboard paste | echo $in
|
||||||
|
```
|
||||||
|
|
||||||
|
### Copying tables and objects
|
||||||
|
|
||||||
|
- Tables and objects are internally converted to **JSON**.
|
||||||
|
- When pasting, `clipboard paste` tries to parse JSON into a table or object.
|
||||||
|
- If parsing fails, the content is returned as a string.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$env | clipboard copy
|
||||||
|
clipboard paste
|
||||||
|
|
||||||
|
ps | clipboard copy
|
||||||
|
clipboard paste
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔧 Installation
|
||||||
|
|
||||||
|
### 🚀 Recommended: Using [nupm](https://github.com/nushell/nupm)
|
||||||
|
|
||||||
|
This method automatically handles dependencies and features:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/FMotalleb/nu_plugin_clipboard.git
|
||||||
|
nupm install --path nu_plugin_clipboard -f
|
||||||
|
```
|
||||||
|
|
||||||
|
### ⚙️ Supported Features
|
||||||
|
|
||||||
|
- **`use-wayland`**: Prioritizes the Wayland API, but falls back to X11 if needed.
|
||||||
|
- **`enforce-daemon`**: _(Deprecated)_ Now always enabled on Linux. Disable with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$env.config.plugins.clipboard.NO_DAEMON = true
|
||||||
|
```
|
||||||
|
|
||||||
|
### 🛠️ Manual Compilation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/FMotalleb/nu_plugin_clipboard.git
|
||||||
|
cd nu_plugin_clipboard
|
||||||
|
cargo build -r
|
||||||
|
plugin add target/release/nu_plugin_clipboard
|
||||||
|
```
|
||||||
|
|
||||||
|
### 📦 Install via Cargo (using git)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo install --git https://github.com/FMotalleb/nu_plugin_clipboard.git
|
||||||
|
plugin add ~/.cargo/bin/nu_plugin_clipboard
|
||||||
|
```
|
||||||
|
|
||||||
|
### 📦 Install via Cargo (crates.io) _Not Recommended_
|
||||||
|
|
||||||
|
- Since I live in Iran and crates.io won't let me update my packages like a normal person, most of the time crates.io is outdated.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo install nu_plugin_clipboard
|
||||||
|
plugin add ~/.cargo/bin/nu_plugin_clipboard
|
||||||
|
```
|
44
nu_plugin_clipboard/build.nu
Normal file
44
nu_plugin_clipboard/build.nu
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
use std log
|
||||||
|
|
||||||
|
let messages = {
|
||||||
|
"use-wayland" : $"Found (ansi blue)wayland(ansi reset) in env\(`(ansi blue)XDG_SESSION_TYPE(ansi reset)`\): activating `(ansi green)use-wayland(ansi reset)` feature"
|
||||||
|
"debug" : $"Debug mode is enabled: activating `(ansi green)debug(ansi reset)` feature"
|
||||||
|
}
|
||||||
|
def append-feature [active: bool,feature: string] {
|
||||||
|
if $active {
|
||||||
|
$in | append $feature
|
||||||
|
} else {
|
||||||
|
$in
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def main [package_file: path = nupm.nuon] {
|
||||||
|
let repo_root = (ls -f $package_file | first | get name | path dirname)
|
||||||
|
let install_root = $env.NUPM_HOME | path join "plugins"
|
||||||
|
|
||||||
|
let name = open ($repo_root | path join "Cargo.toml") | get package.name
|
||||||
|
let debug = (([no,yes] | input list "Enable debug mode (do not enable unless u wanna debug)") == "yes")
|
||||||
|
let use_wayland = ($nu.os-info.name == "linux" and ($env.XDG_SESSION_TYPE? == "wayland"))
|
||||||
|
let features = []
|
||||||
|
| append-feature $use_wayland use-wayland
|
||||||
|
| append-feature $debug debug
|
||||||
|
for feature in $features {
|
||||||
|
let message = $messages | get $feature
|
||||||
|
if $message != null {
|
||||||
|
log info $message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let channel = $repo_root
|
||||||
|
| path join rust-toolchain.toml
|
||||||
|
| open $in
|
||||||
|
| get toolchain?.channel?
|
||||||
|
| default stable
|
||||||
|
|
||||||
|
let cmd = $"cargo +($channel) install --path ($repo_root) --root ($install_root) --features=($features | str join ",")"
|
||||||
|
log info $"building plugin using: (ansi blue)($cmd)(ansi reset)"
|
||||||
|
nu -c $cmd
|
||||||
|
let ext: string = if ($nu.os-info.name == 'windows') { '.exe' } else { '' }
|
||||||
|
plugin add $"($install_root | path join "bin" $name)($ext)"
|
||||||
|
log info "do not forget to restart Nushell for the plugin to be fully available!"
|
||||||
|
}
|
7
nu_plugin_clipboard/nupm.nuon
Normal file
7
nu_plugin_clipboard/nupm.nuon
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"name": "nu_plugin_clipboard",
|
||||||
|
"version": "0.105.2",
|
||||||
|
"description": "A nushell plugin to copy text into clipboard or get text from it.",
|
||||||
|
"license": "LICENSE",
|
||||||
|
"type": "custom"
|
||||||
|
}
|
3
nu_plugin_clipboard/rust-toolchain.toml
Normal file
3
nu_plugin_clipboard/rust-toolchain.toml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[toolchain]
|
||||||
|
profile = "default"
|
||||||
|
channel = "stable"
|
12
nu_plugin_clipboard/src/clipboard/arboard_provider.rs
Normal file
12
nu_plugin_clipboard/src/clipboard/arboard_provider.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
use super::error_mapper::map_arboard_err_to_label;
|
||||||
|
use nu_protocol::LabeledError;
|
||||||
|
pub(crate) fn with_clipboard_instance<
|
||||||
|
U,
|
||||||
|
F: FnOnce(&mut arboard::Clipboard) -> Result<U, arboard::Error>,
|
||||||
|
>(
|
||||||
|
op: F,
|
||||||
|
) -> Result<U, LabeledError> {
|
||||||
|
let mut clipboard = arboard::Clipboard::new().map_err(map_arboard_err_to_label)?;
|
||||||
|
|
||||||
|
op(&mut clipboard).map_err(map_arboard_err_to_label)
|
||||||
|
}
|
60
nu_plugin_clipboard/src/clipboard/clipboard.rs
Normal file
60
nu_plugin_clipboard/src/clipboard/clipboard.rs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
use std::io::Error;
|
||||||
|
|
||||||
|
use nu_protocol::LabeledError;
|
||||||
|
|
||||||
|
use super::arboard_provider::with_clipboard_instance;
|
||||||
|
|
||||||
|
pub enum CheckResult {
|
||||||
|
Continue,
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
Exit(String, i32),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
fn no_daemon(config: Option<nu_protocol::Value>) -> Result<bool, Error> {
|
||||||
|
use crate::clipboard::detect_display::DisplayServer;
|
||||||
|
|
||||||
|
match config {
|
||||||
|
None => Ok(!DisplayServer::should_use_daemon()),
|
||||||
|
Some(nu_protocol::Value::Record { val, .. }) => {
|
||||||
|
return no_daemon(val.get("NO_DAEMON").cloned());
|
||||||
|
}
|
||||||
|
Some(nu_protocol::Value::Bool { val, .. }) => Ok(val),
|
||||||
|
Some(nu_protocol::Value::String { val, .. }) => match val.as_str() {
|
||||||
|
"true" | "True" | "1" => Ok(true),
|
||||||
|
_ => Ok(!DisplayServer::should_use_daemon()),
|
||||||
|
},
|
||||||
|
Some(nu_protocol::Value::Int { val, .. }) => Ok(val == 1),
|
||||||
|
_ => Ok(!DisplayServer::should_use_daemon()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub fn create_clipboard(config: Option<nu_protocol::Value>) -> impl Clipboard {
|
||||||
|
crate::clipboard::linux::ClipBoardLinux::new(!no_daemon(config).unwrap_or(false))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
pub fn create_clipboard(_: Option<nu_protocol::Value>) -> impl Clipboard {
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
{
|
||||||
|
crate::clipboard::mac_os::ClipBoardMacos::new()
|
||||||
|
}
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
{
|
||||||
|
crate::clipboard::windows::ClipBoardWindows::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Clipboard {
|
||||||
|
fn pre_execute_check(&self) -> CheckResult {
|
||||||
|
CheckResult::Continue
|
||||||
|
}
|
||||||
|
fn copy_text(&self, text: &str) -> Result<(), LabeledError> {
|
||||||
|
with_clipboard_instance(|clip| clip.set_text(text))
|
||||||
|
}
|
||||||
|
fn get_text(&self) -> Result<String, LabeledError> {
|
||||||
|
with_clipboard_instance(|clip| clip.get_text())
|
||||||
|
}
|
||||||
|
}
|
48
nu_plugin_clipboard/src/clipboard/detect_display.rs
Normal file
48
nu_plugin_clipboard/src/clipboard/detect_display.rs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
use std::env;
|
||||||
|
|
||||||
|
use crate::debug_println;
|
||||||
|
|
||||||
|
pub enum DisplayServer {
|
||||||
|
Wayland,
|
||||||
|
X11,
|
||||||
|
Unknown(String),
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DisplayServer {
|
||||||
|
fn detect() -> DisplayServer {
|
||||||
|
if let Ok(session_type) = env::var("XDG_SESSION_TYPE") {
|
||||||
|
debug_println!("found XDG_SESSION: {}", session_type);
|
||||||
|
match session_type.to_lowercase().as_str() {
|
||||||
|
"wayland" => DisplayServer::Wayland,
|
||||||
|
"x11" => DisplayServer::X11,
|
||||||
|
_ => DisplayServer::Unknown(session_type),
|
||||||
|
}
|
||||||
|
} else if env::var("WAYLAND_DISPLAY").is_ok() {
|
||||||
|
debug_println!("WAYLAND_DISPLAY is found");
|
||||||
|
DisplayServer::Wayland
|
||||||
|
} else if env::var("DISPLAY").is_ok() {
|
||||||
|
debug_println!("DISPLAY (X11) is found");
|
||||||
|
DisplayServer::X11
|
||||||
|
} else {
|
||||||
|
DisplayServer::None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn should_use_daemon() -> bool {
|
||||||
|
match Self::detect() {
|
||||||
|
DisplayServer::Wayland => false,
|
||||||
|
DisplayServer::X11 => true,
|
||||||
|
// Iam not sure about these values, let's assume they do not require a daemon
|
||||||
|
DisplayServer::Unknown(srv) => {
|
||||||
|
eprintln!(
|
||||||
|
"Unknown display server detected, assuming no daemon is needed, please report your display server's name in issue tracker. {}
|
||||||
|
If you experience issues with clipboard, follow daemon settings in https://github.com/FMotalleb/nu_plugin_clipboard.",
|
||||||
|
srv,
|
||||||
|
);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
DisplayServer::None => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3
nu_plugin_clipboard/src/clipboard/error_mapper.rs
Normal file
3
nu_plugin_clipboard/src/clipboard/error_mapper.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub(crate) fn map_arboard_err_to_label(err: arboard::Error) -> nu_protocol::LabeledError {
|
||||||
|
nu_protocol::LabeledError::new(format!("Clipboard Error: {}", err.to_string()))
|
||||||
|
}
|
103
nu_plugin_clipboard/src/clipboard/linux.rs
Normal file
103
nu_plugin_clipboard/src/clipboard/linux.rs
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
use std::{
|
||||||
|
env,
|
||||||
|
io::{stderr, stdout, Read, Write},
|
||||||
|
process::{Command, Stdio},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
arboard_provider::with_clipboard_instance,
|
||||||
|
clipboard::{CheckResult, Clipboard},
|
||||||
|
};
|
||||||
|
|
||||||
|
use arboard::Error;
|
||||||
|
use nu_protocol::LabeledError;
|
||||||
|
|
||||||
|
const DAEMONIZE_ARG: &str = "daemon-copy";
|
||||||
|
|
||||||
|
pub(crate) struct ClipBoardLinux {
|
||||||
|
pub(crate) use_daemon: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClipBoardLinux {
|
||||||
|
pub fn new(use_daemon: bool) -> Self {
|
||||||
|
Self { use_daemon }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_daemon_request() -> bool {
|
||||||
|
env::args().nth(1).as_deref() == Some(DAEMONIZE_ARG)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn request_daemon(&self, text: &str) -> Result<(), nu_protocol::LabeledError> {
|
||||||
|
match env::current_exe().map(|exe| spawn_daemon(text, exe)) {
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(err) => Err(nu_protocol::LabeledError::new(format!(
|
||||||
|
"Failed to spawn daemon process: {}",
|
||||||
|
err.to_string()
|
||||||
|
))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn copy_with_daemon() -> Result<(), LabeledError> {
|
||||||
|
with_clipboard_instance(|clip: &mut arboard::Clipboard| {
|
||||||
|
clip.clear()?;
|
||||||
|
let mut input = String::new();
|
||||||
|
if let Err(err) = std::io::stdin().read_to_string(&mut input) {
|
||||||
|
return Err(Error::Unknown {
|
||||||
|
description: format!("Failed to read from stdin: {}", err.to_string()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
clip.set_text(input)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spawn_daemon(text: &str, exe: std::path::PathBuf) -> Result<(), LabeledError> {
|
||||||
|
let child = Command::new(exe)
|
||||||
|
.arg(DAEMONIZE_ARG)
|
||||||
|
.stdin(Stdio::piped())
|
||||||
|
.stdout(stdout())
|
||||||
|
.stderr(stderr())
|
||||||
|
.current_dir(env::temp_dir())
|
||||||
|
.spawn();
|
||||||
|
if let Err(err) = child {
|
||||||
|
return Err(LabeledError::new(format!(
|
||||||
|
"failed to spawn daemon process: {}",
|
||||||
|
err
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
let mut child = child.unwrap();
|
||||||
|
if let Some(mut stdin) = child.stdin.take() {
|
||||||
|
if let Err(err) = stdin.write_all(text.as_bytes()) {
|
||||||
|
return Err(LabeledError::new(format!(
|
||||||
|
"Failed to write to stdin: {}",
|
||||||
|
err
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optionally, wait for the child process to finish
|
||||||
|
let status = child.wait();
|
||||||
|
status
|
||||||
|
.map(|_| ())
|
||||||
|
.map_err(|e| LabeledError::new(e.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clipboard for ClipBoardLinux {
|
||||||
|
fn pre_execute_check(&self) -> CheckResult {
|
||||||
|
match Self::is_daemon_request() {
|
||||||
|
true => match Self::copy_with_daemon() {
|
||||||
|
Err(e) => CheckResult::Exit(e.msg, 1),
|
||||||
|
_ => CheckResult::Exit("".to_string(), 0),
|
||||||
|
},
|
||||||
|
false => CheckResult::Continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn copy_text(&self, text: &str) -> Result<(), LabeledError> {
|
||||||
|
if self.use_daemon {
|
||||||
|
self.request_daemon(text)
|
||||||
|
} else {
|
||||||
|
with_clipboard_instance(|clip: &mut arboard::Clipboard| clip.set_text(text))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
nu_plugin_clipboard/src/clipboard/mac_os.rs
Normal file
11
nu_plugin_clipboard/src/clipboard/mac_os.rs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
use super::clipboard::Clipboard;
|
||||||
|
|
||||||
|
pub(crate) struct ClipBoardMacos;
|
||||||
|
|
||||||
|
impl ClipBoardMacos {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clipboard for ClipBoardMacos {}
|
14
nu_plugin_clipboard/src/clipboard/mod.rs
Normal file
14
nu_plugin_clipboard/src/clipboard/mod.rs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
mod arboard_provider;
|
||||||
|
pub mod clipboard;
|
||||||
|
mod error_mapper;
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
mod detect_display;
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub(crate) mod linux;
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
pub(crate) mod mac_os;
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
pub(crate) mod windows;
|
11
nu_plugin_clipboard/src/clipboard/windows.rs
Normal file
11
nu_plugin_clipboard/src/clipboard/windows.rs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
use super::clipboard::Clipboard;
|
||||||
|
|
||||||
|
pub(crate) struct ClipBoardWindows;
|
||||||
|
|
||||||
|
impl ClipBoardWindows {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clipboard for ClipBoardWindows {}
|
77
nu_plugin_clipboard/src/command/copy.rs
Normal file
77
nu_plugin_clipboard/src/command/copy.rs
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
use crate::clipboard::clipboard::Clipboard;
|
||||||
|
use crate::ClipboardPlugins;
|
||||||
|
use crate::{clipboard::clipboard::create_clipboard, utils::json};
|
||||||
|
use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand};
|
||||||
|
use nu_protocol::{Category, IntoPipelineData, LabeledError, PipelineData, Signature, Type, Value};
|
||||||
|
|
||||||
|
pub struct ClipboardCopy;
|
||||||
|
|
||||||
|
impl ClipboardCopy {
|
||||||
|
pub fn new() -> ClipboardCopy {
|
||||||
|
ClipboardCopy {}
|
||||||
|
}
|
||||||
|
fn format_json(input: &Value) -> Result<String, LabeledError> {
|
||||||
|
let json_value =
|
||||||
|
json::value_to_json_value(&input).map(|v| nu_json::to_string_with_indent(&v, 4));
|
||||||
|
|
||||||
|
match json_value {
|
||||||
|
Ok(Ok(text)) => Ok(text.to_owned()), // Return the owned String
|
||||||
|
Ok(Err(err)) => Err(LabeledError::new(format!(
|
||||||
|
"JSON Serialization Error: {}",
|
||||||
|
err,
|
||||||
|
))),
|
||||||
|
Err(err) => Err(LabeledError::new(format!("JSON Conversion Error: {}", err))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn copy(engine: &EngineInterface, input: &Value) -> Result<(), LabeledError> {
|
||||||
|
let text: Result<String, LabeledError> = match input {
|
||||||
|
Value::String { val, .. } => Ok(val.to_owned()),
|
||||||
|
_ => Self::format_json(input),
|
||||||
|
};
|
||||||
|
|
||||||
|
match text.map(|text| {
|
||||||
|
create_clipboard(engine.get_plugin_config().ok().unwrap_or(None))
|
||||||
|
.copy_text(text.as_str())
|
||||||
|
}) {
|
||||||
|
Ok(Ok(_)) => Ok(()),
|
||||||
|
Err(err) | Ok(Err(err)) => Err(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PluginCommand for ClipboardCopy {
|
||||||
|
type Plugin = ClipboardPlugins;
|
||||||
|
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"clipboard copy"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("clipboard copy")
|
||||||
|
.input_output_types(vec![(Type::Any, Type::Any)])
|
||||||
|
.category(Category::System)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_plugin: &Self::Plugin,
|
||||||
|
engine: &EngineInterface,
|
||||||
|
call: &EvaluatedCall,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, LabeledError> {
|
||||||
|
let value = input.into_value(call.head);
|
||||||
|
match value {
|
||||||
|
Ok(value) => {
|
||||||
|
if let Err(err) = Self::copy(engine, &value) {
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
Ok(value.into_pipeline_data())
|
||||||
|
}
|
||||||
|
Err(err) => Err(LabeledError::new(err.to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
"copy the input into the clipboard"
|
||||||
|
}
|
||||||
|
}
|
2
nu_plugin_clipboard/src/command/mod.rs
Normal file
2
nu_plugin_clipboard/src/command/mod.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
pub mod copy;
|
||||||
|
pub mod paste;
|
62
nu_plugin_clipboard/src/command/paste.rs
Normal file
62
nu_plugin_clipboard/src/command/paste.rs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
use crate::{
|
||||||
|
clipboard::clipboard::{create_clipboard, Clipboard},
|
||||||
|
utils::json::json_to_value,
|
||||||
|
ClipboardPlugins,
|
||||||
|
};
|
||||||
|
use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand};
|
||||||
|
use nu_protocol::{Category, IntoPipelineData, LabeledError, PipelineData, Type, Value};
|
||||||
|
|
||||||
|
pub struct ClipboardPaste;
|
||||||
|
|
||||||
|
impl ClipboardPaste {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PluginCommand for ClipboardPaste {
|
||||||
|
type Plugin = ClipboardPlugins;
|
||||||
|
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"clipboard paste"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
|
nu_protocol::Signature::build("clipboard paste")
|
||||||
|
.switch("raw", "disable json formatting", Some('r'))
|
||||||
|
.input_output_types(vec![(Type::Nothing, Type::Any)])
|
||||||
|
.category(Category::System)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
"Outputs the current value in clipboard"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_plugin: &Self::Plugin,
|
||||||
|
engine: &EngineInterface,
|
||||||
|
call: &EvaluatedCall,
|
||||||
|
_input: PipelineData,
|
||||||
|
) -> Result<PipelineData, LabeledError> {
|
||||||
|
let text = create_clipboard(engine.get_plugin_config().ok().flatten()).get_text()?;
|
||||||
|
if text.trim().is_empty() {
|
||||||
|
return Err(LabeledError::new("Empty clipboard".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if call.has_flag("raw").unwrap_or(false) {
|
||||||
|
return Ok(Value::string(text, call.head).into_pipeline_data());
|
||||||
|
}
|
||||||
|
|
||||||
|
match nu_json::from_str::<nu_json::Value>(&text) {
|
||||||
|
Ok(value) => json_to_value(value, call.head).map(|v| v.into_pipeline_data()),
|
||||||
|
Err(nu_json::Error::Syntax(_, _, _)) => {
|
||||||
|
Ok(Value::string(text, call.head).into_pipeline_data())
|
||||||
|
}
|
||||||
|
Err(e) => Err(LabeledError::new(format!(
|
||||||
|
"JSON Deserialization error: {}",
|
||||||
|
e
|
||||||
|
))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
47
nu_plugin_clipboard/src/main.rs
Normal file
47
nu_plugin_clipboard/src/main.rs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
mod clipboard;
|
||||||
|
mod command;
|
||||||
|
pub mod utils;
|
||||||
|
use std::io;
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
use std::{
|
||||||
|
io::{stderr, stdout, Write},
|
||||||
|
process::exit,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::command::copy::ClipboardCopy;
|
||||||
|
use crate::command::paste::ClipboardPaste;
|
||||||
|
use clipboard::clipboard::{create_clipboard, CheckResult, Clipboard};
|
||||||
|
use nu_plugin::PluginCommand;
|
||||||
|
|
||||||
|
pub struct ClipboardPlugins;
|
||||||
|
|
||||||
|
impl nu_plugin::Plugin for ClipboardPlugins {
|
||||||
|
fn commands(&self) -> Vec<Box<dyn PluginCommand<Plugin = Self>>> {
|
||||||
|
vec![
|
||||||
|
Box::new(ClipboardCopy::new()),
|
||||||
|
Box::new(ClipboardPaste::new()),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn version(&self) -> String {
|
||||||
|
env!("CARGO_PKG_VERSION").into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), io::Error> {
|
||||||
|
match create_clipboard(None).pre_execute_check() {
|
||||||
|
CheckResult::Continue => Ok(nu_plugin::serve_plugin(
|
||||||
|
&mut ClipboardPlugins {},
|
||||||
|
nu_plugin::MsgPackSerializer {},
|
||||||
|
)),
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
CheckResult::Exit(message, code) => {
|
||||||
|
if code != 0 {
|
||||||
|
writeln!(stderr(), "Error ({}): {}", code, message)?;
|
||||||
|
} else if !message.is_empty() {
|
||||||
|
writeln!(stdout(), "{}", message)?;
|
||||||
|
}
|
||||||
|
exit(code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
87
nu_plugin_clipboard/src/utils/json.rs
Normal file
87
nu_plugin_clipboard/src/utils/json.rs
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
use nu_protocol::ast::PathMember;
|
||||||
|
use nu_protocol::{LabeledError, Record, ShellError, Span, Value};
|
||||||
|
|
||||||
|
pub fn value_to_json_value(v: &Value) -> Result<nu_json::Value, ShellError> {
|
||||||
|
let span = v.span();
|
||||||
|
Ok(match v {
|
||||||
|
Value::Bool { val, .. } => nu_json::Value::Bool(*val),
|
||||||
|
Value::Filesize { val, .. } => nu_json::Value::I64(val.get()),
|
||||||
|
Value::Duration { val, .. } => nu_json::Value::I64(*val),
|
||||||
|
Value::Date { val, .. } => nu_json::Value::String(val.to_string()),
|
||||||
|
Value::Float { val, .. } => nu_json::Value::F64(*val),
|
||||||
|
Value::Int { val, .. } => nu_json::Value::I64(*val),
|
||||||
|
Value::Nothing { .. } => nu_json::Value::Null,
|
||||||
|
Value::String { val, .. } => nu_json::Value::String(val.to_string()),
|
||||||
|
Value::Glob { val, .. } => nu_json::Value::String(val.to_string()),
|
||||||
|
Value::CellPath { val, .. } => nu_json::Value::Array(
|
||||||
|
val.members
|
||||||
|
.iter()
|
||||||
|
.map(|x| match &x {
|
||||||
|
PathMember::String { val, .. } => Ok(nu_json::Value::String(val.clone())),
|
||||||
|
PathMember::Int { val, .. } => Ok(nu_json::Value::U64(*val as u64)),
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<nu_json::Value>, ShellError>>()?,
|
||||||
|
),
|
||||||
|
|
||||||
|
Value::List { vals, .. } => nu_json::Value::Array(json_list(vals)?),
|
||||||
|
Value::Error { error, .. } => return Err(*error.clone()),
|
||||||
|
Value::Closure { .. } | Value::Range { .. } => nu_json::Value::Null,
|
||||||
|
Value::Binary { val, .. } => {
|
||||||
|
nu_json::Value::Array(val.iter().map(|x| nu_json::Value::U64(*x as u64)).collect())
|
||||||
|
}
|
||||||
|
Value::Record { val, .. } => {
|
||||||
|
let mut m = nu_json::Map::new();
|
||||||
|
for (k, v) in &**val {
|
||||||
|
m.insert(k.clone(), value_to_json_value(v)?);
|
||||||
|
}
|
||||||
|
nu_json::Value::Object(m)
|
||||||
|
}
|
||||||
|
Value::Custom { val, .. } => {
|
||||||
|
let collected = val.to_base_value(span)?;
|
||||||
|
value_to_json_value(&collected)?
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn json_list(input: &[Value]) -> Result<Vec<nu_json::Value>, ShellError> {
|
||||||
|
let mut out = vec![];
|
||||||
|
|
||||||
|
for value in input {
|
||||||
|
out.push(value_to_json_value(value)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn json_to_value(v: nu_json::Value, span: Span) -> Result<Value, LabeledError> {
|
||||||
|
Ok(match v {
|
||||||
|
nu_json::Value::Null => Value::nothing(span),
|
||||||
|
nu_json::Value::Bool(val) => Value::bool(val, span),
|
||||||
|
nu_json::Value::I64(val) => Value::int(val, span),
|
||||||
|
nu_json::Value::U64(val) => {
|
||||||
|
if val <= i64::MAX as u64 {
|
||||||
|
let val = val as i64;
|
||||||
|
Value::int(val, span)
|
||||||
|
} else {
|
||||||
|
Value::string(format!("{}", val), span)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nu_json::Value::F64(val) => Value::float(val, span),
|
||||||
|
nu_json::Value::String(val) => Value::string(val, span),
|
||||||
|
nu_json::Value::Array(vec) => {
|
||||||
|
let arr: &mut Vec<Value> = &mut vec![];
|
||||||
|
for jval in vec {
|
||||||
|
arr.push(json_to_value(jval, span)?);
|
||||||
|
}
|
||||||
|
Value::list(arr.to_vec(), span)
|
||||||
|
}
|
||||||
|
nu_json::Value::Object(val) => {
|
||||||
|
let mut rec = Record::new();
|
||||||
|
for (k, v) in val {
|
||||||
|
let value = json_to_value(v, span)?;
|
||||||
|
rec.insert(k.clone(), value);
|
||||||
|
}
|
||||||
|
Value::record(rec, span)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
9
nu_plugin_clipboard/src/utils/log.rs
Normal file
9
nu_plugin_clipboard/src/utils/log.rs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#[macro_export]
|
||||||
|
macro_rules! debug_println {
|
||||||
|
($($arg:tt)*) => {
|
||||||
|
#[cfg(feature = "debug")]
|
||||||
|
{
|
||||||
|
eprintln!($($arg)*);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
3
nu_plugin_clipboard/src/utils/mod.rs
Normal file
3
nu_plugin_clipboard/src/utils/mod.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub mod json;
|
||||||
|
|
||||||
|
pub mod log;
|
30
nu_plugin_desktop_notifications/.github/workflows/publish_crate.yaml
vendored
Normal file
30
nu_plugin_desktop_notifications/.github/workflows/publish_crate.yaml
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
name: Publish Crate
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- Cargo.toml
|
||||||
|
release:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Rust toolchain
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
override: true
|
||||||
|
|
||||||
|
- name: Publish to crates.io
|
||||||
|
uses: katyo/publish-crates@v2
|
||||||
|
with:
|
||||||
|
registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
||||||
|
env:
|
||||||
|
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
2
nu_plugin_desktop_notifications/.gitignore
vendored
Normal file
2
nu_plugin_desktop_notifications/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
.vscode
|
2702
nu_plugin_desktop_notifications/Cargo.lock
generated
Normal file
2702
nu_plugin_desktop_notifications/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
22
nu_plugin_desktop_notifications/Cargo.toml
Normal file
22
nu_plugin_desktop_notifications/Cargo.toml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
[dependencies]
|
||||||
|
nu-plugin = { version = "0.105.2", path = "../nushell/crates/nu-plugin" }
|
||||||
|
|
||||||
|
[dependencies.notify-rust]
|
||||||
|
version = "4.11.7"
|
||||||
|
|
||||||
|
[dependencies.nu-protocol]
|
||||||
|
features = ["plugin"]
|
||||||
|
version = "0.105.2"
|
||||||
|
path = "../nushell/crates/nu-protocol"
|
||||||
|
|
||||||
|
[package]
|
||||||
|
authors = ["Motalleb Fallahnezhad <fmotalleb@gmail.com>"]
|
||||||
|
description = "A nushell plugin to send desktop notifications"
|
||||||
|
edition = "2024"
|
||||||
|
homepage = "https://github.com/FMotalleb/nu_plugin_desktop_notifications"
|
||||||
|
keywords = ["nushell", "desktop", "notification", "plugin"]
|
||||||
|
license = "MIT"
|
||||||
|
name = "nu_plugin_desktop_notifications"
|
||||||
|
readme = "README.md"
|
||||||
|
repository = "https://github.com/FMotalleb/nu_plugin_desktop_notifications"
|
||||||
|
version = "1.2.12"
|
7
nu_plugin_desktop_notifications/LICENSE
Normal file
7
nu_plugin_desktop_notifications/LICENSE
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Copyright 2023 "Motalleb Fallahnezhad"
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
93
nu_plugin_desktop_notifications/README.md
Normal file
93
nu_plugin_desktop_notifications/README.md
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
# 🔔 nu_plugin_desktop_notifications
|
||||||
|
|
||||||
|
A [Nushell](https://www.nushell.sh/) plugin for sending desktop notifications using [notify-rust](https://github.com/hoodie/notify-rust).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✨ Features
|
||||||
|
|
||||||
|
- **Send notifications** with custom title, body, icon, and app name.
|
||||||
|
- **Supports macOS, Windows, and Linux (XDG Desktop)**.
|
||||||
|
- **Configurable timeout** (for macOS and XDG desktops).
|
||||||
|
- **Error handling** with optional crash reporting.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📌 Usage
|
||||||
|
|
||||||
|
### **Sending a Notification**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
notify -t "Test notification body" --summary "Test title"
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Flags**
|
||||||
|
|
||||||
|
- `-h, --help` → Show help message.
|
||||||
|
- `-s, --summary <string>` → Title of the notification.
|
||||||
|
- `-t, --body <string>` → Body message of the notification.
|
||||||
|
- `--subtitle <string>` → Subtitle (macOS & Windows only).
|
||||||
|
- `-a, --app-name <string>` → App name for the notification.
|
||||||
|
- `-i, --icon <filepath>` → Path to an icon for the notification.
|
||||||
|
- `--timeout <duration>` → Duration before the notification disappears _(macOS & XDG Desktop only)_. Defaults to system settings.
|
||||||
|
- `--crash-on-error <filepath>` → Return an error if the notification fails.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 Example: Notify on Task Completion
|
||||||
|
|
||||||
|
Send a notification after a task completes, displaying the elapsed time:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
```bash
|
||||||
|
def "notify on done" [
|
||||||
|
task: closure
|
||||||
|
] {
|
||||||
|
let start = date now
|
||||||
|
let result = do $task
|
||||||
|
let end = date now
|
||||||
|
let total = $end - $start | format duration sec
|
||||||
|
let body = $"Task completed in ($total)"
|
||||||
|
notify -s "Task Finished" -t $body
|
||||||
|
return $result
|
||||||
|
}
|
||||||
|
|
||||||
|
notify on done { port scan 8.8.8.8 53 }
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 Installation
|
||||||
|
|
||||||
|
### 🚀 Recommended: Using [nupm](https://github.com/nushell/nupm)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/FMotalleb/nu_plugin_desktop_notifications.git
|
||||||
|
nupm install --path nu_plugin_desktop_notifications -f
|
||||||
|
```
|
||||||
|
|
||||||
|
### 🛠️ Manual Compilation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/FMotalleb/nu_plugin_desktop_notifications.git
|
||||||
|
cd nu_plugin_desktop_notifications
|
||||||
|
cargo build -r
|
||||||
|
register target/release/nu_plugin_desktop_notifications
|
||||||
|
```
|
||||||
|
|
||||||
|
### 📦 Install via Cargo (using git)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo install --git https://github.com/FMotalleb/nu_plugin_desktop_notifications.git
|
||||||
|
register ~/.cargo/bin/nu_plugin_desktop_notifications
|
||||||
|
```
|
||||||
|
|
||||||
|
### 📦 Install via Cargo (crates.io) _Not Recommended_
|
||||||
|
>
|
||||||
|
> _Since I live in Iran and crates.io often restricts package updates, the version there might be outdated._
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo install nu_plugin_desktop_notifications
|
||||||
|
register ~/.cargo/bin/nu_plugin_desktop_notifications
|
||||||
|
```
|
17
nu_plugin_desktop_notifications/build.nu
Normal file
17
nu_plugin_desktop_notifications/build.nu
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
use std log
|
||||||
|
|
||||||
|
|
||||||
|
def main [package_file: path = nupm.nuon] {
|
||||||
|
let repo_root = (ls -f $package_file | first | get name | path dirname)
|
||||||
|
let install_root = $env.NUPM_HOME | path join "plugins"
|
||||||
|
|
||||||
|
let name = open ($repo_root | path join "Cargo.toml") | get package.name
|
||||||
|
let features = []
|
||||||
|
|
||||||
|
let cmd = $"cargo install --path ($repo_root) --root ($install_root) --features=($features | str join ",")"
|
||||||
|
log info $"building plugin using: (ansi blue)($cmd)(ansi reset)"
|
||||||
|
nu -c $cmd
|
||||||
|
let ext: string = if ($nu.os-info.name == 'windows') { '.exe' } else { '' }
|
||||||
|
plugin add $"($install_root | path join "bin" $name)($ext)"
|
||||||
|
log info "do not forget to restart Nushell for the plugin to be fully available!"
|
||||||
|
}
|
7
nu_plugin_desktop_notifications/nupm.nuon
Normal file
7
nu_plugin_desktop_notifications/nupm.nuon
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"name": "nu_plugin_desktop_notifications",
|
||||||
|
"version": "1.2.11",
|
||||||
|
"description": "A nushell plugin to send desktop notifications",
|
||||||
|
"license": "LICENSE",
|
||||||
|
"type": "custom"
|
||||||
|
}
|
3
nu_plugin_desktop_notifications/rust-toolchain.toml
Normal file
3
nu_plugin_desktop_notifications/rust-toolchain.toml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[toolchain]
|
||||||
|
profile = "default"
|
||||||
|
channel = "stable"
|
19
nu_plugin_desktop_notifications/src/main.rs
Normal file
19
nu_plugin_desktop_notifications/src/main.rs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
use nu_plugin::{serve_plugin, Plugin};
|
||||||
|
|
||||||
|
use crate::notify::NotifyCommand;
|
||||||
|
mod notify;
|
||||||
|
pub struct NotifyPlugin;
|
||||||
|
|
||||||
|
impl Plugin for NotifyPlugin {
|
||||||
|
fn commands(&self) -> Vec<Box<dyn nu_plugin::PluginCommand<Plugin = Self>>> {
|
||||||
|
vec![Box::new(NotifyCommand::new())]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn version(&self) -> String {
|
||||||
|
env!("CARGO_PKG_VERSION").into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
serve_plugin(&mut NotifyPlugin {}, nu_plugin::MsgPackSerializer {})
|
||||||
|
}
|
132
nu_plugin_desktop_notifications/src/notify.rs
Normal file
132
nu_plugin_desktop_notifications/src/notify.rs
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use notify_rust::{Notification, Timeout};
|
||||||
|
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
|
||||||
|
use nu_protocol::{Category, LabeledError, Signature, SyntaxShape, Value};
|
||||||
|
|
||||||
|
use crate::NotifyPlugin;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct NotifyCommand;
|
||||||
|
impl NotifyCommand {
|
||||||
|
pub(crate) fn new() -> NotifyCommand {
|
||||||
|
NotifyCommand {}
|
||||||
|
}
|
||||||
|
pub fn load_string(call: &EvaluatedCall, name: &str) -> Option<String> {
|
||||||
|
let value = call.get_flag_value(name);
|
||||||
|
match value {
|
||||||
|
Some(Value::String { val, .. }) => Some(val),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SimplePluginCommand for NotifyCommand {
|
||||||
|
type Plugin = NotifyPlugin;
|
||||||
|
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"notify"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("notify")
|
||||||
|
.named(
|
||||||
|
"summary",
|
||||||
|
SyntaxShape::String,
|
||||||
|
"summary of the notification",
|
||||||
|
Some('s'),
|
||||||
|
)
|
||||||
|
.named(
|
||||||
|
"body",
|
||||||
|
SyntaxShape::String,
|
||||||
|
"body of the notification",
|
||||||
|
Some('t'),
|
||||||
|
)
|
||||||
|
.named(
|
||||||
|
"subtitle",
|
||||||
|
SyntaxShape::String,
|
||||||
|
"subtitle of the notification [macOS only]",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.named(
|
||||||
|
"app-name",
|
||||||
|
SyntaxShape::String,
|
||||||
|
"app name of the notification",
|
||||||
|
Some('a'),
|
||||||
|
)
|
||||||
|
.named(
|
||||||
|
"icon",
|
||||||
|
SyntaxShape::Filepath,
|
||||||
|
"path to the icon of the notification",
|
||||||
|
Some('i'),
|
||||||
|
)
|
||||||
|
.named(
|
||||||
|
"timeout",
|
||||||
|
SyntaxShape::Duration,
|
||||||
|
"duration of the notification [XDG Desktops only] (defaults to system default)",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.named(
|
||||||
|
"crash-on-error",
|
||||||
|
SyntaxShape::Filepath,
|
||||||
|
"returns notification error if encountered",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.category(Category::Experimental)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
"Send a desktop notification with customizable parameters."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_plugin: &Self::Plugin,
|
||||||
|
_engine: &EngineInterface,
|
||||||
|
call: &EvaluatedCall,
|
||||||
|
input: &Value,
|
||||||
|
) -> Result<Value, LabeledError> {
|
||||||
|
let mut notification = Notification::new();
|
||||||
|
if let Some(summary) = Self::load_string(call, "summary") {
|
||||||
|
notification.summary(&summary);
|
||||||
|
}
|
||||||
|
if let Some(body) = Self::load_string(call, "body") {
|
||||||
|
notification.body(&body);
|
||||||
|
}
|
||||||
|
if let Some(subtitle) = Self::load_string(call, "subtitle") {
|
||||||
|
notification.subtitle(&subtitle);
|
||||||
|
}
|
||||||
|
if let Some(app_name) = Self::load_string(call, "app-name") {
|
||||||
|
notification.appname(&app_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(icon) = Self::load_string(call, "icon") {
|
||||||
|
notification.icon(&icon);
|
||||||
|
} else {
|
||||||
|
notification.auto_icon();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(duration_value) = call.get_flag_value("timeout") {
|
||||||
|
match duration_value.as_duration() {
|
||||||
|
Ok(timeout) => {
|
||||||
|
if let Ok(nanos) = timeout.try_into() {
|
||||||
|
let duration = Timeout::from(Duration::from_nanos(nanos));
|
||||||
|
notification.timeout(duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match notification.show() {
|
||||||
|
Ok(_) => Ok(input.clone()),
|
||||||
|
Err(err) => {
|
||||||
|
if let Ok(true) = call.has_flag("crash-on-error") {
|
||||||
|
return Err(LabeledError::new(err.to_string())
|
||||||
|
.with_label("Notification Exception", call.head));
|
||||||
|
}
|
||||||
|
Ok(input.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
nu_plugin_hashes/.gitignore
vendored
Normal file
1
nu_plugin_hashes/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/target
|
2524
nu_plugin_hashes/Cargo.lock
generated
Normal file
2524
nu_plugin_hashes/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
96
nu_plugin_hashes/Cargo.toml
Normal file
96
nu_plugin_hashes/Cargo.toml
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
[package]
|
||||||
|
name = "nu_plugin_hashes"
|
||||||
|
description = "A Nushell plugin that adds 63 cryptographic hash functions from Hashes project"
|
||||||
|
keywords = ["nu", "plugin", "hash"]
|
||||||
|
categories = ["algorithms"]
|
||||||
|
repository = "https://github.com/ArmoredPony/nu_plugin_hashes"
|
||||||
|
license = "MIT"
|
||||||
|
version = "0.1.8"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = [
|
||||||
|
"ascon-hash",
|
||||||
|
"belt-hash",
|
||||||
|
"blake2",
|
||||||
|
"blake3",
|
||||||
|
"fsb",
|
||||||
|
"gost94",
|
||||||
|
"groestl",
|
||||||
|
"jh",
|
||||||
|
"md2",
|
||||||
|
"md4",
|
||||||
|
"ripemd",
|
||||||
|
"sha1",
|
||||||
|
"sha2",
|
||||||
|
"sha3",
|
||||||
|
"shabal",
|
||||||
|
"skein",
|
||||||
|
"sm3",
|
||||||
|
"streebog",
|
||||||
|
"tiger",
|
||||||
|
"whirlpool",
|
||||||
|
]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
nu-cmd-base = { version = "0.105.2", path = "../nushell/crates/nu-cmd-base" }
|
||||||
|
nu-plugin = { version = "0.105.2", path = "../nushell/crates/nu-plugin" }
|
||||||
|
nu-protocol = { version = "0.105.2", path = "../nushell/crates/nu-protocol" }
|
||||||
|
digest = "0.10.7"
|
||||||
|
ascon-hash = { version = "0.2.0", optional = true }
|
||||||
|
belt-hash = { version = "0.1.1", optional = true }
|
||||||
|
blake2 = { version = "0.10.6", optional = true }
|
||||||
|
blake3 = { version = "1.6.1", optional = true, default-features = false, features = [
|
||||||
|
"std",
|
||||||
|
"traits-preview",
|
||||||
|
] }
|
||||||
|
fsb = { version = "0.1.3", optional = true }
|
||||||
|
gost94 = { version = "0.10.4", optional = true }
|
||||||
|
groestl = { version = "0.10.1", optional = true }
|
||||||
|
jh = { version = "0.1.0", optional = true }
|
||||||
|
md2 = { version = "0.10.2", optional = true }
|
||||||
|
md4 = { version = "0.10.2", optional = true }
|
||||||
|
ripemd = { version = "0.1.3", optional = true }
|
||||||
|
sha1 = { version = "0.10.6", optional = true }
|
||||||
|
sha2 = { version = "0.10.9", optional = true }
|
||||||
|
sha3 = { version = "0.10.8", optional = true }
|
||||||
|
shabal = { version = "0.4.1", optional = true }
|
||||||
|
skein = { version = "0.1.0", optional = true }
|
||||||
|
sm3 = { version = "0.4.2", optional = true }
|
||||||
|
streebog = { version = "0.10.2", optional = true }
|
||||||
|
tiger = { version = "0.2.1", optional = true }
|
||||||
|
whirlpool = { version = "0.10.4", optional = true }
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
digest = "0.10.7"
|
||||||
|
ascon-hash = { version = "0.2.0", optional = true }
|
||||||
|
belt-hash = { version = "0.1.1", optional = true }
|
||||||
|
blake2 = { version = "0.10.6", optional = true }
|
||||||
|
blake3 = { version = "1.6.1", optional = true, default-features = false, features = [
|
||||||
|
"std",
|
||||||
|
"traits-preview",
|
||||||
|
] }
|
||||||
|
fsb = { version = "0.1.3", optional = true }
|
||||||
|
gost94 = { version = "0.10.4", optional = true }
|
||||||
|
groestl = { version = "0.10.1", optional = true }
|
||||||
|
jh = { version = "0.1.0", optional = true }
|
||||||
|
md2 = { version = "0.10.2", optional = true }
|
||||||
|
md4 = { version = "0.10.2", optional = true }
|
||||||
|
ripemd = { version = "0.1.3", optional = true }
|
||||||
|
sha1 = { version = "0.10.6", optional = true }
|
||||||
|
sha2 = { version = "0.10.8", optional = true }
|
||||||
|
sha3 = { version = "0.10.8", optional = true }
|
||||||
|
shabal = { version = "0.4.1", optional = true }
|
||||||
|
skein = { version = "0.1.0", optional = true }
|
||||||
|
sm3 = { version = "0.4.2", optional = true }
|
||||||
|
streebog = { version = "0.10.2", optional = true }
|
||||||
|
tiger = { version = "0.2.1", optional = true }
|
||||||
|
whirlpool = { version = "0.10.4", optional = true }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
nu-plugin-test-support = { version = "0.105.2", path = "../nushell/crates/nu-plugin-test-support" }
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
strip = true
|
||||||
|
lto = true
|
||||||
|
codegen-units = 1
|
7
nu_plugin_hashes/LICENSE
Normal file
7
nu_plugin_hashes/LICENSE
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Copyright 2024 ArmoredPony
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
126
nu_plugin_hashes/README.md
Normal file
126
nu_plugin_hashes/README.md
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
# Hashes for Nushell
|
||||||
|
|
||||||
|
A [Nushell](https://www.nushell.sh) plugin that adds a collection of **63**
|
||||||
|
cryptographic hash functions from [Hashes](https://github.com/RustCrypto/hashes)
|
||||||
|
project.
|
||||||
|
|
||||||
|
This plugin's implementation is based on code stolen from the official Nushell
|
||||||
|
repository and on compile-time code generation with a build script.
|
||||||
|
|
||||||
|
Excess algorithms can be filtered off by selecting only specific features of the
|
||||||
|
crate.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
To install this plugin with all algorithms available run
|
||||||
|
```nu
|
||||||
|
cargo install nu_plugin_hashes
|
||||||
|
plugin add ($env.CARGO_HOME ++ /bin/nu_plugin_hashes)
|
||||||
|
```
|
||||||
|
|
||||||
|
or on Windows
|
||||||
|
```nu
|
||||||
|
cargo install nu_plugin_hashes
|
||||||
|
plugin add ($env.CARGO_HOME ++ /bin/nu_plugin_hashes.exe)
|
||||||
|
```
|
||||||
|
|
||||||
|
After loading the plugin, execute `help hash` to list newly added commands
|
||||||
|
|
||||||
|
```nu
|
||||||
|
~> help hash
|
||||||
|
Apply hash function.
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
Subcommands:
|
||||||
|
hash ascon - Hash a value using the ascon hash algorithm.
|
||||||
|
hash ascon-a - Hash a value using the ascon-a hash algorithm.
|
||||||
|
hash belt - Hash a value using the belt hash algorithm.
|
||||||
|
hash blake2b-512 - Hash a value using the blake2b-512 hash algorithm.
|
||||||
|
hash blake2s-256 - Hash a value using the blake2s-256 hash algorithm.
|
||||||
|
hash fsb160 - Hash a value using the fsb160 hash algorithm.
|
||||||
|
hash fsb224 - Hash a value using the fsb224 hash algorithm.
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
If you only need some algorithms, disable default features and select only
|
||||||
|
those you need
|
||||||
|
```nu
|
||||||
|
cargo install nu_plugin_hashes --no-default-features --features sha2,streebog
|
||||||
|
```
|
||||||
|
|
||||||
|
Then check what's installed
|
||||||
|
```nu
|
||||||
|
~> help hash
|
||||||
|
Apply hash function.
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
Subcommands:
|
||||||
|
hash md5 - Hash a value using the md5 hash algorithm.
|
||||||
|
hash sha224 - Hash a value using the sha224 hash algorithm.
|
||||||
|
hash sha256 - Hash a value using the sha256 hash algorithm.
|
||||||
|
hash sha384 - Hash a value using the sha384 hash algorithm.
|
||||||
|
hash sha512 - Hash a value using the sha512 hash algorithm.
|
||||||
|
hash sha512-224 - Hash a value using the sha512-224 hash algorithm.
|
||||||
|
hash sha512-256 - Hash a value using the sha512-256 hash algorithm.
|
||||||
|
hash streebog256 - Hash a value using the streebog256 hash algorithm.
|
||||||
|
hash streebog512 - Hash a value using the streebog512 hash algorithm.
|
||||||
|
|
||||||
|
Flags:
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Hashes
|
||||||
|
|
||||||
|
The list of implemented algorithms provided by the plugin can be found
|
||||||
|
in the Hashes project [repository](https://github.com/RustCrypto/hashes).
|
||||||
|
|
||||||
|
Almost all algorithms from this project are included in this plugin. The
|
||||||
|
exceptions are MD5 and SHA-256 as they are already present in Nushell, and
|
||||||
|
those algorithms, that don't implement the `Digest` trait or require additional
|
||||||
|
arguments for them to be run.
|
||||||
|
|
||||||
|
If you want to import only particular algorithms, build this plugin with those
|
||||||
|
features, which names correspond to the crates, that supply the algorithms you
|
||||||
|
want. Consult this [table](https://github.com/RustCrypto/hashes?tab=readme-ov-file#supported-algorithms)
|
||||||
|
to match features with required algorithms.
|
||||||
|
|
||||||
|
If you disable the default features and forget to enable at least one of them,
|
||||||
|
the plugin won't compile.
|
||||||
|
|
||||||
|
## Implemetation details
|
||||||
|
|
||||||
|
All the functions are implemented via generic code that I borrowed from Nushell
|
||||||
|
source file [generic_digest.rs](https://github.com/nushell/nushell/blob/0.101.0/crates/nu-command/src/hash/generic_digest.rs).
|
||||||
|
Help page for each command is generated with a [build script](./build.rs).
|
||||||
|
Hashes of the test text for each example are generated during compilation by
|
||||||
|
this script: the test text is fed to each hashing algorithm, and resulting bytes
|
||||||
|
are inserted into examples. This approach isdifferent from Nushell's
|
||||||
|
implementations, where examples are hardcoded as strings. Then, generated
|
||||||
|
examples are tested with [nu-plugin-test-support](https://crates.io/crates/nu-plugin-test-support)
|
||||||
|
crate. This ensures that the code in examples always behaves exactly as printed.
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
Here is a list of things that could not be implemented with code generation
|
||||||
|
as they don't implement the `Digest` trait or require additional arguments
|
||||||
|
to be executed. I doubt that I will ever complete this list so you are welcome
|
||||||
|
to contribute.
|
||||||
|
|
||||||
|
- [ ] [blake2b] algorithm with runtime defined output size
|
||||||
|
- [ ] [sha1] algorithm with collision detection
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This crate is licensed under [MIT license](LICENSE).
|
||||||
|
|
||||||
|
---
|
||||||
|
<h6>I only created this plugin to explore compile-time code generation and
|
||||||
|
educate myself on subject of features. The product of my activity terrifies
|
||||||
|
me and I'm surprised it worked out at all.</h6>
|
||||||
|
|
||||||
|
[blake2b]: https://github.com/RustCrypto/hashes/blob/1dbb9535207176fceb93a8ec1d450712714aedec/blake2/src/lib.rs#L67
|
||||||
|
[sha1]: https://github.com/RustCrypto/hashes/tree/master/sha1-checked
|
675
nu_plugin_hashes/build.rs
Normal file
675
nu_plugin_hashes/build.rs
Normal file
@ -0,0 +1,675 @@
|
|||||||
|
use digest::DynDigest;
|
||||||
|
|
||||||
|
const TEST_TEXT: &str = "abcdefghijklmnopqrstuvwxyz";
|
||||||
|
|
||||||
|
struct GeneratedHasherImplMeta {
|
||||||
|
crate_name: &'static str,
|
||||||
|
hasher_type_name: &'static str,
|
||||||
|
hasher_command: &'static str,
|
||||||
|
hasher: Box<dyn DynDigest>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(
|
||||||
|
feature = "ascon-hash",
|
||||||
|
feature = "belt-hash",
|
||||||
|
feature = "blake2",
|
||||||
|
feature = "blake3",
|
||||||
|
feature = "fsb",
|
||||||
|
feature = "gost94",
|
||||||
|
feature = "groestl",
|
||||||
|
feature = "jh",
|
||||||
|
feature = "md2",
|
||||||
|
feature = "md4",
|
||||||
|
feature = "ripemd",
|
||||||
|
feature = "sha1",
|
||||||
|
feature = "sha2",
|
||||||
|
feature = "sha3",
|
||||||
|
feature = "shabal",
|
||||||
|
feature = "skein",
|
||||||
|
feature = "sm3",
|
||||||
|
feature = "streebog",
|
||||||
|
feature = "tiger",
|
||||||
|
feature = "whirlpool",
|
||||||
|
))]
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
use std::{env, io::Write, path::Path};
|
||||||
|
|
||||||
|
// MD5 and SHA256 are skipped on purpose
|
||||||
|
let hasher_impls: Vec<GeneratedHasherImplMeta> = vec![
|
||||||
|
#[cfg(feature = "ascon-hash")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "ascon_hash",
|
||||||
|
hasher_type_name: "AsconHash",
|
||||||
|
hasher_command: "ascon",
|
||||||
|
hasher: Box::new(ascon_hash::AsconHash::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "ascon-hash")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "ascon_hash",
|
||||||
|
hasher_type_name: "AsconAHash",
|
||||||
|
hasher_command: "ascon-a",
|
||||||
|
hasher: Box::new(ascon_hash::AsconAHash::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "belt-hash")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "belt_hash",
|
||||||
|
hasher_type_name: "BeltHash",
|
||||||
|
hasher_command: "belt",
|
||||||
|
hasher: Box::new(belt_hash::BeltHash::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "blake2")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "blake2",
|
||||||
|
hasher_type_name: "Blake2s256",
|
||||||
|
hasher_command: "blake2s-256",
|
||||||
|
hasher: Box::new(blake2::Blake2s256::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "blake2")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "blake2",
|
||||||
|
hasher_type_name: "Blake2b512",
|
||||||
|
hasher_command: "blake2b-512",
|
||||||
|
hasher: Box::new(blake2::Blake2b512::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "blake3")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "blake3",
|
||||||
|
hasher_type_name: "Hasher",
|
||||||
|
hasher_command: "blake3",
|
||||||
|
hasher: Box::new(blake3::Hasher::new()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "fsb")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "fsb",
|
||||||
|
hasher_type_name: "Fsb160",
|
||||||
|
hasher_command: "fsb160",
|
||||||
|
hasher: Box::new(fsb::Fsb160::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "fsb")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "fsb",
|
||||||
|
hasher_type_name: "Fsb224",
|
||||||
|
hasher_command: "fsb224",
|
||||||
|
hasher: Box::new(fsb::Fsb224::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "fsb")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "fsb",
|
||||||
|
hasher_type_name: "Fsb256",
|
||||||
|
hasher_command: "fsb256",
|
||||||
|
hasher: Box::new(fsb::Fsb256::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "fsb")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "fsb",
|
||||||
|
hasher_type_name: "Fsb384",
|
||||||
|
hasher_command: "fsb384",
|
||||||
|
hasher: Box::new(fsb::Fsb384::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "fsb")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "fsb",
|
||||||
|
hasher_type_name: "Fsb512",
|
||||||
|
hasher_command: "fsb512",
|
||||||
|
hasher: Box::new(fsb::Fsb512::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "gost94")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "gost94",
|
||||||
|
hasher_type_name: "Gost94CryptoPro",
|
||||||
|
hasher_command: "gost94-crypto-pro",
|
||||||
|
hasher: Box::new(gost94::Gost94CryptoPro::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "gost94")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "gost94",
|
||||||
|
hasher_type_name: "Gost94UA",
|
||||||
|
hasher_command: "gost94-ua",
|
||||||
|
hasher: Box::new(gost94::Gost94UA::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "gost94")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "gost94",
|
||||||
|
hasher_type_name: "Gost94s2015",
|
||||||
|
hasher_command: "gost94-2015",
|
||||||
|
hasher: Box::new(gost94::Gost94s2015::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "groestl")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "groestl",
|
||||||
|
hasher_type_name: "Groestl224",
|
||||||
|
hasher_command: "groestl224",
|
||||||
|
hasher: Box::new(groestl::Groestl224::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "groestl")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "groestl",
|
||||||
|
hasher_type_name: "Groestl256",
|
||||||
|
hasher_command: "groestl256",
|
||||||
|
hasher: Box::new(groestl::Groestl256::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "groestl")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "groestl",
|
||||||
|
hasher_type_name: "Groestl384",
|
||||||
|
hasher_command: "groestl384",
|
||||||
|
hasher: Box::new(groestl::Groestl384::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "groestl")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "groestl",
|
||||||
|
hasher_type_name: "Groestl512",
|
||||||
|
hasher_command: "groestl512",
|
||||||
|
hasher: Box::new(groestl::Groestl512::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "jh")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "jh",
|
||||||
|
hasher_type_name: "Jh224",
|
||||||
|
hasher_command: "jh224",
|
||||||
|
hasher: Box::new(jh::Jh224::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "jh")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "jh",
|
||||||
|
hasher_type_name: "Jh256",
|
||||||
|
hasher_command: "jh256",
|
||||||
|
hasher: Box::new(jh::Jh256::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "jh")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "jh",
|
||||||
|
hasher_type_name: "Jh384",
|
||||||
|
hasher_command: "jh384",
|
||||||
|
hasher: Box::new(jh::Jh384::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "jh")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "jh",
|
||||||
|
hasher_type_name: "Jh512",
|
||||||
|
hasher_command: "jh512",
|
||||||
|
hasher: Box::new(jh::Jh512::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "md2")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "md2",
|
||||||
|
hasher_type_name: "Md2",
|
||||||
|
hasher_command: "md2",
|
||||||
|
hasher: Box::new(md2::Md2::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "md4")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "md4",
|
||||||
|
hasher_type_name: "Md4",
|
||||||
|
hasher_command: "md4",
|
||||||
|
hasher: Box::new(md4::Md4::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "ripemd")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "ripemd",
|
||||||
|
hasher_type_name: "Ripemd128",
|
||||||
|
hasher_command: "ripemd128",
|
||||||
|
hasher: Box::new(ripemd::Ripemd128::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "ripemd")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "ripemd",
|
||||||
|
hasher_type_name: "Ripemd160",
|
||||||
|
hasher_command: "ripemd160",
|
||||||
|
hasher: Box::new(ripemd::Ripemd160::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "ripemd")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "ripemd",
|
||||||
|
hasher_type_name: "Ripemd256",
|
||||||
|
hasher_command: "ripemd256",
|
||||||
|
hasher: Box::new(ripemd::Ripemd256::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "ripemd")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "ripemd",
|
||||||
|
hasher_type_name: "Ripemd320",
|
||||||
|
hasher_command: "ripemd320",
|
||||||
|
hasher: Box::new(ripemd::Ripemd320::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "sha1")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "sha1",
|
||||||
|
hasher_type_name: "Sha1",
|
||||||
|
hasher_command: "sha1",
|
||||||
|
hasher: Box::new(sha1::Sha1::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "sha2")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "sha2",
|
||||||
|
hasher_type_name: "Sha224",
|
||||||
|
hasher_command: "sha224",
|
||||||
|
hasher: Box::new(sha2::Sha224::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "sha2")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "sha2",
|
||||||
|
hasher_type_name: "Sha384",
|
||||||
|
hasher_command: "sha384",
|
||||||
|
hasher: Box::new(sha2::Sha384::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "sha2")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "sha2",
|
||||||
|
hasher_type_name: "Sha512",
|
||||||
|
hasher_command: "sha512",
|
||||||
|
hasher: Box::new(sha2::Sha512::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "sha2")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "sha2",
|
||||||
|
hasher_type_name: "Sha512_224",
|
||||||
|
hasher_command: "sha512-224",
|
||||||
|
hasher: Box::new(sha2::Sha512_224::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "sha2")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "sha2",
|
||||||
|
hasher_type_name: "Sha512_256",
|
||||||
|
hasher_command: "sha512-256",
|
||||||
|
hasher: Box::new(sha2::Sha512_256::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "sha3")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "sha3",
|
||||||
|
hasher_type_name: "Sha3_224",
|
||||||
|
hasher_command: "sha3-224",
|
||||||
|
hasher: Box::new(sha3::Sha3_224::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "sha3")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "sha3",
|
||||||
|
hasher_type_name: "Sha3_256",
|
||||||
|
hasher_command: "sha3-256",
|
||||||
|
hasher: Box::new(sha3::Sha3_256::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "sha3")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "sha3",
|
||||||
|
hasher_type_name: "Sha3_384",
|
||||||
|
hasher_command: "sha3-384",
|
||||||
|
hasher: Box::new(sha3::Sha3_384::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "sha3")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "sha3",
|
||||||
|
hasher_type_name: "Sha3_512",
|
||||||
|
hasher_command: "sha3-512",
|
||||||
|
hasher: Box::new(sha3::Sha3_512::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "sha3")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "sha3",
|
||||||
|
hasher_type_name: "Keccak224",
|
||||||
|
hasher_command: "keccak224",
|
||||||
|
hasher: Box::new(sha3::Keccak224::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "sha3")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "sha3",
|
||||||
|
hasher_type_name: "Keccak256",
|
||||||
|
hasher_command: "keccak256",
|
||||||
|
hasher: Box::new(sha3::Keccak256::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "sha3")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "sha3",
|
||||||
|
hasher_type_name: "Keccak384",
|
||||||
|
hasher_command: "keccak384",
|
||||||
|
hasher: Box::new(sha3::Keccak384::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "sha3")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "sha3",
|
||||||
|
hasher_type_name: "Keccak512",
|
||||||
|
hasher_command: "keccak512",
|
||||||
|
hasher: Box::new(sha3::Keccak512::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "shabal")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "shabal",
|
||||||
|
hasher_type_name: "Shabal192",
|
||||||
|
hasher_command: "shabal192",
|
||||||
|
hasher: Box::new(shabal::Shabal192::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "shabal")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "shabal",
|
||||||
|
hasher_type_name: "Shabal224",
|
||||||
|
hasher_command: "shabal224",
|
||||||
|
hasher: Box::new(shabal::Shabal224::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "shabal")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "shabal",
|
||||||
|
hasher_type_name: "Shabal256",
|
||||||
|
hasher_command: "shabal256",
|
||||||
|
hasher: Box::new(shabal::Shabal256::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "shabal")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "shabal",
|
||||||
|
hasher_type_name: "Shabal384",
|
||||||
|
hasher_command: "shabal384",
|
||||||
|
hasher: Box::new(shabal::Shabal384::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "shabal")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "shabal",
|
||||||
|
hasher_type_name: "Shabal512",
|
||||||
|
hasher_command: "shabal512",
|
||||||
|
hasher: Box::new(shabal::Shabal512::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "skein")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "skein",
|
||||||
|
hasher_type_name: "Skein256::<skein::consts::U32>",
|
||||||
|
hasher_command: "skein256-32",
|
||||||
|
hasher: Box::new(skein::Skein256::<skein::consts::U32>::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "skein")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "skein",
|
||||||
|
hasher_type_name: "Skein256::<skein::consts::U64>",
|
||||||
|
hasher_command: "skein256-64",
|
||||||
|
hasher: Box::new(skein::Skein256::<skein::consts::U64>::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "skein")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "skein",
|
||||||
|
hasher_type_name: "Skein256::<skein::consts::U128>",
|
||||||
|
hasher_command: "skein256-128",
|
||||||
|
hasher: Box::new(skein::Skein256::<skein::consts::U128>::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "skein")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "skein",
|
||||||
|
hasher_type_name: "Skein512::<skein::consts::U32>",
|
||||||
|
hasher_command: "skein512-32",
|
||||||
|
hasher: Box::new(skein::Skein512::<skein::consts::U32>::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "skein")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "skein",
|
||||||
|
hasher_type_name: "Skein512::<skein::consts::U64>",
|
||||||
|
hasher_command: "skein512-64",
|
||||||
|
hasher: Box::new(skein::Skein512::<skein::consts::U64>::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "skein")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "skein",
|
||||||
|
hasher_type_name: "Skein512::<skein::consts::U128>",
|
||||||
|
hasher_command: "skein512-128",
|
||||||
|
hasher: Box::new(skein::Skein512::<skein::consts::U128>::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "skein")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "skein",
|
||||||
|
hasher_type_name: "Skein1024::<skein::consts::U32>",
|
||||||
|
hasher_command: "skein1024-32",
|
||||||
|
hasher: Box::new(skein::Skein1024::<skein::consts::U32>::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "skein")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "skein",
|
||||||
|
hasher_type_name: "Skein1024::<skein::consts::U64>",
|
||||||
|
hasher_command: "skein1024-64",
|
||||||
|
hasher: Box::new(skein::Skein1024::<skein::consts::U64>::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "skein")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "skein",
|
||||||
|
hasher_type_name: "Skein1024::<skein::consts::U128>",
|
||||||
|
hasher_command: "skein1024-128",
|
||||||
|
hasher: Box::new(skein::Skein1024::<skein::consts::U128>::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "sm3")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "sm3",
|
||||||
|
hasher_type_name: "Sm3",
|
||||||
|
hasher_command: "sm3",
|
||||||
|
hasher: Box::new(sm3::Sm3::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "streebog")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "streebog",
|
||||||
|
hasher_type_name: "Streebog256",
|
||||||
|
hasher_command: "streebog256",
|
||||||
|
hasher: Box::new(streebog::Streebog256::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "streebog")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "streebog",
|
||||||
|
hasher_type_name: "Streebog512",
|
||||||
|
hasher_command: "streebog512",
|
||||||
|
hasher: Box::new(streebog::Streebog512::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "tiger")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "tiger",
|
||||||
|
hasher_type_name: "Tiger",
|
||||||
|
hasher_command: "tiger",
|
||||||
|
hasher: Box::new(tiger::Tiger::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "tiger")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "tiger",
|
||||||
|
hasher_type_name: "Tiger2",
|
||||||
|
hasher_command: "tiger2",
|
||||||
|
hasher: Box::new(tiger::Tiger2::default()),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "whirlpool")]
|
||||||
|
GeneratedHasherImplMeta {
|
||||||
|
crate_name: "whirlpool",
|
||||||
|
hasher_type_name: "Whirlpool",
|
||||||
|
hasher_command: "whirlpool",
|
||||||
|
hasher: Box::new(whirlpool::Whirlpool::default()),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||||
|
let hashers_generated_path = Path::new(&out_dir).join("hashers_generated.rs");
|
||||||
|
let commands_generated_path =
|
||||||
|
Path::new(&out_dir).join("commands_generated.rs");
|
||||||
|
let mut hashers_generated_file =
|
||||||
|
std::fs::File::create(hashers_generated_path).unwrap();
|
||||||
|
let mut commands_generated_file =
|
||||||
|
std::fs::File::create(commands_generated_path).unwrap();
|
||||||
|
|
||||||
|
write!(
|
||||||
|
hashers_generated_file,
|
||||||
|
"use nu_protocol::{{Example, Span, Value, ShellError}};
|
||||||
|
use nu_plugin::PluginCommand;
|
||||||
|
use crate::HashesPlugin;
|
||||||
|
use crate::hasher::{{Hasher, GenericHasher}};
|
||||||
|
"
|
||||||
|
)?;
|
||||||
|
|
||||||
|
write!(
|
||||||
|
commands_generated_file,
|
||||||
|
"use nu_plugin::PluginCommand;
|
||||||
|
use crate::{{HashesPlugin, hasher::GenericHasher}};
|
||||||
|
|
||||||
|
pub fn commands() -> Vec<Box<dyn PluginCommand<Plugin = HashesPlugin>>> {{
|
||||||
|
vec![
|
||||||
|
"
|
||||||
|
)?;
|
||||||
|
|
||||||
|
for mut hasher_impl_meta in hasher_impls {
|
||||||
|
let feature_name =
|
||||||
|
hasher_impl_meta.crate_name.replace("-", "_").to_uppercase();
|
||||||
|
if std::env::var(format!("CARGO_FEATURE_{feature_name}")).is_ok() {
|
||||||
|
hashers_generated_file
|
||||||
|
.write_all(build_impl_str(&mut hasher_impl_meta).as_bytes())?;
|
||||||
|
hashers_generated_file
|
||||||
|
.write_all(build_test_str(&hasher_impl_meta).as_bytes())?;
|
||||||
|
|
||||||
|
let crate_name = hasher_impl_meta.crate_name;
|
||||||
|
let hasher_type_name = hasher_impl_meta.hasher_type_name;
|
||||||
|
writeln!(
|
||||||
|
commands_generated_file,
|
||||||
|
" Box::new(GenericHasher::<{crate_name}::{hasher_type_name}>::default()),"
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(
|
||||||
|
commands_generated_file,
|
||||||
|
" ]
|
||||||
|
}}"
|
||||||
|
)?;
|
||||||
|
|
||||||
|
hashers_generated_file.flush()?;
|
||||||
|
commands_generated_file.flush()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(
|
||||||
|
feature = "ascon-hash",
|
||||||
|
feature = "belt-hash",
|
||||||
|
feature = "blake2",
|
||||||
|
feature = "blake3",
|
||||||
|
feature = "fsb",
|
||||||
|
feature = "gost94",
|
||||||
|
feature = "groestl",
|
||||||
|
feature = "jh",
|
||||||
|
feature = "md2",
|
||||||
|
feature = "md4",
|
||||||
|
feature = "ripemd",
|
||||||
|
feature = "sha1",
|
||||||
|
feature = "sha2",
|
||||||
|
feature = "sha3",
|
||||||
|
feature = "shabal",
|
||||||
|
feature = "skein",
|
||||||
|
feature = "sm3",
|
||||||
|
feature = "streebog",
|
||||||
|
feature = "tiger",
|
||||||
|
feature = "whirlpool",
|
||||||
|
))]
|
||||||
|
fn build_impl_str(meta: &mut GeneratedHasherImplMeta) -> String {
|
||||||
|
let crate_name = meta.crate_name;
|
||||||
|
let hasher_type_name = meta.hasher_type_name;
|
||||||
|
let command = meta.hasher_command;
|
||||||
|
let mut hasher = meta.hasher.clone();
|
||||||
|
hasher.update(TEST_TEXT.as_bytes());
|
||||||
|
let hash = hasher.clone().finalize();
|
||||||
|
format!(
|
||||||
|
"
|
||||||
|
impl Hasher for {crate_name}::{hasher_type_name} {{
|
||||||
|
fn name() -> &'static str {{
|
||||||
|
\"{command}\"
|
||||||
|
}}
|
||||||
|
|
||||||
|
fn examples() -> Vec<Example<'static>> {{
|
||||||
|
vec![
|
||||||
|
Example {{
|
||||||
|
description: \"Return the {command} hash of a string, hex-encoded\",
|
||||||
|
example: \"'{TEST_TEXT}' | hash {command}\",
|
||||||
|
result: Some(Value::string(
|
||||||
|
\"{}\".to_owned(),
|
||||||
|
Span::test_data(),
|
||||||
|
)),
|
||||||
|
}},
|
||||||
|
Example {{
|
||||||
|
description: \"Return the {command} hash of a string, as binary\",
|
||||||
|
example: \"'{TEST_TEXT}' | hash {command} --binary\",
|
||||||
|
result: Some(Value::binary(
|
||||||
|
vec![{}],
|
||||||
|
Span::test_data(),
|
||||||
|
)),
|
||||||
|
}},
|
||||||
|
Example {{
|
||||||
|
description: \"Return the {command} hash of a file's contents\",
|
||||||
|
example: \"open ./nu_0_24_1_windows.zip | hash {command}\",
|
||||||
|
result: None,
|
||||||
|
}},
|
||||||
|
]
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
",
|
||||||
|
hash
|
||||||
|
.iter()
|
||||||
|
.map(|b| format!("{b:02x?}"))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(""),
|
||||||
|
hash
|
||||||
|
.iter()
|
||||||
|
.map(|b| format!("0x{b:02x?}"))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(",")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(
|
||||||
|
feature = "ascon-hash",
|
||||||
|
feature = "belt-hash",
|
||||||
|
feature = "blake2",
|
||||||
|
feature = "blake3",
|
||||||
|
feature = "fsb",
|
||||||
|
feature = "gost94",
|
||||||
|
feature = "groestl",
|
||||||
|
feature = "jh",
|
||||||
|
feature = "md2",
|
||||||
|
feature = "md4",
|
||||||
|
feature = "ripemd",
|
||||||
|
feature = "sha1",
|
||||||
|
feature = "sha2",
|
||||||
|
feature = "sha3",
|
||||||
|
feature = "shabal",
|
||||||
|
feature = "skein",
|
||||||
|
feature = "sm3",
|
||||||
|
feature = "streebog",
|
||||||
|
feature = "tiger",
|
||||||
|
feature = "whirlpool",
|
||||||
|
))]
|
||||||
|
fn build_test_str(meta: &GeneratedHasherImplMeta) -> String {
|
||||||
|
let crate_name = meta.crate_name;
|
||||||
|
let hasher_type_name = meta.hasher_type_name;
|
||||||
|
let command = meta.hasher_command;
|
||||||
|
let test_name = command.replace("-", "_");
|
||||||
|
format!(
|
||||||
|
"
|
||||||
|
#[test]
|
||||||
|
fn test_{test_name}_examples() -> Result<(), ShellError> {{
|
||||||
|
nu_plugin_test_support::PluginTest::new
|
||||||
|
(
|
||||||
|
\"hash {command}\",
|
||||||
|
HashesPlugin.into()
|
||||||
|
)?
|
||||||
|
.test_examples(&GenericHasher::<{crate_name}::{hasher_type_name}>::default().examples())
|
||||||
|
}}
|
||||||
|
"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(
|
||||||
|
feature = "ascon-hash",
|
||||||
|
feature = "belt-hash",
|
||||||
|
feature = "blake2",
|
||||||
|
feature = "blake3",
|
||||||
|
feature = "fsb",
|
||||||
|
feature = "gost94",
|
||||||
|
feature = "groestl",
|
||||||
|
feature = "jh",
|
||||||
|
feature = "md2",
|
||||||
|
feature = "md4",
|
||||||
|
feature = "ripemd",
|
||||||
|
feature = "sha1",
|
||||||
|
feature = "sha2",
|
||||||
|
feature = "sha3",
|
||||||
|
feature = "shabal",
|
||||||
|
feature = "skein",
|
||||||
|
feature = "sm3",
|
||||||
|
feature = "streebog",
|
||||||
|
feature = "tiger",
|
||||||
|
feature = "whirlpool",
|
||||||
|
)))]
|
||||||
|
fn main() {
|
||||||
|
compile_error!("enable at least one feature to compile this plugin");
|
||||||
|
}
|
1
nu_plugin_hashes/src/commands_generated.rs
Normal file
1
nu_plugin_hashes/src/commands_generated.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
include!(concat!(env!("OUT_DIR"), "/commands_generated.rs"));
|
173
nu_plugin_hashes/src/hasher.rs
Normal file
173
nu_plugin_hashes/src/hasher.rs
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
//! Contains a generic trait for hashers that implement `Digest`.
|
||||||
|
//! This implementation is stolen with minimal changes from *generic_digest.rs*
|
||||||
|
//! source code file of nushell v0.101.0 which can be found at
|
||||||
|
//! https://github.com/nushell/nushell/blob/0.101.0/crates/nu-command/src/hash/generic_digest.rs
|
||||||
|
//! The *hash* module is private, so I had no choice.
|
||||||
|
|
||||||
|
use std::{io::Write, marker::PhantomData, ops::Not};
|
||||||
|
|
||||||
|
use digest::{Digest, Output};
|
||||||
|
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
||||||
|
use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand};
|
||||||
|
use nu_protocol::{
|
||||||
|
ast::CellPath,
|
||||||
|
Category,
|
||||||
|
Example,
|
||||||
|
IntoPipelineData,
|
||||||
|
LabeledError,
|
||||||
|
PipelineData,
|
||||||
|
ShellError,
|
||||||
|
Signature,
|
||||||
|
Span,
|
||||||
|
SyntaxShape,
|
||||||
|
Type,
|
||||||
|
Value,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::HashesPlugin;
|
||||||
|
|
||||||
|
pub trait Hasher: Digest + Clone {
|
||||||
|
fn name() -> &'static str;
|
||||||
|
fn examples() -> Vec<Example<'static>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct GenericHasher<H: Hasher> {
|
||||||
|
name: String,
|
||||||
|
description: String,
|
||||||
|
_hasher: PhantomData<H>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H: Hasher> Default for GenericHasher<H> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
name: format!("hash {}", H::name()),
|
||||||
|
description: format!(
|
||||||
|
"Hash a value using the {} hash algorithm.",
|
||||||
|
H::name()
|
||||||
|
),
|
||||||
|
_hasher: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Arguments {
|
||||||
|
cell_paths: Option<Vec<CellPath>>,
|
||||||
|
binary: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CmdArgument for Arguments {
|
||||||
|
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||||
|
self.cell_paths.take()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H> PluginCommand for GenericHasher<H>
|
||||||
|
where
|
||||||
|
H: Hasher + Write + Send + Sync + 'static,
|
||||||
|
Output<H>: core::fmt::LowerHex,
|
||||||
|
{
|
||||||
|
type Plugin = HashesPlugin;
|
||||||
|
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build(self.name())
|
||||||
|
.category(Category::Hash)
|
||||||
|
.input_output_types(vec![
|
||||||
|
(Type::Binary, Type::Any),
|
||||||
|
(Type::String, Type::Any),
|
||||||
|
(Type::table(), Type::table()),
|
||||||
|
(Type::record(), Type::record()),
|
||||||
|
])
|
||||||
|
.allow_variants_without_examples(true)
|
||||||
|
.switch(
|
||||||
|
"binary",
|
||||||
|
"Output binary instead of hexadecimal representation",
|
||||||
|
Some('b'),
|
||||||
|
)
|
||||||
|
.rest(
|
||||||
|
"rest",
|
||||||
|
SyntaxShape::CellPath,
|
||||||
|
format!("Optionally {} hash data by cell path.", H::name()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
&self.description
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
H::examples()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_plugin: &HashesPlugin,
|
||||||
|
engine: &EngineInterface,
|
||||||
|
call: &EvaluatedCall,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, LabeledError> {
|
||||||
|
let head = call.head;
|
||||||
|
let binary = call.has_flag("binary")?;
|
||||||
|
let cell_paths: Vec<CellPath> = call.rest(0)?;
|
||||||
|
let cell_paths = cell_paths.is_empty().not().then_some(cell_paths);
|
||||||
|
|
||||||
|
if let PipelineData::ByteStream(stream, ..) = input {
|
||||||
|
let mut hasher = H::new();
|
||||||
|
stream.write_to(&mut hasher)?;
|
||||||
|
let digest = hasher.finalize();
|
||||||
|
if binary {
|
||||||
|
Ok(Value::binary(digest.to_vec(), head).into_pipeline_data())
|
||||||
|
} else {
|
||||||
|
Ok(Value::string(format!("{digest:x}"), head).into_pipeline_data())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
operate(
|
||||||
|
action::<H>,
|
||||||
|
Arguments { binary, cell_paths },
|
||||||
|
input,
|
||||||
|
head,
|
||||||
|
engine.signals(),
|
||||||
|
)
|
||||||
|
.map_err(Into::into)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn action<H>(input: &Value, args: &Arguments, _span: Span) -> Value
|
||||||
|
where
|
||||||
|
H: Hasher,
|
||||||
|
Output<H>: core::fmt::LowerHex,
|
||||||
|
{
|
||||||
|
let span = input.span();
|
||||||
|
let (bytes, span) = match input {
|
||||||
|
Value::String { val, .. } => (val.as_bytes(), span),
|
||||||
|
Value::Binary { val, .. } => (val.as_slice(), span),
|
||||||
|
// Propagate existing errors
|
||||||
|
Value::Error { .. } => return input.clone(),
|
||||||
|
other => {
|
||||||
|
let span = input.span();
|
||||||
|
|
||||||
|
return Value::error(
|
||||||
|
ShellError::OnlySupportsThisInputType {
|
||||||
|
exp_input_type: "string or binary".into(),
|
||||||
|
wrong_type: other.get_type().to_string(),
|
||||||
|
dst_span: span,
|
||||||
|
src_span: other.span(),
|
||||||
|
},
|
||||||
|
span,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let digest = H::digest(bytes);
|
||||||
|
|
||||||
|
if args.binary {
|
||||||
|
Value::binary(digest.to_vec(), span)
|
||||||
|
} else {
|
||||||
|
Value::string(format!("{digest:x}"), span)
|
||||||
|
}
|
||||||
|
}
|
2
nu_plugin_hashes/src/hashers_generated.rs
Normal file
2
nu_plugin_hashes/src/hashers_generated.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
#![allow(unused_imports)]
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/hashers_generated.rs"));
|
17
nu_plugin_hashes/src/lib.rs
Normal file
17
nu_plugin_hashes/src/lib.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
mod commands_generated;
|
||||||
|
mod hasher;
|
||||||
|
mod hashers_generated;
|
||||||
|
|
||||||
|
use nu_plugin::Plugin;
|
||||||
|
|
||||||
|
pub struct HashesPlugin;
|
||||||
|
|
||||||
|
impl Plugin for HashesPlugin {
|
||||||
|
fn version(&self) -> String {
|
||||||
|
env!("CARGO_PKG_VERSION").into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn commands(&self) -> Vec<Box<dyn nu_plugin::PluginCommand<Plugin = Self>>> {
|
||||||
|
commands_generated::commands()
|
||||||
|
}
|
||||||
|
}
|
6
nu_plugin_hashes/src/main.rs
Normal file
6
nu_plugin_hashes/src/main.rs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
use nu_plugin::{serve_plugin, MsgPackSerializer};
|
||||||
|
use nu_plugin_hashes::HashesPlugin;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
serve_plugin(&HashesPlugin, MsgPackSerializer);
|
||||||
|
}
|
5
nu_plugin_highlight/.gitignore
vendored
Normal file
5
nu_plugin_highlight/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Cargo build output
|
||||||
|
/target
|
||||||
|
|
||||||
|
# JetBrains IDE
|
||||||
|
/.idea
|
2376
nu_plugin_highlight/Cargo.lock
generated
Normal file
2376
nu_plugin_highlight/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
35
nu_plugin_highlight/Cargo.toml
Normal file
35
nu_plugin_highlight/Cargo.toml
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
[package]
|
||||||
|
name = "nu_plugin_highlight"
|
||||||
|
version = "1.4.7+0.105.2"
|
||||||
|
authors = ["Tim 'Piepmatz' Hesse"]
|
||||||
|
edition = "2024"
|
||||||
|
repository = "https://github.com/cptpiepmatz/nu-plugin-highlight"
|
||||||
|
description = "A nushell plugin for syntax highlighting"
|
||||||
|
license = "MIT"
|
||||||
|
keywords = ["nu", "plugin", "syntax", "highlighting"]
|
||||||
|
categories = ["command-line-utilities", "development-tools", "value-formatting"]
|
||||||
|
|
||||||
|
[workspace.dependencies]
|
||||||
|
# share dependencies with build dependencies
|
||||||
|
syntect = "5"
|
||||||
|
bat = { version = "0.24", default-features = false }
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
# nu
|
||||||
|
nu-plugin = { version = "0.105.2", path = "../nushell/crates/nu-plugin" }
|
||||||
|
nu-protocol = { version = "0.105.2", path = "../nushell/crates/nu-protocol" }
|
||||||
|
nu-path = { version = "0.105.2", path = "../nushell/crates/nu-path" }
|
||||||
|
|
||||||
|
# highlighting
|
||||||
|
syntect = { workspace = true }
|
||||||
|
nu-ansi-term = "0.50"
|
||||||
|
ansi_colours = "1"
|
||||||
|
bat = { workspace = true }
|
||||||
|
|
||||||
|
# guess the type
|
||||||
|
mime_guess = "2"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
patch-apply = "0.8.3"
|
||||||
|
syntect = { workspace = true }
|
||||||
|
bat = { workspace = true }
|
21
nu_plugin_highlight/LICENSE
Normal file
21
nu_plugin_highlight/LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2023 Tim 'Piepmatz' Hesse
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
146
nu_plugin_highlight/README.md
Normal file
146
nu_plugin_highlight/README.md
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
<h1 align="center">nu-plugin-highlight</h1>
|
||||||
|
<p align="center">
|
||||||
|
<b>
|
||||||
|
A <a href="https://www.nushell.sh">nushell</a>
|
||||||
|
<a href="https://www.nushell.sh/book/plugins.html">plugin</a> for syntax
|
||||||
|
highlighting.
|
||||||
|
</b>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
|
||||||
|
[](https://crates.io/crates/nu-plugin-highlight)
|
||||||
|
[](https://github.com/cptpiepmatz/nu-plugin-highlight/blob/main/LICENSE)
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
## About
|
||||||
|
`nu-plugin-highlight` is a plugin for [Nushell](https://www.nushell.sh) that
|
||||||
|
provides syntax highlighting for source code.
|
||||||
|
It uses the [`syntect`](https://crates.io/crates/syntect) library for syntax
|
||||||
|
highlighting and the [`bat`](https://crates.io/crates/bat) library for easy
|
||||||
|
access to its ready-to-use assets.
|
||||||
|
Custom themes can be loaded too.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
The `highlight` command can be used for syntax highlighting source code.
|
||||||
|
Here are a few examples:
|
||||||
|
```nushell
|
||||||
|
# Highlight a Markdown file by guessing the type from the pipeline metadata
|
||||||
|
open README.md | highlight
|
||||||
|
|
||||||
|
# Highlight a TOML file by its file extension
|
||||||
|
open Cargo.toml -r | echo $in | highlight toml
|
||||||
|
|
||||||
|
# Highlight a Rust file by programming language name
|
||||||
|
open src/main.rs | echo $in | highlight Rust
|
||||||
|
|
||||||
|
# Highlight a bash script by inferring the language (the file should start with a shebang)
|
||||||
|
open example.sh | echo $in | highlight
|
||||||
|
|
||||||
|
# Highlight a TOML file with a different theme
|
||||||
|
open Cargo.toml -r | highlight -t ansi
|
||||||
|
|
||||||
|
# List all available themes
|
||||||
|
highlight --list-themes
|
||||||
|
```
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
- `language <string>`:
|
||||||
|
This is an optional parameter that can be used to specify the language or file
|
||||||
|
extension to aid language detection.
|
||||||
|
|
||||||
|
### Flags
|
||||||
|
- `-h, --help`:
|
||||||
|
Display the help message for the highlight command.
|
||||||
|
|
||||||
|
- `-t, --theme <string>`:
|
||||||
|
The theme used for highlighting.
|
||||||
|
|
||||||
|
- `--list-themes`:
|
||||||
|
List all possible themes.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
The plugin can be configured using the
|
||||||
|
[`$env.config.plugins.highlight`](https://github.com/nushell/nushell/pull/10955)
|
||||||
|
variable.
|
||||||
|
|
||||||
|
### `true_colors`
|
||||||
|
Enable or disable true colors (24-bit).
|
||||||
|
By default, this is enabled.
|
||||||
|
```nushell
|
||||||
|
$env.config.plugins.highlight.true_colors = true
|
||||||
|
```
|
||||||
|
|
||||||
|
### `theme`
|
||||||
|
Set a theme to use.
|
||||||
|
The default theme depends on the operating system.
|
||||||
|
Use `highlight --list-themes | where default == true` to see your default theme.
|
||||||
|
Setting this environment variable should allow
|
||||||
|
`highlight --list-themes | where id == $env.config.plugins.highlight.theme` to
|
||||||
|
result in a single row with your selected theme.
|
||||||
|
If you get no results, you have set an invalid theme.
|
||||||
|
```nushell
|
||||||
|
$env.config.plugins.highlight.theme = ansi
|
||||||
|
```
|
||||||
|
|
||||||
|
### `custom_themes`
|
||||||
|
Set a directory to load custom themes from.
|
||||||
|
Using `synctect`s theme loader, you can load custom themes in the `.tmtheme`
|
||||||
|
format from a directory that is passed as this configuration value.
|
||||||
|
```nushell
|
||||||
|
$env.config.plugins.highlight.custom_themes = ~/.nu/highlight/themes
|
||||||
|
```
|
||||||
|
|
||||||
|
## Plugin Installation
|
||||||
|
Installing and registering the `nu-plugin-highlight` is a straightforward
|
||||||
|
process.
|
||||||
|
Follow these steps:
|
||||||
|
|
||||||
|
1. Install the plugin from crates.io using cargo:
|
||||||
|
```nushell
|
||||||
|
cargo install nu_plugin_highlight
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Restart your terminal session to ensure the newly installed plugin is recognized.
|
||||||
|
|
||||||
|
3. Find path of your installation:
|
||||||
|
```nushell
|
||||||
|
which nu_plugin_highlight
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Register the plugin with Nushell:
|
||||||
|
|
||||||
|
If you are using a version **lower** than **0.93.0**, use `register` instead of `plugin add`.
|
||||||
|
```nushell
|
||||||
|
plugin add path/to/the/plugin/binary
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Make the plugin available for use:
|
||||||
|
|
||||||
|
Tip: You can simply restart the shell or terminal. When nushell starts, it loads all plugins.
|
||||||
|
|
||||||
|
If you are using a version **lower** than **0.93.0**, you do **not need** to do this.
|
||||||
|
```nushell
|
||||||
|
plugin use highlight
|
||||||
|
```
|
||||||
|
|
||||||
|
After registering, the plugin is available as part of your set of commands:
|
||||||
|
|
||||||
|
```nushell
|
||||||
|
help commands | where command_type == "plugin"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Version Numbering
|
||||||
|
Starting with version `v1.1.0`, the version number of `nu-plugin-highlight`
|
||||||
|
incorporates the version number of its dependency, `nu-plugin`.
|
||||||
|
This is denoted in the format `v1.1.0+0.90.1`, where `v1.1.0` refers to the
|
||||||
|
version of `nu-plugin-highlight` and `0.90.1` refers to the version of the
|
||||||
|
`nu-plugin` dependency.
|
||||||
|
|
||||||
|
## License
|
||||||
|
`nu_plugin_highlight` is licensed under the MIT License.
|
||||||
|
See [LICENSE](LICENSE) for more information.
|
34
nu_plugin_highlight/build.rs
Normal file
34
nu_plugin_highlight/build.rs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use bat::assets::HighlightingAssets;
|
||||||
|
use patch_apply::Patch;
|
||||||
|
use syntect::parsing::SyntaxDefinition;
|
||||||
|
|
||||||
|
const NUSHELL_SYNTAX: &str = include_str!("./syntaxes/nushell/nushell.sublime-syntax");
|
||||||
|
const NUSHELL_PATCH: &str = include_str!("./syntaxes/patches/nushell.sublime-syntax.patch");
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
|
println!("cargo:rerun-if-changed=Cargo.toml");
|
||||||
|
println!("cargo:rerun-if-changed=syntaxes/nushell/nushell.sublime-syntax");
|
||||||
|
println!("cargo:rerun-if-changed=syntaxes/patches/nushell.sublime-syntax.patch");
|
||||||
|
println!("cargo:rerun-if-env-changed=OUT_DIR");
|
||||||
|
|
||||||
|
let syntax_set = HighlightingAssets::from_binary()
|
||||||
|
.get_syntax_set()
|
||||||
|
.unwrap()
|
||||||
|
.clone();
|
||||||
|
let mut syntax_set_builder = syntax_set.into_builder();
|
||||||
|
|
||||||
|
let patch = Patch::from_single(NUSHELL_PATCH).unwrap();
|
||||||
|
let syntax = NUSHELL_SYNTAX.to_string();
|
||||||
|
let syntax = patch_apply::apply(syntax, patch);
|
||||||
|
let syntax = SyntaxDefinition::load_from_str(&syntax, true, Some("nushell")).unwrap();
|
||||||
|
|
||||||
|
syntax_set_builder.add(syntax);
|
||||||
|
let syntax_set = syntax_set_builder.build();
|
||||||
|
|
||||||
|
let out_path = std::env::var("OUT_DIR").unwrap();
|
||||||
|
let out_path = PathBuf::from(out_path).join("syntax_set.bin");
|
||||||
|
syntect::dumps::dump_to_uncompressed_file(&syntax_set, out_path).unwrap();
|
||||||
|
}
|
14
nu_plugin_highlight/rustfmt.toml
Normal file
14
nu_plugin_highlight/rustfmt.toml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
unstable_features = true
|
||||||
|
edition = "2021"
|
||||||
|
binop_separator = "Back"
|
||||||
|
control_brace_style = "ClosingNextLine"
|
||||||
|
format_strings = true
|
||||||
|
hex_literal_case = "Upper"
|
||||||
|
imports_granularity = "Module"
|
||||||
|
overflow_delimited_expr = true
|
||||||
|
reorder_impl_items = true
|
||||||
|
reorder_imports = true
|
||||||
|
group_imports = "StdExternalCrate"
|
||||||
|
trailing_comma = "Never"
|
||||||
|
use_field_init_shorthand = true
|
||||||
|
wrap_comments = true
|
158
nu_plugin_highlight/src/highlight.rs
Normal file
158
nu_plugin_highlight/src/highlight.rs
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
use std::ops::Deref;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use bat::assets::HighlightingAssets;
|
||||||
|
use syntect::easy::HighlightLines;
|
||||||
|
use syntect::highlighting::ThemeSet;
|
||||||
|
use syntect::parsing::{SyntaxReference, SyntaxSet};
|
||||||
|
use syntect::LoadingError;
|
||||||
|
|
||||||
|
use crate::terminal;
|
||||||
|
use crate::theme::{ListThemes, ThemeDescription};
|
||||||
|
|
||||||
|
const SYNTAX_SET: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/syntax_set.bin"));
|
||||||
|
|
||||||
|
/// The struct that handles the highlighting of code.
|
||||||
|
pub struct Highlighter {
|
||||||
|
syntax_set: SyntaxSet,
|
||||||
|
highlighting_assets: HighlightingAssets,
|
||||||
|
custom_themes: Option<ThemeSet>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Highlighter {
|
||||||
|
/// Creates a new instance of the Highlighter.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Highlighter {
|
||||||
|
syntax_set: syntect::dumps::from_uncompressed_data(SYNTAX_SET).unwrap(),
|
||||||
|
highlighting_assets: HighlightingAssets::from_binary(),
|
||||||
|
custom_themes: None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn custom_themes_from_folder(
|
||||||
|
&mut self,
|
||||||
|
path: impl AsRef<Path>
|
||||||
|
) -> Result<(), LoadingError> {
|
||||||
|
let path = nu_path::expand_to_real_path(path);
|
||||||
|
self.custom_themes = Some(ThemeSet::load_from_folder(path)?);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lists all the available themes.
|
||||||
|
pub fn list_themes(&self, user_default: Option<&str>) -> ListThemes {
|
||||||
|
let ha = &self.highlighting_assets;
|
||||||
|
let default_theme_id = user_default.unwrap_or(HighlightingAssets::default_theme());
|
||||||
|
|
||||||
|
let mut themes: Vec<_> = ha
|
||||||
|
.themes()
|
||||||
|
.map(|t_id| {
|
||||||
|
let theme = ha.get_theme(t_id);
|
||||||
|
ThemeDescription {
|
||||||
|
id: t_id.to_owned(),
|
||||||
|
name: theme.name.clone(),
|
||||||
|
author: theme.author.clone(),
|
||||||
|
default: default_theme_id == t_id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if let Some(custom_themes) = self.custom_themes.as_ref() {
|
||||||
|
for (id, theme) in custom_themes.themes.iter() {
|
||||||
|
themes.push(ThemeDescription {
|
||||||
|
id: id.to_owned(),
|
||||||
|
name: theme.name.clone(),
|
||||||
|
author: theme.author.clone(),
|
||||||
|
default: default_theme_id == id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ListThemes(themes)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if a given theme id is valid.
|
||||||
|
pub fn is_valid_theme(&self, theme_name: &str) -> bool {
|
||||||
|
let ha = &self.highlighting_assets;
|
||||||
|
let custom_themes = self
|
||||||
|
.custom_themes
|
||||||
|
.as_ref()
|
||||||
|
.map(|themes| themes.themes.keys())
|
||||||
|
.unwrap_or_default()
|
||||||
|
.map(Deref::deref);
|
||||||
|
custom_themes.chain(ha.themes()).any(|t| t == theme_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Highlights the given input text based on the provided language and
|
||||||
|
/// theme.
|
||||||
|
pub fn highlight(
|
||||||
|
&self,
|
||||||
|
input: &str,
|
||||||
|
language: Option<&str>,
|
||||||
|
theme: Option<&str>,
|
||||||
|
true_colors: bool
|
||||||
|
) -> String {
|
||||||
|
let syntax_set = &self.syntax_set;
|
||||||
|
let syntax_ref: Option<&SyntaxReference> = match language {
|
||||||
|
Some(language) if !language.is_empty() => {
|
||||||
|
// allow multiple variants to write the language
|
||||||
|
let language_lowercase = language.to_lowercase();
|
||||||
|
let language_capitalized = {
|
||||||
|
let mut chars = language.chars();
|
||||||
|
let mut out = String::with_capacity(language.len());
|
||||||
|
chars
|
||||||
|
.next()
|
||||||
|
.expect("language not empty")
|
||||||
|
.to_uppercase()
|
||||||
|
.for_each(|c| out.push(c));
|
||||||
|
chars.for_each(|c| out.push(c));
|
||||||
|
out
|
||||||
|
};
|
||||||
|
|
||||||
|
syntax_set
|
||||||
|
.find_syntax_by_name(language)
|
||||||
|
.or_else(|| syntax_set.find_syntax_by_name(&language_lowercase))
|
||||||
|
.or_else(|| syntax_set.find_syntax_by_name(&language_capitalized))
|
||||||
|
.or_else(|| syntax_set.find_syntax_by_extension(language))
|
||||||
|
.or_else(|| syntax_set.find_syntax_by_extension(&language_lowercase))
|
||||||
|
.or_else(|| syntax_set.find_syntax_by_extension(&language_capitalized))
|
||||||
|
}
|
||||||
|
_ => None
|
||||||
|
};
|
||||||
|
let syntax_ref = syntax_ref
|
||||||
|
.or(syntax_set.find_syntax_by_first_line(input))
|
||||||
|
.unwrap_or(syntax_set.find_syntax_plain_text());
|
||||||
|
|
||||||
|
let theme_id = match theme {
|
||||||
|
None => HighlightingAssets::default_theme(),
|
||||||
|
Some(theme) => theme
|
||||||
|
};
|
||||||
|
let theme = self
|
||||||
|
.custom_themes
|
||||||
|
.as_ref()
|
||||||
|
.map(|themes| themes.themes.get(theme_id))
|
||||||
|
.flatten();
|
||||||
|
let theme = theme.unwrap_or_else(|| self.highlighting_assets.get_theme(theme_id));
|
||||||
|
|
||||||
|
let mut highlighter = HighlightLines::new(syntax_ref, theme);
|
||||||
|
let line_count = input.lines().count();
|
||||||
|
input
|
||||||
|
.lines()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, l)| {
|
||||||
|
// insert a newline in between lines, this is necessary for bats syntax set
|
||||||
|
let l = match i == line_count - 1 {
|
||||||
|
false => format!("{}\n", l.trim_end()),
|
||||||
|
true => l.trim_end().to_owned()
|
||||||
|
};
|
||||||
|
|
||||||
|
let styled_lines = highlighter.highlight_line(&l, &syntax_set).unwrap();
|
||||||
|
styled_lines
|
||||||
|
.iter()
|
||||||
|
.map(|(style, s)| {
|
||||||
|
terminal::as_terminal_escaped(*style, s, true_colors, true, false, None)
|
||||||
|
})
|
||||||
|
.collect::<String>()
|
||||||
|
})
|
||||||
|
.collect::<String>()
|
||||||
|
}
|
||||||
|
}
|
12
nu_plugin_highlight/src/main.rs
Normal file
12
nu_plugin_highlight/src/main.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
use nu_plugin::{serve_plugin, MsgPackSerializer};
|
||||||
|
use plugin::HighlightPlugin;
|
||||||
|
|
||||||
|
mod highlight;
|
||||||
|
mod plugin;
|
||||||
|
mod terminal;
|
||||||
|
mod theme;
|
||||||
|
|
||||||
|
/// The main function that serves the plugin using MsgPackSerializer.
|
||||||
|
fn main() {
|
||||||
|
serve_plugin(&HighlightPlugin, MsgPackSerializer);
|
||||||
|
}
|
249
nu_plugin_highlight/src/plugin.rs
Normal file
249
nu_plugin_highlight/src/plugin.rs
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use mime_guess::Mime;
|
||||||
|
use nu_plugin::{EngineInterface, EvaluatedCall, Plugin, PluginCommand};
|
||||||
|
use nu_protocol::shell_error::io::IoError;
|
||||||
|
use nu_protocol::{
|
||||||
|
Category, DataSource, ErrorLabel, Example, FromValue, IntoValue, LabeledError, PipelineData,
|
||||||
|
PipelineMetadata, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value
|
||||||
|
};
|
||||||
|
use syntect::LoadingError;
|
||||||
|
|
||||||
|
use crate::highlight::Highlighter;
|
||||||
|
|
||||||
|
/// The struct that handles the plugin itself.
|
||||||
|
pub struct HighlightPlugin;
|
||||||
|
|
||||||
|
impl Plugin for HighlightPlugin {
|
||||||
|
fn commands(&self) -> Vec<Box<dyn PluginCommand<Plugin = Self>>> {
|
||||||
|
vec![Box::new(Highlight)]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn version(&self) -> String {
|
||||||
|
env!("CARGO_PKG_VERSION").into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, FromValue, Default)]
|
||||||
|
struct Config {
|
||||||
|
pub theme: Option<Spanned<String>>,
|
||||||
|
pub true_colors: Option<bool>,
|
||||||
|
pub custom_themes: Option<Spanned<PathBuf>>
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Highlight;
|
||||||
|
|
||||||
|
impl PluginCommand for Highlight {
|
||||||
|
type Plugin = HighlightPlugin;
|
||||||
|
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"highlight"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build(PluginCommand::name(self))
|
||||||
|
.optional(
|
||||||
|
"language",
|
||||||
|
SyntaxShape::String,
|
||||||
|
"language or file extension to help language detection"
|
||||||
|
)
|
||||||
|
.named(
|
||||||
|
"theme",
|
||||||
|
SyntaxShape::String,
|
||||||
|
"them used for highlighting",
|
||||||
|
Some('t')
|
||||||
|
)
|
||||||
|
.switch("list-themes", "list all possible themes", None)
|
||||||
|
.category(Category::Strings)
|
||||||
|
.input_output_type(Type::String, Type::String)
|
||||||
|
.input_output_type(
|
||||||
|
Type::Any,
|
||||||
|
Type::Table(
|
||||||
|
vec![
|
||||||
|
(String::from("id"), Type::String),
|
||||||
|
(String::from("name"), Type::String),
|
||||||
|
(String::from("author"), Type::String),
|
||||||
|
(String::from("default"), Type::Bool),
|
||||||
|
]
|
||||||
|
.into()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
"Syntax highlight source code."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_plugin: &Self::Plugin,
|
||||||
|
engine: &EngineInterface,
|
||||||
|
call: &EvaluatedCall,
|
||||||
|
input: PipelineData
|
||||||
|
) -> Result<PipelineData, LabeledError> {
|
||||||
|
let mut highlighter = Highlighter::new();
|
||||||
|
|
||||||
|
let config = Option::<Config>::from_value(engine.get_plugin_config()?.unwrap_or_default())?
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
if let Some(custom_themes_path) = config.custom_themes {
|
||||||
|
match highlighter.custom_themes_from_folder(&custom_themes_path.item) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(LoadingError::Io(err)) => {
|
||||||
|
return Err(LabeledError::from(ShellError::from(
|
||||||
|
IoError::new_with_additional_context(
|
||||||
|
err,
|
||||||
|
custom_themes_path.span,
|
||||||
|
custom_themes_path.item,
|
||||||
|
"Error while loading custom themes"
|
||||||
|
)
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
return Err(labeled_error(
|
||||||
|
err,
|
||||||
|
"Error while loading custom themes",
|
||||||
|
custom_themes_path.span,
|
||||||
|
None
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let theme = call
|
||||||
|
.get_flag_value("theme")
|
||||||
|
.map(Spanned::<String>::from_value)
|
||||||
|
.transpose()?
|
||||||
|
.or(config.theme);
|
||||||
|
if let Some(theme) = &theme {
|
||||||
|
if !highlighter.is_valid_theme(&theme.item) {
|
||||||
|
return Err(labeled_error(
|
||||||
|
"use `highlight --list-themes` to list all themes",
|
||||||
|
format!("Unknown passed theme {:?}", &theme.item),
|
||||||
|
theme.span,
|
||||||
|
None
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let theme = theme.map(|spanned| spanned.item);
|
||||||
|
let theme = theme.as_deref();
|
||||||
|
|
||||||
|
let true_colors = config.true_colors.unwrap_or(true);
|
||||||
|
|
||||||
|
if call.has_flag("list-themes")? {
|
||||||
|
let themes = highlighter.list_themes(theme).into_value(call.head);
|
||||||
|
return Ok(PipelineData::Value(themes, None));
|
||||||
|
}
|
||||||
|
|
||||||
|
let metadata = input.metadata();
|
||||||
|
let input = input.into_value(call.head)?;
|
||||||
|
let Spanned { item: input, span } = Spanned::<String>::from_value(input)?;
|
||||||
|
|
||||||
|
let language = language_hint(call, metadata.as_ref())?;
|
||||||
|
let highlighted = highlighter.highlight(&input, language.as_deref(), theme, true_colors);
|
||||||
|
let highlighted = Value::string(highlighted, span);
|
||||||
|
Ok(PipelineData::Value(highlighted, metadata))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
|
vec!["syntax", "highlight", "highlighting"]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
const fn example<'e>(description: &'e str, example: &'e str) -> Example<'e>
|
||||||
|
where
|
||||||
|
'e: 'static
|
||||||
|
{
|
||||||
|
Example {
|
||||||
|
example,
|
||||||
|
description,
|
||||||
|
result: None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vec![
|
||||||
|
example(
|
||||||
|
"Highlight a Markdown file by guessing the type from the pipeline metadata",
|
||||||
|
"open README.md | highlight"
|
||||||
|
),
|
||||||
|
example(
|
||||||
|
"Highlight a toml file by its file extension",
|
||||||
|
"open Cargo.toml -r | echo $in | highlight toml"
|
||||||
|
),
|
||||||
|
example(
|
||||||
|
"Highlight a rust file by programming language",
|
||||||
|
"open src/main.rs | echo $in | highlight Rust"
|
||||||
|
),
|
||||||
|
example(
|
||||||
|
"Highlight a bash script by inferring the language (needs shebang)",
|
||||||
|
"open example.sh | echo $in | highlight"
|
||||||
|
),
|
||||||
|
example(
|
||||||
|
"Highlight a toml file with another theme",
|
||||||
|
"open Cargo.toml -r | highlight -t ansi"
|
||||||
|
),
|
||||||
|
example("List all available themes", "highlight --list-themes"),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn language_hint(
|
||||||
|
call: &EvaluatedCall,
|
||||||
|
metadata: Option<&PipelineMetadata>
|
||||||
|
) -> Result<Option<String>, ShellError> {
|
||||||
|
// first use passed argument
|
||||||
|
let arg = call.opt(0)?.map(String::from_value).transpose()?;
|
||||||
|
|
||||||
|
// then try to parse a mime type
|
||||||
|
let content_type = || -> Option<String> {
|
||||||
|
let metadata = metadata?;
|
||||||
|
let content_type = metadata.content_type.as_ref();
|
||||||
|
let content_type = content_type?.as_str();
|
||||||
|
let content_type = Mime::from_str(content_type).ok()?;
|
||||||
|
let sub_type = content_type.subtype().to_string();
|
||||||
|
match sub_type.as_str() {
|
||||||
|
"tab-separated-values" => Some("tsv".to_string()),
|
||||||
|
"x-toml" => Some("toml".to_string()),
|
||||||
|
"x-nuscript" | "x-nushell" | "x-nuon" => Some("nushell".to_string()),
|
||||||
|
s if s.starts_with("x-") => None, // we cannot be sure about this type,
|
||||||
|
_ => Some(sub_type)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// as last resort, try to use the extension of data source
|
||||||
|
let data_source = || -> Option<String> {
|
||||||
|
let data_source = &metadata?.data_source;
|
||||||
|
let DataSource::FilePath(path) = data_source
|
||||||
|
else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
let extension = path.extension()?.to_string_lossy();
|
||||||
|
Some(extension.to_string())
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(arg.or_else(content_type).or_else(data_source))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Simple constructor for [`LabeledError`].
|
||||||
|
fn labeled_error(
|
||||||
|
msg: impl ToString,
|
||||||
|
label: impl ToString,
|
||||||
|
span: Span,
|
||||||
|
inner: impl Into<Option<ShellError>>
|
||||||
|
) -> LabeledError {
|
||||||
|
LabeledError {
|
||||||
|
msg: msg.to_string(),
|
||||||
|
labels: Box::new(vec![ErrorLabel {
|
||||||
|
text: label.to_string(),
|
||||||
|
span
|
||||||
|
}]),
|
||||||
|
code: None,
|
||||||
|
url: None,
|
||||||
|
help: None,
|
||||||
|
inner: match inner.into() {
|
||||||
|
Some(inner) => Box::new(vec![inner.into()]),
|
||||||
|
None => Box::new(vec![])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
87
nu_plugin_highlight/src/terminal.rs
Normal file
87
nu_plugin_highlight/src/terminal.rs
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
// pulled from:
|
||||||
|
// https://github.com/sharkdp/bat/blob/8676bbf97f2832ad2231e102ca9c9b7b72267fda/src/terminal.rs
|
||||||
|
// applied patch to 64-67 to fix default update construction
|
||||||
|
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
|
|
||||||
|
use nu_ansi_term::Color::{self, Fixed, Rgb};
|
||||||
|
use nu_ansi_term::{self, Style};
|
||||||
|
|
||||||
|
use syntect::highlighting::{self, FontStyle};
|
||||||
|
|
||||||
|
pub fn to_ansi_color(color: highlighting::Color, true_color: bool) -> Option<nu_ansi_term::Color> {
|
||||||
|
if color.a == 0 {
|
||||||
|
// Themes can specify one of the user-configurable terminal colors by
|
||||||
|
// encoding them as #RRGGBBAA with AA set to 00 (transparent) and RR set
|
||||||
|
// to the 8-bit color palette number. The built-in themes ansi, base16,
|
||||||
|
// and base16-256 use this.
|
||||||
|
Some(match color.r {
|
||||||
|
// For the first 8 colors, use the Color enum to produce ANSI escape
|
||||||
|
// sequences using codes 30-37 (foreground) and 40-47 (background).
|
||||||
|
// For example, red foreground is \x1b[31m. This works on terminals
|
||||||
|
// without 256-color support.
|
||||||
|
0x00 => Color::Black,
|
||||||
|
0x01 => Color::Red,
|
||||||
|
0x02 => Color::Green,
|
||||||
|
0x03 => Color::Yellow,
|
||||||
|
0x04 => Color::Blue,
|
||||||
|
0x05 => Color::Purple,
|
||||||
|
0x06 => Color::Cyan,
|
||||||
|
0x07 => Color::White,
|
||||||
|
// For all other colors, use Fixed to produce escape sequences using
|
||||||
|
// codes 38;5 (foreground) and 48;5 (background). For example,
|
||||||
|
// bright red foreground is \x1b[38;5;9m. This only works on
|
||||||
|
// terminals with 256-color support.
|
||||||
|
//
|
||||||
|
// TODO: When ansi_term adds support for bright variants using codes
|
||||||
|
// 90-97 (foreground) and 100-107 (background), we should use those
|
||||||
|
// for values 0x08 to 0x0f and only use Fixed for 0x10 to 0xff.
|
||||||
|
n => Fixed(n),
|
||||||
|
})
|
||||||
|
} else if color.a == 1 {
|
||||||
|
// Themes can specify the terminal's default foreground/background color
|
||||||
|
// (i.e. no escape sequence) using the encoding #RRGGBBAA with AA set to
|
||||||
|
// 01. The built-in theme ansi uses this.
|
||||||
|
None
|
||||||
|
} else if true_color {
|
||||||
|
Some(Rgb(color.r, color.g, color.b))
|
||||||
|
} else {
|
||||||
|
Some(Fixed(ansi_colours::ansi256_from_rgb((
|
||||||
|
color.r, color.g, color.b,
|
||||||
|
))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_terminal_escaped(
|
||||||
|
style: highlighting::Style,
|
||||||
|
text: &str,
|
||||||
|
true_color: bool,
|
||||||
|
colored: bool,
|
||||||
|
italics: bool,
|
||||||
|
background_color: Option<highlighting::Color>,
|
||||||
|
) -> String {
|
||||||
|
if text.is_empty() {
|
||||||
|
return text.to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut style = if !colored {
|
||||||
|
Style::default()
|
||||||
|
} else {
|
||||||
|
let mut color = Style {
|
||||||
|
foreground: to_ansi_color(style.foreground, true_color),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
if style.font_style.contains(FontStyle::BOLD) {
|
||||||
|
color = color.bold();
|
||||||
|
}
|
||||||
|
if style.font_style.contains(FontStyle::UNDERLINE) {
|
||||||
|
color = color.underline();
|
||||||
|
}
|
||||||
|
if italics && style.font_style.contains(FontStyle::ITALIC) {
|
||||||
|
color = color.italic();
|
||||||
|
}
|
||||||
|
color
|
||||||
|
};
|
||||||
|
|
||||||
|
style.background = background_color.and_then(|c| to_ansi_color(c, true_color));
|
||||||
|
style.paint(text).to_string()
|
||||||
|
}
|
20
nu_plugin_highlight/src/theme.rs
Normal file
20
nu_plugin_highlight/src/theme.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
use nu_protocol::{IntoValue, Span, Value};
|
||||||
|
|
||||||
|
/// Description of a theme.
|
||||||
|
#[derive(Debug, IntoValue)]
|
||||||
|
pub struct ThemeDescription {
|
||||||
|
pub id: String,
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub author: Option<String>,
|
||||||
|
pub default: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List of theme descriptions.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ListThemes(pub Vec<ThemeDescription>);
|
||||||
|
|
||||||
|
impl IntoValue for ListThemes {
|
||||||
|
fn into_value(self, span: Span) -> Value {
|
||||||
|
self.0.into_value(span)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"clients": {
|
||||||
|
"nushell": {
|
||||||
|
"command": [
|
||||||
|
"/home/kira/.cargo/bin/nu",
|
||||||
|
"--lsp",
|
||||||
|
"--no-config-file"
|
||||||
|
],
|
||||||
|
"enabled": true,
|
||||||
|
"selector": "source.nu"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
21
nu_plugin_highlight/syntaxes/nushell/LICENSE
Normal file
21
nu_plugin_highlight/syntaxes/nushell/LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2023 kurokirasama
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
24
nu_plugin_highlight/syntaxes/nushell/README.md
Normal file
24
nu_plugin_highlight/syntaxes/nushell/README.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Nushell syntax highlight for sublime text
|
||||||
|
- Just copied matlab syntax file and modified it for nushell
|
||||||
|
- Use nushell lsp language server (need lsp sublime package)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
1. In sublime text install the LSP package.
|
||||||
|
2. After cloning and cd-ing into the directory, in nushell run (tested in Ubuntu 20.04):
|
||||||
|
```nu
|
||||||
|
ls
|
||||||
|
| find -v README & export & color
|
||||||
|
| get name
|
||||||
|
| ansi strip
|
||||||
|
| each {|file|
|
||||||
|
cp -f $file (~/.config/sublime-text/Packages/User | path expand)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
3. In sublime, open `Preferences > Customize Color Scheme` and add the contents of `sublime-color-scheme`. Modify to your liking.
|
||||||
|
|
||||||
|
## Update commands
|
||||||
|
If you need to update nushell functions or add your custom commands and aliases, run:
|
||||||
|
```nu
|
||||||
|
chmod +x export.nu
|
||||||
|
./export.nu
|
||||||
|
```
|
58
nu_plugin_highlight/syntaxes/nushell/export.nu
Normal file
58
nu_plugin_highlight/syntaxes/nushell/export.nu
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
#update nushell sublime syntax
|
||||||
|
export def "nushell-syntax-2-sublime" [] {
|
||||||
|
let builtin = filter-command built-in
|
||||||
|
let plugins = filter-command plugin
|
||||||
|
let custom = filter-command custom
|
||||||
|
let keywords = filter-command keyword
|
||||||
|
|
||||||
|
let aliases = (
|
||||||
|
scope aliases
|
||||||
|
| get name
|
||||||
|
| uniq
|
||||||
|
| str join " | "
|
||||||
|
)
|
||||||
|
|
||||||
|
let personal_external = (
|
||||||
|
$env.PATH
|
||||||
|
| find bash & nushell
|
||||||
|
| get 0
|
||||||
|
| ansi strip
|
||||||
|
| ls $in
|
||||||
|
| find -v Readme
|
||||||
|
| get name
|
||||||
|
| path parse
|
||||||
|
| get stem
|
||||||
|
| str join " | "
|
||||||
|
)
|
||||||
|
|
||||||
|
let extra_keywords = " | else | catch"
|
||||||
|
let builtin = " (?x: " + $builtin + ")"
|
||||||
|
let plugins = " (?x: " + $plugins + ")"
|
||||||
|
let custom = " (?x: " + $custom + ")"
|
||||||
|
let keywords = " (?x: " + $keywords + $extra_keywords + ")"
|
||||||
|
let aliases = " (?x: " + $aliases + ")"
|
||||||
|
let personal_external = " (?x: " + $personal_external + ")"
|
||||||
|
let operators = " (?x: and | or | mod | in | not-in | not | xor | bit-or | bit-xor | bit-and | bit-shl | bit-shr | starts-with | ends-with)"
|
||||||
|
|
||||||
|
let new_commands = [] ++ $builtin ++ $custom ++ $plugins ++ $keywords ++ $aliases ++ $personal_external ++ $operators
|
||||||
|
|
||||||
|
mut file = open ~/.config/sublime-text/Packages/User/nushell.sublime-syntax | lines
|
||||||
|
let idx = $file | indexify | find '(?x:' | get index | drop | enumerate
|
||||||
|
|
||||||
|
for i in $idx {
|
||||||
|
$file = ($file | upsert $i.item ($new_commands | get $i.index))
|
||||||
|
}
|
||||||
|
|
||||||
|
$file | save -f ~/.config/sublime-text/Packages/User/nushell.sublime-syntax
|
||||||
|
}
|
||||||
|
|
||||||
|
#add a hidden column with the content of the # column
|
||||||
|
export def indexify [
|
||||||
|
column_name?: string = 'index'
|
||||||
|
] {
|
||||||
|
enumerate
|
||||||
|
| upsert $column_name {|el|
|
||||||
|
$el.index
|
||||||
|
}
|
||||||
|
| flatten
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>scope</key>
|
||||||
|
<string>source.nu</string>
|
||||||
|
<key>settings</key>
|
||||||
|
<dict>
|
||||||
|
<key>shellVariables</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>TM_COMMENT_START</string>
|
||||||
|
<key>value</key>
|
||||||
|
<string># </string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>TM_COMMENT_START_2</string>
|
||||||
|
<key>value</key>
|
||||||
|
<string>
|
||||||
|
</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>TM_COMMENT_END_2</string>
|
||||||
|
<key>value</key>
|
||||||
|
<string>
|
||||||
|
</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"extensions":
|
||||||
|
[
|
||||||
|
"nu",
|
||||||
|
],
|
||||||
|
}
|
1256
nu_plugin_highlight/syntaxes/nushell/nushell.sublime-syntax
Normal file
1256
nu_plugin_highlight/syntaxes/nushell/nushell.sublime-syntax
Normal file
File diff suppressed because it is too large
Load Diff
46
nu_plugin_highlight/syntaxes/nushell/sublime-color-scheme
Normal file
46
nu_plugin_highlight/syntaxes/nushell/sublime-color-scheme
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
//add this to your theme.sublime-color-scheme
|
||||||
|
{
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
// "scope": "string",
|
||||||
|
// "foreground": "#00FF00",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"scope": "comment",
|
||||||
|
"foreground": "#ff0000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "support.function.builtin.nu",
|
||||||
|
"foreground": "#BB00FF",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "support.function.custom.nu",
|
||||||
|
"foreground": "#FF00FF",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "support.function.plugin.nu",
|
||||||
|
"foreground": "#004CFF",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "support.function.keywords.nu",
|
||||||
|
"foreground": "#A020F0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "support.function.aliases.nu",
|
||||||
|
"foreground": "#D580FF",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "support.function.personal.nu",
|
||||||
|
"foreground": "#004CFF",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "support.function.operators.nu",
|
||||||
|
"foreground": "#FFFF00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "support.function.boolean.nu",
|
||||||
|
"foreground": "#00FFFF",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
diff --git a/nushell.sublime-syntax b/nushell.sublime-syntax
|
||||||
|
index b164bbf..c66d6f1 100644
|
||||||
|
--- a/nushell.sublime-syntax
|
||||||
|
+++ b/nushell.sublime-syntax
|
||||||
|
@@ -25,7 +25,7 @@ variables:
|
||||||
|
(?x: all | ansi | any | append | ast | bits | bytes | cd | char | chunks | clear | collect | columns | commandline | compact | complete | config | cp | date | debug | decode | default | describe | detect | do | drop | du | each | echo | encode | enumerate | error | every | exec | exit | explain | explore | fill | filter | find | first | flatten | fmt | format | from | generate | get | glob | grid | group-by | hash | headers | help | hide-env | histogram | history | http | ignore | input | insert | inspect | interleave | into | is-admin | is-empty | is-not-empty | is-terminal | items | join | keybindings | kill | last | length | let-env | lines | load-env | ls | math | merge | metadata | mkdir | mktemp | move | mv | nu-check | nu-highlight | open | overlay | panic | par-each | parse | path | plugin | port | prepend | print | ps | query | random | range | reduce | reject | rename | reverse | rm | roll | rotate | run-external | save | schema | scope | select | seq | shuffle | skip | sleep | sort | sort-by | split | split-by | start | stor | str | sys | table | take | tee | term | timeit | to | touch | transpose | tutor | ulimit | uname | uniq | update | upsert | url | values | version | view | watch | which | whoami | window | with-env | wrap | zip)
|
||||||
|
|
||||||
|
custom_functions: |-
|
||||||
|
- (?x: 7z | ? | _atuin_search_cmd | activate | adbtasker | add-preamble | ai | ansi-strip-table | apps-update | askai | askdalle | askpdf | autolister | autouse-file | balena | banner | bar | base2dec | batstat | bhe-update | bitly | cblue | cd-pipe | chat_gpt | chatpdf | check-link | claude_ai | clean-analytics | clone-ubuntu-install | code | column | column2 | const-table | copy-downloads-2-ubbdrive | copy-research-2-ubbdrive | copy-scripts-and-commit | copy-webies-2-ubbdrive | copy-yandex-and-commit | coretemp | countdown | cp-pipe | cpwd | create_left_prompt | create_right_prompt | dall_e | date | debunk-table | dec2base | default-table | dpx | echo-c | echo-g | echo-r | exchange_rates | export-nushell-docs | find-file | find-index | fix-docker | fix-green-dirs | format-mails | fuzzy-dispatcher | fuzzy-select-fs | gcal | get-aliases | get-devices | get-dirs | get-env | get-files | get-github-latest | get-input | get-ips | get-keybindings | get-miss-chapters | get-monitors | get-pass | get-phone-number | get-rows | get-vers | get_weather_by_interval | gg | gg-trans | github-app-update | gmail | gnome-settings | gnu-plot | google_ai | google_search | goto-nuconfigdir | grep-nu | group-list | guake | h | habitica | history | history-stats | http | indexify | install-font | intersect | into | is-column | is-in | is-mounted | iselect | isleap | jd | jdown | join-text-files | killn | le | left_prompt | lg | libreoff | list-diff | list-sum | listen-ports | lister | lists2table | ln | lo | ls-ports | lt | m | maps | math | matlab-cli | mcx | med_discord | media | mk-anime | mkcd | monitor | mpv | multiwhere | mv-anime | mv-manga | mv-pipe | mv-torrents | my-pandoc | my-pdflatex | nchat | nerd-fonts-clean | network-switcher | nu-crypt | nu-sloc | nufetch | nushell-syntax-2-sublime | o_llama | obs | op | open-analytics | open-credential | open-link | openf | openl | openm | patch-font | pdf | pip3-upgrade | pivot-table | plot-table | png-plot | print-file | progress_bar | ps | psn | pwd | pwd-short | qrenc | quick-ubuntu-and-tools-update-module | ram | rand-select | randi | random | range2list | rclone | re-enamerate | rebrandly | rename-all | rename-date | rename-file | replicate-tree | reset-alpine-auth | return-error | rm-empty-dirs | rm-pipe | rml | rmount | save-credential | scale-minmax | scale-minmax-table | scompact | send-gmail | set-env | set-screen | setdiff | show-ips | show_banner | speedtest-plot | ssh-sin-pass | ssh-termux | ssh-to | std | stop-net-apps | str | subl | sum-size | supgrade | svg2pdf | sys | t | table-diff | table2record | tasker | tasker-join | to-conversiones | to-onedrive | tokei | token2word | trans | tts | typeof | ubb | ubb_announce | um | umall | union | uniq-by | unset-env | up2ubb | update-nu-config | upload-debs-to-gdrive | usage | ver | verify | weather | wget-all | which-cd | wifi-info | wifi-pass | xls2csv | ydx | yt-api | ytcli | ytm | z | zi)
|
||||||
|
+ (?x: 7z | \? | _atuin_search_cmd | activate | adbtasker | add-preamble | ai | ansi-strip-table | apps-update | askai | askdalle | askpdf | autolister | autouse-file | balena | banner | bar | base2dec | batstat | bhe-update | bitly | cblue | cd-pipe | chat_gpt | chatpdf | check-link | claude_ai | clean-analytics | clone-ubuntu-install | code | column | column2 | const-table | copy-downloads-2-ubbdrive | copy-research-2-ubbdrive | copy-scripts-and-commit | copy-webies-2-ubbdrive | copy-yandex-and-commit | coretemp | countdown | cp-pipe | cpwd | create_left_prompt | create_right_prompt | dall_e | date | debunk-table | dec2base | default-table | dpx | echo-c | echo-g | echo-r | exchange_rates | export-nushell-docs | find-file | find-index | fix-docker | fix-green-dirs | format-mails | fuzzy-dispatcher | fuzzy-select-fs | gcal | get-aliases | get-devices | get-dirs | get-env | get-files | get-github-latest | get-input | get-ips | get-keybindings | get-miss-chapters | get-monitors | get-pass | get-phone-number | get-rows | get-vers | get_weather_by_interval | gg | gg-trans | github-app-update | gmail | gnome-settings | gnu-plot | google_ai | google_search | goto-nuconfigdir | grep-nu | group-list | guake | h | habitica | history | history-stats | http | indexify | install-font | intersect | into | is-column | is-in | is-mounted | iselect | isleap | jd | jdown | join-text-files | killn | le | left_prompt | lg | libreoff | list-diff | list-sum | listen-ports | lister | lists2table | ln | lo | ls-ports | lt | m | maps | math | matlab-cli | mcx | med_discord | media | mk-anime | mkcd | monitor | mpv | multiwhere | mv-anime | mv-manga | mv-pipe | mv-torrents | my-pandoc | my-pdflatex | nchat | nerd-fonts-clean | network-switcher | nu-crypt | nu-sloc | nufetch | nushell-syntax-2-sublime | o_llama | obs | op | open-analytics | open-credential | open-link | openf | openl | openm | patch-font | pdf | pip3-upgrade | pivot-table | plot-table | png-plot | print-file | progress_bar | ps | psn | pwd | pwd-short | qrenc | quick-ubuntu-and-tools-update-module | ram | rand-select | randi | random | range2list | rclone | re-enamerate | rebrandly | rename-all | rename-date | rename-file | replicate-tree | reset-alpine-auth | return-error | rm-empty-dirs | rm-pipe | rml | rmount | save-credential | scale-minmax | scale-minmax-table | scompact | send-gmail | set-env | set-screen | setdiff | show-ips | show_banner | speedtest-plot | ssh-sin-pass | ssh-termux | ssh-to | std | stop-net-apps | str | subl | sum-size | supgrade | svg2pdf | sys | t | table-diff | table2record | tasker | tasker-join | to-conversiones | to-onedrive | tokei | token2word | trans | tts | typeof | ubb | ubb_announce | um | umall | union | uniq-by | unset-env | up2ubb | update-nu-config | upload-debs-to-gdrive | usage | ver | verify | weather | wget-all | which-cd | wifi-info | wifi-pass | xls2csv | ydx | yt-api | ytcli | ytm | z | zi)
|
||||||
|
|
||||||
|
plugin_functions: |-
|
||||||
|
(?x: from | gstat | highlight | hist | inc | plot | polars | port | query | to | xyplot)
|
49
nu_plugin_image/.github/workflows/dependency-update.yaml
vendored
Normal file
49
nu_plugin_image/.github/workflows/dependency-update.yaml
vendored
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 */2 * *'
|
||||||
|
|
||||||
|
name: Update dependencies
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
update:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Setup Nushell
|
||||||
|
uses: hustcer/setup-nu@main
|
||||||
|
with:
|
||||||
|
version: "*"
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: prepare
|
||||||
|
shell: nu {0}
|
||||||
|
run: |
|
||||||
|
nu -c '
|
||||||
|
cargo install cargo-edit cargo-upgrades nu_plugin_inc -f
|
||||||
|
'
|
||||||
|
- name: Update Dependencies
|
||||||
|
shell: nu {0}
|
||||||
|
run: |
|
||||||
|
nu -c '
|
||||||
|
register /home/runner/.cargo/bin/nu_plugin_inc
|
||||||
|
cargo upgrade
|
||||||
|
let changed = git status -s | is-empty | not $in
|
||||||
|
if ($changed) {
|
||||||
|
open Cargo.toml
|
||||||
|
| upsert package.version ( $in
|
||||||
|
| get package.version
|
||||||
|
| inc --patch
|
||||||
|
)
|
||||||
|
| save Cargo.toml -f
|
||||||
|
|
||||||
|
open package.nuon
|
||||||
|
| upsert version ( open Cargo.toml | get package.version )
|
||||||
|
| save package.nuon -f
|
||||||
|
cargo upgrade
|
||||||
|
}
|
||||||
|
|
||||||
|
echo { "changed": $changed }
|
||||||
|
'
|
||||||
|
|
||||||
|
- uses: EndBug/add-and-commit@v9
|
||||||
|
with:
|
||||||
|
author_name: GitHub-Action
|
30
nu_plugin_image/.github/workflows/publish.yml
vendored
Normal file
30
nu_plugin_image/.github/workflows/publish.yml
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
name: Publish Crate
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- Cargo.tom
|
||||||
|
release:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Rust toolchain
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
override: true
|
||||||
|
|
||||||
|
- name: Publish to crates.io
|
||||||
|
uses: katyo/publish-crates@v2
|
||||||
|
with:
|
||||||
|
registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
||||||
|
env:
|
||||||
|
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
5
nu_plugin_image/.gitignore
vendored
Normal file
5
nu_plugin_image/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/target
|
||||||
|
.vscode
|
||||||
|
*.png
|
||||||
|
*.ansi
|
||||||
|
*.tmp
|
8
nu_plugin_image/.idea/.gitignore
generated
vendored
Normal file
8
nu_plugin_image/.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
8
nu_plugin_image/.idea/modules.xml
generated
Normal file
8
nu_plugin_image/.idea/modules.xml
generated
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/nu_plugin_image.iml" filepath="$PROJECT_DIR$/.idea/nu_plugin_image.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
11
nu_plugin_image/.idea/nu_plugin_image.iml
generated
Normal file
11
nu_plugin_image/.idea/nu_plugin_image.iml
generated
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="EMPTY_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
6
nu_plugin_image/.idea/vcs.xml
generated
Normal file
6
nu_plugin_image/.idea/vcs.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
3324
nu_plugin_image/Cargo.lock
generated
Normal file
3324
nu_plugin_image/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
48
nu_plugin_image/Cargo.toml
Normal file
48
nu_plugin_image/Cargo.toml
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
[dependencies]
|
||||||
|
slog = "2.7.0"
|
||||||
|
termcolor = "1.4.1"
|
||||||
|
ansi_colours = "1.2.3"
|
||||||
|
crossterm = "0.29.0"
|
||||||
|
image = "0.25.6"
|
||||||
|
imageproc = "0.25.0"
|
||||||
|
include-flate = "0.3.0"
|
||||||
|
ab_glyph = "0.2.29"
|
||||||
|
vte = "0.15.0"
|
||||||
|
lazy_static = "1.5.0"
|
||||||
|
slog-term = "2.9.1"
|
||||||
|
slog-async = "2.8.0"
|
||||||
|
|
||||||
|
[dependencies.clap]
|
||||||
|
features = ["derive"]
|
||||||
|
version = "4.5.40"
|
||||||
|
|
||||||
|
[dependencies.nu-plugin]
|
||||||
|
version = "0.105.2"
|
||||||
|
path = "../nushell/crates/nu-plugin"
|
||||||
|
|
||||||
|
[dependencies.nu-protocol]
|
||||||
|
features = ["plugin"]
|
||||||
|
version = "0.105.2"
|
||||||
|
path = "../nushell/crates/nu-protocol"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
all-fonts = ["font-iosevka_term", "font-anonymous_pro", "font-ubuntu"]
|
||||||
|
default = []
|
||||||
|
font-anonymous_pro = []
|
||||||
|
font-iosevka_term = []
|
||||||
|
font-ubuntu = []
|
||||||
|
|
||||||
|
with-debug = ["slog/max_level_debug", "slog/release_max_level_debug"]
|
||||||
|
with-trace = ["slog/max_level_trace", "slog/release_max_level_trace"]
|
||||||
|
|
||||||
|
[package]
|
||||||
|
authors = ["Motalleb Fallahnezhad <fmotalleb@gmail.com>"]
|
||||||
|
description = "A nushell plugin to open png images in the shell and save ansi string as images (like tables or ...)"
|
||||||
|
edition = "2024"
|
||||||
|
homepage = "https://github.com/FMotalleb/nu_plugin_image"
|
||||||
|
keywords = ["nushell", "image", "render", "plugin"]
|
||||||
|
license = "MIT"
|
||||||
|
name = "nu_plugin_image"
|
||||||
|
readme = "README.md"
|
||||||
|
repository = "https://github.com/FMotalleb/nu_plugin_image"
|
||||||
|
version = "0.105.1"
|
7
nu_plugin_image/LICENSE
Normal file
7
nu_plugin_image/LICENSE
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Copyright 2023 "Motalleb Fallahnezhad"
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
115
nu_plugin_image/README.md
Normal file
115
nu_plugin_image/README.md
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
# 🖼 nu_plugin_image
|
||||||
|
|
||||||
|
A [Nushell](https://www.nushell.sh/) plugin to convert ANSI strings into PNG images and create ANSI text from images.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✨ Features
|
||||||
|
|
||||||
|
This plugin allows you to:
|
||||||
|
|
||||||
|
- Convert ANSI strings to PNG images with customizable fonts and themes.
|
||||||
|
- Create ANSI text from an image, enabling you to transform visual data into a textual representation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **`to png`** – Convert ANSI String to PNG Image
|
||||||
|
|
||||||
|
The `to png` command converts an ANSI string into a PNG image. Customizable font and theme options are available, with custom flags overriding the default settings.
|
||||||
|
|
||||||
|
#### 📌 Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
> to png {flags} (output-path)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### ⚙️ Available Flags
|
||||||
|
|
||||||
|
- `-h, --help` → Display the help message for this command.
|
||||||
|
- `-w, --width <int>` → Output width.
|
||||||
|
- `-t, --theme <string>` → Select the theme of the output. Available themes: ["vscode", "xterm", "ubuntu", "eclipse", "mirc", "putty", "winxp", "terminal", "win10", "win_power-shell", "win_ps"]. Defaults to `vscode`.
|
||||||
|
- `--font <string>` → Select the font from one of ["SourceCodePro", "Ubuntu", "IosevkaTerm", "AnonymousPro"]. Defaults to the first font in the list.
|
||||||
|
- `--custom-font-regular <path>` → Path to a custom regular font.
|
||||||
|
- `--custom-font-bold <path>` → Path to a custom bold font.
|
||||||
|
- `--custom-font-italic <path>` → Path to a custom italic font.
|
||||||
|
- `--custom-font-bold_italic <path>` → Path to a custom bold italic font.
|
||||||
|
- `--custom-theme-fg <string>` → Custom foreground color in hex format (e.g., `#FFFFFF` for white).
|
||||||
|
- `--custom-theme-bg <string>` → Custom background color in hex format (e.g., `#00000000` for transparent).
|
||||||
|
- `--custom-theme-black <string>` → Custom black color in hex format (e.g., `#1C1C1C`).
|
||||||
|
- `--custom-theme-red <string>` → Custom red color in hex format (e.g., `#FF0000`).
|
||||||
|
- `--custom-theme-green <string>` → Custom green color in hex format (e.g., `#00FF00`).
|
||||||
|
- `--custom-theme-yellow <string>` → Custom yellow color in hex format (e.g., `#FFFF00`).
|
||||||
|
- `--custom-theme-blue <string>` → Custom blue color in hex format (e.g., `#0000FF`).
|
||||||
|
- `--custom-theme-magenta <string>` → Custom magenta color in hex format (e.g., `#FF00FF`).
|
||||||
|
- `--custom-theme-cyan <string>` → Custom cyan color in hex format (e.g., `#00FFFF`).
|
||||||
|
- `--custom-theme-white <string>` → Custom white color in hex format (e.g., `#FFFFFF`).
|
||||||
|
- `--custom-theme-bright_black <string>` → Custom bright black color in hex format (e.g., `#808080`).
|
||||||
|
- `--custom-theme-bright_red <string>` → Custom bright red color in hex format (e.g., `#FF5555`).
|
||||||
|
- `--custom-theme-bright_green <string>` → Custom bright green color in hex format (e.g., `#55FF55`).
|
||||||
|
- `--custom-theme-bright_yellow <string>` → Custom bright yellow color in hex format (e.g., `#FFFF55`).
|
||||||
|
- `--custom-theme-bright_blue <string>` → Custom bright blue color in hex format (e.g., `#5555FF`).
|
||||||
|
- `--custom-theme-bright_magenta <string>` → Custom bright magenta color in hex format (e.g., `#FF55FF`).
|
||||||
|
- `--custom-theme-bright_cyan <string>` → Custom bright cyan color in hex format (e.g., `#55FFFF`).
|
||||||
|
- `--custom-theme-bright_white <string>` → Custom bright white color in hex format (e.g., `#FFFFFF`).
|
||||||
|
- `--log-level <string>` → Set log level. Options: `CRITICAL (c)`, `ERROR (e)`, `WARN (w)`, `INFO (i)`, `DEBUG (d)`, `TRACE (t)`. Defaults to `INFO`.
|
||||||
|
|
||||||
|
#### 📊 Example: Convert ANSI String to PNG with Custom Theme
|
||||||
|
|
||||||
|
```bash
|
||||||
|
> to png --theme "xterm" --custom-theme-fg "#FF00FF" --custom-theme-bg "#00000000" output.png
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **`from png`** – Create ANSI Text from an Image
|
||||||
|
|
||||||
|
The `from png` command converts an image into its corresponding ANSI text representation.
|
||||||
|
|
||||||
|
#### 📌 Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
> from png {flags}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### ⚙️ Available Flags
|
||||||
|
|
||||||
|
- `-h, --help` → Display the help message for this command.
|
||||||
|
- `-x, --width <int>` → Output width, in characters.
|
||||||
|
- `-y, --height <int>` → Output height, in characters.
|
||||||
|
- `--log-level <string>` → Set log level. Options: `CRITICAL (c)`, `ERROR (e)`, `WARN (w)`, `INFO (i)`, `DEBUG (d)`, `TRACE (t)`. Defaults to `INFO`.
|
||||||
|
|
||||||
|
#### 📊 Example: Convert PNG Image to ANSI Text
|
||||||
|
|
||||||
|
```bash
|
||||||
|
> from png --width 80 --height 20 image.png
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 Installation
|
||||||
|
|
||||||
|
### 🚀 Recommended: Using [nupm](https://github.com/nushell/nupm)
|
||||||
|
|
||||||
|
This method automatically handles dependencies and features.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/FMotalleb/nu_plugin_image.git
|
||||||
|
nupm install --path nu_plugin_image -f
|
||||||
|
```
|
||||||
|
|
||||||
|
### 🛠️ Manual Compilation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/FMotalleb/nu_plugin_image.git
|
||||||
|
cd nu_plugin_image
|
||||||
|
cargo build -r
|
||||||
|
plugin add target/release/nu_plugin_image
|
||||||
|
```
|
||||||
|
|
||||||
|
### 📦 Install via Cargo (using git)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo install --git https://github.com/FMotalleb/nu_plugin_image.git
|
||||||
|
plugin add ~/.cargo/bin/nu_plugin_image
|
||||||
|
```
|
||||||
|
|
45
nu_plugin_image/build.nu
Normal file
45
nu_plugin_image/build.nu
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
use std log
|
||||||
|
|
||||||
|
|
||||||
|
# TODO add licenses
|
||||||
|
let fonts = [
|
||||||
|
[name, feature];
|
||||||
|
[
|
||||||
|
"AnonymousPro Font",
|
||||||
|
font-anonymous_pro
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"IosevkaTerm Font",
|
||||||
|
font-iosevka_term
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"Ubuntu Font",
|
||||||
|
font-ubuntu
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"Debug log level (only used for debuging)",
|
||||||
|
with-debug
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"Trace log level (only used for advanced debuging)",
|
||||||
|
with-trace
|
||||||
|
],
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def main [package_file: path] {
|
||||||
|
let repo_root = $package_file | path dirname
|
||||||
|
let install_root = $env.NUPM_HOME | path join "plugins"
|
||||||
|
let selected_fonts = $fonts
|
||||||
|
| input list -d name -m "select features to install"
|
||||||
|
| get feature
|
||||||
|
|
||||||
|
let name = open ($repo_root | path join "Cargo.toml") | get package.name
|
||||||
|
let ext = if ($nu.os-info.name == 'windows') { '.exe' } else { '' }
|
||||||
|
let command = $"cargo install --path ($repo_root) --root ($install_root) --features=\"($selected_fonts | str join ',')\""
|
||||||
|
log info $"building using `($command)`"
|
||||||
|
nu --commands $"($command)"
|
||||||
|
plugin add $"($install_root | path join "bin" $name)($ext)"
|
||||||
|
log info "do not forget to restart Nushell for the plugin to be fully available!"
|
||||||
|
nu ($repo_root | path join scripts theme_exporter.nu)
|
||||||
|
}
|
7
nu_plugin_image/nupm.nuon
Normal file
7
nu_plugin_image/nupm.nuon
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"name": "nu_plugin_image",
|
||||||
|
"version": "0.104.0",
|
||||||
|
"description": "A nushell plugin to open png images in the shell and save ansi string as images (like tables or ...)",
|
||||||
|
"license": "LICENSE",
|
||||||
|
"type": "custom"
|
||||||
|
}
|
BIN
nu_plugin_image/resources/fonts/Anonymous_Pro/Bold.ttf
Normal file
BIN
nu_plugin_image/resources/fonts/Anonymous_Pro/Bold.ttf
Normal file
Binary file not shown.
BIN
nu_plugin_image/resources/fonts/Anonymous_Pro/BoldItalic.ttf
Normal file
BIN
nu_plugin_image/resources/fonts/Anonymous_Pro/BoldItalic.ttf
Normal file
Binary file not shown.
BIN
nu_plugin_image/resources/fonts/Anonymous_Pro/Italic.ttf
Normal file
BIN
nu_plugin_image/resources/fonts/Anonymous_Pro/Italic.ttf
Normal file
Binary file not shown.
BIN
nu_plugin_image/resources/fonts/Anonymous_Pro/Regular.ttf
Normal file
BIN
nu_plugin_image/resources/fonts/Anonymous_Pro/Regular.ttf
Normal file
Binary file not shown.
BIN
nu_plugin_image/resources/fonts/IosevkaTerm/Bold.ttf
Normal file
BIN
nu_plugin_image/resources/fonts/IosevkaTerm/Bold.ttf
Normal file
Binary file not shown.
BIN
nu_plugin_image/resources/fonts/IosevkaTerm/BoldItalic.ttf
Normal file
BIN
nu_plugin_image/resources/fonts/IosevkaTerm/BoldItalic.ttf
Normal file
Binary file not shown.
BIN
nu_plugin_image/resources/fonts/IosevkaTerm/Italic.ttf
Normal file
BIN
nu_plugin_image/resources/fonts/IosevkaTerm/Italic.ttf
Normal file
Binary file not shown.
BIN
nu_plugin_image/resources/fonts/IosevkaTerm/Medium.ttf
Normal file
BIN
nu_plugin_image/resources/fonts/IosevkaTerm/Medium.ttf
Normal file
Binary file not shown.
BIN
nu_plugin_image/resources/fonts/SourceCodePro/Bold.otf
Normal file
BIN
nu_plugin_image/resources/fonts/SourceCodePro/Bold.otf
Normal file
Binary file not shown.
BIN
nu_plugin_image/resources/fonts/SourceCodePro/BoldItalic.otf
Normal file
BIN
nu_plugin_image/resources/fonts/SourceCodePro/BoldItalic.otf
Normal file
Binary file not shown.
BIN
nu_plugin_image/resources/fonts/SourceCodePro/Italic.otf
Normal file
BIN
nu_plugin_image/resources/fonts/SourceCodePro/Italic.otf
Normal file
Binary file not shown.
BIN
nu_plugin_image/resources/fonts/SourceCodePro/Regular.otf
Normal file
BIN
nu_plugin_image/resources/fonts/SourceCodePro/Regular.otf
Normal file
Binary file not shown.
BIN
nu_plugin_image/resources/fonts/Ubuntu/Bold.ttf
Normal file
BIN
nu_plugin_image/resources/fonts/Ubuntu/Bold.ttf
Normal file
Binary file not shown.
BIN
nu_plugin_image/resources/fonts/Ubuntu/BoldItalic.ttf
Normal file
BIN
nu_plugin_image/resources/fonts/Ubuntu/BoldItalic.ttf
Normal file
Binary file not shown.
BIN
nu_plugin_image/resources/fonts/Ubuntu/Italic.ttf
Normal file
BIN
nu_plugin_image/resources/fonts/Ubuntu/Italic.ttf
Normal file
Binary file not shown.
BIN
nu_plugin_image/resources/fonts/Ubuntu/Regular.ttf
Normal file
BIN
nu_plugin_image/resources/fonts/Ubuntu/Regular.ttf
Normal file
Binary file not shown.
3
nu_plugin_image/rust-toolchain.toml
Normal file
3
nu_plugin_image/rust-toolchain.toml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[toolchain]
|
||||||
|
profile = "default"
|
||||||
|
channel = "stable"
|
78
nu_plugin_image/scripts/theme_exporter.nu
Normal file
78
nu_plugin_image/scripts/theme_exporter.nu
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
use std log
|
||||||
|
def confirm [message: string] : any -> bool {
|
||||||
|
["yes","no"] | input list $message | $in == "yes"
|
||||||
|
}
|
||||||
|
def "from binary" [] : binary -> string {
|
||||||
|
$in | encode base64 | base64 -d
|
||||||
|
}
|
||||||
|
|
||||||
|
def get-terminal-colors [] {
|
||||||
|
|
||||||
|
let colors = (0..15 | each {|i| $"4;($i);?"
|
||||||
|
| (term query $"(ansi osc)(ansi -o $in)(ansi st)" --terminator "\e\\"
|
||||||
|
| from binary
|
||||||
|
| split row :
|
||||||
|
| get 1
|
||||||
|
| split row /)
|
||||||
|
})
|
||||||
|
|
||||||
|
let env_vars = [
|
||||||
|
[NU_PLUGIN_IMAGE_FG, ($colors | get 07)]
|
||||||
|
[NU_PLUGIN_IMAGE_BG , ($colors | get 00)]
|
||||||
|
[NU_PLUGIN_IMAGE_BLACK , ($colors | get 00)]
|
||||||
|
[NU_PLUGIN_IMAGE_RED, ($colors | get 01)]
|
||||||
|
[NU_PLUGIN_IMAGE_GREEN, ($colors | get 02)]
|
||||||
|
[NU_PLUGIN_IMAGE_YELLOW, ($colors | get 03)]
|
||||||
|
[NU_PLUGIN_IMAGE_BLUE, ($colors | get 04)]
|
||||||
|
[NU_PLUGIN_IMAGE_MAGENTA, ($colors | get 05)]
|
||||||
|
[NU_PLUGIN_IMAGE_CYAN, ($colors | get 06)]
|
||||||
|
[NU_PLUGIN_IMAGE_WHITE, ($colors | get 07)]
|
||||||
|
[NU_PLUGIN_IMAGE_BRIGHT_BLACK, ($colors | get 08)]
|
||||||
|
[NU_PLUGIN_IMAGE_BRIGHT_RED, ($colors | get 09)]
|
||||||
|
[NU_PLUGIN_IMAGE_BRIGHT_GREEN, ($colors | get 10)]
|
||||||
|
[NU_PLUGIN_IMAGE_BRIGHT_YELLOW, ($colors | get 11)]
|
||||||
|
[NU_PLUGIN_IMAGE_BRIGHT_BLUE, ($colors | get 12)]
|
||||||
|
[NU_PLUGIN_IMAGE_BRIGHT_MAGENTA, ($colors | get 13)]
|
||||||
|
[NU_PLUGIN_IMAGE_BRIGHT_CYAN, ($colors | get 14)]
|
||||||
|
[NU_PLUGIN_IMAGE_BRIGHT_WHITE, ($colors | get 15)]
|
||||||
|
] | each {|col|
|
||||||
|
let rgb = $col | get 1
|
||||||
|
# 16bit rgb to 8bit = 0xe7e7 | bits and 0x00ff
|
||||||
|
let red = ($"0x($rgb | get 0)" | into int | bits and 0x00ff)
|
||||||
|
let green = ($"0x($rgb | get 1)" | into int | bits and 0x00ff)
|
||||||
|
let blue = ($"0x($rgb | get 2)" | into int | bits and 0x00ff)
|
||||||
|
let red_hx = ($red | fmt).lowerhex | str substring 2..
|
||||||
|
let green_hx = ($green | fmt).lowerhex | str substring 2..
|
||||||
|
let blue_hx = ($blue | fmt).lowerhex | str substring 2..
|
||||||
|
$"$env.($col | first) = 0x($red_hx)($green_hx)($blue_hx)"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (confirm "write config to the env file?") {
|
||||||
|
|
||||||
|
let default = ($nu.env-path | path dirname | path join nu_image_plugin_conf.nu)
|
||||||
|
let config_path = input $"where should i save the env file? \(default: ($default)\)\n~> "
|
||||||
|
| if (not ($in | is-empty)) {
|
||||||
|
$in
|
||||||
|
} else {
|
||||||
|
($default)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (not ( $config_path | path exists)) {
|
||||||
|
$"source ($config_path)" | save $nu.env-path --append
|
||||||
|
}
|
||||||
|
|
||||||
|
$"# Auto generated code\n($env_vars | str join "\n")" | save $config_path -f
|
||||||
|
|
||||||
|
log info "Please restart the shell"
|
||||||
|
} else {
|
||||||
|
for i in $env_vars {
|
||||||
|
print $"($i)\n"
|
||||||
|
}
|
||||||
|
print "add thse values to environment variables using `config env`"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (confirm "do you want to save your current shell's theme as default for `to png`?") {
|
||||||
|
print (get-terminal-colors)
|
||||||
|
}
|
71
nu_plugin_image/src/ansi_to_image/ansi_to_image.rs
Normal file
71
nu_plugin_image/src/ansi_to_image/ansi_to_image.rs
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
use image::RgbaImage;
|
||||||
|
use std::{
|
||||||
|
io::{BufReader, Read},
|
||||||
|
path::Path,
|
||||||
|
};
|
||||||
|
use vte::Parser;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ansi_to_image::{
|
||||||
|
font_family::FontFamily,
|
||||||
|
palette::Palette,
|
||||||
|
printer::{self, Settings},
|
||||||
|
},
|
||||||
|
warn,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::internal_scale::InternalScale;
|
||||||
|
|
||||||
|
pub fn make_image(
|
||||||
|
output_path: &Path,
|
||||||
|
font_family: FontFamily,
|
||||||
|
png_width: Option<u32>,
|
||||||
|
input: &[u8],
|
||||||
|
palette: Palette,
|
||||||
|
) {
|
||||||
|
// let = FontFamily::default();
|
||||||
|
|
||||||
|
let font = font_family.regular;
|
||||||
|
let font_bold = font_family.bold;
|
||||||
|
let font_italic = font_family.italic;
|
||||||
|
let font_italic_bold = font_family.bold_italic;
|
||||||
|
|
||||||
|
let font_height = 50.0;
|
||||||
|
let scale = InternalScale {
|
||||||
|
x: font_height,
|
||||||
|
y: font_height,
|
||||||
|
};
|
||||||
|
|
||||||
|
// let palette = Palette::Vscode;
|
||||||
|
|
||||||
|
let mut state_machine = Parser::new();
|
||||||
|
let mut performer = printer::new(Settings {
|
||||||
|
font,
|
||||||
|
font_bold,
|
||||||
|
font_italic,
|
||||||
|
font_italic_bold,
|
||||||
|
font_height,
|
||||||
|
scale,
|
||||||
|
palette,
|
||||||
|
png_width,
|
||||||
|
});
|
||||||
|
let reader = &mut BufReader::new(input);
|
||||||
|
let mut buf = [0; 2048];
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match reader.read(&mut buf) {
|
||||||
|
Ok(0) => break,
|
||||||
|
|
||||||
|
Ok(n) => state_machine.advance(&mut performer, &buf[..n]),
|
||||||
|
|
||||||
|
Err(err) => {
|
||||||
|
warn!("{err}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let image: RgbaImage = performer.into();
|
||||||
|
|
||||||
|
image.save(output_path).unwrap();
|
||||||
|
}
|
21
nu_plugin_image/src/ansi_to_image/color.rs
Normal file
21
nu_plugin_image/src/ansi_to_image/color.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub(super) enum ColorType {
|
||||||
|
PrimaryForeground,
|
||||||
|
PrimaryBackground,
|
||||||
|
Normal(Color),
|
||||||
|
Bright(Color),
|
||||||
|
Fixed(u8),
|
||||||
|
Rgb { field1: (u8, u8, u8) },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub(super) enum Color {
|
||||||
|
Black,
|
||||||
|
Red,
|
||||||
|
Green,
|
||||||
|
Yellow,
|
||||||
|
Blue,
|
||||||
|
Magenta,
|
||||||
|
Cyan,
|
||||||
|
White,
|
||||||
|
}
|
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