1
Fork 0
mirror of https://github.com/RGBCube/nu_scripts synced 2025-08-01 06:37:46 +00:00

refactor: (#418)

* refactor:  move in one commit

Eveything in modules should probably be changed to `exported` defs.
The idea is to move everything first to keep proper history.

* refactor: 📝 add modules readme (wip)

* refactor:  small move

* refactor: 📝 changed nestring, updated modules readme

* refactor: 📝 to document or not to document

* fix: 🐛 themes

replaced the template to use `main` and regenerated them
from lemnos themes.

* Revert "fix: 🐛 themes"

This reverts commit 4918d3633c8d2d81950a0ed0cfd9eb84241bc886.

* refactor:  introduce sourced

- Created a source `root` in which sourcable demos are stored.
  Some might get converted to modules later on.
- Moved some files to bin too.

* fix: 🐛 fehbg.nu

* fix: 🐛 modules/after.nu

* moved some other stuff around

---------

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
This commit is contained in:
Mel Massadian 2023-04-26 00:56:25 +02:00 committed by GitHub
parent 382696cd21
commit c47ccd42b8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
128 changed files with 185 additions and 12 deletions

156
modules/README.md Normal file
View file

@ -0,0 +1,156 @@
# Modules
- [Modules](#modules)
- [after](#after)
- [api\_wrappers](#api_wrappers)
- [background\_task](#background_task)
- [base16](#base16)
- [coloring](#coloring)
- [data\_extraction](#data_extraction)
- [docker](#docker)
- [filesystem](#filesystem)
- [formats](#formats)
- [fun](#fun)
- [github](#github)
- [gitlab](#gitlab)
- [kubernetes](#kubernetes)
- [make\_release](#make_release)
- [maths](#maths)
- [network](#network)
- [nvim](#nvim)
- [progress\_bar](#progress_bar)
- [rbenv](#rbenv)
- [virtual\_environments](#virtual_environments)
- [weather](#weather)
- [webscraping](#webscraping)
## [after](./after)
Run a function after the given program (by PID)
## api_wrappers
Demo of various API wrappers:
- [Wolfram Alpha](./api_wrappers/wolframalpha.nu)
- [AWS]()
## background_task
make nushell "support" background task feature.
see [README](./background_task/)
## base16
Base16 theme generator (for Linux, might work for other OS)
see [README](./base16/)
## coloring
These scripts are used to demonstrate the `ansi` command using `ansi` coloring. This is mainly a demo area where we have taken typical `bash` scripts and ported them to nushell scripts. It would be nice if all scripts here showed the "other" version of script and the ported nushell version. We can show "other" flavors of scripts by including them as comments in the nushell scripts or by naming the nushell script and the other script the same basename.
## data_extraction
- [Ultimate Extractor](./data_extraction/ultimate_extractor.nu) - Extract any compressed archive, UE will call the proper program under the hood 😎
## [docker](./docker/)
An extensive example of a wrapper for docker operations, with nushell completions.
## filesystem
- [cdpath](./filesystem/cdpath.nu) - ???
- [up](./filesystem/up.nu) - Cd up `X` times
## formats
Examples of input/output formatters:
- [from-cpuinfo](./formats/from-cpuinfo.nu)
- [from-dmidecode](./formats/from-dmidecode.nu)
- [to-ini](./formats/to-ini.nu)
- [remove-diacritics](./strings/remove-diacritics.nu) - Turns `Zażółć gęślą jaźń` into `Zazolc gesla jazn`
## fun
- [spark](./fun/spark.nu) - send an array into spark and get a sparkline out:
```console
> let v = [2, 250, 670, 890, 2, 430, 11, 908, 123, 57]
> spark $v
▁▂▆▇▁▄▁█▁▁
```
- [website-builder](./fun/website_builder.nu) - converts markdown into their equivalent html pages
- [wordle](./fun/wordle.nu) - A Terminal Wordle game. The code is based on this [gist](https://gist.github.com/huytd/6a1a6a7b34a0d0abcac00b47e3d01513), but slightly personalized.
## github
- [branch-protections](./github/branch-protections/) - Do you have hundreds or thousands of GitHub repositories in your organization? Are you tired of manually managing their branch protection rules? Don't! Let nushell do it for you! see [README](./github/branch-protections/)
- [merged-branches](./github/merged-branches/) - Do your developers often forget to delete their branches after merging PRs? Are you tired of manually going into every repository and deleting them? Don't! Let nushell do it for you! see [README](./github/merged-branches/)
## [gitlab](./gitlab/)
Search files on your GitLab server
## [kubernetes](./kubernetes/)
???
## [make_release](./make_release/)
???
## [maths](./maths/)
- [math_functions] - module with the following commands:
- `root` - root with a custom denominator
- `croot` - cube root
- `aroot` - root with a custom scaler and denominator
- `delta` - calculate the delta of the quadratic function
- `fact` - factorial of the given number
- `q_roots` - calculare roots of the quadratic function: ax^2+bx+x
- `isprime` - check if integer is prime
- `primelist` - list primes until given number
- `mtable` - multiplication table of n till max
- `isleap` - check if year is leap
- `gcd` - greatest common divisor between 2 integers
- `lcm` - least commoin multiple between 2 integers
- `dec2base` - decimal number to custom base representation
- `scale-minmax` - scale list to `[a,b]` interval
- `scale-minmax-table` - Scale every column of a table (separately) to `[a,b]` interval
- `math exp` - exp function
## network
- [remoting](./network/remoting/) - This module provide convenient way to manage multiple remote clients. see [README](./network/remoting)
- [sockets](./network/sockets/) - The `sockets` command returns a table containing information on network sockets and the processes they belong to.
It is basically a join of the tables produced by the `lsof` command, and the nushell `ps` command.
<img width="1486" alt="image" src="https://user-images.githubusercontent.com/52205/196287615-00e46f8e-06ed-45ce-8fe7-a5c5f38afaaa.png">
- [ssh](./network/ssh.nu) wrapper that provides the following commands:
- `ssh`
- `scp`
- `ssh-list`
- `parse-ssh-file`
- `str max-length`
- `ensure-index`
## [nvim](./nvim/)
??? (not sure how universal this is) Mix of hooks, defs and alias wrapper around neovim.
## [progress_bar](./progress_bar/)
??? (make a module out of these scripts?) - Collection of progress bars
## [rbenv](./rbenv/)
??? (not sure how universal this is) This script provides minimal working rbenv setup.
## [virtual_environments](./virtual_environments/)
The scripts in this directory activate virtual environments for Conda environments.
## [weather](./weather/)
These scripts should be used to demonstrate how get your local weather and/or weather forecasts.
## [webscraping](./webscraping/)
Simple scripts to demonstrate how to scrape websites in nushell. Requires `query web` plugin

9
modules/after/after.nu Normal file
View file

@ -0,0 +1,9 @@
def "nu-complete ps" [] {
ps | each {|x| { value: $x.pid, description: $x.name } }
}
# after <pid> { do something ... }
export def main [pid: string@"nu-complete ps" action ] {
do -i { tail --pid $pid -f /dev/null }
do $action
}

View file

@ -0,0 +1,24 @@
# Background task with pueue
make nushell "support" background task feature.
# Prerequisite
Install [pueue](https://github.com/Nukesor/pueue) and make sure `pueued` is running with default config, and, `pueue` is in `PATH`
# Usage
```nushell
use job.nu
# spawn a new job in background.
> job spawn { sleep 3sec ; echo gg | save gg.txt }
# get job status
> job status
# get one job's log
> job log $job_id
# kill one job
> job kill $job_id
# clean pueued finished job
> job clean
```

View file

@ -0,0 +1,47 @@
# spawn task to run in the background
#
# please note that a fresh nushell is spawned to execute the given command
# So it doesn't inherit current scope's variables, custom commands, alias definition, except env variables which value can convert to string.
#
# e.g:
# spawn { echo 3 }
export def spawn [
command: block # the command to spawn
] {
let config_path = $nu.config-path
let env_path = $nu.env-path
let source_code = (view source $command | str trim -l -c '{' | str trim -r -c '}')
let job_id = (pueue add -p $"nu --config \"($config_path)\" --env-config \"($env_path)\" -c '($source_code)'")
{"job_id": $job_id}
}
export def log [
id: int # id to fetch log
] {
pueue log $id -f --json
| from json
| transpose -i info
| flatten --all
| flatten --all
| flatten status
}
# get job running status
export def status () {
pueue status --json
| from json
| get tasks
| transpose -i status
| flatten
| flatten status
}
# kill specific job
export def kill (id: int) {
pueue kill $id
}
# clean job log
export def clean () {
pueue clean
}

48
modules/base16/README.md Normal file
View file

@ -0,0 +1,48 @@
# Base16 themes integration
This folder contains a couple of files that let you configure Nushell and other terminal tools to use base16 themes.
## Requirements
Assumes a Linux system with an X-based window manager.
Specific packages and environment variable requirements are listed in each file.
## Files explanation
* **base16.nu** - Main module -- see comments inside
* **auto_base16.nu** - Generates a base16 theme from a wallpaper
* **alacritty_colors.template** - Template for Alacritty terminal colors
## Example integration
Fist, set up base16 theme generation from a wallpaper:
```
# ~/.xinitrc
...
[ -f ~/.auto_base16.nu ] && ~/.auto_base16.nu &
```
This will generate a base16 theme to a file from a current wallpaper.
You can combine this with a random wallpaper generation using the fehbg.nu
script for more chaos fun.
Next, set up Nushell config, assuming the base16 colors are generated
```
# config.nu
use ~/.config/nushell/base16.nu
# File containing base16 colors
let-env BASE16_TXT = "/tmp/base16.txt"
let config = {
color_config: (base16 build-nu-config $env.BASE16_TXT)
}
```
To select a new wallpaper and regenerate the base16 config:
```
> base16 new-wallpaper
> source ~/.config/nushell/config.nu
```

View file

@ -0,0 +1,52 @@
# Base16 template for Alacritty, 256 colors
#
# Adapted from :
# github.com/aarowill/base16-alacritty/blob/master/templates/default-256.mustache
#
# The "{{baseXX-hex}}" strings will be replaced with a Nushell script and saved
# as ~/.config/alacritty/alacritty_colors.yml. The main config (alacritty.yml)
# should be configured to load the generated file.
#
# I'm setting only the background and the foreground but in the comments is the
# full config for reference.
colors:
# Default colors
primary:
background: '{{base00-hex}}'
foreground: '{{base05-hex}}'
# Colors the cursor will use if `custom_cursor_colors` is true
# cursor:
# text: '{{base00-hex}}'
# cursor: '{{base05-hex}}'
# Normal colors
# normal:
# black: '{{base00-hex}}'
# red: '{{base08-hex}}'
# green: '{{base0b-hex}}'
# yellow: '{{base0a-hex}}'
# blue: '{{base0d-hex}}'
# magenta: '{{base0e-hex}}'
# cyan: '{{base0c-hex}}'
# white: '{{base05-hex}}'
# Bright colors
# bright:
# black: '{{base03-hex}}'
# red: '{{base08-hex}}'
# green: '{{base0b-hex}}'
# yellow: '{{base0a-hex}}'
# blue: '{{base0d-hex}}'
# magenta: '{{base0e-hex}}'
# cyan: '{{base0c-hex}}'
# white: '{{base07-hex}}'
# indexed_colors:
# - { index: 16, color: '{{base09-hex}}' }
# - { index: 17, color: '{{base0f-hex}}' }
# - { index: 18, color: '{{base01-hex}}' }
# - { index: 19, color: '{{base02-hex}}' }
# - { index: 20, color: '{{base04-hex}}' }
# - { index: 21, color: '{{base06-hex}}' }

View file

@ -0,0 +1,21 @@
#!/usr/bin/env engine-q
#
# ~/.auto_base16.nu
#
# This is a wrapper around fehbg.nu that on top of setting a random wallpaper
# also generates a base16 theme from it.
#
# This script is intended to run on desktop startup, e.g., by calling it in
# ~/.xinitrc.
#
# Requirements:
# 1. go, Python 3, fehbg.nu
# 2. Install schemer2 from https://github.com/makuto/auto-base16-theme and put
# it into your PATH
# 3. Expected environment variables:
# * WALLPAPER_IMG - The wallpaper used for generating the base16 scheme
# * BASE16_TXT - Target file to store the generated base16 scheme
# 4. Both this script and fehbg.nu are expected to be in the home directory as
# '~/.auto_base16.nu' and '~/.fehbg.nu'.
schemer2 -format img::colors -in $env.WALLPAPER_IMG -out $env.BASE16_TXT

150
modules/base16/base16.nu Normal file
View file

@ -0,0 +1,150 @@
# Build a color config for engine-q based on generated base16 file
#
# The input file is supposed to have 16 lines, each base16 colors on a separate
# line like this:
#
# ```
# #base00-hex
# #base01-hex
# ...etc.
# #base0f-hex
# ```
export def build-nu-config [base_txt: path] {
let b16 = (from-file $base_txt)
{
separator: $b16.base03
leading_trailing_space_bg: $b16.base04
header: $b16.base0b
date: $b16.base0e
filesize: $b16.base0d
row_index: $b16.base0c
bool: $b16.base08
int: $b16.base0b
duration: $b16.base08
range: $b16.base08
float: $b16.base08
string: $b16.base04
nothing: $b16.base08
binary: $b16.base08
cellpath: $b16.base08
hints: dark_gray
# base16 white on red
flatshape_garbage: { fg: $b16.base07 bg: $b16.base08 attr: b}
# if you like the regular white on red for parse errors:
# flatshape_garbage: { fg: "#FFFFFF" bg: "#FF0000" attr: b}
flatshape_bool: $b16.base0d
flatshape_int: { fg: $b16.base0e attr: b}
flatshape_float: { fg: $b16.base0e attr: b}
flatshape_range: { fg: $b16.base0a attr: b}
flatshape_internalcall: { fg: $b16.base0c attr: b}
flatshape_external: $b16.base0c
flatshape_externalarg: { fg: $b16.base0b attr: b}
flatshape_literal: $b16.base0d
flatshape_operator: $b16.base0a
flatshape_signature: { fg: $b16.base0b attr: b}
flatshape_string: $b16.base0b
flatshape_filepath: $b16.base0d
flatshape_globpattern: { fg: $b16.base0d attr: b}
flatshape_variable: $b16.base0e
flatshape_flag: { fg: $b16.base0d attr: b}
flatshape_custom: {attr: b}
}
}
# Generate Alacritty color config to be included in the main Alacritty config
#
# It injects the base16 colors into the Alacritty config template
export def build-alacritty-config [base_txt: path] {
let base16 = (from-file-table $base_txt)
let template = (
open "~/.config/nushell/alacritty_colors.mustache" |
decode utf-8
)
# TODO: need a save command:
# | save --raw "~/.config/alacritty/alacritty_colors.yml"
let conf = ($base16 | apply-base16-mustache $template)
let-env ALACRITTY_CONFIG = $conf
nu -c "$nu.env.ALACRITTY_CONFIG | save --raw '~/.config/alacritty/alacritty_colors.yml'"
}
# Generate LS_COLORS value
export def build-lscolors [base_txt: path] {
# TODO
}
# Show the current base16 colors
export def show [base_txt: path] {
from-file $base_txt | each { |it|
{ $it.column: $"(ansi -e { fg: ($it.value) bg: ($it.value) })($it.value)(ansi reset)" }
}
}
# Regenerate the wallpaper, its base16 theme, and apply it to external tools
export def new-wallpaper [] {
~/.fehbg.nu
~/.auto_base16.nu
build-alacritty-config $env.BASE16_TXT
}
# Get base16 as a record from an input file
export def from-file [base_txt: path] {
let base16_lines = (open $base_txt | lines | where ($it | str length) > 0)
{
base00 : $base16_lines.0 # Default Background
base01 : $base16_lines.1 # Lighter Background (Used for status bars, line number and folding marks)
base02 : $base16_lines.2 # Selection Background
base03 : $base16_lines.3 # Comments, Invisibles, Line Highlighting
base04 : $base16_lines.4 # Dark Foreground (Used for status bars)
base05 : $base16_lines.5 # Default Foreground, Caret, Delimiters, Operators
base06 : $base16_lines.6 # Light Foreground (Not often used)
base07 : $base16_lines.7 # Light Background (Not often used)
base08 : $base16_lines.8 # Variables, XML Tags, Markup Link Text, Markup Lists, Diff Deleted
base09 : $base16_lines.9 # Integers, Boolean, Constants, XML Attributes, Markup Link Url
base0a : $base16_lines.10 # Classes, Markup Bold, Search Text Background
base0b : $base16_lines.11 # Strings, Inherited Class, Markup Code, Diff Inserted
base0c : $base16_lines.12 # Support, Regular Expressions, Escape Characters, Markup Quotes
base0d : $base16_lines.13 # Functions, Methods, Attribute IDs, Headings
base0e : $base16_lines.14 # Keywords, Storage, Selector, Markup Italic, Diff Changed
base0f : $base16_lines.15 # Deprecated, Opening/Closing Embedded Language Tags, e.g. <?php ?>
}
}
# Get base16 as a table from an input file
export def from-file-table [base_txt: path] {
let base16_lines = (open $base_txt | lines | where ($it | str length) > 0)
[
[ name color ];
[ base00 $base16_lines.0 ] # Default Background
[ base01 $base16_lines.1 ] # Lighter Background (Used for status bars, line number and folding marks)
[ base02 $base16_lines.2 ] # Selection Background
[ base03 $base16_lines.3 ] # Comments, Invisibles, Line Highlighting
[ base04 $base16_lines.4 ] # Dark Foreground (Used for status bars)
[ base05 $base16_lines.5 ] # Default Foreground, Caret, Delimiters, Operators
[ base06 $base16_lines.6 ] # Light Foreground (Not often used)
[ base07 $base16_lines.7 ] # Light Background (Not often used)
[ base08 $base16_lines.8 ] # Variables, XML Tags, Markup Link Text, Markup Lists, Diff Deleted
[ base09 $base16_lines.9 ] # Integers, Boolean, Constants, XML Attributes, Markup Link Url
[ base0a $base16_lines.10 ] # Classes, Markup Bold, Search Text Background
[ base0b $base16_lines.11 ] # Strings, Inherited Class, Markup Code, Diff Inserted
[ base0c $base16_lines.12 ] # Support, Regular Expressions, Escape Characters, Markup Quotes
[ base0d $base16_lines.13 ] # Functions, Methods, Attribute IDs, Headings
[ base0e $base16_lines.14 ] # Keywords, Storage, Selector, Markup Italic, Diff Changed
[ base0f $base16_lines.15 ] # Deprecated, Opening/Closing Embedded Language Tags, e.g. <?php ?>
]
}
# Replace {{baseXX-hex}} with proper colors, such as #ffeedd
def apply-base16-mustache [template: string] {
reduce -f $template {
let subs = $"\{\{($it.item.name)-hex\}\}" # regex
let color = $it.item.color
$it.acc | str replace -a $subs $color
}
}

View file

@ -0,0 +1,37 @@
export def draw [] {
let term_cols = ((term size).columns - 1)
# let's itertate through each of the columns of our terminal
0..$term_cols | each { |col|
let r = (255 - ($col * 255 / $term_cols) | math round)
let g = ($col * 510 / $term_cols | math round)
let b = ($col * 255 / $term_cols | math round)
if $g > 255 {
let g = (510 - $g)
build-colorstr $r $g $b $col
} else {
build-colorstr $r $g $b $col
}
} | str join
}
def build-colorstr [
r:int # Red
g:int # Green
b:int # Blue
c:int # Column
] {
# Heavy use of string interpolation below
let bg = $"(ansi rgb_bg)($r);($g);($b)m"
let fg = $"(ansi rgb_fg)(255 - $r);(255 - $g);(255 - $b)m"
let idx = ($c mod 2)
let slash_str = (if $idx == 0 {
$'/(ansi reset)'
} else {
$'\(ansi reset)'
})
$"($bg)($fg)($slash_str)"
# sleep 10ms | ignore
}
draw

View file

@ -0,0 +1,69 @@
def contrast_colour [ colour:int ] {
# The first 16 colors
if $colour < 16 {
if $colour == 0 {
15
} else {
0
}
} else {
# The gray colors
if $colour > 231 {
if $colour < 244 {
15
} else {
0
}
} else {
# The rest
let r = ($colour - 16) / 36
let g = (($colour - 16) mod 36) / 6
let b = ($colour - 16) mod 6
let luminance = ($r * 299) + ($g * 587) + ($b * 114)
if $luminance > 2500 {
0
} else {
15
}
}
}
}
def print_colour [ colour:int ] {
let contrast = (contrast_colour $colour)
let bg_color = $"(ansi idx_bg)($colour)m" # Start block of colour
let fg_color = $"(ansi idx_fg)($contrast)m" # In contrast, print number
let text = $"($colour | into string | fill -c ' ' -w 3 -a r)(ansi reset)"
$bg_color + $fg_color + $text + " "
}
let printable_colours = 256
def print_run [start:int, amount:int] {
$start..<($start + $amount) | each { |i|
if $i < $printable_colours {
print_colour $i
} else {
""
}
} | append " " | str join
}
def print_blocks [start:int, end:int, block_cols:int, block_rows:int, blocks_per_line:int] {
let block_length = ($block_cols * $block_rows)
let end = (($end - $start) / (($blocks_per_line) * $block_length))
0..<$end | each { |i|
0..<$block_rows | each { |row|
0..<$blocks_per_line | each { |block|
print_run ($start + $block * $block_length + $row * $block_cols + $i * $block_length * $blocks_per_line) $block_cols
} | append (char nl) | str join
} | str join
} | str join
}
print (print_run 0 16) # The first 16 colours are spread over the whole spectrum
print "" # Single line
print (print_blocks 16 123 6 6 3) # 6x6x6 colour cube between 16 and 123 inclusive
print (print_blocks 124 231 6 6 3) # 6x6x6 colour cube between 124 and 231 inclusive
print (print_blocks 232 255 12 2 1) # Not 50, but 24 Shades of Grey

View file

@ -0,0 +1,5 @@
# Coloring Scripts
### Definition
These scripts are used to demonstrate the `ansi` command using `ansi` coloring. This is mainly a demo area where we have taken typical `bash` scripts and ported them to nushell scripts. It would be nice if all scripts here showed the "other" version of script and the ported nushell version. We can show "other" flavors of scripts by including them as comments in the nushell scripts or by naming the nushell script and the other script the same basename.

56
modules/coloring/hl.nu Normal file
View file

@ -0,0 +1,56 @@
export def combine [txt: string, fg: record, bg: record] {
{
txt: $txt,
fg: $fg.fg,
bg: $bg.fg,
bli: ($fg.bli or $bg.bli),
bol: ($fg.bol or $bg.bol),
dim: ($fg.dim or $bg.dim),
hid: ($fg.hid or $bg.hid),
ita: ($fg.ita or $bg.ita),
rev: ($fg.rev or $bg.rev),
stk: ($fg.stk or $bg.stk),
und: ($fg.und or $bg.und)
}
}
export def create [txt: string,
fg = "n", bg = "n",
bli = false, bol = false, dim = false, hid = false,
ita = false, rev = false, stk = false, und = false] {
{
txt: $txt,
fg: $fg,
bg: $bg,
bli: $bli,
bol: $bol,
dim: $dim,
hid: $hid,
ita: $ita,
rev: $rev,
stk: $stk,
und: $und
}
}
export def render [obj: record] {
let attr = ""
let attr = $"($attr)(if $obj.bli {'l'})"
let attr = $"($attr)(if $obj.bol {'b'})"
let attr = $"($attr)(if $obj.dim {'d'})"
let attr = $"($attr)(if $obj.hid {'h'})"
let attr = $"($attr)(if $obj.ita {'i'})"
let attr = $"($attr)(if $obj.rev {'r'})"
let attr = $"($attr)(if $obj.stk {'s'})"
let attr = $"($attr)(if $obj.und {'u'})"
let color = {fg: $obj.fg, bg: $obj.bg, attr: $attr}
$"(ansi $color)($obj.txt)(ansi reset)"
}
export def reverse [obj: record] {
let r = ($obj | update fg $obj.bg)
let r = ($r | update bg $obj.fg)
$r
}

View file

@ -0,0 +1,149 @@
# these are common colors from https://en.wikipedia.org/wiki/web_colors
let aliceblue = "#f0f8ff"
let antiquewhite = "#faebd7"
let aqua = "#00ffff"
let aquamarine = "#7fffd4"
let azure = "#f0ffff"
let beige = "#f5f5dc"
let bisque = "#ffe4c4"
let black = "#000000"
let blanchedalmond = "#ffebcd"
let blue = "#0000ff"
let blueviolet = "#8a2be2"
let brown = "#a52a2a"
let burlywood = "#deb887"
let cadetblue = "#5f9ea0"
let chartreuse = "#7fff00"
let chocolate = "#d2691e"
let coral = "#ff7f50"
let cornflowerblue = "#6495ed"
let cornsilk = "#fff8dc"
let crimson = "#dc143c"
let cyan = "#00ffff"
let darkblue = "#00008b"
let darkcyan = "#008b8b"
let darkgoldenrod = "#b8860b"
let darkgray = "#a9a9a9"
let darkgrey = "#a9a9a9"
let darkgreen = "#006400"
let darkkhaki = "#bdb76b"
let darkmagenta = "#8b008b"
let darkolivegreen = "#556b2f"
let darkorange = "#ff8c00"
let darkorchid = "#9932cc"
let darkred = "#8b0000"
let darksalmon = "#e9967a"
let darkseagreen = "#8fbc8f"
let darkslateblue = "#483d8b"
let darkslategray = "#2f4f4f"
let darkslategrey = "#2f4f4f"
let darkturquoise = "#00ced1"
let darkviolet = "#9400d3"
let deeppink = "#ff1493"
let deepskyblue = "#00bfff"
let dimgray = "#696969"
let dimgrey = "#696969"
let dodgerblue = "#1e90ff"
let firebrick = "#b22222"
let floralwhite = "#fffaf0"
let forestgreen = "#228b22"
let fuchsia = "#ff00ff"
let gainsboro = "#dcdcdc"
let ghostwhite = "#f8f8ff"
let gold = "#ffd700"
let goldenrod = "#daa520"
let gray = "#808080"
let grey = "#808080"
let green = "#008000"
let greenyellow = "#adff2f"
let honeydew = "#f0fff0"
let hotpink = "#ff69b4"
let indianred = "#cd5c5c"
let indigo = "#4b0082"
let ivory = "#fffff0"
let khaki = "#f0e68c"
let lavender = "#e6e6fa"
let lavenderblush = "#fff0f5"
let lawngreen = "#7cfc00"
let lemonchiffon = "#fffacd"
let lightblue = "#add8e6"
let lightcoral = "#f08080"
let lightcyan = "#e0ffff"
let lightgoldenrodyellow = "#fafad2"
let lightgray = "#d3d3d3"
let lightgrey = "#d3d3d3"
let lightgreen = "#90ee90"
let lightpink = "#ffb6c1"
let lightsalmon = "#ffa07a"
let lightseagreen = "#20b2aa"
let lightskyblue = "#87cefa"
let lightslategray = "#778899"
let lightslategrey = "#778899"
let lightsteelblue = "#b0c4de"
let lightyellow = "#ffffe0"
let lime = "#00ff00"
let limegreen = "#32cd32"
let linen = "#faf0e6"
let magenta = "#ff00ff"
let maroon = "#800000"
let mediumaquamarine = "#66cdaa"
let mediumblue = "#0000cd"
let mediumorchid = "#ba55d3"
let mediumpurple = "#9370d8"
let mediumseagreen = "#3cb371"
let mediumslateblue = "#7b68ee"
let mediumspringgreen = "#00fa9a"
let mediumturquoise = "#48d1cc"
let mediumvioletred = "#c71585"
let midnightblue = "#191970"
let mintcream = "#f5fffa"
let mistyrose = "#ffe4e1"
let moccasin = "#ffe4b5"
let navajowhite = "#ffdead"
let navy = "#000080"
let oldlace = "#fdf5e6"
let olive = "#808000"
let olivedrab = "#6b8e23"
let orange = "#ffa500"
let orangered = "#ff4500"
let orchid = "#da70d6"
let palegoldenrod = "#eee8aa"
let palegreen = "#98fb98"
let paleturquoise = "#afeeee"
let palevioletred = "#d87093"
let papayawhip = "#ffefd5"
let peachpuff = "#ffdab9"
let peru = "#cd853f"
let pink = "#ffc0cb"
let plum = "#dda0dd"
let powderblue = "#b0e0e6"
let purple = "#800080"
let rebeccapurple = "#663399"
let red = "#ff0000"
let rosybrown = "#bc8f8f"
let royalblue = "#4169e1"
let saddlebrown = "#8b4513"
let salmon = "#fa8072"
let sandybrown = "#f4a460"
let seagreen = "#2e8b57"
let seashell = "#fff5ee"
let sienna = "#a0522d"
let silver = "#c0c0c0"
let skyblue = "#87ceeb"
let slateblue = "#6a5acd"
let slategray = "#708090"
let slategrey = "#708090"
let snow = "#fffafa"
let springgreen = "#00ff7f"
let steelblue = "#4682b4"
let tan = "#d2b48c"
let teal = "#008080"
let thistle = "#d8bfd8"
let tomato = "#ff6347"
let turquoise = "#40e0d0"
let violet = "#ee82ee"
let wheat = "#f5deb3"
let white = "#ffffff"
let whitesmoke = "#f5f5f5"
let yellow = "#ffff00"
let yellowgreen = "#9acd32"

View file

@ -0,0 +1,27 @@
#Function to extract archives with different extensions
export def extract [name:string #name of the archive to extract
] {
let exten = [ [ex com];
['.tar.bz2' 'tar xjf']
['.tar.gz' 'tar xzf']
['.bz2' 'bunzip2']
['.rar' 'unrar x']
['.tbz2' 'tar xjf']
['.tgz' 'tar xzf']
['.zip' 'unzip']
['.7z' '/usr/bin/7z x']
['.deb' 'ar x']
['.tar.xz' 'tar xvf']
['.tar.zst' 'tar xvf']
['.tar' 'tar xvf']
['.gz' 'gunzip']
['.Z' 'uncompress']
]
let command = ($exten|where $name =~ $it.ex|first)
if ($command|is-empty) {
echo 'Error! Unsupported file extension'
} else {
nu -c ($command.com + ' ' + $name)
}
}

385
modules/docker/docker.nu Normal file
View file

@ -0,0 +1,385 @@
export-env {
for c in [podman nerdctl docker] {
if not (which $c | is-empty) {
let-env docker-cli = $c
break
}
}
}
def "nu-complete docker ns" [] {
if $env.docker-cli == 'nerdctl' {
^$env.docker-cli namespace list
| from ssv -a
| each {|x| { value: $x.NAMES }}
} else {
[]
}
}
export def dp [-n: string@"nu-complete docker ns"] {
# ^$env.docker-cli ps --all --no-trunc --format='{{json .}}' | jq
let cli = $env.docker-cli
if $cli == 'docker' {
^$cli ps -a --format '{"id":"{{.ID}}", "image": "{{.Image}}", "name":"{{.Names}}", "cmd":{{.Command}}, "port":"{{.Ports}}", "status":"{{.Status}}", "created":"{{.CreatedAt}}"}'
| lines
| each {|x|
let r = ($x | from json)
let t = ($r.created | str substring ..25 | into datetime -f '%Y-%m-%d %H:%M:%S %z' )
$r | upsert created $t
}
} else if $cli == 'podman' {
^$cli ps -a --format '{"id":"{{.ID}}", "image": "{{.Image}}", "name":"{{.Names}}", "cmd":"{{.Command}}", "port":"{{.Ports}}", "status":"{{.Status}}", "created":"{{.Created}}"}'
| lines
| each {|x|
let r = ($x | from json)
let t = ($r.created | str substring ..32 | into datetime )
$r | upsert created $t
}
} else {
let ns = if ($n|is-empty) { [] } else { [-n $n] }
^$cli $ns ps -a
| from ssv
| rename id image cmd created status port name
}
}
export def di [-n: string@"nu-complete docker ns"] {
let ns = if ($n|is-empty) { [] } else { [-n $n] }
^$env.docker-cli $ns images
| from ssv -a
| rename repo tag id created size
| each {|x|
let size = ($x.size | into filesize)
let path = ($x.repo | split row '/')
let image = ($path | last)
let repo = ($path | range ..(($path|length) - 2) | str join '/')
{
repo: $repo
image: $image
tag: $x.tag
id: $x.id
created: $x.created
size: $size
}
}
}
def "nu-complete docker ps" [] {
^$env.docker-cli ps
| from ssv -a
| each {|x| {description: $x.NAMES value: $x.'CONTAINER ID'}}
}
def "nu-complete docker container" [] {
^$env.docker-cli ps
| from ssv -a
| each {|x| {description: $x.'CONTAINER ID' value: $x.NAMES}}
}
def "nu-complete docker all container" [] {
^$env.docker-cli ps -a
| from ssv -a
| each {|x| {description: $x.'CONTAINER ID' value: $x.NAMES}}
}
def "nu-complete docker images" [] {
^$env.docker-cli images
| from ssv
| each {|x| $"($x.REPOSITORY):($x.TAG)"}
}
export def dl [ctn: string@"nu-complete docker container"
-l: int = 100 # line
] {
let l = if $l == 0 { [] } else { [--tail $l] }
^$env.docker-cli logs -f $l $ctn
}
export def dln [ctn: string@"nu-complete docker container"
-l: int = 100 # line
-n: string@"nu-complete docker ns" # namespace
] {
let l = if $l == 0 { [] } else { [--tail $l] }
let ns = if ($n|is-empty) { [] } else { [-n $n] }
^$env.docker-cli $ns logs -f $l $ctn
}
export def da [
ctn: string@"nu-complete docker container"
-n: string@"nu-complete docker ns"
...args
] {
let ns = if ($n|is-empty) { [] } else { [-n $n] }
if ($args|is-empty) {
^$env.docker-cli $ns exec -it $ctn /bin/sh -c "[ -e /bin/zsh ] && /bin/zsh || [ -e /bin/bash ] && /bin/bash || /bin/sh"
} else {
^$env.docker-cli $ns exec -it $ctn $args
}
}
def "nu-complete docker cp" [cmd: string, offset: int] {
let argv = ($cmd | str substring ..$offset | split row ' ')
let p = if ($argv | length) > 2 { $argv | get 2 } else { $argv | get 1 }
let ctn = (
^$env.docker-cli ps
| from ssv -a
| each {|x| {description: $x.'CONTAINER ID' value: $"($x.NAMES):" }}
)
let n = ($p | split row ':')
if $"($n | get 0):" in ($ctn | get value) {
^$env.docker-cli exec ($n | get 0) sh -c $"ls -dp ($n | get 1)*"
| lines
| each {|x| $"($n | get 0):($x)"}
} else {
let files = (do -i {
ls -a $"($p)*"
| each {|x| if $x.type == dir { $"($x.name)/"} else { $x.name }}
})
$files | append $ctn
}
}
export def dcp [
lhs: string@"nu-complete docker cp",
rhs: string@"nu-complete docker cp"
] {
^$env.docker-cli cp $lhs $rhs
}
export def dcr [ctn: string@"nu-complete docker all container" -n: string@"nu-complete docker ns"] {
let ns = if ($n|is-empty) { [] } else { [-n $n] }
^$env.docker-cli $ns container rm -f $ctn
}
export def dis [img: string@"nu-complete docker images" -n: string@"nu-complete docker ns"] {
let ns = if ($n|is-empty) { [] } else { [-n $n] }
^$env.docker-cli $ns inspect $img
}
export def dh [img: string@"nu-complete docker images" -n: string@"nu-complete docker ns"] {
let ns = if ($n|is-empty) { [] } else { [-n $n] }
^$env.docker-cli $ns history --no-trunc $img | from ssv -a
}
export def dsv [-n: string@"nu-complete docker ns" ...img: string@"nu-complete docker images"] {
let ns = if ($n|is-empty) { [] } else { [-n $n] }
^$env.docker-cli $ns save $img
}
export def dld [-n: string@"nu-complete docker ns"] {
let ns = if ($n|is-empty) { [] } else { [-n $n] }
^$env.docker-cli $ns load
}
export def dsp [-n: string@"nu-complete docker ns"] {
let ns = if ($n|is-empty) { [] } else { [-n $n] }
^$env.docker-cli $ns system prune -f
}
export def dspall [-n: string@"nu-complete docker ns"] {
let ns = if ($n|is-empty) { [] } else { [-n $n] }
^$env.docker-cli $ns system prune --all --force --volumes
}
export def drmi [img: string@"nu-complete docker images" -n: string@"nu-complete docker ns"] {
let ns = if ($n|is-empty) { [] } else { [-n $n] }
^$env.docker-cli $ns rmi $img
}
export def dt [from: string@"nu-complete docker images" to: string -n: string@"nu-complete docker ns"] {
let ns = if ($n|is-empty) { [] } else { [-n $n] }
^$env.docker-cli $ns tag $from $to
}
export def dps [img: string@"nu-complete docker images" -n: string@"nu-complete docker ns"] {
let ns = if ($n|is-empty) { [] } else { [-n $n] }
^$env.docker-cli $ns push $img
}
export def dpl [img -n: string@"nu-complete docker ns"] {
let ns = if ($n|is-empty) { [] } else { [-n $n] }
^$env.docker-cli $ns pull $img
}
### volume
export def dvl [-n: string@"nu-complete docker ns"] {
let ns = if ($n|is-empty) { [] } else { [-n $n] }
^$env.docker-cli $ns volume ls | from ssv -a
}
def "nu-complete docker volume" [] {
dvl | get name
}
export def dvc [name: string -n: string@"nu-complete docker ns"] {
let ns = if ($n|is-empty) { [] } else { [-n $n] }
^$env.docker-cli $ns volume create
}
export def dvi [name: string@"nu-complete docker volume" -n: string@"nu-complete docker ns"] {
let ns = if ($n|is-empty) { [] } else { [-n $n] }
^$env.docker-cli $ns volume inspect $name
}
export def dvr [...name: string@"nu-complete docker volume" -n: string@"nu-complete docker ns"] {
let ns = if ($n|is-empty) { [] } else { [-n $n] }
^$env.docker-cli $ns volume rm $name
}
### run
def "nu-complete docker run vol" [] {
[
$"($env.PWD):/world"
$"($env.PWD):/app"
$"($env.PWD):/srv"
$"($env.HOME)/.config/nvim:/etc/nvim"
]
}
def "nu-complete docker run sshkey" [ctx: string, pos: int] {
(do { cd ~/.ssh; ls **/*.pub } | get name)
}
def "nu-complete docker run proxy" [] {
let hostaddr = (do -i { hostname -I | split row ' ' | get 0 })
[ $"http://($hostaddr):7890" $"http://($hostaddr):" ]
}
def host-path [path] {
match ($path | str substring ..1) {
'/' => { $path }
'~' => { [ $nu.home-path ($path | str substring 2..) ] | path join }
'$' => { ($env | get ($path | str substring 1..)) }
_ => { [ $env.PWD $path ] | path join }
}
}
export def dr [
--debug(-x): bool
--appimage: bool
--netadmin: bool
--proxy: string@"nu-complete docker run proxy" # proxy
--ssh(-s): string@"nu-complete docker run sshkey" # specify ssh key
--sshuser: string=root # default root
--cache(-c): string # cache
--mnt(-m): string@"nu-complete docker run vol" # mnt
--vols(-v): any # { host: container }
--ports(-p): any # { 8080: 80 }
--envs(-e): any # { FOO: BAR }
--daemon(-d): bool
--attach(-a): string@"nu-complete docker container" # attach
--workdir(-w): string # workdir
--entrypoint: string # entrypoint
--dry-run: bool
--with-x: bool
--namespace(-n): string@"nu-complete docker ns"
img: string@"nu-complete docker images" # image
...cmd # command args
] {
let ns = if ($namespace|is-empty) { [] } else { [-n $namespace] }
let entrypoint = if ($entrypoint|is-empty) { [] } else { [--entrypoint $entrypoint] }
let daemon = if $daemon { [-d] } else { [--rm -it] }
let mnt = if ($mnt|is-empty) { [] } else { [-v $mnt] }
let workdir = if ($workdir|is-empty) { [] } else { [-w $workdir] }
let vols = if ($vols|is-empty) { [] } else { $vols | transpose k v | each {|x| $"-v '(host-path $x.k):($x.v)'"} }
let envs = if ($envs|is-empty) { [] } else { $envs | transpose k v | each {|x| $"-e ($x.k)=($x.v)"} }
let ports = if ($ports|is-empty) { [] } else { $ports | transpose k v | each {|x|[-p $"($x.k):($x.v)"]} | flatten }
let debug = if $debug { [--cap-add=SYS_ADMIN --cap-add=SYS_PTRACE --security-opt seccomp=unconfined] } else { [] }
#let appimage = if $appimage { [--device /dev/fuse --security-opt apparmor:unconfined] } else { [] }
let appimage = if $appimage { [--device /dev/fuse] } else { [] }
let netadmin = if $netadmin { [--cap-add=NET_ADMIN --device /dev/net/tun] } else { [] }
let clip = if $with_x { [-e DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix] } else { [] }
let ssh = if ($ssh|is-empty) { [] } else {
let sshkey = (cat ([$env.HOME .ssh $ssh] | path join) | split row ' ' | get 1)
[-e $"ed25519_($sshuser)=($sshkey)"]
}
let proxy = if ($proxy|is-empty) { [] } else {
[-e $"http_proxy=($proxy)" -e $"https_proxy=($proxy)"]
}
let attach = if ($attach|is-empty) { [] } else {
let c = $"container:($attach)"
[--uts $c --ipc $c --pid $c --network $c]
}
let cache = if ($cache|is-empty) { [] } else { [-v $cache] }
let args = ([
$entrypoint $attach $daemon
$ports $envs $ssh $proxy
$debug $appimage $netadmin $clip
$mnt $vols $workdir $cache
] | flatten)
let name = $"($img | split row '/' | last | str replace ':' '-')_(date now | date format %m%d%H%M)"
if $dry_run {
echo $"docker ($ns | str join ' ') run --name ($name) ($args|str join ' ') ($img) ($cmd | flatten)"
} else {
^$env.docker-cli $ns run --name $name $args $img ($cmd | flatten)
}
}
def "nu-complete registry list" [cmd: string, offset: int] {
let cmd = ($cmd | split row ' ')
let url = (do -i { $cmd | get 2 })
let reg = (do -i { $cmd | get 3 })
let tag = (do -i { $cmd | get 4 })
if ($reg|is-empty) {
if ($env | has 'REGISTRY_TOKEN') {
http get -H [authorization $"Basic ($env.REGISTRY_TOKEN)"] $"($url)/v2/_catalog"
} else {
http get $"($url)/v2/_catalog"
}
| get repositories
} else if ($tag|is-empty) {
if ($env | has 'REGISTRY_TOKEN') {
http get $"($url)/v2/($reg)/tags/list"
} else {
http get -H [authorization $"Basic ($env.REGISTRY_TOKEN)"] $"($url)/v2/($reg)/tags/list"
}
| get tags
}
}
### docker registry list
export def "registry list" [
url: string
reg: string@"nu-complete registry list"
] {
if ($env.REGISTRY_TOKEN? | is-empty) {
http get $"($url)/v2/($reg)/tags/list"
} else {
http get -H [authorization $"Basic ($env.REGISTRY_TOKEN)"] $"($url)/v2/($reg)/tags/list"
}
| get tags
}
### buildah
export def "bud img" [] {
buildah images
| from ssv -a
| rename repo tag id created size
| upsert size { |i| $i.size | into filesize }
}
export def "bud ls" [] {
buildah list
| from ssv -a
| rename id builder image-id image container
}
export def "bud ps" [] {
buildah ps
| from ssv -a
| rename id builder image-id image container
}
def "nu-complete bud ps" [] {
bud ps
| select 'CONTAINER ID' "CONTAINER NAME"
| rename value description
}
export def "bud rm" [
id: string@"nu-complete bud ps"
] {
buildah rm $id
}

View file

@ -0,0 +1,5 @@
# Formatting scripts
### Definition
The scripts in this folder are used to help formatting nicely inputs/outputs of nushell.

View file

@ -0,0 +1,22 @@
# Convert from contents of /proc/cpuinfo to structured data
export def "from cpuinfo" [] {
lines
| split list ''
| each {
split column ':'
| str trim
| update column1 {
get column1
| str replace -a -s ' ' '_'
}
| transpose -r -d
| update flags {
get flags
| split row ' '
}
| update bugs {
get bugs
| split row ' '
}
}
}

View file

@ -0,0 +1,36 @@
# Convert from output of dmidecode to structured data
export def "from dmidecode" [] {
lines
| skip until {|x|
$x starts-with 'Handle'
}
| split list ''
| each {|entry|
let parsed_entry = (
$entry
| get 0
| parse 'Handle {handle}, DMI type {type}, {bytes} bytes'
| get 0
| insert description ($entry|get 1)
| insert values {
if ($entry|length) > 2 {
if ($entry|get 2|str trim) == 'Header and Data:' {
{'header_and_data': ($entry|skip 3|str trim)}
} else {
$entry
| skip 2
| split column ':'
| str trim
| str downcase column1
| str replace -a -s ' ' '_' column1
| transpose -r -d
}
} else {
{}
}
}
)
$parsed_entry
}
}

View file

@ -0,0 +1,45 @@
#diacritics map for Polish but the function is universal provided diacritics_map contains mappings for specific language
# Usage: (remove-diacritics 'Zażółć gęślą jaźń') == Zazolc gesla jazn
export def main [
arg: string
] {
let diacritics_map = {
# Polish
"Ą": "A",
"ą": "a",
"Ć": "C",
"ć": "c",
"Ę": "E",
"ę": "e",
"Ł": "L",
"ł": "l",
"Ń": "N",
"ń": "n",
"Ó": "O",
"ó": "o",
"Ś": "S",
"ś": "s",
"Ż": "Z",
"ż": "z",
"Ź": "Z",
"ź": "z",
# German
"ä": "ae",
"Ä": "Ae",
"ö": "oe",
"Ö": "Oe",
"ü": "ue",
"Ü": "Ue",
"ß": "ss"
}
$arg
|split chars
|each {|char|
$diacritics_map
|get -i -s $char
|default $char
}
|str join ''
}

13
modules/formats/to-ini.nu Normal file
View file

@ -0,0 +1,13 @@
# converts records into .ini files
export def "to ini" [] {
transpose key value
| update value {|row|
get value
| transpose key value
| format '{key}={value}'
| prepend $"[($row.key)]"
| str join (char nl)
}
| get value
| str join (char nl)
}

View file

@ -0,0 +1,68 @@
export def number-format [
num # Number to format
--thousands_delim (-t) = ' ' # Thousands delimiter: number-format 1000 -t ': 1'000
--whole_part_length (-w) = 0 # Length of padding whole-part digits: number-format 123 -w 6: 123
--decimal_digits (-d) = 0 # Number of digits after decimal delimiter: number-format 1000.1234 -d 2: 1000.12
--denom (-D) = "" # Denom `--denom "Wt": number-format 1000 --denom 'Wt': 1000Wt
] {
let parts = (
$num
| into string
| split row "."
)
let whole_part = (
$parts.0
| split chars
| reverse
| reduce -n -f [] {
|it, acc| if ((($it.index + 1) mod 3) == 0) {
$acc.item
| append $it.item
| append $thousands_delim
} else {
$acc.item
| append $it.item
}
}
| reverse
)
let whole_part2 = (
if ($whole_part | first) == $thousands_delim {
($whole_part | skip 1)
} else {
$whole_part
}
| str join ''
)
let whole_part3 = (
if $whole_part_length == 0 {
$whole_part2
} else {
$whole_part2
| fill -w $whole_part_length -c ' ' -a r
}
)
let dec_part = (
if ($parts | length) == 1 {
"0"
} else {
$parts.1
}
)
let dec_part2 = (
if $decimal_digits == 0 {
""
} else {
$".($dec_part)" | fill -w ($decimal_digits + 1) -c '0' -a l
}
)
let out = $"(ansi green)($whole_part3)($dec_part2)(ansi reset)(ansi green_bold)($denom)(ansi reset)"
$out
}

55
modules/fun/wordle.nu Normal file
View file

@ -0,0 +1,55 @@
# A Terminal Wordle game.
# The code is based on https://gist.github.com/huytd/6a1a6a7b34a0d0abcac00b47e3d01513 ,but slightly personalized.
# a simple termninal Wordle game!
export def main [
--unlimited(-u) # Play the game in unlimited mode.
--max_count(-M) : int = 6 # Give yourself more chances than default
--alternative_source(-a) : string = "https://raw.githubusercontent.com/charlesreid1/five-letter-words/master/sgb-words.txt" # Alternative link to provide as a word source
] {
let words = (if ($alternative_source | str substring 0..4 | str contains "http") {http get $alternative_source} else {open $alternative_source} | from ssv -n)
let word = ($words | get (random integer 0..($words | length)) | get column1)
if ((($words | each {|it| ($it.column1 | str length)}) | where $it != 5 | length) != 0 ) {
echo $"(ansi rb)Warning:(ansi reset) The words list contains words that are not length 5"
}
mut end = false
mut guess_count = 0
mut avail = "abcdefghijklmnopqrstuvyxwz"
while (not ($end)) {
$guess_count += 1
if ($guess_count <= $max_count or $unlimited) {
echo $"(ansi xterm_aquamarine1a)Enter your guess (ansi reset)\((ansi green)($guess_count)(ansi reset)/(ansi yellow)(if ($unlimited) {inf} else {$max_count})(ansi reset)\)"
mut guess = (input | str downcase )
if (((($words | where column1 =~ $guess) | length) >= 1) and ($guess | str length) == 5) {
mut out = ""
mut checked = $word
for i in ($guess | split chars) -n {
if ($i.item == ($word | str substring ($i.index)..($i.index + 1)) ) {
$out += $"(ansi green_reverse)($i.item)(ansi reset)"
$avail = ($avail | str replace $i.item $"(ansi green_reverse)($i.item)(ansi white_reverse)")
$checked = ($checked | str replace $i.item "")
} else if ( $i.item in $checked) {
$out += $"(ansi yellow_reverse)($i.item)(ansi reset)"
$avail = ($avail | str replace $i.item $"(ansi yellow_reverse)($i.item)(ansi white_reverse)")
$checked = ($checked | str replace $i.item "")
} else {
$out += $"(ansi white_reverse)($i.item)(ansi reset)"
$avail = ($avail | str replace $i.item "")
}
}
$avail = $"(ansi white_reverse)($avail)(ansi reset)"
echo $"($out) possible -> ($avail)"
if ($guess == $word) {
$end = true
echo $"(ansi xterm_green1 )You guessed right!(ansi reset)"
}
} else {
echo "please enter a valid [5 letter] word!"
$guess_count -= 1
}
} else {
echo $"(ansi yellow )You loose, the word was: (ansi red)($word)(ansi reset)"
$end = true
}
}
}

3
modules/git/README.md Normal file
View file

@ -0,0 +1,3 @@
# Git branch cleanup
Remove any local git branch no longer being tracked in origin / remote repo.

280
modules/git/git.nu Normal file
View file

@ -0,0 +1,280 @@
export def _git_stat [n] {
do -i {
git log -n $n --pretty=»¦«%h --stat
| lines
| reduce -f { c: '', r: [] } {|it, acc|
if ($it | str starts-with '»¦«') {
$acc | upsert c ($it | str substring 6.. )
} else if ($it | find -r '[0-9]+ file.+change' | is-empty) {
$acc
} else {
let x = (
$it
| split row ','
| each {|x| $x
| str trim
| parse -r "(?P<num>[0-9]+) (?P<col>.+)"
| get 0
}
| reduce -f {sha: $acc.c file:0 ins:0 del:0} {|i,a|
let col = if ($i.col | str starts-with 'file') {
'file'
} else {
$i.col | str substring ..3
}
let num = ($i.num | into int)
$a | upsert $col $num
}
)
$acc | upsert r ($acc.r | append $x)
}
}
| get r
}
}
export def _git_log [v num] {
let stat = if $v {
_git_stat $num
} else { {} }
let r = (do -i {
git log -n $num --pretty=%h»¦«%s»¦«%aN»¦«%aE»¦«%aD
| lines
| split column "»¦«" sha message author email date
| each {|x| ($x| upsert date ($x.date | into datetime))}
})
if $v {
$r | merge $stat | reverse
} else {
$r | reverse
}
}
# FIXME: number-like
def "nu-complete git log" [] {
git log -n 32 --pretty=%h»¦«%s
| lines
| split column "»¦«" value description
| each {|x| $x | update value $"`($x.value)`"}
}
# FIXME: number-like
def "nu-complete git branches" [] {
git branch
| lines
| filter {|x| not ($x | str starts-with '*')}
| each {|x| $"'($x|str trim)'"}
}
export def gl [
commit?: string@"nu-complete git log"
--verbose(-v):bool
--num(-n):int=32
] {
if ($commit|is-empty) {
_git_log $verbose $num
} else {
git log --stat -p -n 1 $commit
}
}
export def glv [
commit?: string@"nu-complete git log"
--num(-n):int=32
] {
if ($commit|is-empty) {
_git_log true $num
} else {
git log --stat -p -n 1 $commit
}
}
export def gco [branch: string@"nu-complete git branches"] {
git checkout $branch
}
export def gbD [branch: string@"nu-complete git branches"] {
git branch -D $branch
}
export def gpp! [] {
git pull
git add --all
git commit -v -a --no-edit --amend
git push --force
}
export def gha [] {
git log --pretty=%h»¦«%aN»¦«%s»¦«%aD
| lines
| split column "»¦«" sha1 committer desc merged_at
| histogram committer merger
| sort-by merger
| reverse
}
export def gsq [] {
git reflog expire --all --expire=now
git gc --prune=now --aggressive
}
def "nu-complete git remotes" [] {
^git remote | lines | each { |line| $line | str trim }
}
export def gr [remote?: string@"nu-complete git remotes"] {
let remote = if ($remote|is-empty) { 'origin' } else { $remote }
git remote show $remote
}
export def grh [commit: string@"nu-complete git log"] {
git reset $commit
}
export def gf [
branch: string@"nu-complete git branches"
remote?: string@"nu-complete git remotes"
] {
let remote = if ($remote|is-empty) { 'origin' } else { $remote }
git fetch $remote $branch
}
export def gm [branch:string@"nu-complete git branches"] {
git merge $branch
}
export def grb [branch:string@"nu-complete git branches"] {
git rebase (gstat).branch $branch
}
def git_main_branch [] {
git remote show origin
| lines
| str trim
| find --regex 'HEAD .*?[: ].+'
| first
| str replace 'HEAD .*?[: ](.+)' '$1'
}
def git_current_branch [] {
(gstat).branch
}
export def gmom [] {
let main = (git_main_branch)
git merge $"origin/($main)"
}
export alias gps = git push
export alias gpf! = git push --force
export alias gpsup = git push --set-upstream origin (git_current_branch)
export alias gpl = git pull
export alias glo = git log --oneline
export alias ga = git add
export alias gaa = git add --all
export alias gapa = git add --patch
export alias gau = git add --update
export alias gav = git add --verbose
export alias gap = git apply
export alias gapt = git apply --3way
export alias gb = git branch
export alias gba = git branch -a
export alias gbd = git branch -d
export alias gbda = 'git branch --no-color --merged | command grep -vE "^(\+|\*|\s*($(git_main_branch)|development|develop|devel|dev)\s*$)" | command xargs -n 1 git branch -d'
export alias gbl = git blame -b -w
export alias gbnm = git branch --no-merged
export alias gbr = git branch --remote
export alias gbs = git bisect
export alias gbsb = git bisect bad
export alias gbsg = git bisect good
export alias gbsr = git bisect reset
export alias gbss = git bisect start
export alias gc = git commit -v
export alias gc! = git commit -v --amend
export alias gcn! = git commit -v --no-edit --amend
export alias gca = git commit -v -a
export alias gca! = git commit -v -a --amend
export alias gcan! = git commit -v -a --no-edit --amend
export alias gcans! = git commit -v -a -s --no-edit --amend
export alias gcam = git commit -a -m
export alias gcsm = git commit -s -m
export alias gcb = ^git checkout -b
export alias gcf = git config --list
export alias gcl = git clone --recurse-submodules
export alias gclean = git clean -id
# export alias gpristine = git reset --hard and git clean -dffx
export alias gcm = git checkout (git_main_branch)
export alias gcd = git checkout develop
export alias gcmsg = git commit -m
export alias gcount = git shortlog -sn
export alias gcp = git cherry-pick
export alias gcpa = git cherry-pick --abort
export alias gcpc = git cherry-pick --continue
export alias gcs = git commit -S
export alias gd = git diff
export alias gdca = git diff --cached
export alias gdcw = git diff --cached --word-diff
export alias gdct = git describe --tags (git rev-list --tags --max-count=1)
export alias gds = git diff --staged
export alias gdt = git diff-tree --no-commit-id --name-only -r
export alias gdw = git diff --word-diff
export alias gra = git remote add
export alias grba = git rebase --abort
export alias grbc = git rebase --continue
export alias grbd = git rebase develop
export alias grbi = git rebase -i
export alias grbm = git rebase (git_main_branch)
export alias grbo = git rebase --onto
export alias grbs = git rebase --skip
export alias grev = git revert
export alias grhh = git reset --hard
export alias groh = git reset origin/$(git_current_branch) --hard
export alias grm = git rm
export alias grmc = git rm --cached
export alias grmv = git remote rename
export alias grrm = git remote remove
export alias grs = git restore
export alias grset = git remote set-url
export alias grss = git restore --source
export alias grst = git restore --staged
export alias grt = cd "$(git rev-parse --show-toplevel or echo .)"
# export alias gru = git reset --
export alias grup = git remote update
export alias grv = git remote -v
export alias gsb = git status -sb
export alias gsd = git svn dcommit
export alias gsh = git show
export alias gsi = git submodule init
export alias gsps = git show --pretty=short --show-signature
export alias gsr = git svn rebase
export alias gss = git status -s
export alias gs = git status
export alias gstaa = git stash apply
export alias gstc = git stash clear
export alias gstd = git stash drop
export alias gstl = git stash list
export alias gstp = git stash pop
export alias gsts = git stash show --text
export alias gstu = gsta --include-untracked
export alias gstall = git stash --all
export alias gsu = git submodule update
export alias gsw = git switch
export alias gswc = git switch -c
export alias gts = git tag -s
export alias gunignore = git update-index --no-assume-unchanged
export alias gpr = git pull --rebase
export alias gprv = git pull --rebase -v
export alias gpra = git pull --rebase --autostash
export alias gprav = git pull --rebase --autostash -v
export alias glum = git pull upstream (git_main_branch)
# cat ($nu.config-path | path dirname | path join 'scripts' | path join 'a.nu' )

View file

@ -0,0 +1,4 @@
#!/usr/bin/env nu
git branch -vl | lines | split column " " BranchName Hash Status --collapse-empty | where Status == '[gone]' | each { |it| git branch -D $it.BranchName }

View file

@ -0,0 +1,535 @@
export def "parse cmd" [] {
$in
| split row ' '
| reduce -f { args: [], sw: '' } {|it, acc|
if ($acc.sw|is-empty) {
if ($it|str starts-with '-') {
$acc | upsert sw $it
} else {
let args = ($acc.args | append $it)
$acc | upsert args $args
}
} else {
if ($it|str starts-with '-') {
$acc
| upsert $acc.sw true
| upsert sw $it
} else {
$acc | upsert $acc.sw $it | upsert sw ''
}
}
}
| reject sw
}
export def ensure-index [index path action] {
let ts = (do -i { ls $path | sort-by modified | reverse | get 0.modified })
if ($ts | is-empty) { return false }
let tc = (do -i { ls $index | get 0.modified })
if not (($index | path exists) and ($ts < $tc)) {
mkdir (dirname $index)
do $action
}
}
export-env {
let-env KUBERNETES_SCHEMA_URL = $"file:///($env.HOME)/.config/kubernetes-json-schema/all.json"
}
### file
export def kaf [p: path] {
kubectl apply -f $p
}
export def kdf [p: path] {
kubectl diff -f $p
}
export def kdelf [p: path] {
kubectl delete -f $p
}
export def kak [p: path] {
kubectl apply -k $p
}
export def kdk [p: path] {
kubectl diff -k $p
}
export def kdelk [p: path] {
kubectl delete -k $p
}
export def kk [p: path] {
kubectl kustomize $p
}
### ctx
export def "kube-config" [] {
let file = if ($env.KUBECONFIG? | is-empty) { $"($env.HOME)/.kube/config" } else { $env.KUBECONFIG }
{ path: $file, data: (cat $file | from yaml)}
}
def "nu-complete kube ctx" [] {
let k = (kube-config)
let cache = $'($env.HOME)/.cache/nu-complete/k8s/(basename $k.path).json'
ensure-index $cache $k.path { ||
let clusters = ($k.data | get clusters | select name cluster.server)
let data = ($k.data
| get contexts
| reduce -f {completion:[], mx_ns: 0, mx_cl: 0} {|x, a|
let ns = (if ($x.context.namespace? | is-empty) { '' } else { $x.context.namespace })
let max_ns = ($ns | str length)
let cluster = ($"($x.context.user)@($clusters | where name == $x.context.cluster | get cluster_server.0)")
let max_cl = ($cluster | str length)
$a
| upsert mx_ns (if $max_ns > $a.mx_ns { $max_ns } else $a.mx_ns)
| upsert mx_cl (if $max_cl > $a.mx_cl { $max_cl } else $a.mx_cl)
| upsert completion ($a.completion | append {value: $x.name, ns: $ns, cluster: $cluster})
})
{completion: $data.completion, max: {ns: $data.mx_ns, cluster: $data.mx_cl}} | save -f $cache
}
let data = (cat $cache | from json)
$data.completion | each {|x|
let ns = ($x.ns | fill -a l -w $data.max.ns -c ' ')
let cl = ($x.cluster | fill -a l -w $data.max.cluster -c ' ')
{value: $x.value, description: $"\t($ns) ($cl)"}
}
}
def "nu-complete kube ns" [] {
kubectl get namespaces
| from ssv -a
| each {|x|
{value: $x.NAME, description: $"($x.AGE)\t($x.STATUS)"}
}
}
export def kcc [ctx: string@"nu-complete kube ctx"] {
kubectl config use-context $ctx
}
export def kn [ns: string@"nu-complete kube ns"] {
kubectl config set-context --current $"--namespace=($ns)"
}
export def 'kconf import' [name: string, path: string] {
let k = (kube-config)
let d = $k.data
let i = (cat $path | from yaml)
let c = [{
name: $name,
context: {
cluster: $name,
namespace: default,
user: $name
}
}]
$d
| upsert clusters ($d.clusters | append ($i.clusters.0 | upsert name $name))
| upsert users ($d.users | append ($i.users.0 | upsert name $name))
| upsert contexts ($d.contexts | append $c)
| to yaml
}
export def 'kconf export' [name: string@"nu-complete kube ctx"] {
let d = (kube-config).data
let ctx = ($d | get contexts | where name == $name | get 0)
let user = ($d | get users | where name == $ctx.context.user)
let cluster = ($d | get clusters | where name == $ctx.context.cluster)
{
apiVersion: 'v1',
current-context: $ctx.name,
kind: Config,
clusters: $cluster,
preferences: {},
contexts: [$ctx],
users: $user,
} | to yaml
}
export def-env kcconf [name: string@"nu-complete kube ctx"] {
let dist = $"($env.HOME)/.kube/config.d"
mkdir $dist
kconf export $name | save -fr $"($dist)/($name)"
let-env KUBECONFIG = $"($dist)/($name)"
}
### common
def "nu-complete kube def" [] {
[
pod deployment svc endpoints
configmap secret event
namespace node pv pvc ingress
job cronjob daemonset statefulset
clusterrole clusterrolebinding role serviceaccount rolebinding
] | append (kubectl get crd | from ssv -a | get NAME)
}
def "nu-complete kube res" [context: string, offset: int] {
let ctx = ($context | parse cmd)
let def = ($ctx | get args.1)
let ns = (do -i { $ctx | get '-n' })
let ns = if ($ns|is-empty) { [] } else { [-n $ns] }
kubectl get $ns $def | from ssv -a | get NAME
}
export def kg [
r: string@"nu-complete kube def"
-n: string@"nu-complete kube ns"
--all (-A):bool
] {
let n = if $all {
[-A]
} else if ($n | is-empty) {
[]
} else {
[-n $n]
}
#let h = ($d | columns | str kebab-case)
#$d | rename ...$h
kubectl get $n $r | from ssv -a
}
export def kc [
r: string@"nu-complete kube def"
-n: string@"nu-complete kube ns"
name:string
] {
let n = if ($n|is-empty) { [] } else { [-n $n] }
kubectl create $n $r $name
}
export def ky [
r: string@"nu-complete kube def"
i: string@"nu-complete kube res"
-n: string@"nu-complete kube ns"
] {
let n = if ($n|is-empty) { [] } else { [-n $n] }
kubectl get $n -o yaml $r $i
}
export def kd [
r: string@"nu-complete kube def"
i: string@"nu-complete kube res"
-n: string@"nu-complete kube ns"
] {
let n = if ($n|is-empty) { [] } else { [-n $n] }
kubectl describe $n $r $i
}
export def ke [
r: string@"nu-complete kube def"
i: string@"nu-complete kube res"
-n: string@"nu-complete kube ns"
] {
let n = if ($n|is-empty) { [] } else { [-n $n] }
kubectl edit $n $r $i
}
export def kdel [
r: string@"nu-complete kube def"
i: string@"nu-complete kube res"
-n: string@"nu-complete kube ns"
--force(-f): bool
] {
let n = if ($n|is-empty) { [] } else { [-n $n] }
let f = if $force { [--grace-period=0 --force] } else { [] }
kubectl delete $n $f $r $i
}
### node
export def kgno [] {
kubectl get nodes -o wide | from ssv -a
| rename name status roles age version internal-ip external-ip os kernel runtime
}
### pods
def "nu-complete kube pods" [context: string, offset: int] {
let ctx = ($context | parse cmd)
let ns = (do -i { $ctx | get '-n' })
let ns = if ($ns|is-empty) { [] } else { [-n $ns] }
kubectl get $ns pods | from ssv -a | get NAME
}
def "nu-complete kube ctns" [context: string, offset: int] {
let ctx = ($context | parse cmd)
let ns = (do -i { $ctx | get '-n' })
let ns = if ($ns|is-empty) { [] } else { [-n $ns] }
let ctn = (do -i { $ctx | get '-c' })
let ctn = if ($ctn|is-empty) { [] } else { [-c $ctn] }
let pod = ($ctx | get args.1)
kubectl get $ns pod $pod -o jsonpath={.spec.containers[*].name} | split row ' '
}
export def kgpl [] {
kubectl get pods -o json
| from json
| get items
| each {|x|
let rs = $x.status.containerStatuses.0.restartCount
{
namespace: $x.metadata.namespace,
name: $x.metadata.name,
status: $x.status.phase,
restarts: ($rs | split row ' '| get 0 | into int),
age: ($x.status.startTime | into datetime)
}}
}
export def kgpa [] {
kubectl get pods -o wide -A | from ssv -a
| rename namespace name ready status restarts age ip node
| each {|x| ($x| upsert restarts ($x.restarts|split row ' '| get 0 | into int)) }
| reject 'NOMINATED NODE' 'READINESS GATES'
}
export def kgp [-n: string@"nu-complete kube ns"] {
let n = if ($n|is-empty) { [] } else { [-n $n] }
kubectl get pods $n -o wide | from ssv -a
| rename name ready status restarts age ip node
| each {|x| ($x| upsert restarts ($x.restarts|split row ' '| get 0 | into int)) }
| reject 'NOMINATED NODE' 'READINESS GATES'
}
export def kgpw [] {
kubectl get pods --watch
}
export def kep [-n: string@"nu-complete kube ns", pod: string@"nu-complete kube pods"] {
let n = if ($n|is-empty) { [] } else { [-n $n] }
kubectl edit pod $n $pod
}
export def kdp [-n: string@"nu-complete kube ns", pod: string@"nu-complete kube pods"] {
let n = if ($n|is-empty) { [] } else { [-n $n] }
kubectl describe pod $n $pod
}
export def ka [
pod: string@"nu-complete kube pods"
-n: string@"nu-complete kube ns"
--container(-c): string@"nu-complete kube ctns"
...args
] {
let n = if ($n|is-empty) { [] } else { [-n $n] }
let c = if ($container|is-empty) { [] } else { [-c $container] }
kubectl exec $n -it $pod $c -- (if ($args|is-empty) { 'bash' } else { $args })
}
export def kl [
pod: string@"nu-complete kube pods"
--namespace(-n): string@"nu-complete kube ns"
--container(-c): string@"nu-complete kube ctns"
] {
let n = if ($namespace|is-empty) { [] } else { [-n $namespace] }
let c = if ($container|is-empty) { [] } else { [-c $container] }
kubectl logs $n $pod $c
}
export def klf [
pod: string@"nu-complete kube pods"
--namespace(-n): string@"nu-complete kube ns"
--container(-c): string@"nu-complete kube ctns"
] {
let n = if ($namespace|is-empty) { [] } else { [-n $namespace] }
let c = if ($container|is-empty) { [] } else { [-c $container] }
kubectl logs $n -f $pod $c
}
def "nu-complete port forward type" [] {
[pod svc]
}
export def kpf [
res: string@"nu-complete port forward type"
target: string@"nu-complete kube res"
-n: string@"nu-complete kube ns"
port: string ### reflect port num
] {
let n = if ($n|is-empty) { [] } else { [-n $n] }
kubectl port-forward $n $"($res)/($target)" $port
}
def "nu-complete kube cp" [cmd: string, offset: int] {
let ctx = ($cmd | str substring ..$offset | parse cmd)
let p = ($ctx.args | get (($ctx.args | length) - 1))
let ns = (do -i { $ctx | get '-n' })
let ns = if ($ns|is-empty) { [] } else { [-n $ns] }
let c = (do -i { $ctx | get '-c' })
let c = if ($c|is-empty) { [] } else { [-c $c] }
let ctn = (kubectl get pod $ns | from ssv -a | each {|x| {description: $x.READY value: $"($x.NAME):" }})
let n = ($p | split row ':')
if $"($n | get 0):" in ($ctn | get value) {
kubectl exec $ns ($n | get 0) $c -- sh -c $"ls -dp ($n | get 1)*"
| lines
| each {|x| $"($n | get 0):($x)"}
} else {
let files = (do -i { ls -a $"($p)*"
| each {|x| if $x.type == dir { $"($x.name)/"} else { $x.name }}
})
$files | append $ctn
}
}
export def kcp [
lhs: string@"nu-complete kube cp"
rhs: string@"nu-complete kube cp"
-c: string@"nu-complete kube ctns"
-n: string@"nu-complete kube ns"
] {
let n = if ($n|is-empty) { [] } else { [-n $n] }
let c = if ($c|is-empty) { [] } else { [-c $c] }
kubectl cp $n $lhs $c $rhs
}
### service
def "nu-complete kube service" [context: string, offset: int] {
let ctx = ($context | parse cmd)
let ns = (do -i { $ctx | get '-n' })
let ns = if ($ns|is-empty) { [] } else { [-n $ns] }
kubectl get $ns services | from ssv -a | get NAME
}
export def kgs [-n: string@"nu-complete kube ns"] {
let n = if ($n|is-empty) { [] } else { [-n $n] }
kubectl get $n services | from ssv -a
| rename name type cluster-ip external-ip ports age selector
}
export def kes [svc: string@"nu-complete kube service", -n: string@"nu-complete kube ns"] {
let n = if ($n|is-empty) { [] } else { [-n $n] }
kubectl edit $n service $svc
}
export def kdels [svc: string@"nu-complete kube service", -n: string@"nu-complete kube ns"] {
let n = if ($n|is-empty) { [] } else { [-n $n] }
kubectl delete $n service $svc
}
### deployments
def "nu-complete kube deployments" [context: string, offset: int] {
let ctx = ($context | parse cmd)
let ns = (do -i { $ctx | get '-n' })
let ns = if ($ns|is-empty) { [] } else { [-n $ns] }
kubectl get $ns deployments | from ssv -a | get NAME
}
export def kgd [-n: string@"nu-complete kube ns"] {
let n = if ($n|is-empty) { [] } else { [-n $n] }
kubectl get $n deployments -o wide | from ssv -a
| rename name ready up-to-date available age containers images selector
| reject selector
}
export def ked [d: string@"nu-complete kube deployments", -n: string@"nu-complete kube ns"] {
let n = if ($n|is-empty) { [] } else { [-n $n] }
kubectl edit $n deployments $d
}
def "nu-complete num9" [] { [1 2 3] }
export def ksd [
d: string@"nu-complete kube deployments"
num: string@"nu-complete num9"
-n: string@"nu-complete kube ns"
] {
if ($num | into int) > 9 {
"too large"
} else {
let n = if ($n|is-empty) { [] } else { [-n $n] }
kubectl scale $n deployments $d --replicas $num
}
}
export def ksdr [
d: string@"nu-complete kube deployments"
num: int@"nu-complete num9"
-n: string@"nu-complete kube ns"
] {
if $num > 9 {
"too large"
} else if $num <= 0 {
"too small"
} else {
let n = if ($n|is-empty) { [] } else { [-n $n] }
kubectl scale $n deployments $d --replicas 0
kubectl scale $n deployments $d --replicas $num
}
}
export alias krsd = kubectl rollout status deployment
export alias kgrs = kubectl get rs
export def krh [-n: string@"nu-complete kube ns", --revision (-v): int, dpl: string@"nu-complete kube deployments"] {
let n = if ($n|is-empty) { [] } else { [-n $n] }
let v = if ($revision|is-empty) { [] } else { [ $"--revision=($revision)" ] }
kubectl $n rollout history $"deployment/($dpl)" $v
}
export def kru [-n: string@"nu-complete kube ns", --revision (-v): int, dpl: string@"nu-complete kube deployments"] {
let n = if ($n|is-empty) { [] } else { [-n $n] }
let v = if ($revision|is-empty) { [] } else { [ $"--to-revision=($revision)" ] }
kubectl $n rollout undo $"deployment/($dpl)" $v
}
export alias ksss = kubectl scale statefulset
export alias krsss = kubectl rollout status statefulset
### kubectl top pod
export def ktp [-n: string@"nu-complete kube ns"] {
let n = if ($n|is-empty) { [] } else { [-n $n] }
kubectl top pod $n | from ssv -a | rename name cpu mem
| each {|x| {
name: $x.name
cpu: ($x.cpu| str substring ..-1 | into decimal)
mem: ($x.mem | str substring ..-2 | into decimal)
} }
}
export def ktpa [] {
kubectl top pod -A | from ssv -a | rename namespace name cpu mem
| each {|x| {
namespace: $x.namespace
name: $x.name
cpu: ($x.cpu| str substring ..-1 | into decimal)
mem: ($x.mem | str substring ..-2 | into decimal)
} }
}
### kube top node
export def ktn [] {
kubectl top node | from ssv -a | rename name cpu pcpu mem pmem
| each {|x| {
name: $x.name
cpu: ($x.cpu| str substring ..-1 | into decimal)
cpu%: (($x.pcpu| str substring ..-1 | into decimal) / 100)
mem: ($x.mem | str substring ..-2 | into decimal)
mem%: (($x.pmem | str substring ..-1 | into decimal) / 100)
} }
}
###
export def "kclean evicted" [] {
kubectl get pods -A
| from ssv -a
| where STATUS == Evicted
| each { |x| kdel pod -n $x.NAMESPACE $x.NAME }
}
### fixme:
export def "kclean stucked ns" [ns: string] {
kubectl get namespace $ns -o json \
| tr -d "\n"
| sed 's/\"finalizers\": \[[^]]\+\]/\"finalizers\": []/' \
| kubectl replace --raw /api/v1/namespaces/$1/finalize -f -
}
export alias "kclean finalizer" = kubectl patch -p '{\"metadata\":{\"finalizers\":null}}'
export alias "kadm renew" = kubeadm alpha certs renew all
### cert-manager
export def kgcert [] {
kubectl get certificates -o wide | from ssv | rename certificates
kubectl get certificaterequests -o wide | from ssv | rename certificaterequests
kubectl get order.acme -o wide | from ssv | rename order.acme
kubectl get challenges.acme -o wide | from ssv | rename challenges.acme
}

View file

@ -0,0 +1,159 @@
#Root with a custom denominator
export def root [ denominator, num ] {
$num ** ( 1 / $denominator ) | math round -p 10
}
#Cube root
export def croot [num] {
$num ** ( 1 / 3 ) | math round -p 10
}
#Root with a custom scaler and denominator
export def aroot [ scaler, denominator, num] {
$num ** ($scaler / $denominator) | math round -p 10
}
#calculate delta of the quadratic function
export def delta [ a,#x^2 factor
b, #x factor
c #the rest
] {
( $b ** 2 ) - ( 4 * $a * $c)
}
#Factorial of the given number
export def fact [num: int] {
if $num >= 0 {
if $num < 2 {
$num
} else {
seq 2 $num | math product
}
} else {
error make -u {msg: "can only calculate non-negative integers"}
}
}
#Calculate roots of the quadratic function: ax^2+bx+x
export def q_roots [
a # x^2
b # x
c # independent term
] {
let d = $b ** 2 - 4 * $a * $c
if $d > 0 {
let s = ($d | math sqrt)
let r1 = (($s - $b) / (2 * $a))
let r2 = (0 - (($s + $b) / (2 * $a)))
echo $"root #1: ($r1)"
echo $"root #2: ($r2)"
} else if $d == 0 {
let s = ($d | math sqrt)
let r = (($s - $b) / (2 * $a))
echo $"root: ($r)"
} else {
let s = ((0 - $d) | math sqrt)
let r = ((0 - $b) / (2 * $a))
let i = ($s / (2 * $a))
echo $"root #1: ($r) + ($i)*i"
echo $"root #2: ($r) - ($i)*i"
}
}
#Check if integer is prime
export def isprime [n: int] {
let max = ($n | math sqrt | math ceil)
let flag = ([[isPrime];[true]] | update isPrime {if ($n mod 2) == 0 { false } else { seq 3 1 $max | each { |it| if ($n mod $it) == 0 { false }}}})
if ($flag.isPrime.0 | is-empty) { echo 'prime' } else { echo 'not prime' }
}
#Prime list <= n
export def primelist [n: int] {
let primes = [2 3]
let primes2 = (seq 5 2 $n | each {|it| if (isprime $it) == 'prime' {$it}})
$primes | append $primes2
}
#Multiplication table of n till max
export def mtable [n: int, max: int] {
seq 1 $max | each {|it| echo $"($it)*($n) = ($n * $it)"}
}
#Check if year is leap
export def isleap [year: int] {
if ( (($year mod 4) == 0 and ($year mod 100) != 0) or ($year mod 400) == 0 ) { echo "It is a leap year." } else { echo "It is not a leap year."}
}
#Greatest common divisior (gcd) between 2 integers
export def gcd [a: int, b:int] {
if $a < $b {
gcd $b $a
} else if $b == 0 {
$a
} else {
gcd $b ($a mod $b)
}
}
#Least common multiple (lcm) between 2 integers
export def lcm [a: int, b:int] {
if $a == $b and $b == 0 {
0
} else {
$a * ($b / (gcd $a $b))
}
}
#Decimal number to custom base representation
export def dec2base [
n: string #decimal number
b: string #base in [2,16]
] {
let base = if ( ($b | into int) < 2 or ($b | into int) > 16 ) {
echo "Wrong base, it must be an integer between 2 and 16"
10
} else {
$b | into int
}
let number = ($n | into int)
let chars = ['0' '1' '2' '3' '4' '5' '6' '7' '8' '9' 'A' 'B' 'C' 'D' 'E' 'F']
if $number == 0 {
''
} else {
let newNumber = (($number - ($number mod $base)) / $base)
[(dec2base $newNumber $base) ($chars | get ($number mod $base))] | str join
}
}
# Scale list to [a,b] interval
export def scale-minmax [a, b,input?] {
let x = if ($input | is-empty) {$in} else {$input}
let min = ($x | math min)
let max = ($x | math max)
$x | each {|it| ((($it - $min) / ($max - $min)) * ($b - $a) + $a) }
}
# Scale every column of a table (separately) to [a,b] interval
export def scale-minmax-table [a, b,input?] {
let x = if ($input | is-empty) {$in} else {$input}
let n_cols = ($x | transpose | length)
let name_cols = ($x | transpose | column2 0)
0..($n_cols - 1)
| each {|i|
($x | column2 $i) | scale-minmax $a $b | wrap ($name_cols | get $i)
} | reduce {|it, acc| $acc | merge {$it}}
}

View file

@ -0,0 +1,46 @@
# Remoting
This module provide convenient way to manage multiple remote clients.
## Prerequisites
* Both `ssh` and `ssh script` require ssh to be installed on the host
* `ssh script` requires nushell to be installed on the client
* `ssh` can work both with and without nushell installed on the client however having it installed enables more sophisticated interaction between host and clients
* `wake` requires wakeonlan to be installed on the host and clients to be configured to accept Wake on Lan magic packets
## `ssh` function
This function serves as a wrapper around standard ssh command.
Function accepts following arguments:
* name - Name of the client as specified in the config. This parameter is required (autocompletion enabled)
* args - command to run on the client. If not provided ssh starts an interactive session with the client. If provided and specified client has nushell configured output will be returned as nushell table on the host allowing further parsing. If provided and specified client does not have nushell configured output will be passed back as is
## `ssh script` function
This function executes a nushell script on a client that has nushell configured.
Function accepts following arguments:
* name - Name of the client as specified in the config. This parameter is required (autocompletion enabled)
* script - Name of the script to be executed on the client. This parameter is required The script must be available in current scope in order to be used (autocompletion enabled)
* args - Arguments to be passed to the script. Named parameters should be put in quotes to avoid parsing errors like this: `ssh script my-host my-script '--arg value'`
## `wake` function
This function wakes specified clients via Wake on Lan.
Function accepts following arguments:
* names - Name of clients to be woken up as specified in the config. (autocompletion enabled)
## Config
Client config is kept within (non-exported) `hosts` function as a list of records. Each record should contain following fields:
| Parameter | Is required? | Usage | Type |
|-----------|----------------------------------------|------------------------------|--------|
| name | Required | DNS name of the client | string |
| domain | Either domain or IP must be specified | DNS domain of the client | string |
| ip | Either domain or IP must be specified | IP address of the client | string |
| username | Required | Username used for connection | string |
| port | Required | SSH port | int |
| mac | Required only for `wake` | MAC address of the client | string |
| nu | Required | Whether nushell is installed | bool |
Example configuration records:
* {name: 'host1', domain: 'nushell.sh', ip: '', username: 'username', port: 22, mac: 'AA:BB:CC:DD:EE;FF', nu: false}
* {name: 'host2', domain: '', ip: '192.168.0.1', username: 'username', port: 2222, mac: '', nu: true}

View file

@ -0,0 +1,98 @@
# internal function that holds the host data. We could store it in a yaml file as well but that would require nushell to read it from disk every single time
def hosts [] {
[
# Put your config here
]
}
def "nu-complete wol" [] {
hosts
|where mac != ''
|get name
}
def "nu-complete nu" [] {
hosts
|where nu
|get name
}
def "nu-complete hosts" [] {
hosts
|get name
}
def "nu-complete scripts" [] {
$nu.scope.commands
|where is_custom
|get -i command
}
# Returns ssh connection as url to be consumed by original ssh command
def get-url [
host: record
] {
if 'ip' in ($host|columns) {
echo $"ssh://($host.username)@($host.ip):($host.port)"
} else {
echo $"ssh://($host.username)@($host.name).($host.domain):($host.port)"
}
}
# Connect over ssh to one of predefined hosts, execute nushell commands and parse them on the host
export def ssh [
hostname: string@"nu-complete hosts" # name of the host you want to connect to
...args # commands you wish to run on the host
] {
let host = (hosts|where name == $hostname|get -i 0)
if ($host.nu) {
if ($args|length) > 0 {
^ssh (get-url $host) (build-string ($args|str join ' ') '|to json -r')|from json
} else {
^ssh (get-url $host)
}
} else {
^ssh (get-url $host) $args
}
}
# Connect over ssh to one of predefined hosts, execute nushell script with arguments passed from the host
export def "ssh script" [
hostname: string@"nu-complete nu" # name of the host you want to connect to
script: string@"nu-complete scripts" # name of the script
...args # arguments you wish to pass to the script in key=value format
] {
let span = (metadata $script).span
if $script in ($nu.scope.commands|where is_custom|get command) {
let host = (hosts|where name == $hostname|get 0)
let full_command = (build-string (view-source $script) '; ' $script ' ' ($args|str join ' ') '|to json -r')
^ssh (get-url $host) ($full_command)|from json
} else {
error make {
msg: $"($script) is not a custom command, use regular ssh command instead"
label: {
text: "Not a custom command",
start: $span.start,
end: $span.end
}
}
}
}
# Turns on specified hosts using Wake on Lan
export def wake [
...names: string@"nu-complete wol" # list of host names to wake
] {
hosts
|where name in $names
|each {|host|
if $host.mac != '' {
echo $"Waking ($host.name)"
wakeonlan $host.mac|ignore
} else {
error make {
msg: $"($host.name) does not support Wake on Lan"
}
}
}
}

View file

@ -0,0 +1,4 @@
The `sockets` command returns a table containing information on network sockets and the processes they belong to.
It is basically a join of the tables produced by the `lsof` command, and the nushell `ps` command.
<img width="1486" alt="image" src="https://user-images.githubusercontent.com/52205/196287615-00e46f8e-06ed-45ce-8fe7-a5c5f38afaaa.png">

View file

@ -0,0 +1,32 @@
export def sockets [--abbreviate-java-class-paths (-j)] {
let input = (^lsof +c 0xFFFF -i -n -P)
let header = ($input | lines
| take 1
| each { str downcase | str replace ' name$' ' name state' })
let body = ($input | lines
| skip 1
| each { str replace '([^)])$' '$1 (NONE)' | str replace ' \((.+)\)$' ' $1' })
[$header] | append $body
| to text
| detect columns
| upsert 'pid' { |r| $r.pid | into int }
| rename -c ['name' 'connection']
| reject 'command'
| join-table (ps -l) 'pid' 'pid'
| if $abbreviate_java_class_paths {
upsert 'classpath' { |r| $r.command | java-cmd classpath }
| upsert 'command' { |r| $r.command | java-cmd abbreviate-classpath }
} else { $in }
}
export def 'java-cmd classpath' [] {
str replace '.* -classpath +(.+\.jar) +.*' '$1' | split row ':'
}
export def 'java-cmd abbreviate-classpath' [] {
str replace '[^ ]*\.jar' '*.jar'
}
export def join-table [table: table, left_on: string, right_on: string] {
into df | join ($table | into df) $left_on $right_on | into nu
}

128
modules/network/ssh.nu Normal file
View file

@ -0,0 +1,128 @@
export def ensure-index [index path action] {
let ts = (do -i { ls $path | sort-by modified | reverse | get 0.modified })
if ($ts | is-empty) { return false }
let tc = (do -i { ls $index | get 0.modified })
if not (($index | path exists) and ($ts < $tc)) {
mkdir (dirname $index)
do $action
}
}
export def 'str max-length' [] {
$in | reduce -f 0 {|x, a|
if ($x|is-empty) { return $a }
let l = ($x | str length)
if $l > $a { $l } else { $a }
}
}
def "nu-complete ssh host" [] {
rg -LNI '^Host [a-z0-9_\-\.]+' ~/.ssh | lines | each {|x| $x | split row ' '| get 1}
}
export def parse-ssh-file [group] {
$in
| parse -r '(?P<k>Host|HostName|User|Port|IdentityFile)\s+(?P<v>.+)'
| append { k: Host, v: null}
| reduce -f { rst: [], item: {Host: null} } {|it, acc|
if $it.k == 'Host' {
$acc | upsert rst ($acc.rst | append $acc.item)
| upsert item { Host : $it.v, HostName: null, Port: null, User: null, IdentityFile: null, Group: $group }
} else {
$acc | upsert item ($acc.item | upsert $it.k $it.v)
}
}
| get rst
| where {|x| not (($x.Host | is-empty) or $x.Host =~ '\*')}
}
export def ssh-list [] {
rg -L -l 'Host' ~/.ssh
| lines
| each {|x| cat $x | parse-ssh-file $x}
| flatten
}
def fmt-group [p] {
$p | str replace $"($env.HOME)/.ssh/" ''
}
def "ssh-hosts" [] {
let cache = $'($env.HOME)/.cache/nu-complete/ssh.json'
ensure-index $cache ~/.ssh/**/* { ||
let data = (ssh-list | each {|x|
let uri = $"($x.User)@($x.HostName):($x.Port)"
{
value: $x.Host,
uri: $uri,
group: $"(fmt-group $x.Group)",
identfile: $"($x.IdentityFile)",
}
})
let max = {
value: ($data.value | str max-length),
uri: ($data.uri | str max-length),
group: ($data.group | str max-length),
identfile: ($data.identfile | str max-length),
}
{max: $max, completion: $data} | save -f $cache
}
cat $cache | from json
}
def "nu-complete ssh" [] {
let data = (ssh-hosts)
$data.completion
| each { |x|
let uri = ($x.uri | fill -a l -w $data.max.uri -c ' ')
let group = ($x.group | fill -a l -w $data.max.group -c ' ')
let id = ($x.identfile | fill -a l -w $data.max.identfile -c ' ')
{value: $x.value, description: $"\t($uri) ($group) ($id)" }
}
}
export extern main [
host: string@"nu-complete ssh" # host
...cmd # cmd
-v # verbose
-i: string # key
-p: int # port
-N # n
-T # t
-L # l
-R # r
-D # d
-J: string # j
-W: string # w
]
def "nu-complete scp" [cmd: string, offset: int] {
let argv = ($cmd | str substring ..$offset | split row ' ')
let p = if ($argv | length) > 2 { $argv | get 2 } else { $argv | get 1 }
let ssh = (ssh-hosts | get completion
| each {|x| {value: $"($x.value):" description: $x.uri} }
)
let n = ($p | split row ':')
if $"($n | get 0):" in ($ssh | get value) {
^ssh ($n | get 0) $"sh -c 'ls -dp ($n | get 1)*'"
| lines
| each {|x| $"($n | get 0):($x)"}
} else {
let files = (do -i {
ls -a $"($p)*"
| each {|x| if $x.type == dir { $"($x.name)/"} else { $x.name }}
})
$files | append $ssh
}
}
export def scp [
lhs: string@"nu-complete scp",
rhs: string@"nu-complete scp"
] {
^scp -r $lhs $rhs
}

143
modules/nvim/nvim.nu Normal file
View file

@ -0,0 +1,143 @@
## neovim configurations
# local vcs_root = require('lspconfig.util').root_pattern('.git/')
# function HookPwdChanged(after, before)
# vim.b.pwd = after
# local git_dir = vcs_root(after)
# vim.api.nvim_command('silent tcd! '..(git_dir or after))
# end
# function OppositePwd()
# local tab = vim.api.nvim_get_current_tabpage()
# local wins = vim.api.nvim_tabpage_list_wins(tab)
# local cwin = vim.api.nvim_tabpage_get_win(tab)
# for _, w in ipairs(wins) do
# if cwin ~= w then
# local b = vim.api.nvim_win_get_buf(w)
# local pwd = vim.b[b].pwd
# if pwd then return pwd end
# end
# end
# end
# function ReadTempDrop(path, action)
# vim.api.nvim_command(action or 'botright vnew')
# local win = vim.api.nvim_get_current_win()
# local buf = vim.api.nvim_create_buf(true, true)
# vim.api.nvim_win_set_buf(win, buf)
# vim.api.nvim_command('read '..path)
# vim.fn.delete(path)
# end
def nvim_tcd [] {
[
{|before, after|
if not ($env.NVIM? | is-empty) {
nvim --headless --noplugin --server $env.NVIM --remote-send $"<cmd>lua HookPwdChanged\('($after)', '($before)')<cr>"
}
}
]
}
export-env {
let-env config = ( $env.config | upsert hooks.env_change.PWD { |config|
let o = ($config | get -i hooks.env_change.PWD)
let val = (nvim_tcd)
if $o == $nothing {
$val
} else {
$o | append $val
}
})
}
def edit [action file] {
if ($env.NVIM? | is-empty) {
nvim $file
} else {
let af = ($file | each {|f|
if ($f|str substring ..1) in ['/', '~'] {
$f
} else {
$"($env.PWD)/($f)"
}
})
let cmd = $"<cmd>($action) ($af|str join ' ')<cr>"
nvim --headless --noplugin --server $env.NVIM --remote-send $cmd
}
}
# nvim tcd
export def tcd [path?: string] {
let after = if ($path | is-empty) {
$env.PWD
} else {
$path
}
nvim --headless --noplugin --server $env.NVIM --remote-send $"<cmd>lua HookPwdChanged\('($after)', '($env.PWD)')<cr>"
}
export def e [...file: string] {
if ($file|is-empty) {
nvim
} else {
edit vsplit $file
}
}
export def c [...file: string] {
if ($file|is-empty) {
nvim
} else {
edit split $file
}
}
export def v [...file: string] {
if ($file|is-empty) {
nvim
} else {
edit vsplit $file
}
}
export def x [...file: string] {
if ($file|is-empty) {
nvim
} else {
edit tabnew $file
}
}
# drop stdout to nvim buf
export def drop [] {
if ($env.NVIM? | is-empty) {
echo $in
} else {
let c = $in
let temp = (mktemp -t nuvim.XXXXXXXX|str trim)
$c | save -f $temp
nvim --headless --noplugin --server $env.NVIM --remote-send $"<cmd>lua ReadTempDrop\('($temp)')<cr>"
}
}
export def nvim-lua [...expr: string] {
if ($env.NVIM? | is-empty) {
echo "not found nvim instance"
} else {
nvim --headless --noplugin --server $env.NVIM --remote-send $'<cmd>lua vim.g.remote_expr_lua = ($expr|str join " ")<cr>'
do -i { nvim --headless --noplugin --server $env.NVIM --remote-expr 'g:remote_expr_lua' } | complete | get stderr
}
}
export def opwd [] {
nvim-lua 'OppositePwd()'
}
export def nvim-srv [port: int=1111] {
nvim --headless --listen $"0.0.0.0:($port)"
}
export def nvide-conn [addr: string] {
neovide --multigrid --maximized --remote-tcp addr
}

27
modules/prompt/README.md Normal file
View file

@ -0,0 +1,27 @@
# Prompt Scripts
### Definition
These scripts should be used to draw a custom command prompt in nushell. They can include anything that we think is appropriate for prompts such as `git` commands, `starship`, `oh-my-posh`, etc.
#### starship.nu
File is in [starship](./starship.nu)
This discribe how to use starship to make a leftprompt, the repo of starship is [here](https://github.com/starship/starship).
This script set the output of starship as leftprompt
![starshipshow](./images/starship.png)
#### shell_space.nu
File is in [shell_space](./shell_space.nu)
Use the function of shells in nu, you can view the fucntion with the command following
```
help shells
```
![shell_spaceshow](./images/shell_space.png)

View file

@ -0,0 +1,33 @@
This module exports commands for creating a nushell prompt that computes git status (staged and
unstaged changes) asynchronously. This can be useful in large git repos when it is slow to obtain
this information synchronously.
To use this module:
0. Place the file `async-git-prompt.nu` in the `$nu.config-path` directory (this is the directory
containing `init.nu` and `env.nu`).
1. Use the command `async-git-prompt-string` in your own `PROMPT_COMMAND` (the file prompt.nu
contains an example of doing this.)
At this point, your prompt will be computing the information synchronously, because the cache
file does not yet exist.
2. In a repo where git is slow, run the command `async-git-prompt-refresh-cache`.
Now, your prompt will be fast, but it also won't update automatically. You could investigate a good
way to invalidate the cache automatically, but the manual alternative is:
3. Whenever you think your prompt might be stale, re-run the command `async-git-prompt-refresh-cache`.
Your prompt will update on one of the next times that you hit <enter>.
4. It will probably be convenient to alias this, e.g.
```nu
alias r = async-git-prompt-refresh-cache
```
5. To go back to synchronous mode, run `async-git-prompt-delete-cache`.
### TODO
- Automatic cache invalidation
- Show untracked files (this can be very expensive in large repos)

View file

@ -0,0 +1,77 @@
# This module exports commands for creating a nushell prompt that computes git status (staged and
# unstaged changes) asynchronously. This can be useful in large git repos when it is slow to obtain
# this information synchronously.
# See README.md for usage.
def unstaged-symbol [] { 'અ' }
def staged-symbol [] { 'જ' }
def in-progress-symbol [] { '…' }
def cached-result-symbol [] { $"·" } #〈
def cache-file [] { '.nu-async-git-prompt-cache'}
def do-async [commands: string] {
bash -c $"nu -c '($commands)' &"
}
export def async-git-prompt-string [] {
let cache_path = (cache-path)
if ($cache_path | is-empty) {
""
} else if ($cache_path | path exists) {
$"(cached-result-symbol)(open $cache_path | str trim)"
} else {
async-git-prompt-compute-sync
}
}
export def async-git-prompt-compute-sync [] {
let unstaged = {
let symbol = if ((git diff --quiet | complete).exit_code == 1) {
(unstaged-symbol)
} else {
''
}
{ unstaged: $symbol}
}
let staged = {
let symbol = if ((git diff --cached --quiet | complete).exit_code == 1) {
(staged-symbol)
} else {
''
}
{ staged: $symbol}
}
# Execute the two slow git commands in parallel and merge the results into a single record
let symbols = ([ $unstaged $staged ] | par-each { |it| do $it } | reduce {|a b| $a | merge {$b}})
$"($symbols | get 'unstaged') ($symbols | get 'staged')" | str trim
}
export def async-git-prompt-refresh-cache [] {
let cache_path = (cache-path)
if ($cache_path != null) {
echo (in-progress-symbol) | save $cache_path
do-async $"use ($nu.config-path | path expand | path dirname)/async-git-prompt.nu *; async-git-prompt-compute-sync | save ($cache_path)"
}
}
export def async-git-prompt-delete-cache [] {
let cache_path = (cache-path)
if ($cache_path != null) {
rm -f $cache_path
}
}
def cache-path [] {
let dir = if ('.git' | path exists) {
'.'
} else {
do -i { git rev-parse --show-toplevel | str trim -r }
}
if ($dir | is-empty) {
null
} else {
$dir | path join (cache-file)
}
}

View file

@ -0,0 +1,31 @@
# This file contains an example nushell prompt, making use of the async-git-prompt module.
use async-git-prompt.nu *
def prompt-concat [parts: table] {
$parts
| where (not ($it.text | is-empty))
| each { |it| $"($it.color)($it.text)" }
| str join ' '
}
def prompt-git-branch [] {
do -i { git rev-parse --abbrev-ref HEAD | str trim -r}
}
def prompt-create-left-prompt [] {
let pwd = ($env.PWD | str replace $env.HOME '~')
prompt-concat [
[text color];
[pwd (ansi green_bold)]
[(prompt-git-branch) (ansi blue_bold)]
[(async-git-prompt-string) (ansi green_bold)]
]
}
def prompt-create-right-prompt [] {
$nothing
}
let-env PROMPT_COMMAND = { prompt-create-left-prompt }
let-env PROMPT_COMMAND_RIGHT = { prompt-create-right-prompt }
let-env PROMPT_INDICATOR = { $" (ansi green_bold)〉" }

View file

@ -0,0 +1,49 @@
# Nushell "Full Line" Prompt
# Fills the entire line above the prompt with "useful" info, creates an easy-to-read separation between commands outputs.
# Just the thing for portrait format terminal windows.
# This one includes working directory in the middle and has option for timestamp info on right.
# (many) other permutations are possible.
# You can play with this by `source full-line.nu` in an active shell.
# To enable permanently, modify your `env.nu` file; add `create_center_prompt` and modify `let-env PROMPT_COMMAND` line as shown here.
# TODO: revisit the below comment with fill
# Life made complicated by `str lpad` counting all the ansi sequences as visible, so it doesn't pad enough.
def create_center_prompt [] {
let path_segment = if (is-admin) {
$" (ansi red_bold)($env.PWD)(ansi reset) "
} else {
$" (ansi green_bold)($env.PWD)(ansi reset) "
}
let path_segment_visible_length = ($path_segment | ansi strip | str length)
let path_segment_excess_length = ($path_segment | str length) - $path_segment_visible_length
# to disable the right hand segment, change line below to be simply `let time_segment = ''`.
let time_segment = $"(date now | date format ' %F %r')(ansi reset)"
let time_segment_excess_length = ($time_segment | str length) - ($time_segment | ansi strip | str length)
let path_segment_pad = ((((term size).columns + $path_segment_visible_length) / 2) | into int)
let time_segment_pad = (term size).columns - $path_segment_pad
let pad_char = '-'
let segment = ([
( $path_segment | fill -w ($path_segment_pad + $path_segment_excess_length) -c $pad_char -a r),
( $time_segment | fill -w ($time_segment_pad + $time_segment_excess_length) -c $pad_char -a r)
] | str join)
$segment
}
# For full width prompt we simply print the prompt line but throw away the value;
# Nushell displays PROMPT_INDICATOR in left hand column, leaving lots of room for long command lines.
let-env PROMPT_COMMAND = { print (create_center_prompt); }
# let-env PROMPT_COMMAND_RIGHT = { create_right_prompt }
let-env PROMPT_COMMAND_RIGHT = ''
# The prompt indicators are environmental variables that represent
# the state of the prompt
let-env PROMPT_INDICATOR = { "〉" }
let-env PROMPT_INDICATOR_VI_INSERT = { ": " }
let-env PROMPT_INDICATOR_VI_NORMAL = { "〉" }
let-env PROMPT_MULTILINE_INDICATOR = { "::: " }

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View file

@ -0,0 +1,284 @@
# NOTE: This is meant to run with engine-q and not nushell yet
# It's still being tested. There will be bugs. :)
# REQUIREMENTS #
# you definitely need nerd fonts https://www.nerdfonts.com
# nerd fonts repo https://github.com/ryanoasis/nerd-fonts
# i use "FiraCode Nerd Font Mono" on mac
#
# you also must have the engine-q gstat plugin installed and registered
# ATTRIBUTION #
# A little fancier prompt with git information
# inspired by https://github.com/xcambar/purs
# inspired by https://github.com/IlanCosman/tide
# inspired by https://github.com/JanDeDobbeleer/oh-my-posh
# Abbreviate home path
def home_abbrev [os] {
let is_home_in_path = ($env.PWD | str starts-with $nu.home-path)
if ($is_home_in_path == true) {
if ($os == "Windows") {
let home = ($nu.home-path | str replace -a '\\' '/')
let pwd = ($env.PWD | str replace -a '\\' '/')
$pwd | str replace $home '~'
} else {
$env.PWD | str replace $nu.home-path '~'
}
} else {
$env.PWD | str replace -a '\\' '/'
}
}
export def path_abbrev_if_needed [apath term_width] {
# probably shouldn't do coloring here but since we're coloring
# only certain parts, it's kind of tricky to do it in another place
# if needed, use `ansi strip` to remove coloring
let T = (ansi { fg: "#BCBCBC" bg: "#3465A4"}) # truncated
let P = (ansi { fg: "#E4E4E4" bg: "#3465A4"}) # path
let PB = (ansi { fg: "#E4E4E4" bg: "#3465A4" attr: b}) # path bold
let R = (ansi reset)
let red = (ansi red)
# replace the home path first
let apath = ($apath | str replace $nu.home-path ~)
# split out by path separator into tokens
# don't use psep here because in home_abbrev we're making them all '/'
let splits = ($apath | split row '/')
let splits_len = ($splits | length)
if (($apath | str length) > ($term_width / 2)) {
# get all the tokens except the last
let tokens = ($splits | take ($splits_len - 1) | each {|x|
$"($T)($x | str substring 0..1)($R)"
})
# append the last part of the path
let tokens = ($tokens | append $"($PB)($splits | last)($R)")
# collect
$tokens | str join $"($T)/"
} else {
if ($splits_len == 0) {
# We're at / on the file system
$"/($T)"
} else if ($splits_len == 1) {
let top_part = ($splits | first)
let tokens = $"($PB)($top_part)($R)"
$tokens | str join $"($T)"
} else {
let top_part = ($splits | first ($splits_len - 1))
let end_part = ($splits | last)
let tokens = ($top_part | each {|x|
$"/($T)($x | str substring 0..1)($R)"
})
let tokens = ($tokens | append $"/($PB)($end_part)($R)")
$tokens | skip 1 | str join $"($T)"
}
}
}
def get_os_icon [os use_nerd_fonts] {
# f17c = tux, f179 = apple, f17a = windows
if $use_nerd_fonts {
if ($os =~ Darwin) {
(char -u f179)
} else if ($os =~ Linux) {
(char -u f17c)
} else if ($os =~ Windows) {
(char -u f17a)
} else {
''
}
} else {
if ($os =~ Darwin) {
"M"
} else if ($os =~ Linux) {
"L"
} else if ($os =~ Windows) {
"W"
} else {
''
}
}
}
export def get_left_prompt [os use_nerd_fonts] {
# replace this 30 with whatever the width of the terminal is
let display_path = (path_abbrev_if_needed (home_abbrev $os) 30)
let R = (ansi reset)
# some icons and the unicode char
# e0b0 ▷ 25b7 ⏵ 23f5 ▶ 25b6 ⯈ 2bc8 🞂 1f782
# e0b1
# e0b2 ◁ 25c1 ⏴ 23f4 ◀ 25c0 ⯇ 2bc7 🞀 1f780
# e0b3
# f1d3
# f07c or  f115 📁 1f4c1 🗀 1f5c0
# f015 or  f7db 🏠 1f3e0 ⌂ 2302
let TERM_BG = "#0C0C0C"
let right_transition_nf = (char -u e0b0)
# let right_transition = (char -u 1f782)
let right_transition = ""
let home_nf = (char -u f015)
# let home = (char -u 1f3e0)
let home = ""
let folder_nf = (char -u f07c)
# let folder = (char -u 1f5c0)
let folder = ""
# build segments and then put together the segments for the prompt
let os_segment = ([
(ansi { fg: "#080808" bg: "#CED7CF"}) # os bg color
(char space) # space
(get_os_icon $os $use_nerd_fonts) # os icon
(char space) # space
(ansi { fg: "#CED7CF" bg: "#3465A4"}) # color transition
(if $use_nerd_fonts {
$right_transition_nf # 
} else {
$right_transition
})
(char space) # space
] | str join)
let is_home_in_path = ($env.PWD | str starts-with $nu.home-path)
let path_segment = (if $is_home_in_path {
[
(if $use_nerd_fonts {
$home_nf #  home icon
} else {
$home
})
(char space) # space
$display_path # ~/src/forks/nushell
(ansi { fg: "#CED7CF" bg: "#3465A4"}) # color just to color the next space
(char space) # space
] | str join
} else {
[
(if $use_nerd_fonts {
$folder_nf #  folder icon
} else {
$folder
})
(char space) # space
$display_path # ~/src/forks/nushell
(ansi { fg: "#CED7CF" bg: "#3465A4"}) # color just to color the next space
(char space) # space
] | str join
})
let indicator_segment = (
[
(ansi { fg: "#3465A4" bg: $TERM_BG}) # color
(if $use_nerd_fonts {
$right_transition_nf # 
} else {
# $right_transition
" >"
})
($R) # reset color
] | str join
)
# assemble all segments for final prompt printing
[
$os_segment
$path_segment
$indicator_segment
] | str join
}
export def get_right_prompt [os use_nerd_fonts] {
# right prompt ideas
# 1. just the time on the right
# 2. date and time on the right
# 3. git information on the right
# 4. maybe git and time
# 5. would like to get CMD_DURATION_MS going there too when it's implemented
# 6. all of the above, chosen by def parameters
let R = (ansi reset)
let TIME_BG = "#D3D7CF"
let TERM_FG = "#0C0C0C"
let left_transition_nf = (char -u e0b2)
# let left_transition = (char -u 1f780)
let left_transition = ""
let datetime_segment = ([
(ansi { fg: $TIME_BG bg: $TERM_FG})
(if $use_nerd_fonts {
$left_transition_nf # 
} else {
$left_transition
})
(ansi { fg: $TERM_FG bg: $TIME_BG})
(char space)
(date now | date format '%m/%d/%Y %I:%M:%S%.3f')
(char space)
($R)
] | str join)
let time_segment = ([
(ansi { fg: $TIME_BG bg: $TERM_FG})
(if $use_nerd_fonts {
$left_transition_nf # 
} else {
$left_transition
})
(ansi { fg: $TERM_FG bg: $TIME_BG})
(char space)
(date now | date format '%I:%M:%S %p')
(char space)
($R)
] | str join)
# 1. datetime - working
# $datetime_segment
# 2. time only - working
$time_segment
# 3. git only - working
# $git_segment
# 4. git + time -> need to fix the transition
# [
# $git_segment
# $time_segment
# ] | str join
# 5. fernando wants this on the left prompt
# [
# $os_segment
# $time_segment
# $path_segment
# ]
}
export def get_prompt [nerd?] {
let use_nerd_fonts = ($nerd != $nothing)
let os = ((sys).host.name)
let left_prompt = (get_left_prompt $os $use_nerd_fonts)
let right_prompt = (get_right_prompt $os $use_nerd_fonts)
# return in record literal syntax to be used kind of like a tuple
# so we don't have to run this script more than once per prompt
{
left_prompt: $left_prompt
right_prompt: $right_prompt
}
#
# in the config.nu you would do something like
# use "c:\some\path\to\nu_scripts\engine-q\prompt\oh-my-minimal.nu" get_prompt
# let-env PROMPT_COMMAND = { (get_prompt).left_prompt }
# let-env PROMPT_COMMAND_RIGHT = { (get_prompt).right_prompt }
# let-env PROMPT_INDICATOR = " "
# or with nerdfonts
# let-env PROMPT_COMMAND = { (get_prompt 1).left_prompt }
# let-env PROMPT_COMMAND_RIGHT = { (get_prompt 1).right_prompt }
}

View file

@ -0,0 +1,192 @@
# oh-my.nu v2
This is less a version 2 and more of a different way of thinking. The intent of this script is to start to make oh-my.nu more configurable. We start with that by defining a lot of variables. Then we create a runtime_colors list of records. Those records will have 8bit colors, 24bit colors, or a value. 8bit for terminals like MacOS's Terminal.app and 24bit color for the rest of the world.
The thought would be that at some point this file source read another file for configured settings. That part is not done yet.
In order to use this script you need to source it and then set these.
```
let-env PROMPT_COMMAND = { (get_prompt 8bit).left_prompt }
let-env PROMPT_COMMAND_RIGHT = { (get_prompt 8bit).right_prompt }
let-env PROMPT_INDICATOR = { "" }
```
I'd love for someone to take up the torch and work on this script in order to make it better, configurable, awesome.
Below is some rough documentation on what the configuration points are and could be. Not all of these configuration points are implemented. BTW, this is a total rip-off of the fish [tide](https://github.com/IlanCosman/tide) prompt, but not as nice.
### color mode
* 24bit
* 8bit
### prompt
| Variable | Description | Type |
| -------------------------- | --------------------------------------------------------------------------------------------- | ------- |
| add_newline_before | print an empty line before the prompt | boolean |
| color_frame_and_connection | color of frame and prompt connection | color |
| color_separator_same_color | color of the separator between items with the same background color | color |
| icon_connection | repeated symbol that spans gap between left and right sides of prompt | string |
| min_cols | if using one-line prompt, Tide attempts to have at least this many columns for you to type in | integer |
| pad_items | if true, add a space before and after each item | boolean |
| variable_name | 24bit_color | 8bit_color |
| - | - | - |
|tide_prompt_color_frame_and_connection|#6C6C6C|242|
|tide_prompt_color_separator_same_color|#949494|246|
### left_prompt
| Variable | Description | Type |
| -------------------- | --------------------------------------------------------- | ------- |
| frame_enabled | display the left prompt frame | boolean |
| items | order of items to print in the left prompt | list |
| prefix | string to put at the beginning the left prompt | string |
| separator_diff_color | string to separate items with different background colors | string |
| separator_same_color | string to separate items with the same background color | string |
| suffix | string to put at the end of the left prompt | string |
| variable_name | char |
| - | - |
|tide_left_prompt_separator_diff_color|e0b0|
|tide_left_prompt_separator_same_color|e0b1|
### right_prompt
| Variable | Description | Type |
| -------------------- | --------------------------------------------------------- | ------- |
| frame_enabled | display the right prompt frame | boolean |
| items | order of items to print in the right prompt | list |
| prefix | string to put at the beginning the right prompt | string |
| separator_diff_color | string to separate items with different background colors | string |
| separator_same_color | string to separate items with the same background color | string |
| suffix | string to put at the end of the right prompt | string |
| variable_name | char |
| - | - |
|tide_right_prompt_separator_diff_color|e0b2|
|tide_right_prompt_separator_same_color|e0b3|
### cmd_duration
| Variable | Description | Type |
| --------- | ------------------------------------------------------------------ | ------- |
| bg_color | background color of the cmd_duration item | color |
| color | color of the cmd_duration item | color |
| decimals | number of decimals to display after the seconds place | integer |
| icon | icon for the cmd_duration item | string |
| threshold | number of milliseconds that duration must exceed to produce output | integer |
| variable_name | 24bit_color | 8bit_color |
| - | - | - |
|tide_cmd_duration_bg_color|#C4A000|178|
|tide_cmd_duration_color|#000000|16|
### git
| Variable | Description | Type |
| ----------------- | ---------------------------------------------------------------------- | ------ |
| bg_color | default background color of the git_item | color |
| bg_color_unstable | background color when repository has dirty, staged, or untracked files | color |
| bg_color_urgent | background color when repository has conflicts or ongoing operations | color |
| color_branch | color of branch/SHA | color |
| color_conflicted | color of conflicted files number | color |
| color_dirty | color of dirty files number | color |
| color_operation | color of the current operation | color |
| color_staged | color of staged files number | color |
| color_stash | color of stashes number | color |
| color_untracked | color of untracked files number | color |
| color_upstream | color of upstream behind/ahead numbers | color |
| icon | icon of the git item, colored same as branch | string |
| variable_name | 24bit_color | 8bit_color |
| - | - | - |
|tide_git_bg_color|#4E9A06|70|
|tide_git_bg_color_unstable|#C4A000|178|
|tide_git_bg_color_urgent|#CC0000|160|
|tide_git_color_branch|#000000|16|
|tide_git_color_conflicted|#000000|16|
|tide_git_color_dirty|#000000|16|
|tide_git_color_operation|#000000|16|
|tide_git_color_staged|#000000|16|
|tide_git_color_stash|#000000|16|
|tide_git_color_untracked|#000000|16|
|tide_git_color_upstream|#000000|16|
### os
| Variable | Description | Type |
| -------- | --------------------------- | ----- |
| bg_color | background color of os item | color |
| color | color of os item | color |
| variable_name | 24bit_color | 8bit_color |
| - | - | - |
|tide_os_bg_color|#CED7CF|188|
|tide_os_color|#080808|232|
### pwd
| Variable | Description | Type |
| -------------------- | ---------------------------------------------------------------------------------------------- | ------ |
| bg_color | background color of pwd item | color |
| color_anchors | color of anchor directories. These directories are displayed in bold and immune to truncation. | color |
| color_dirs | color of normal directories | color |
| color_truncated_dirs | color of truncated directories | color |
| icon | default icon for pwd item | string |
| icon_home | icon when the the current directory is the user's HOME | string |
| icon_unwritable | icon when the directory is not writable by the user | string |
| markers | if a directory contains any of these files/directories, it will be anchored | list |
| variable_name | 24bit_color | 8bit_color |
| - | - | - |
|tide_pwd_bg_color|#3465A4|61|
|tide_pwd_color_anchors|#E4E4E4|254|
|tide_pwd_color_dirs|#E4E4E4|254|
|tide_pwd_color_truncated_dirs|#BCBCBC|250|
### rustc
| Variable | Description | Type |
| -------- | ---------------------------------------- | ------ |
| bg_color | background color of rust item | color |
| color | color of rust item | color |
| icon | icon to display next to the rust version | string |
| variable_name | 24bit_color | 8bit_color |
| - | - | - |
|tide_rustc_bg_color|#F74C00|202|
|tide_rustc_color|#000000|16|
### status
| Variable | Description | Type |
| ---------------- | ----------------------------------- | ------ |
| bg_color | background color when `$status` = 0 | color |
| bg_color_failure | background color when `$status` > 0 | color |
| color | color when `$status` = 0 | string |
| color_failure | color when `$status` > 0 | color |
| icon | icon when `$status` = 0 | string |
| icon_failure | icon when `$status` > 0 | string |
| variable_name | 24bit_color | 8bit_color |
| - | - | - |
|tide_status_bg_color|#2E3436|236|
|tide_status_bg_color_failure|#CC0000|160|
|tide_status_color|#4E9A06|70|
|tide_status_color_failure|#FFFF00|226|
### time
| Variable | Description | Type |
| -------- | ------------------------------------------- | ------ |
| bg_color | background color of time item | color |
| color | color of time item | color |
| format | format of time item. Uses `date` formatting | string |
| variable_name | 24bit_color | 8bit_color |
| - | - | - |
|tide_time_bg_color|#D3D7CF|188|
|tide_time_color|#000000|16|

435
modules/prompt/oh-my-v2.nu Normal file
View file

@ -0,0 +1,435 @@
# See the readme for how to use this script
# modes
# * 8bit
# * 24bit
let color_mode = "8bit"
# setup separate characters
let left_prompt_separator_diff_color = (char -u 'e0b0')
let left_prompt_separator_same_color = (char -u 'e0b1')
let right_prompt_separator_diff_color = (char -u 'e0b2')
let right_prompt_separator_same_color = (char -u 'e0b3')
# setup color variables for 24bit and 8bit
# prompt
let prompt_color_frame_and_connection_24 = (ansi -e { fg: "#6C6C6C" })
let prompt_color_separator_same_color_24 = (ansi -e { fg: "#949494" })
let prompt_color_frame_and_connection_8 = $"(ansi idx_fg)242m"
let prompt_color_separator_same_color_8 = $"(ansi idx_fg)246m"
let prompt_add_new_line_before = false
let prompt_color_frame_and_connection = ""
let prompt_color_separator_same_color = ""
let left_separator_diff_color = ""
let left_separator_same_color = ""
let left_items = []
let left_prefix = ""
let left_suffix = ""
let right_separator_diff_color = ""
let right_separator_same_color = ""
let right_items = []
let right_prefix = ""
let right_suffix = ""
# cmd
let cmd_duration_bg_color_24 = (ansi -e { bg: "#C4A000" })
let cmd_duration_color_24 = (ansi -e { fg: "#000000" })
let cmd_duration_bg_color_8 = $"(ansi idx_bg)178m"
let cmd_duration_color_8 = $"(ansi idx_fg)16m"
let cmd_bg_color = ""
let cmd_color = ""
let cmd_decimals = 2
let cmd_icon = ""
# git
let git_bg_color_24 = (ansi -e { bg: "#4E9A06" })
let git_bg_color_unstable_24 = (ansi -e { bg: "#C4A000" })
let git_bg_color_urgent_24 = (ansi -e { bg: "#CC0000" })
let git_color_branch_24 = (ansi -e { fg: "#000000" })
let git_color_conflicted_24 = (ansi -e { fg: "#000000" })
let git_color_dirty_24 = (ansi -e { fg: "#000000" })
let git_color_operation_24 = (ansi -e { fg: "#000000" })
let git_color_staged_24 = (ansi -e { fg: "#000000" })
let git_color_stash_24 = (ansi -e { fg: "#000000" })
let git_color_untracked_24 = (ansi -e { fg: "#000000" })
let git_color_upstream_24 = (ansi -e { fg: "#000000" })
let git_bg_color_8 = $"(ansi idx_bg)70m"
let git_bg_color_unstable_8 = $"(ansi idx_bg)178m"
let git_bg_color_urgent_8 = $"(ansi idx_bg)160m"
let git_color_branch_8 = $"(ansi idx_fg)16m"
let git_color_conflicted_8 = $"(ansi idx_fg)16m"
let git_color_dirty_8 = $"(ansi idx_fg)16m"
let git_color_operation_8 = $"(ansi idx_fg)16m"
let git_color_staged_8 = $"(ansi idx_fg)16m"
let git_color_stash_8 = $"(ansi idx_fg)16m"
let git_color_untracked_8 = $"(ansi idx_fg)16m"
let git_color_upstream_8 = $"(ansi idx_fg)16m"
let git_bg_color = ""
let git_bg_color_unstable = ""
let git_bg_color_urgent = ""
let git_color_branch = ""
let git_color_conflicted = ""
let git_color_dirty = ""
let git_color_operation = ""
let git_color_staged = ""
let git_color_stash = ""
let git_color_untracked = ""
let git_color_upstream = ""
# os
let os_bg_color_24 = (ansi -e { bg: "#CED7CF" })
let os_color_24 = (ansi -e { fg: "#080808" })
let os_bg_color_8 = $"(ansi idx_bg)188m"
let os_color_8 = $"(ansi idx_fg)232m"
let os_bg_color = ""
let os_color = ""
# pwd
let pwd_bg_color_24 = (ansi -e { bg: "#3465A4" })
let pwd_color_anchors_24 = (ansi -e { fg: "#E4E4E4" })
let pwd_color_dirs_24 = (ansi -e { fg: "#E4E4E4" })
let pwd_color_truncated_dirs_24 = (ansi -e { fg: "#BCBCBC" })
let pwd_bg_color_8 = $"(ansi idx_bg)61m"
let pwd_color_anchors_8 = $"(ansi idx_fg)254m"
let pwd_color_dirs_8 = $"(ansi idx_fg)254m"
let pwd_color_truncated_dirs_8 = $"(ansi idx_fg)250m"
let pwd_bg_color = ""
let pwd_color_anchors = ""
let pwd_color_dirs = ""
let pwd_color_truncated_dirs = ""
let pwd_icon = ""
let pwd_icon_home = ""
let pwd_icon_unwritable = ""
let pwd_markers = []
# rustc
let rustc_bg_color_24 = (ansi -e { bg: "#F74C00" })
let rustc_color_24 = (ansi -e { fg: "#000000" })
let rustc_bg_color_8 = $"(ansi idx_bg)202m"
let rustc_color_8 = $"(ansi idx_fg)16m"
let rustc_bg_color = ""
let rustc_color = ""
let rustc_icon = ""
# status
let status_bg_color_24 = (ansi -e { bg: "#2E3436" })
let status_bg_color_failure_24 = (ansi -e { bg: "#CC0000" })
let status_color_24 = (ansi -e { fg: "#4E9A06" })
let status_color_failure_24 = (ansi -e { fg: "#FFFF00" })
let status_bg_color_8 = $"(ansi idx_bg)236m"
let status_bg_color_failure_8 = $"(ansi idx_bg)160m"
let status_color_8 = $"(ansi idx_fg)70m"
let status_color_failure_8 = $"(ansi idx_fg)226m"
let status_bg_color = ""
let status_bg_color_faiure = ""
let status_color = ""
let status_color_failure = ""
let status_icon = ""
let status_icon_failure = ""
# time
let time_bg_color_24 = (ansi -e { bg: "#D3D7CF" })
let time_color_24 = (ansi -e { fg: "#000000" })
let time_bg_color_8 = $"(ansi idx_bg)188m"
let time_color_8 = $"(ansi idx_fg)16m"
let time_bg_color = ""
let time_color = ""
let time_format = ""
# indicator
let indicator_color_24 = (ansi -e { fg: "#3465a4" })
let indicator_bg_color_24 = (ansi -e { bg: "#000000" })
let indicator_color_8 = $"(ansi idx_fg)61m"
let indicator_bg_color_8 = $"(ansi idx_bg)16m"
# terminal background color
let terminal_color_24 = (ansi -e { fg: "#c7c7c7" })
let terminal_color_8 = (ansi white)
let terminal_bg_color_24 = (ansi -e { bg: "#000000" })
let terminal_bg_color_8 = (ansi black)
# cmd_duration_ms
let cmd_duration_ms_color_24 = (ansi -e { fg: "#606060" })
let cmd_duration_ms_color_8 = $"(ansi idx_fg)244m"
let cmd_duration_ms_bg_color_24 = (ansi -e { fg: "#000000" })
let cmd_duration_ms_bg_color_8 = (ansi black)
let runtime_colors = [
{ name: prompt_color_frame_and_connection, '8bit': $prompt_color_frame_and_connection_8, '24bit': $prompt_color_frame_and_connection_24 },
{ name: prompt_color_separator_same_color, '8bit': $prompt_color_separator_same_color_8, '24bit': $prompt_color_separator_same_color_24 },
{ name: left_separator_diff_color, '8bit': $left_prompt_separator_diff_color, '24bit': $left_prompt_separator_diff_color },
{ name: left_separator_same_color, '8bit': $left_prompt_separator_same_color, '24bit': $left_prompt_separator_same_color },
{ name: left_prefix, value: $nothing },
{ name: left_suffix, value: $nothing },
{ name: right_separator_diff_color, '8bit': $right_prompt_separator_diff_color, '24bit': $right_prompt_separator_diff_color },
{ name: right_separator_same_color, '8bit': $right_prompt_separator_same_color, '24bit': $right_prompt_separator_same_color },
{ name: right_prefix, value: $nothing },
{ name: right_suffix, value: $nothing },
{ name: cmd_bg_color, '8bit': $cmd_duration_bg_color_8, '24bit': $cmd_duration_bg_color_24 },
{ name: cmd_color, '8bit': $cmd_duration_color_8, '24bit': $cmd_duration_color_24 },
{ name: cmd_decimals, value: 2 },
{ name: cmd_icon, value:  },
{ name: git_bg_color, '8bit': $git_bg_color_8, '24bit': $git_bg_color_24 },
{ name: git_bg_color_unstable, '8bit': $git_bg_color_unstable_8, '24bit': $git_bg_color_unstable_24 },
{ name: git_bg_color_urgent, '8bit': $git_bg_color_urgent_8 , '24bit': $git_bg_color_urgent_24 },
{ name: git_color_branch, '8bit': $git_color_branch_8, '24bit': $git_color_branch_24 },
{ name: git_color_conflicted, '8bit': $git_color_conflicted_8, '24bit': $git_color_conflicted_24 },
{ name: git_color_dirty, '8bit': $git_color_dirty_8, '24bit': $git_color_dirty_24 },
{ name: git_color_operation, '8bit': $git_color_operation_8, '24bit': $git_color_operation_24 },
{ name: git_color_staged, '8bit': $git_color_staged_8, '24bit': $git_color_staged_24 },
{ name: git_color_stash, '8bit': $git_color_stash_8, '24bit': $git_color_stash_24 },
{ name: git_color_untracked, '8bit': $git_color_untracked_8, '24bit': $git_color_untracked_24 },
{ name: git_color_upstream, '8bit': $git_color_upstream_8, '24bit': $git_color_upstream_24 },
{ name: os_bg_color, '8bit': $os_bg_color_8, '24bit': $os_bg_color_24 },
{ name: os_color, '8bit': $os_color_8, '24bit': $os_color_24 },
{ name: pwd_bg_color, '8bit': $pwd_bg_color_8, '24bit': $pwd_bg_color_24 },
{ name: pwd_color_anchors, '8bit': $pwd_color_anchors_8, '24bit': $pwd_color_anchors_24 },
{ name: pwd_color_dirs, '8bit': $pwd_color_dirs_8, '24bit': $pwd_color_dirs_24 },
{ name: pwd_color_truncated_dirs, '8bit': $pwd_color_truncated_dirs_8, '24bit': $pwd_color_truncated_dirs_24 },
{ name: pwd_icon, value: $nothing },
{ name: pwd_icon_home, value: $nothing },
{ name: pwd_icon_unwritable, value: $nothing },
{ name: rustc_bg_color, '8bit': $rustc_bg_color_8, '24bit': $rustc_bg_color_24 },
{ name: rustc_color, '8bit': $rustc_color_8, '24bit': $rustc_color_24 },
{ name: rustc_icon, value:  },
{ name: status_bg_color, '8bit': $status_bg_color_8, '24bit': $status_bg_color_24 },
{ name: status_bg_color_failure, '8bit': $status_bg_color_failure_8, '24bit': $status_bg_color_failure_24 },
{ name: status_color, '8bit': $status_color_8, '24bit': $status_color_24 },
{ name: status_color_failure, '8bit': $status_color_failure_8 , '24bit': $status_color_failure_24 },
{ name: status_icon, value: $nothing },
{ name: status_icon_failure, value: $nothing },
{ name: time_bg_color, '8bit': $time_bg_color_8, '24bit': $time_bg_color_24 },
{ name: time_color, '8bit': $time_color_8, '24bit': $time_color_24 },
{ name: time_format, value: $nothing },
{ name: indicator_bg_color, '8bit': $indicator_bg_color_8, '24bit': $indicator_bg_color_24 },
{ name: indicator_color, '8bit': $indicator_color_8, '24bit': $indicator_color_24 },
{ name: terminal_color, '8bit': $terminal_color_8, '24bit': $terminal_color_24 },
{ name: terminal_bg_color, '8bit': $terminal_bg_color_8, '24bit': $terminal_bg_color_24 },
{name: cmd_duration_ms_color, '8bit': $cmd_duration_ms_color_8, '24bit': $cmd_duration_ms_color_24 },
{name: cmd_duration_ms_bg_color, '8bit': $cmd_duration_ms_bg_color_8, '24bit': $cmd_duration_ms_bg_color_24 },
]
# get the color from the $runtime_colors array
def get_color [name, mode] {
$runtime_colors | where name == $name | get $mode | get 0
}
######################################################
# Abbreviate home path for the prompt
def home_abbrev [os_name] {
let is_home_in_path = ($env.PWD | str starts-with $nu.home-path)
if $is_home_in_path {
if ($os_name == "windows") {
let home = ($nu.home-path | str replace -a '\\' '/')
let pwd = ($env.PWD | str replace -a '\\' '/')
$pwd | str replace $home '~'
} else {
$env.PWD | str replace $nu.home-path '~'
}
} else {
$env.PWD | str replace -a '\\' '/'
}
}
# get the operating system icon for the prompt
def get_os_icon [os] {
# f17c = tux, f179 = apple, f17a = windows
if ($os.name =~ macos) {
(char -u f179)
} else if ($os.name =~ windows) {
(char -u f17a)
} else if ($os.kernel_version =~ WSL) {
$'(char -u f17a)(char -u f17c)'
} else if ($os.family =~ unix) {
(char -u f17c)
} else {
''
}
}
# get the os segment for the prompt
def get_os_segment [os color_mode] {
let os_bg_color = (get_color os_bg_color $color_mode)
let os_color = (get_color os_color $color_mode)
let os_icon = (get_os_icon $os)
let transition_icon = $left_prompt_separator_diff_color
let transition_bg_color = (get_color pwd_bg_color $color_mode)
let transition_color = (get_color pwd_color_anchors $color_mode)
let os_segment = (
[
($os_color)
($os_bg_color)
(char space)
($os_icon)
(char space)
($transition_color)
($transition_bg_color)
($transition_icon)
(char space)
] | str join
)
$os_segment
}
# get the path segment for the prompt
def get_path_segment [os color_mode] {
let display_path = (home_abbrev $os.name)
let is_home_in_path = ($env.PWD | str starts-with $nu.home-path)
let pwd_bg_color = (get_color pwd_bg_color $color_mode)
let pwd_color = (get_color pwd_color_dirs $color_mode)
let home_or_folder = (if $is_home_in_path { (char nf_house1) } else { (char nf_folder1) })
let path_segment = (
[
$home_or_folder
(char space) # space
$display_path # ~/src/forks/nushell
($pwd_color)
($pwd_bg_color)
(char space) # space
] | str join
)
$path_segment
}
# get the indicator segment for the prompt
def get_indicator_segment [os color_mode] {
let R = (ansi reset)
let indicator_color = (get_color indicator_color $color_mode)
let indicator_bg_color = (get_color indicator_bg_color $color_mode)
let indicator_segment = (
[
($indicator_color)
($indicator_bg_color)
(char nf_segment) # 
($R) # reset color
] | str join
)
$indicator_segment
}
# construct the left prompt
def get_left_prompt [os color_mode] {
let os_segment = (get_os_segment $os $color_mode)
let path_segment = (get_path_segment $os $color_mode)
let indicator_segment = (get_indicator_segment $os $color_mode)
$os_segment + $path_segment + $indicator_segment
}
# get the time segment for the prompt
def get_time_segment [os color_mode] {
let R = (ansi reset)
let time_bg_color = (get_color time_bg_color $color_mode)
let time_color = (get_color time_color $color_mode)
let time_segment = ([
(ansi { fg: $time_bg_color bg: $time_color})
(char nf_right_segment) #(char -u e0b2) # 
($time_color)
($time_bg_color)
(char space)
(date now | date format '%I:%M:%S %p')
(char space)
($R)
] | str join)
$time_segment
}
# get the status segment for the prompt
def get_status_segment [os color_mode] {
let R = (ansi reset)
# set status bg color to foreground since bg is dark
let fg_failure = (get_color status_bg_color_failure $color_mode)
let term_bg_color = (get_color terminal_bg_color $color_mode)
let cmd_dur_fg = (get_color cmd_duration_ms_color $color_mode)
let cmd_dur_bg = (get_color cmd_duration_ms_bg_color $color_mode)
let status_segment = (
[
(if $env.LAST_EXIT_CODE != 0 {
(ansi { fg: $fg_failure bg: $term_bg_color })
} else {
(ansi { fg: $cmd_dur_fg bg: $cmd_dur_bg })
})
(char nf_right_segment_thin)
(char space)
$env.LAST_EXIT_CODE
(char space)
($R)
] | str join
)
$status_segment
}
# get the execution segment for the prompt
def get_execution_time_segment [os color_mode] {
let R = (ansi reset)
let cmd_dur_fg = (get_color cmd_duration_ms_color $color_mode)
let cmd_dur_bg = (get_color cmd_duration_ms_bg_color $color_mode)
let execution_time_segment = (
[
($cmd_dur_fg)
($cmd_dur_bg)
# (ansi { fg: $cmd_dur_fg bg: $cmd_dur_bg })
(char nf_right_segment_thin)
(char space)
$env.CMD_DURATION_MS
(char space)
($R)
] | str join
)
$execution_time_segment
}
# construct the right prompt
def get_right_prompt [os color_mode] {
let status_segment = (get_status_segment $os $color_mode)
let execution_time_segment = (get_execution_time_segment $os $color_mode)
let time_segment = (get_time_segment $os $color_mode)
let exit_if = (if $env.LAST_EXIT_CODE != 0 { $status_segment })
[$exit_if $execution_time_segment $time_segment] | str join
}
# constructe the left and right prompt by color_mode (8bit or 24bit)
def get_prompt [color_mode] {
# modes = 8bit or 24bit
# let os = ((sys).host)
let os = $nu.os-info
let left_prompt = (get_left_prompt $os $color_mode)
let right_prompt = (get_right_prompt $os $color_mode)
# return in record literal syntax to be used kind of like a tuple
# so we don't have to run this script more than once per prompt
{
left_prompt: $left_prompt
right_prompt: $right_prompt
}
}

545
modules/prompt/oh-my.nu Normal file
View file

@ -0,0 +1,545 @@
# REQUIREMENTS #
# you definitely need nerd fonts https://www.nerdfonts.com
# nerd fonts repo https://github.com/ryanoasis/nerd-fonts
# i use "FiraCode Nerd Font Mono" on mac
#
# you also must have the engine-q gstat plugin installed and registered
# ATTRIBUTION #
# A little fancier prompt with git information
# inspired by https://github.com/xcambar/purs
# inspired by https://github.com/IlanCosman/tide
# inspired by https://github.com/JanDeDobbeleer/oh-my-posh
# Abbreviate home path
def home_abbrev [os_name] {
let is_home_in_path = ($env.PWD | str starts-with $nu.home-path)
if $is_home_in_path {
if ($os_name =~ "windows") {
let home = ($nu.home-path | str replace -a '\\' '/')
let pwd = ($env.PWD | str replace -a '\\' '/')
$pwd | str replace $home '~'
} else {
$env.PWD | str replace $nu.home-path '~'
}
} else {
if ($os_name =~ "windows") {
# remove the C: from the path
$env.PWD | str replace -a '\\' '/' | str substring 2..
} else {
$env.PWD
}
}
}
def path_abbrev_if_needed [apath term_width] {
# probably shouldn't do coloring here but since we're coloring
# only certain parts, it's kind of tricky to do it in another place
let T = (ansi { fg: "#BCBCBC" bg: "#3465A4"}) # truncated
let P = (ansi { fg: "#E4E4E4" bg: "#3465A4"}) # path
let PB = (ansi { fg: "#E4E4E4" bg: "#3465A4" attr: b}) # path bold
let R = (ansi reset)
let is_home_in_path = ($env.PWD | str starts-with $nu.home-path)
if (($apath | str length) > ($term_width / 2)) {
# split out by path separator into tokens
# don't use psep here because in home_abbrev we're making them all '/'
let splits = ($apath | split row '/')
let splits_len = ($splits | length)
# get all the tokens except the last
let tokens = (1..<($splits_len - 1) | each {|x|
$"($T)((($splits) | get $x | split chars) | get 0)($R)"
})
# need an insert command
let tokens = ($tokens | prepend $"($T)~")
# append the last part of the path
let tokens = ($tokens | append $"($PB)($splits | last)($R)")
# collect
$tokens | str join $"($T)/"
} else {
let splits = ($apath | split row '/')
let splits_len = ($splits | length)
let apath_len = ($apath | str length)
if ($splits_len == 2 and $apath_len == 1) {
$"/($T)($R)"
} else if ($splits_len == 2) {
let top_part = ($splits | last)
let tokens = $"($PB)($top_part)($R)"
$tokens | str join $"($T)"
} else if ($splits.0 | is-empty) {
let top_part = ($splits | skip | first ($splits_len - 2))
let end_part = ($splits | last)
let tokens = ($top_part | each {|x|
$"($T)/(($x | split chars).0)($R)"
})
let tokens = ($tokens | append $"/($PB)($end_part)($R)")
$tokens | str join $"($T)"
} else {
let top_part = ($splits | first ($splits_len - 1))
let end_part = ($splits | last)
let tokens = ($top_part | each {|x|
if $x == '~' {
$"($T)(($x | split chars).0)($R)"
} else {
$"/($T)(($x | split chars).0)($R)"
}
})
let tokens = ($tokens | append $"/($PB)($end_part)($R)")
$tokens | str join $"($T)"
}
}
}
def get_index_change_count [gs] {
let index_new = ($gs | get idx_added_staged)
let index_modified = ($gs | get idx_modified_staged)
let index_deleted = ($gs | get idx_deleted_staged)
let index_renamed = ($gs | get idx_renamed)
let index_typechanged = ($gs | get idx_type_changed)
$index_new + $index_modified + $index_deleted + $index_renamed + $index_typechanged
}
def get_working_tree_count [gs] {
let wt_modified = ($gs | get wt_modified)
let wt_deleted = ($gs | get wt_deleted)
let wt_typechanged = ($gs | get wt_type_changed)
let wt_renamed = ($gs | get wt_renamed)
$wt_modified + $wt_deleted + $wt_typechanged + $wt_renamed
}
def get_conflicted_count [gs] {
($gs | get conflicts)
}
def get_untracked_count [gs] {
($gs | get wt_untracked)
}
def get_branch_name [gs] {
let br = ($gs | get branch)
if $br == "no_branch" {
""
} else {
$br
}
}
def get_ahead_count [gs] {
($gs | get ahead)
}
def get_behind_count [gs] {
($gs | get behind)
}
def get_icons_list [] {
{
AHEAD_ICON: (char branch_ahead), # "↑" 2191
BEHIND_ICON: (char branch_behind), # "↓" 2193
NO_CHANGE_ICON: (char branch_identical) # ≣ 2263
HAS_CHANGE_ICON: "*",
INDEX_CHANGE_ICON: "♦",
WT_CHANGE_ICON: "✚",
CONFLICTED_CHANGE_ICON: "✖",
UNTRACKED_CHANGE_ICON: (char branch_untracked) # ≢ 2262
INSERT_SYMBOL_ICON: "",
HAMBURGER_ICON: (char hamburger) # "≡" 2261
GITHUB_ICON: "", # f408
BRANCH_ICON: (char nf_branch) # "" e0a0
REBASE_ICON: "", # e728
TAG_ICON: "" # f412
}
}
def get_icon_by_name [name] {
get_icons_list | get $name
}
def get_os_icon [os] {
# f17c = tux, f179 = apple, f17a = windows
if ($os.name =~ macos) {
(char -u f179)
} else if ($os.name =~ windows) {
(char -u f17a)
} else if ($os.kernel_version =~ WSL) {
$'(char -u f17a)(char -u f17c)'
} else if ($os.family =~ unix) {
(char -u f17c)
} else {
''
}
}
# ╭─────────────────────┬───────────────╮
# │ idx_added_staged │ 0 │ #INDEX_NEW
# │ idx_modified_staged │ 0 │ #INDEX_MODIFIED
# │ idx_deleted_staged │ 0 │ #INDEX_DELETED
# │ idx_renamed │ 0 │ #INDEX_RENAMED
# │ idx_type_changed │ 0 │ #INDEX_TYPECHANGE
# │ wt_untracked │ 0 │ #WT_NEW
# │ wt_modified │ 0 │ #WT_MODIFIED
# │ wt_deleted │ 0 │ #WT_DELETED
# │ wt_type_changed │ 0 │ #WT_TYPECHANGE
# │ wt_renamed │ 0 │ #WT_RENAMED
# │ ignored │ 0 │
# │ conflicts │ 0 │ #CONFLICTED
# │ ahead │ 0 │
# │ behind │ 0 │
# │ stashes │ 0 │
# │ repo_name │ nushell │
# │ tag │ no_tag │
# │ branch │ main │
# │ remote │ upstream/main │
# ╰─────────────────────┴───────────────╯
def get_repo_status [gs os] {
let display_path = (path_abbrev_if_needed (home_abbrev $os.name) (term size).columns)
let branch_name = (get_branch_name $gs)
let ahead_cnt = (get_ahead_count $gs)
let behind_cnt = (get_behind_count $gs)
let index_change_cnt = (get_index_change_count $gs)
let wt_change_cnt = (get_working_tree_count $gs)
let conflicted_cnt = (get_conflicted_count $gs)
let untracked_cnt = (get_untracked_count $gs)
let has_no_changes = (
if ($index_change_cnt <= 0) and
($wt_change_cnt <= 0) and
($conflicted_cnt <= 0) and
($untracked_cnt <= 0) {
true
} else {
false
}
)
let GIT_BG = "#C4A000"
let GIT_FG = "#000000"
# let TERM_BG = "#0C0C0C"
# The multi-color fg colors are good if you just have a black background
let AHEAD_ICON = (get_icon_by_name AHEAD_ICON)
# let A_COLOR = (ansi { fg:"#00ffff" bg: ($GIT_BG) })
let A_COLOR = (ansi { fg: ($GIT_FG) bg: ($GIT_BG) })
let BEHIND_ICON = (get_icon_by_name BEHIND_ICON)
# let B_COLOR = (ansi { fg:"#00ffff" bg: ($GIT_BG) })
let B_COLOR = (ansi { fg: ($GIT_FG) bg: ($GIT_BG) })
let INDEX_CHANGE_ICON = (get_icon_by_name INDEX_CHANGE_ICON)
# let I_COLOR = (ansi { fg:"#00ff00" bg: ($GIT_BG) })
let I_COLOR = (ansi { fg: ($GIT_FG) bg: ($GIT_BG) })
let CONFLICTED_CHANGE_ICON = (get_icon_by_name CONFLICTED_CHANGE_ICON)
# let C_COLOR = (ansi { fg:"#ff0000" bg: ($GIT_BG) })
let C_COLOR = (ansi { fg: ($GIT_FG) bg: ($GIT_BG) })
let WT_CHANGE_ICON = (get_icon_by_name WT_CHANGE_ICON)
# let W_COLOR = (ansi { fg:"#ff00ff" bg: ($GIT_BG) })
let W_COLOR = (ansi { fg: ($GIT_FG) bg: ($GIT_BG) })
let UNTRACKED_CHANGE_ICON = (get_icon_by_name UNTRACKED_CHANGE_ICON)
# let U_COLOR = (ansi { fg:"#ffff00" bg: ($GIT_BG) })
let U_COLOR = (ansi { fg: ($GIT_FG) bg: ($GIT_BG) })
let NO_CHANGE_ICON = (get_icon_by_name NO_CHANGE_ICON)
# let N_COLOR = (ansi { fg:"#00ff00" bg: ($GIT_BG) })
let N_COLOR = (ansi { fg: ($GIT_FG) bg: ($GIT_BG) })
let HAS_CHANGE_ICON = (get_icon_by_name HAS_CHANGE_ICON)
# let H_COLOR = (ansi { fg:"#ff0000" bg: ($GIT_BG) attr: b })
let H_COLOR = (ansi { fg: ($GIT_FG) bg: ($GIT_BG) attr: b })
let INSERT_SYMBOL_ICON = (get_icon_by_name INSERT_SYMBOL_ICON)
# let S_COLOR = (ansi { fg:"#00ffff" bg: ($GIT_BG) })
let S_COLOR = (ansi { fg: ($GIT_FG) bg: ($GIT_BG) })
let R = (ansi reset)
let repo_status = (
$"(
if ($ahead_cnt > 0) { $'($A_COLOR)($AHEAD_ICON)($ahead_cnt)($R)' }
)(
if ($behind_cnt > 0) { $'($B_COLOR)($BEHIND_ICON)($behind_cnt)($R)' }
)(
if ($index_change_cnt > 0) { $'($I_COLOR)($INDEX_CHANGE_ICON)($index_change_cnt)($R)' }
)(
if ($conflicted_cnt > 0) { $'($C_COLOR)($CONFLICTED_CHANGE_ICON)($conflicted_cnt)($R)' }
)(
if ($wt_change_cnt > 0) { $'($W_COLOR)($WT_CHANGE_ICON)($wt_change_cnt)($R)' }
)(
if ($untracked_cnt > 0) { $'($U_COLOR)($UNTRACKED_CHANGE_ICON)($untracked_cnt)($R)' }
)(
if $has_no_changes { $'($N_COLOR)($NO_CHANGE_ICON)($R)' } else { $'($H_COLOR)($HAS_CHANGE_ICON)($R)' }
)"
)
$repo_status
}
def git_left_prompt [gs os] {
# replace this 30 with whatever the width of the terminal is
let display_path = (path_abbrev_if_needed (home_abbrev $os.name) (term size).columns)
let branch_name = (get_branch_name $gs)
let R = (ansi reset)
# when reduce is available
# echo "one" "two" "three" | reduce { if ($acc | str starts-with 't') { $acc + $it } { $it }}
# some icons and the unicode char
# e0b0
# e0b1
# e0b2
# e0b3
# f1d3
# f07c or  f115
# f015 or  f7db
let GIT_BG = "#C4A000"
let GIT_FG = "#000000"
let TERM_BG = "#0C0C0C"
let repo_status = (get_repo_status $gs $os)
# build segments and then put together the segments for the prompt
let os_segment = ([
(ansi { fg: "#080808" bg: "#CED7CF"}) # os bg color
(char space) # space
(get_os_icon $os) # os icon
(char space) # space
(ansi { fg: "#CED7CF" bg: "#3465A4"}) # color transition
(char -u e0b0) # 
(char space) # space
] | str join)
let is_home_in_path = ($env.PWD | str starts-with $nu.home-path)
let path_segment = (if (($is_home_in_path) and ($branch_name == "")) {
[
(char -u f015) #  home icon
(char space) # space
$display_path # ~/src/forks/nushell
(ansi { fg: "#CED7CF" bg: "#3465A4"}) # color just to color the next space
(char space) # space
] | str join
} else {
[
(char -u f07c) #  folder icon
(char space) # space
$display_path # ~/src/forks/nushell
(ansi { fg: "#CED7CF" bg: "#3465A4"}) # color just to color the next space
(char space) # space
] | str join
})
let git_segment = (if ($branch_name != "") {
[
(ansi { fg: "#3465A4" bg: "#4E9A06"}) # color
(char -u e0b0) # 
(char space) # space
(ansi { fg: $TERM_BG bg: "#4E9A06"}) # color
# (char -u f1d3) # 
(char -u e0a0) # 
(char space) # space
($branch_name) # main
(char space) # space
(ansi { fg: "#4E9A06" bg: $GIT_BG}) # color
(char -u e0b0) # 
(char space) # space
($R) # reset color
$repo_status # repo status
] | str join
})
let git_right = false
let indicator_segment = (if ($branch_name == "" or $git_right) {
[
(ansi { fg: "#3465A4" bg: $TERM_BG}) # color
(char -u e0b0) # 
($R) # reset color
] | str join
} else {
[
(ansi { fg: $GIT_BG bg: $TERM_BG}) # color
(char -u e0b0) # 
($R) # reset color
] | str join
})
# assemble all segments for final prompt printing
[
$os_segment
$path_segment
(if ($git_right == false) {
$git_segment
})
$indicator_segment
] | str join
}
def git_right_prompt [gs os] {
# right prompt ideas
# 1. just the time on the right
# 2. date and time on the right
# 3. git information on the right
# 4. maybe git and time
# 5. would like to get CMD_DURATION_MS going there too when it's implemented
# 6. all of the above, chosen by def parameters
let branch_name = (get_branch_name $gs)
let repo_status = (get_repo_status $gs $os)
let R = (ansi reset)
let TIME_BG = "#D3D7CF"
let TERM_FG = "#0C0C0C"
let GIT_BG = "#C4A000"
let GIT_FG = "#000000"
let TERM_BG = "#0C0C0C"
let TERM_FG_DEFAULT = "\e[39m"
let TERM_BG_DEFAULT = "\e[49m"
let datetime_segment = ([
(ansi { fg: $TIME_BG bg: $TERM_FG})
(char -u e0b2) # 
(ansi { fg: $TERM_FG bg: $TIME_BG})
(char space)
(date now | date format '%m/%d/%Y %I:%M:%S%.3f')
(char space)
($R)
] | str join)
let time_segment = ([
(ansi { fg: $TIME_BG bg: $TERM_FG})
(char -u e0b2) # 
(ansi { fg: $TERM_FG bg: $TIME_BG})
(char space)
(date now | date format '%I:%M:%S %p')
(char space)
($R)
] | str join)
let git_segment = (if ($branch_name != "") {
[
(ansi { fg: $GIT_BG bg: $TERM_BG}) # color
(char -u e0b2) # 
(ansi { fg: $TERM_FG bg: $GIT_BG}) # color
(char space) # space
$repo_status # repo status
(ansi { fg: $TERM_FG bg: $GIT_BG}) # color
(char space)
(ansi { fg: "#4E9A06" bg: $GIT_BG }) # color
(char -u e0b2) # 
(ansi { fg: $TERM_BG bg: "#4E9A06"}) # color
(char space) # space
# (char -u f1d3) # 
# (char -u e0a0) # 
(char nf_git_branch) # 
(char space) # space
$branch_name # main
(char space) # space
($R) # reset color
] | str join
})
let execution_time_segment = (
[
# (ansi { fg: "#606060" bg: "#191323"})
(ansi { fg: "#606060"})
$TERM_BG_DEFAULT
(char -u e0b3)
(char space)
$env.CMD_DURATION_MS
(char space)
($R)
] | str join
)
let status_segment = (
[
(if $env.LAST_EXIT_CODE != 0 {
(ansi { fg: "#CC0000" bg: "#191323"})
} else {
(ansi { fg: "#606060" bg: "#191323"})
})
(char -u e0b3)
(char space)
$env.LAST_EXIT_CODE
(char space)
($R)
] | str join
)
# 1. datetime - working
# $datetime_segment
# 2. time only - working
[
(if $env.LAST_EXIT_CODE != 0 {
$status_segment
})
$execution_time_segment
$time_segment
] | str join
# 3. git only - working
# $git_segment
# 4. git + time -> need to fix the transition
# [
# $git_segment
# $time_segment
# ] | str join
# 5. fernando wants this on the left prompt
# [
# $os_segment
# $time_segment
# $path_segment
# ]
}
export def git_prompt [] {
let gs = (gstat)
let os = $nu.os-info
let left_prompt = (git_left_prompt $gs $os)
let right_prompt = (git_right_prompt $gs $os)
# set the title of the window/tab
# Wezterm accepts:
# osc0 \x1b]0;
# osc1 \x1b]1;
# osc2 \x1b]2;
# the typical way to set the terminal title is:
# osc2 some_string bel aka (char osc)2;($some_string)(char bel) or "\u001b]2;($some_string)\a"
# bel is escape \a or \x7 or \u0007
# but i've also seen it as
# osc2 some_string string_terminator aka (char osc)2;($some_string)(ansi st) or "\u001b];($some_string)\\"
# where string_terminator is \
# so you might want to play around with these settings a bit
#let abbrev = ((path_abbrev_if_needed (home_abbrev $os.name) 30) | ansi strip)
# $"\u001b]0;($abbrev)"
# note that this isn't ending properly with a bel or a st, that's
# because it makes the string echo to the screen as an empty line
# turning off now since a similar thing is built into nushell + it breaks kitty
#$"(ansi osc)2;($abbrev)"
# return in record literal syntax to be used kind of like a tuple
# so we don't have to run this script more than once per prompt
{
left_prompt: $left_prompt
right_prompt: $right_prompt
}
#
# in the config.nu you would do something like
# use "c:\some\path\to\nu_scripts\prompt\oh-my.nu" git_prompt
# let-env PROMPT_COMMAND = { (git_prompt).left_prompt }
# let-env PROMPT_COMMAND_RIGHT = { (git_prompt).right_prompt }
# let-env PROMPT_INDICATOR = " "
}

View file

@ -0,0 +1,471 @@
# panache-git
# An opinionated Git prompt for Nushell, styled after posh-git
#
# Quick Start:
# - Download this file (panache-git.nu)
# - In your Nushell config:
# - Import the panache-git command from the panache-git.nu module file
# - Set panache-git as your prompt command
# - Disable the separate prompt indicator by setting it to an empty string
# - For example, with this file in your home directory:
# use ~/panache-git.nu panache-git
# let-env PROMPT_COMMAND = { panache-git }
# let-env PROMPT_INDICATOR = { "" }
# - Restart Nushell
#
# For more documentation or to file an issue, see https://github.com/ehdevries/panache-git
# An opinionated Git prompt for Nushell, styled after posh-git
export def prompt-with-panache [] {
let prompt = ($'(current-dir) (repo-styled)' | str trim)
$'($prompt)> '
}
# Get the current directory with home abbreviated
export def current-dir [] {
let current_dir = ($env.PWD)
let current_dir_relative_to_home = (
do --ignore-errors { $current_dir | path relative-to $nu.home-path } | str join
)
let in_sub_dir_of_home = ($current_dir_relative_to_home | is-empty | nope)
let current_dir_abbreviated = (if $in_sub_dir_of_home {
$'~(char separator)($current_dir_relative_to_home)' | str replace -a '\\' '/'
} else {
$current_dir | str replace -a '\\' '/'
})
$'(ansi reset)($current_dir_abbreviated)'
}
# Get repository status as structured data
export def repo-structured [] {
let in_git_repo = (do --ignore-errors { git rev-parse --abbrev-ref HEAD } | is-empty | nope)
let status = (if $in_git_repo {
git --no-optional-locks status --porcelain=2 --branch | lines
} else {
[]
})
let on_named_branch = (if $in_git_repo {
$status
| where ($it | str starts-with '# branch.head')
| first
| str contains '(detached)'
| nope
} else {
false
})
let branch_name = (if $on_named_branch {
$status
| where ($it | str starts-with '# branch.head')
| split column ' ' col1 col2 branch
| get branch
| first
} else {
''
})
let commit_hash = (if $in_git_repo {
$status
| where ($it | str starts-with '# branch.oid')
| split column ' ' col1 col2 full_hash
| get full_hash
| first
| str substring 0..7
} else {
''
})
let tracking_upstream_branch = (if $in_git_repo {
$status
| where ($it | str starts-with '# branch.upstream')
| str join
| is-empty
| nope
} else {
false
})
let upstream_exists_on_remote = (if $in_git_repo {
$status
| where ($it | str starts-with '# branch.ab')
| str join
| is-empty
| nope
} else {
false
})
let ahead_behind_table = (if $upstream_exists_on_remote {
$status
| where ($it | str starts-with '# branch.ab')
| split column ' ' col1 col2 ahead behind
} else {
[[]]
})
let commits_ahead = (if $upstream_exists_on_remote {
$ahead_behind_table
| get ahead
| first
| into int
} else {
0
})
let commits_behind = (if $upstream_exists_on_remote {
$ahead_behind_table
| get behind
| first
| into int
| math abs
} else {
0
})
let has_staging_or_worktree_changes = (if $in_git_repo {
$status
| where ($it | str starts-with '1') or ($it | str starts-with '2')
| str join
| is-empty
| nope
} else {
false
})
let has_untracked_files = (if $in_git_repo {
$status
| where ($it | str starts-with '?')
| str join
| is-empty
| nope
} else {
false
})
let has_unresolved_merge_conflicts = (if $in_git_repo {
$status
| where ($it | str starts-with 'u')
| str join
| is-empty
| nope
} else {
false
})
let staging_worktree_table = (if $has_staging_or_worktree_changes {
$status
| where ($it | str starts-with '1') or ($it | str starts-with '2')
| split column ' '
| get column2
| split column '' staging worktree --collapse-empty
} else {
[[]]
})
let staging_added_count = (if $has_staging_or_worktree_changes {
$staging_worktree_table
| where staging == 'A'
| length
} else {
0
})
let staging_modified_count = (if $has_staging_or_worktree_changes {
$staging_worktree_table
| where staging in ['M', 'R']
| length
} else {
0
})
let staging_deleted_count = (if $has_staging_or_worktree_changes {
$staging_worktree_table
| where staging == 'D'
| length
} else {
0
})
let untracked_count = (if $has_untracked_files {
$status
| where ($it | str starts-with '?')
| length
} else {
0
})
let worktree_modified_count = (if $has_staging_or_worktree_changes {
$staging_worktree_table
| where worktree in ['M', 'R']
| length
} else {
0
})
let worktree_deleted_count = (if $has_staging_or_worktree_changes {
$staging_worktree_table
| where worktree == 'D'
| length
} else {
0
})
let merge_conflict_count = (if $has_unresolved_merge_conflicts {
$status
| where ($it | str starts-with 'u')
| length
} else {
0
})
{
in_git_repo: $in_git_repo,
on_named_branch: $on_named_branch,
branch_name: $branch_name,
commit_hash: $commit_hash,
tracking_upstream_branch: $tracking_upstream_branch,
upstream_exists_on_remote: $upstream_exists_on_remote,
commits_ahead: $commits_ahead,
commits_behind: $commits_behind,
staging_added_count: $staging_added_count,
staging_modified_count: $staging_modified_count,
staging_deleted_count: $staging_deleted_count,
untracked_count: $untracked_count,
worktree_modified_count: $worktree_modified_count,
worktree_deleted_count: $worktree_deleted_count,
merge_conflict_count: $merge_conflict_count
}
}
# Get repository status as a styled string
export def repo-styled [] {
let status = (repo-structured)
let is_local_only = ($status.tracking_upstream_branch != true)
let upstream_deleted = (
$status.tracking_upstream_branch and
$status.upstream_exists_on_remote != true
)
let is_up_to_date = (
$status.upstream_exists_on_remote and
$status.commits_ahead == 0 and
$status.commits_behind == 0
)
let is_ahead = (
$status.upstream_exists_on_remote and
$status.commits_ahead > 0 and
$status.commits_behind == 0
)
let is_behind = (
$status.upstream_exists_on_remote and
$status.commits_ahead == 0 and
$status.commits_behind > 0
)
let is_ahead_and_behind = (
$status.upstream_exists_on_remote and
$status.commits_ahead > 0 and
$status.commits_behind > 0
)
let branch_name = (if $status.in_git_repo {
(if $status.on_named_branch {
$status.branch_name
} else {
['(' $status.commit_hash '...)'] | str join
})
} else {
''
})
let branch_styled = (if $status.in_git_repo {
(if $is_local_only {
(branch-local-only $branch_name)
} else if $is_up_to_date {
(branch-up-to-date $branch_name)
} else if $is_ahead {
(branch-ahead $branch_name $status.commits_ahead)
} else if $is_behind {
(branch-behind $branch_name $status.commits_behind)
} else if $is_ahead_and_behind {
(branch-ahead-and-behind $branch_name $status.commits_ahead $status.commits_behind)
} else if $upstream_deleted {
(branch-upstream-deleted $branch_name)
} else {
$branch_name
})
} else {
''
})
let has_staging_changes = (
$status.staging_added_count > 0 or
$status.staging_modified_count > 0 or
$status.staging_deleted_count > 0
)
let has_worktree_changes = (
$status.untracked_count > 0 or
$status.worktree_modified_count > 0 or
$status.worktree_deleted_count > 0 or
$status.merge_conflict_count > 0
)
let has_merge_conflicts = $status.merge_conflict_count > 0
let staging_summary = (if $has_staging_changes {
(staging-changes $status.staging_added_count $status.staging_modified_count $status.staging_deleted_count)
} else {
''
})
let worktree_summary = (if $has_worktree_changes {
(worktree-changes $status.untracked_count $status.worktree_modified_count $status.worktree_deleted_count)
} else {
''
})
let merge_conflict_summary = (if $has_merge_conflicts {
(unresolved-conflicts $status.merge_conflict_count)
} else {
''
})
let delimiter = (if ($has_staging_changes and $has_worktree_changes) {
('|' | bright-yellow)
} else {
''
})
let local_summary = (
$'($staging_summary) ($delimiter) ($worktree_summary) ($merge_conflict_summary)' | str trim
)
let local_indicator = (if $status.in_git_repo {
(if $has_worktree_changes {
('!' | red)
} else if $has_staging_changes {
('~' | bright-cyan)
} else {
''
})
} else {
''
})
let repo_summary = (
$'($branch_styled) ($local_summary) ($local_indicator)' | str trim
)
let left_bracket = ('[' | bright-yellow)
let right_bracket = (']' | bright-yellow)
(if $status.in_git_repo {
$'($left_bracket)($repo_summary)($right_bracket)'
} else {
''
})
}
# Helper commands to encapsulate style and make everything else more readable
def nope [] {
each { |it| $it == false }
}
def bright-cyan [] {
each { |it| $"(ansi -e '96m')($it)(ansi reset)" }
}
def bright-green [] {
each { |it| $"(ansi -e '92m')($it)(ansi reset)" }
}
def bright-red [] {
each { |it| $"(ansi -e '91m')($it)(ansi reset)" }
}
def bright-yellow [] {
each { |it| $"(ansi -e '93m')($it)(ansi reset)" }
}
def green [] {
each { |it| $"(ansi green)($it)(ansi reset)" }
}
def red [] {
each { |it| $"(ansi red)($it)(ansi reset)" }
}
def branch-local-only [
branch: string
] {
$branch | bright-cyan
}
def branch-upstream-deleted [
branch: string
] {
$'($branch) (char failed)' | bright-cyan
}
def branch-up-to-date [
branch: string
] {
$'($branch) (char identical_to)' | bright-cyan
}
def branch-ahead [
branch: string
ahead: int
] {
$'($branch) (char branch_ahead)($ahead)' | bright-green
}
def branch-behind [
branch: string
behind: int
] {
$'($branch) (char branch_behind)($behind)' | bright-red
}
def branch-ahead-and-behind [
branch: string
ahead: int
behind: int
] {
$'($branch) (char branch_behind)($behind) (char branch_ahead)($ahead)' | bright-yellow
}
def staging-changes [
added: int
modified: int
deleted: int
] {
$'+($added) ~($modified) -($deleted)' | green
}
def worktree-changes [
added: int
modified: int
deleted: int
] {
$'+($added) ~($modified) -($deleted)' | red
}
def unresolved-conflicts [
conflicts: int
] {
$'!($conflicts)' | red
}

View file

@ -0,0 +1,21 @@
# use shells to to show workspaces
def workspaces [] {
shells | each {|item index|
if $item.active {
$"(ansi green)($index) "
} else {
$"(ansi blue)($index) "
}
}| str join
}
def create_right_prompt [] {
let time_segment = ([
(date now | date format '%r'),
" ",
(workspaces)
] | str join)
$time_segment
}
let-env PROMPT_COMMAND_RIGHT = { create_right_prompt }

22
modules/prompt/simple.nu Normal file
View file

@ -0,0 +1,22 @@
export def create_left_prompt [] {
let path_segment = if (is-admin) {
$"(ansi red_bold)($env.PWD)"
} else {
$"(ansi green_bold)($env.PWD)"
}
let duration_segment = do {
let duration_secs = ($env.CMD_DURATION_MS | into int) / 1000
if ($duration_secs >= 5) {
$"(ansi yellow_bold)($duration_secs | math round | into string | append "sec" | str join | into duration) "
} else {
""
}
}
let exit_code_segment = if ($env.LAST_EXIT_CODE == 0) {
""
} else {
$"(ansi red_bold)($env.LAST_EXIT_CODE) "
}
[$duration_segment, $exit_code_segment, $path_segment] | str join
}

View file

@ -0,0 +1,11 @@
def create_left_prompt [] {
^starship prompt --cmd-duration $env.CMD_DURATION_MS $'--status=($env.LAST_EXIT_CODE)'
}
let-env PROMPT_COMMAND = { create_left_prompt }
# avoid same PROMPT_INDICATOR
let-env PROMPT_INDICATOR = { "" }
let-env PROMPT_INDICATOR_VI_INSERT = { ": " }
let-env PROMPT_INDICATOR_VI_NORMAL = { "〉" }
let-env PROMPT_MULTILINE_INDICATOR = { "::: " }

10
modules/rbenv/README.md Normal file
View file

@ -0,0 +1,10 @@
# rbenv script
This script provides minimal working rbenv setup.
It can be used by importing its exported commands via:
```
use path/to/rbenv.nu *
```
With `path/to` being either the relative path of the file to your current working directory or its absolute path.

38
modules/rbenv/rbenv.nu Normal file
View file

@ -0,0 +1,38 @@
# rbenv
export-env {
load-env {
PATH: ($env.PATH | split row (char esep) | prepend [$"($env.HOME)/.rbenv/bin" $"($env.HOME)/.rbenv/shims"])
RBENV_VERSION: ""
RBENV_VERSION_OLD: ""
RBENV_SHELL: "nu"
}
}
export def-env rbenv [
command?: string@'nu-complete rbenv',
...args
] {
let new_env = if $command in ["rehash", "shell"] {
# implement each on indiviudaly e.g.
if $command == "shell" {
{ RBENV_VERSION_OLD: $env.RBENV_VERSION RBENV_VERSION: $args.0 }
} else {
error make { msg: $"`($command)` command is not supported yet" }
}
} else {
if ($command | is-empty) {
^rbenv
} else {
^rbenv $command $args
}
{}
}
load-env $new_env
}
def 'nu-complete rbenv' [] {
^rbenv help
| lines
| where ($it | str starts-with " ")
| each {|entry| $entry | split row ' ' | get 0 }
}

View file

@ -0,0 +1,29 @@
# Virtual environment scripts
The scripts in this directory activate virtual environments for Conda environments.
## Usage
The activation and deactivation commands are exported from the `conda` module.
```
> use conda.nu
> conda activate foo
[foo] > conda deactivate
>
```
The `activate` command also includes custom completions for the environment names.
To disable the prompt changes (e.g., to let [Starship](https://starship.rs) include its own), set the environment variable `CONDA_NO_PROMPT`.
To set it globally, add the line `let-env CONDA_NO_PROMPT = true` to `$nu.config-path`.
## Limitations
_(old text, not tested)_
- The "root_prefix" might not actually correspond to the correct path to the Conda envs. You can fix
this for your setup by changing how the root prefix is found in the `conda-env` command.
- Nested envs are not well supported. If you activate a Conda env while another one is
activated, new elements will be appended to the PATH, but the other environment
variables will be overwritten. There's no way to then restore the PATH to the state
it was in before activating the *first* env (at least not with this script directly).

View file

@ -0,0 +1,33 @@
# Auto virtual environment scripts
The scripts in this directory activate virtual environments whenever you cd into a directory with a "trigger" file
## Usage
1. set `$env.AUTO_VENV_TRIGGER` to the name of a script file
1. import `auto-venv` into your environment
1. cd into a folder that contains your trigger file
ex.
```nu
# config.nu
export-env {
let-env AUTO_VENV_TRIGGER = '__auto-venv.nu'
source-env ~/path/to/nu_scripts/virtual_environments/auto-venv/auto-venv.nu
}
use ~/path/to/nu_scripts/virtual_environments/auto-venv/auto-venv.nu *
```
When `auto-venv` detects that a file of that name exists in the folder you cd'ed into (or one of it's parents), it will automatically enable an overlay (as defined by the trigger file).
An example overlay for python venv is in `./venvs`
NOTE: the trigger file *must* export a custom command called `auto-venv-on-enter` that takes in the `$env` (this is the environment *before* the overlay was enabled).
## Limitations
- Due to limitations with overlay naming, you cannot nest auto-venv triggers, even for separate languages / toolchains

View file

@ -0,0 +1,86 @@
# must source-env and 'use *' this file
# must be in 'global' scope when `condition` is called
export use venv_helpers.nu
export use path_extensions.nu
export-env {
# this needs to be set somewhere
# let-env AUTO_VENV_TRIGGER = '__auto-venv.nu'
let hooks = default-hooks
let hooks = ($hooks | append (build-hooks))
let-env config = ($env.config | upsert hooks.env_change.PWD $hooks )
}
def default-hooks [] {
(if ($env.config.hooks.env_change.PWD != $nothing) {
$env.config.hooks.env_change.PWD
}
else {
[]
})
}
def build-hooks [] {
let trigger = $env.AUTO_VENV_TRIGGER
let on_enter = '
let _env = $env
let pwd = $_env.PWD
let trigger = path_extensions path find-sub . __trigger__ --type "file"
cd ($trigger | path dirname)
overlay use __trigger__ as __auto_venv
cd ($pwd)
auto-venv-on-enter $_env
hide _env
hide pwd
hide trigger
'
let on_exit = '
overlay hide __auto_venv --keep-env [PWD]
'
let on_enter = ($on_enter | str replace -a '__trigger__' $trigger)
let hook = [
# activate on entry
{
condition: {|before, after| venv_helpers has-entered-venv $after }
code: $'
($on_enter)
'
}
# re-activate on swap
{
condition: {|before, after| venv_helpers has-swapped-venv $after }
code: $'
($on_exit)
($on_enter)
'
}
# deactivate on exit
{
condition: { |before, after| venv_helpers has-exited-venv $after }
code: $'
($on_exit)
'
}
]
$hook
}

View file

@ -0,0 +1,59 @@
# Returns a list of full paths starting from root, to the path given
#
#
# walk '/home/user/projects';
# > [
# > '/home/',
# > '/home/user/',
# > '/home/user/projects/',
# > ]
#
export def "path walk" [
path: any
] {
let list = ($path | path expand | path split);
$list | each -n { |$part| (
$list | first ($part.index + 1) | path join;
)}
}
# Returns true if 'subfolder' is found along the path of 'folder', with a given type
export def "path check-sub" [
folder: any,
subfolder: string,
--type: string
] {
(ls -a $folder
| where (
($type == $nothing or $it.type == $type)
and ($it.name | path basename) == $subfolder
)
| length
) > 0;
}
# Walks the path along 'folder', and returns the path of the first subfolder found, or nothing if the subfolder was not found.
#
# path find-sub '/home/user/projects/code' '.venv';
# > /home/user/projects/.venv
export def "path find-sub" [
folder: any,
subfolder: string,
--type: string
] {
let paths = path walk $folder;
let paths = ( $paths
| where (
path check-sub $it $subfolder --type $type
)
);
if ($paths != $nothing) and ($paths | length) > 0 {
[ ($paths | first), $subfolder ] | path join
}
}

View file

@ -0,0 +1,73 @@
use path_extensions.nu *
def has-sub [
folder: string,
subfolder: string
] {
not (path find-sub $folder $subfolder | is-empty);
}
def get-env [
name: string
default: any
] {
(if ($name in $env) {
$env | get $name
}
else {
$default
})
}
export def venv-is-active [] {
'__auto_venv' in (overlay list)
}
export def has-entered-venv [
after: path,
] {
let target = path find-sub $after $env.AUTO_VENV_TRIGGER
(if ($target | is-empty) {
false
}
else {
# if venv is already active, handle it in "venv swap" hook
not (venv-is-active)
})
}
export def has-swapped-venv [
after: path,
] {
(if not (venv-is-active) {
false
}
else {
let target = path find-sub $after $env.AUTO_VENV_TRIGGER
(if ($target | is-empty) {
false
}
else {
$env.VIRTUAL_ENV != $target
})
})
}
export def has-exited-venv [
after: path,
] {
(if not (venv-is-active) {
false
}
else {
not (has-sub $after $env.AUTO_VENV_TRIGGER)
})
}

View file

@ -0,0 +1,99 @@
###
# An example auto-venv module.
# Copy this into `~/your/project/__auto-venv.nu` (or whatever you named your trigger file)
# adapted from https://github.com/pypa/virtualenv/blob/46f68d67c79f2280554f47f3c21265b3a1e899a4/src/virtualenv/activation/nushell/activate.nu
export def-env auto-venv-on-enter [
_env: record,
] {
def is-string [x] {
($x | describe) == 'string'
}
def has-env [name: string] {
$name in ($_env)
}
let virtual_env = (path_extensions path find-sub ($_env.PWD | into string) '.venv')
let bin = ([$virtual_env, "bin"] | path join)
let virtual_prompt = ""
let is_windows = ((sys).host.name | str downcase) == 'windows'
let path_name = if $is_windows {
if (has-env 'Path') {
'Path'
} else {
'PATH'
}
} else {
'PATH'
}
let path_sep = (if $nu.os-info.name == "windows" {
'\'
}
else {
'/'
})
let old_path = (
if $is_windows {
if (has-env 'Path') {
$_env.Path
} else {
$_env.PATH
}
} else {
$_env.PATH
} | if (is-string $in) {
# if Path/PATH is a string, make it a list
$in | split row $path_sep | path expand
} else {
$in
}
)
let venv_path = ([$virtual_env $bin] | path join)
let new_path = ($old_path | prepend $venv_path | str join $path_sep)
# Creating the new prompt for the session
let virtual_prompt = if ($virtual_prompt == '') {
$'(char lparen)($virtual_env | path basename)(char rparen) '
} else {
'(' + $virtual_prompt + ') '
}
let old_prompt_command = if (has-env 'PROMPT_COMMAND') {
$_env.PROMPT_COMMAND
} else {
''
}
# If there is no default prompt, then only the env is printed in the prompt
let new_prompt = if (has-env 'PROMPT_COMMAND') {
if ($old_prompt_command | describe) == 'block' {
{ $'($virtual_prompt)(do $old_prompt_command)' }
} else {
{ $'($virtual_prompt)($old_prompt_command)' }
}
} else {
{ $'($virtual_prompt)' }
}
# Environment variables that will be batched loaded to the virtual env
let new_env = {
$path_name : $new_path
VIRTUAL_ENV : $virtual_env
PROMPT_COMMAND : $new_prompt
VIRTUAL_PROMPT : $virtual_prompt
}
# Activate the environment variables
load-env $new_env
}
export alias pydoc = python -m pydoc
export alias pip = python -m pip

View file

@ -0,0 +1,158 @@
# Activate conda environment
export def-env activate [
env_name?: string@'nu-complete conda envs' # name of the environment
] {
let conda_info = (conda info --envs --json | from json)
let env_name = if $env_name == null {
"base"
} else {
$env_name
}
let env_dir = if $env_name != "base" {
if ($env_name | path exists) and (($env_name | path expand) in $conda_info.envs ) {
($env_name | path expand)
} else {
((check-if-env-exists $env_name $conda_info) | into string)
}
} else {
$conda_info.root_prefix
}
let old_path = (system-path | str join (char esep))
let new_path = if (windows?) {
conda-create-path-windows $env_dir
} else {
conda-create-path-unix $env_dir
}
let virtual_prompt = $'[($env_name)] '
let new_env = ({
CONDA_DEFAULT_ENV: $env_name
CONDA_PREFIX: $env_dir
CONDA_PROMPT_MODIFIER: $virtual_prompt
CONDA_SHLVL: "1"
CONDA_OLD_PATH: $old_path
} | merge $new_path)
let new_env = if not (has-env CONDA_NO_PROMPT) {
let old_prompt_command = if (has-env CONDA_OLD_PROMPT_COMMAND) {
$env.CONDA_OLD_PROMPT_COMMAND
} else {
if (has-env 'PROMPT_COMMAND') {
$env.PROMPT_COMMAND
} else {
''
}
}
let new_prompt = if (has-env 'PROMPT_COMMAND') {
if 'closure' in ($old_prompt_command | describe) {
{|| $'($virtual_prompt)(do $old_prompt_command)' }
} else {
{|| $'($virtual_prompt)($old_prompt_command)' }
}
} else {
{|| $'($virtual_prompt)' }
}
$new_env | merge {
CONDA_OLD_PROMPT_COMMAND: $old_prompt_command
PROMPT_COMMAND: $new_prompt
}
} else {
$new_env | merge { CONDA_OLD_PROMPT_COMMAND: $nothing }
}
load-env $new_env
}
# Deactivate currently active conda environment
export def-env deactivate [] {
let path_name = if "PATH" in $env { "PATH" } else { "Path" }
let-env $path_name = $env.CONDA_OLD_PATH
hide-env CONDA_PROMPT_MODIFIER
hide-env CONDA_PREFIX
hide-env CONDA_SHLVL
hide-env CONDA_DEFAULT_ENV
hide-env CONDA_OLD_PATH
let-env PROMPT_COMMAND = if $env.CONDA_OLD_PROMPT_COMMAND == $nothing {
$env.PROMPT_COMMAND
} else {
$env.CONDA_OLD_PROMPT_COMMAND
}
hide-env CONDA_OLD_PROMPT_COMMAND
}
def check-if-env-exists [ env_name: string, conda_info: record ] {
let env_dirs = (
$conda_info.envs_dirs |
each { || path join $env_name }
)
let en = ($env_dirs | each {|en| $conda_info.envs | where $it == $en } | where ($it | length) == 1 | flatten)
if ($en | length) > 1 {
error make --unspanned {msg: $"You have enviroments in multiple locations: ($en)"}
}
if ($en | length) == 0 {
error make --unspanned {msg: $"Could not find given environment: ($env_name)"}
}
$en.0
}
def 'nu-complete conda envs' [] {
conda info --envs
| lines
| where not ($it | str starts-with '#')
| where not ($it | is-empty)
| each {|entry| $entry | split row ' ' | get 0 }
}
def conda-create-path-windows [env_dir: path] {
# Conda on Windows needs a few additional Path elements
let env_path = [
$env_dir
([$env_dir "Scripts"] | path join)
([$env_dir "Library" "mingw-w64"] | path join)
([$env_dir "Library" "bin"] | path join)
([$env_dir "Library" "usr" "bin"] | path join)
]
let new_path = ([$env_path (system-path)]
| flatten
| str join (char esep))
{ Path: $new_path }
}
def conda-create-path-unix [env_dir: path] {
let env_path = [
([$env_dir "bin"] | path join)
]
let new_path = ([$env_path $env.PATH]
| flatten
| str join (char esep))
{ PATH: $new_path }
}
def windows? [] {
((sys).host.name | str downcase) == "windows"
}
def system-path [] {
if "PATH" in $env { $env.PATH } else { $env.Path }
}
def has-env [name: string] {
$name in $env
}

View file

@ -0,0 +1,61 @@
# Conda Module for Nushell
A simple module for activating and deactivating Conda environments.
## Prerequisites
- [nushell](https://github.com/nushell/nushell) >= 0.73.0
## Installation
Put `nu_conda.nu` into the module folder of your nushell configuration workspace.
## Usage
```nu
use nu_conda.nu # activate module
nu_conda activate py36 # activate a Conda environment, e.g. py36
nu_conda deactivate # deactivate the activated Conda environment
```
## How It Works
This module re-implements the activation and deactivation functionalities of
the [conda.nu](https://github.com/Neur1n/nu_scripts/blob/main/virtual_environments/conda.nu)
module while providing a better performance, but not fully replacing it.
This module adds paths of a target Conda environment to `PATH`/`Path` while
activating the environment, and recover the original `PATH`/`Path` while
deactivating an environment. Several environment variables are exported:
- `CONDA_BASE_PATH`: The original `PATH`/`Path` before any activation/deactivation.
- `CONDA_ROOT`: Root directory of Conda installation.
- `CONDA_ENVS`: Available Conda environments for activation.
- `CONDA_CURR`: Current activated Conda environments.
## FAQ
**Q**: How better is the performance?\
**A**: Activating a Conda environment costs ~20ms while conda.nu costs ~1500ms on
a PC with Windows 10 Enterprise OS and Intel i7-8700 3.20GHz CPU.
**Q**: How to show the current Conda environment in the prompt?\
**A**: This module does not automatically change the prompt when a Conda
environment is activated, but an environment variable `$env.CONDA_CURR` is set
to the name of the current Conda environment which can be used to customize the
prompt.
**Q**: Does it support Mamba/Micromamba?\
**A**: As [Mamba's documentation](https://mamba.readthedocs.io/en/latest/) said,
`mamba` is drop-in replacement for `conda`, and `micromamba` seems to be
another thing. This module only uses results of `conda/mamba info --envs --json`.
Therefore, I would say Mamba is (partially?) supported but I'm not sure about
Micromamba.
**Q**: How does it choose between Conda and Mamba?\
**A**: This module prefers calling `mamba` than `conda`, but it should be very
easy to change the preference by modifying the source code.
**Q**: Completions?\
**A**: PRs are welcomed.

View file

@ -0,0 +1,80 @@
export-env {
let-env CONDA_BASE_PATH = (if ((sys).host.name == "Windows") {$env.Path} else {$env.PATH})
let info = (
if not (which mamba | is-empty) {
(mamba info --envs --json | from json)
} else if not (which conda | is-empty) {
(conda info --envs --json | from json)
} else {
('{"root_prefix": "", "envs": ""}' | from json)
})
let-env CONDA_ROOT = $info.root_prefix
let-env CONDA_ENVS = ($info.envs | reduce -f {} {|it, acc|
if $it == $info.root_prefix {
$acc | upsert "base" $it
} else {
$acc | upsert ($it | path basename) $it
}})
let-env CONDA_CURR = null
}
export def-env activate [name: string] {
if ($env.CONDA_ROOT | is-empty) {
echo "Neither Conda nor Mamba is valid."
return
}
if not $name in $env.CONDA_ENVS {
echo $"Environment ($name) is invalid. Available:"
echo $env.CONDA_ENVS
return
}
let new_path = (
if ((sys).host.name == "Windows") {
update-path-windows ($env.CONDA_ENVS | get $name)
} else {
update-path-linux ($env.CONDA_ENVS | get $name)
})
load-env ({CONDA_CURR: $name} | merge $new_path)
}
export def-env deactivate [] {
if ($env.CONDA_ROOT | is-empty) {
echo "Neither Conda nor Mamba is valid."
return
}
let-env CONDA_CURR = null
load-env {Path: $env.CONDA_BASE_PATH, PATH: $env.CONDA_BASE_PATH}
}
def update-path-linux [env_path: path] {
let env_path = [
$env_path,
([$env_path, "bin"] | path join)
]
return {
Path: ($env.PATH | prepend $env_path),
PATH: ($env.PATH | prepend $env_path)
}
}
def update-path-windows [env_path: path] {
let env_path = [
$env_path,
([$env_path, "Scripts"] | path join),
]
return {
Path: ($env.Path | prepend $env_path),
PATH: ($env.Path | prepend $env_path)
}
}

View file

@ -0,0 +1,43 @@
# MSVS Module for Nushell
A module for Using Microsoft Visual Studio (MSVS) command line tools from Nushell.
## Prerequisites
- [nushell](https://github.com/nushell/nushell) >= 0.73.0
- [vswhere](https://github.com/microsoft/vswhere) standalone or comes with VS
## Installation
Put `nu_msvs.nu` into the module folder of your nushell configuration workspace.
## Usage
```nu
use nu_msvs.nu
nu_msvs activate # Use 'nu_msvs activate --help' to see all available options
nu_msvs deactivate
```
## How It Works
MSVS provides scripts (such as `vsdevcmd.bat` and `vsvarsall.bat`) for
developers to use its command line tools (such as `cl.exe`) in Command Prompt
and Powershell, but these scripts are not available for nushell. One method to
use MSVS command line tools in nushell was described in nushell/nushell#5803,
which requires launching nushell within an Command Prompt/Powershell instance.
However, running `vsdevcmd.bat` or launching Powershell is quite slow.
Therefore, `nu_msvs.nu` is introduced and works similar to a virtual
environment.
When activating the MSVS environment (take MSVC tools for example):
1. Necessary path are added to `PATH`/`Path` to allow command line tools (e.g.
`cl.exe`) to be run in nushell.
2. An environment variable `INCLUDE` is defined and consists of Win32 header
directories, which will be used by `cl.exe`.
3. An environment variable `LIB` is defined and consists of Win32 library
directories, which will be used by `link.exe`.
## Supported Tools
- [x] MSVC
- [ ] ...

View file

@ -0,0 +1,140 @@
export-env {
let-env MSVS_BASE_PATH = $env.Path
let info = (
if not (which vswhere | is-empty) {
(vswhere -format json | from json)
} else {
('{"installationPath": [""]}' | from json)
}
)
let-env MSVS_ROOT = ($info.installationPath.0 | str replace -a '\\' '/')
let-env MSVS_MSVC_ROOT = (
if not ($"($env.MSVS_ROOT)/VC/Tools/MSVC/" | path exists) {
""
} else if (ls $"($env.MSVS_ROOT)/VC/Tools/MSVC/*" | is-empty) {
""
} else {
((ls $"($env.MSVS_ROOT)/VC/Tools/MSVC/*").name.0 | str replace -a '\\' '/')
})
let-env MSVS_MSDK_ROOT = (REG QUERY 'HKLM\SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\Windows\v10.0' /v "InstallationFolder" | str replace '(.|\n)+REG_SZ\s+(.+)' "$2")
let-env MSVS_MSDK_ROOT = ($env.MSVS_MSDK_ROOT | str replace -a '\\' '/')
let-env MSVS_MSDK_VER = (REG QUERY 'HKLM\SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\Windows\v10.0' /v "ProductVersion" | str replace '(.|\n)+REG_SZ\s+(.+)' "$2")
let-env MSVS_MSDK_VER = $"($env.MSVS_MSDK_VER).0"
let-env MSVS_INCLUDE_PATH = ([
$"($env.MSVS_ROOT)/Include/($env.MSVS_MSDK_VER)/cppwinrt/winrt",
$"($env.MSVS_MSVC_ROOT)/include",
$"($env.MSVS_MSDK_ROOT)Include/($env.MSVS_MSDK_VER)/cppwinrt/winrt",
$"($env.MSVS_MSDK_ROOT)Include/($env.MSVS_MSDK_VER)/shared",
$"($env.MSVS_MSDK_ROOT)Include/($env.MSVS_MSDK_VER)/ucrt",
$"($env.MSVS_MSDK_ROOT)Include/($env.MSVS_MSDK_VER)/um",
$"($env.MSVS_MSDK_ROOT)Include/($env.MSVS_MSDK_VER)/winrt"
] | str join ";")
}
export def-env activate [
--host (-h): string = "x64", # Host architecture, must be x64 or x86 (case insensitive)
--target (-t): string = "x64", # Target architecture, must be x64 or x86 (case insensitive)
--sdk (-s): string = "latest" # Version of Windows SDK, must be "latest" or a valid version string
] {
if (($env.MSVS_ROOT | is-empty) or ($env.MSVS_MSVC_ROOT | is-empty)) {
echo "Either Microsoft Visual Studio or MSVC is valid."
return
}
let fh = ($host | str downcase)
let ft = ($target | str downcase)
let fs = (
if ($sdk != "latest") {
$sdk
} else {
$env.MSVS_MSDK_VER
})
if (($fh != "x64") and ($fh != "x86")) {
echo $"Wrong host architecture specified: ($fh)."
help n_msvc activate
return
}
if (($ft != "x64") and ($ft != "x86")) {
echo $"Wrong target architecture specified: ($ft)."
help n_msvc activate
return
}
if not ($"($env.MSVS_MSDK_ROOT)bin/($fs)" | path exists) {
echo $"Invalid Windows SDK version specified: ($fs)."
return
}
let env_path = [
$"($env.MSVS_ROOT)/../Shared/Common/VSPerfCollectionTools/vs2019",
$"($env.MSVS_ROOT)/Common7/IDE",
$"($env.MSVS_ROOT)/Common7/IDE/CommonExtensions/Microsoft/TestWindow",
$"($env.MSVS_ROOT)/Common7/IDE/CommonExtensions/Microsoft/TeamFoundation/Team Explorer",
$"($env.MSVS_ROOT)/Common7/IDE/Extensions/Microsoft/IntelliCode/CLI",
$"($env.MSVS_ROOT)/Common7/IDE/Tools",
$"($env.MSVS_ROOT)/Common7/IDE/VC/VCPackages",
$"($env.MSVS_ROOT)/Common7/Tools/devinit",
$"($env.MSVS_ROOT)/MSBuild/Current/bin",
$"($env.MSVS_ROOT)/MSBuild/Current/bin/Roslyn",
$"($env.MSVS_ROOT)/Team Tools/Performance Tools",
$"($env.MSVS_MSVC_ROOT)/bin/Host($fh)/($ft)",
$"($env.MSVS_MSDK_ROOT)bin/($ft)",
$"($env.MSVS_MSDK_ROOT)bin/($fs)/($ft)"
]
let env_path = (
if ($ft == "x64") {
($env_path | prepend $"($env.MSVS_ROOT)/../Shared/Common/VSPerfCollectionTools/vs2019/x64")
} else {
$env_path
})
let env_path = (
if ($ft == "x64") {
($env_path | prepend $"($env.MSVS_ROOT)/Team Tools/Performance Tools/x64")
} else {
$env_path
})
let env_path = (
if ($ft != $fh) {
($env_path | prepend $"($env.MSVS_MSVC_ROOT)/bin/Host($fh)/($fh)")
} else {
$env_path
})
let env_path = ($env.MSVS_BASE_PATH | prepend $env_path)
let lib_path = ([
$"($env.MSVS_MSDK_ROOT)Lib/($env.MSVS_MSDK_VER)/ucrt/($ft)",
$"($env.MSVS_MSDK_ROOT)Lib/($env.MSVS_MSDK_VER)/um/($ft)",
$"($env.MSVS_MSVC_ROOT)/lib/($ft)",
] | str join ";")
load-env {
Path: $env_path,
PATH: $env_path,
INCLUDE: $env.MSVS_INCLUDE_PATH,
LIB: $lib_path
}
}
export def-env deactivate [] {
if (($env.MSVS_ROOT | is-empty) or ($env.MSVS_MSVC_ROOT | is-empty)) {
echo "Either Microsoft Visual Studio or MSVC is valid."
return
}
load-env {
Path: $env.MSVS_BASE_PATH,
PATH: $env.MSVS_BASE_PATH
}
}

View file

@ -0,0 +1,5 @@
# Weather Scripts
### Definition
These scripts should be used to demonstrate how get your local weather and/or weather forecasts.

View file

@ -0,0 +1,313 @@
###################################################
## Weather Script based on IP Address v1.0
###################################################
def locations [] {
[
[location city_column state_column country_column lat_column lon_column];
["http://ip-api.com/json/" city region countryCode lat lon]
["https://ipapi.co/json/" city region_code country_code latitude longitude]
# ["https://freegeoip.app/json/" city region_code country_code latitude longitude] # doesn't appear to be free any longer
["https://ipwhois.app/json/" city region country_code latitude longitude]
]
}
def get_my_location [index: int] {
let loc_json = (http get (locations | select $index).0.location)
let city_column = (locations | select $index).0.city_column
let state_column = (locations | select $index).0.state_column
let country_column = (locations | select $index).0.country_column
let lat_column = (locations | select $index).0.lat_column
let lon_column = (locations | select $index).0.lon_column
# echo $loc_json
if ($city_column | str length) > 1 {
if ($state_column | str length) > 1 {
if ($country_column | str length) > 1 {
let lookup_state = ($loc_json | get ($state_column))
if ($lookup_state | str length) > 2 {
let state = (state_abbrev_lookup $lookup_state)
$"($loc_json | get ($city_column)),($state),($loc_json | get ($country_column))"
} else {
$"($loc_json | get ($city_column)),($loc_json | get ($state_column)),($loc_json | get ($country_column))"
}
} else {
$"($loc_json | get ($city_column)),($loc_json | get ($state_column))"
}
} else {
$"($loc_json | get ($city_column))"
}
} else {
"No City Found"
}
}
def get_location_by_ip [locIdx: int, token: string] {
let URL_QUERY_LOCATION = "https://api.openweathermap.org/geo/1.0/direct"
let location = (get_my_location $locIdx)
let url = $"($URL_QUERY_LOCATION)?q=($location)&limit=5&appid=($token)"
http get $url
}
def show-error [msg label err] {
let span = (metadata $err).span;
error make {msg: $msg, label: {text: $label, start: $span.start, end: $span.end } }
}
def get_weather_by_ip [locIdx: int, units: string, token: string] {
# units
# f = imperial aka Fahrenheit
# c = metric aka Celcius
let URL_WEATHER = "https://api.openweathermap.org/data/2.5/weather"
let URL_FORECAST = "http://api.openweathermap.org/data/2.5/forecast/daily"
let coords = (get_location_by_ip $locIdx $token)
if ($coords | length) > 1 {
show-error "Error getting location" "There were more than one locations found" $coords
}
if $units == "f" {
let units = "imperial"
let url = $"($URL_WEATHER)?lat=($coords.lat.0)&lon=($coords.lon.0)&units=($units)&appid=($token)"
let url_forecast = $"($URL_FORECAST)?lat=($coords.lat.0)&lon=($coords.lon.0)&units=($units)&appid=($token)"
let weather = (http get $url)
let forecast_data = (http get $url_forecast)
let forecast = ($forecast_data.list | each {|day|
{
id: ($day.weather.0.id)
dt: ($day.dt | into string | into datetime -z local | date format '%a, %b %e') #'%Y-%m-%d')
high: ($day.temp.max)
low: ($day.temp.min)
}
})
let day1 = $"($forecast | get 0.dt) (get_emoji_by_id ($forecast | get 0.id | into string)) high: ($forecast | get 0.high | into string -d 1) low: ($forecast | get 0.low | into string -d 1)"
let day2 = $"($forecast | get 1.dt) (get_emoji_by_id ($forecast | get 1.id | into string)) high: ($forecast | get 1.high | into string -d 1) low: ($forecast | get 1.low | into string -d 1)"
let day3 = $"($forecast | get 2.dt) (get_emoji_by_id ($forecast | get 2.id | into string)) high: ($forecast | get 2.high | into string -d 1) low: ($forecast | get 2.low | into string -d 1)"
let day4 = $"($forecast | get 3.dt) (get_emoji_by_id ($forecast | get 3.id | into string)) high: ($forecast | get 3.high | into string -d 1) low: ($forecast | get 3.low | into string -d 1)"
let day5 = $"($forecast | get 4.dt) (get_emoji_by_id ($forecast | get 4.id | into string)) high: ($forecast | get 4.high | into string -d 1) low: ($forecast | get 4.low | into string -d 1)"
let day6 = $"($forecast | get 5.dt) (get_emoji_by_id ($forecast | get 5.id | into string)) high: ($forecast | get 5.high | into string -d 1) low: ($forecast | get 5.low | into string -d 1)"
let day7 = $"($forecast | get 6.dt) (get_emoji_by_id ($forecast | get 6.id | into string)) high: ($forecast | get 6.high | into string -d 1) low: ($forecast | get 6.low | into string -d 1)"
{
'Weather Location': $"($weather.name), ($weather.sys.country)"
Longitude: $weather.coord.lon
Latitude: $weather.coord.lat
Temperature: $"($weather.main.temp | into string -d 1) °F"
'Feels Like': $"($weather.main.feels_like | into string -d 1) °F"
Humidity: $weather.main.humidity
Pressure: $weather.main.pressure
Emoji: (get_icon_from_table $weather.weather.main.0)
'Forecast Day 1': $day1
'Forecast Day 2': $day2
'Forecast Day 3': $day3
'Forecast Day 4': $day4
'Forecast Day 5': $day5
'Forecast Day 6': $day6
'Forecast Day 7': $day7
}
} else {
let units = "metric"
let url = $"($URL_WEATHER)?lat=($coords.lat.0)&lon=($coords.lon.0)&units=($units)&appid=($token)"
let url_forecast = $"($URL_FORECAST)?lat=($coords.lat.0)&lon=($coords.lon.0)&units=($units)&appid=($token)"
let weather = (http get $url)
let forecast_data = (http get $url_forecast)
let forecast = ($forecast_data.list | each {|day|
{
id: ($day.weather.0.id)
dt: ($day.dt | into string | into datetime -z local | date format '%a, %b %e') #'%Y-%m-%d')
high: ($day.temp.max)
low: ($day.temp.min)
}
})
let day1 = $"($forecast | get 0.dt) (get_emoji_by_id ($forecast | get 0.id | into string)) high: ($forecast | get 0.high | into string -d 1) low: ($forecast | get 0.low | into string -d 1)"
let day2 = $"($forecast | get 1.dt) (get_emoji_by_id ($forecast | get 1.id | into string)) high: ($forecast | get 1.high | into string -d 1) low: ($forecast | get 1.low | into string -d 1)"
let day3 = $"($forecast | get 2.dt) (get_emoji_by_id ($forecast | get 2.id | into string)) high: ($forecast | get 2.high | into string -d 1) low: ($forecast | get 2.low | into string -d 1)"
let day4 = $"($forecast | get 3.dt) (get_emoji_by_id ($forecast | get 3.id | into string)) high: ($forecast | get 3.high | into string -d 1) low: ($forecast | get 3.low | into string -d 1)"
let day5 = $"($forecast | get 4.dt) (get_emoji_by_id ($forecast | get 4.id | into string)) high: ($forecast | get 4.high | into string -d 1) low: ($forecast | get 4.low | into string -d 1)"
let day6 = $"($forecast | get 5.dt) (get_emoji_by_id ($forecast | get 5.id | into string)) high: ($forecast | get 5.high | into string -d 1) low: ($forecast | get 5.low | into string -d 1)"
let day7 = $"($forecast | get 6.dt) (get_emoji_by_id ($forecast | get 6.id | into string)) high: ($forecast | get 6.high | into string -d 1) low: ($forecast | get 6.low | into string -d 1)"
{
'Weather Location': $"($weather.name), ($weather.sys.country)"
Longitude: $weather.coord.lon
Latitude: $weather.coord.lat
Temperature: $"($weather.main.temp | into string -d 1) °C"
'Feels Like': $"($weather.main.feels_like | into string -d 1) °C"
Humidity: $weather.main.humidity
Pressure: $weather.main.pressure
Emoji: (get_icon_from_table $weather.weather.main.0)
'Forecast Day 1': $day1
'Forecast Day 2': $day2
'Forecast Day 3': $day3
'Forecast Day 4': $day4
'Forecast Day 5': $day5
'Forecast Day 6': $day6
'Forecast Day 7': $day7
}
}
}
def weather_emoji_table [] {
{
Clear: (char sun)
Clouds: (char clouds)
Rain: (char rain)
Fog: (char fog)
Mist: (char mist)
Haze: (char haze)
Snow: (char snow)
Thunderstorm: (char thunderstorm)
}
}
def get_icon_from_table [w] {
weather_emoji_table | get $w
}
# Get the local weather by ip address
export def get_weather [
--locIdx(-l): int # The location id 0-2
--units(-u): string # The units "f" or "c"
] {
let token = "85a4e3c55b73909f42c6a23ec35b7147"
let is_loc_empty = ($locIdx == $nothing)
let is_units_empty = ($units == $nothing)
let no_loc_no_unit = ($is_loc_empty == true and $is_units_empty == true)
let no_loc_with_unit = ($is_loc_empty == true and $is_units_empty == false)
let with_loc_no_unit = ($is_loc_empty == false and $is_units_empty == true)
let with_loc_with_unit = ($is_loc_empty == false and $is_units_empty == false)
# This is a cautionary tale, the commented out code below is returning
# and autoview is viewing the data, so no structured data is being returned.
# The ramification to this is you can't do get_weather | select Temperature Emoji
# like you should be able to. The following uncommented section below fixes it.
# Hopefully we'll be able to fix this somehow because it's easy to fall into
# this hole without knowing.
# if $no_loc_no_unit {
# echo "no_loc_no_unit"
# (get_weather_by_ip 0 "f")
# } { }
# if $no_loc_with_unit {
# echo "no_loc_with_unit"
# (get_weather_by_ip 0 $units)
# } { }
# if $with_loc_no_unit {
# echo "with_loc_no_unit"
# (get_weather_by_ip $locIdx "f")
# } { }
# if $with_loc_with_unit {
# echo "with_loc_with_unit"
# (get_weather_by_ip $locIdx $units)
# } { }
if $no_loc_no_unit {
(get_weather_by_ip 0 "f" $token)
} else if $no_loc_with_unit {
(get_weather_by_ip 0 $units $token)
} else if $with_loc_no_unit {
(get_weather_by_ip $locIdx "f" $token)
} else if $with_loc_with_unit {
(get_weather_by_ip $locIdx $units $token)
}
}
def state_abbrev_lookup [state_name: string] {
# Weather Location 3 does not return state name abbreviations
# so we have to convert a state full name to a state abbreviation
let lookup_table = {
Alabama: AL
Alaska: AK
Arizona: AZ
Arkansas: AR
California: CA
Colorado: CO
Connecticut: CT
Delaware: DE
Florida: FL
Georgia: GA
Hawaii: HI
Idaho: ID
Illinois: IL
Indiana: IN
Iowa: IA
Kansas: KS
Kentucky: KY
Louisiana: LA
Maine: ME
Maryland: MD
Massachusetts: MA
Michigan: MI
Minnesota: MN
Mississippi: MS
Missouri: MO
Montana: MT
Nebraska: NE
Nevada: NV
'New Hampshire': NH
'New Jersey': NJ
'New Mexico': NM
'New York': NY
'North Carolina': NC
'North Dakota': ND
Ohio: OH
Oklahoma: OK
Oregon: OR
Pennsylvania: PA
'Rhode Island': RI
'South Carolina': SC
'South Dakota': SD
Tennessee: TN
Texas: TX
Utah: UT
Vermont: VT
Virginia: VA
Washington: WA
'West Virginia': WV
Wisconsin: WI
Wyoming: WY
}
$lookup_table | get $state_name
}
def get_emoji_by_id [id] {
let emoji_dict = ({
"200": "⚡", "201": "⚡", "202": "⚡", "210": "⚡", "211": "⚡", "212": "⚡", "221": "⚡", "230": "⚡",
"231": "⚡", "232": "⚡",
"300": "☔", "301": "☔", "302": "☔", "310": "☔", "311": "☔",
"312": "☔", "313": "☔", "314": "☔", "321": "☔",
"500": "☔", "501": "☔", "502": "☔", "503": "☔", "504": "☔",
"511": "☔", "520": "☔", "521": "☔", "522": "☔", "531": "☔",
"600": "❄️", "601": "❄️", "602": "❄️", "611": "❄️", "612": "❄️",
"613": "❄️", "615": "❄️", "616": "❄️", "620": "❄️", "621": "❄️",
"622": "❄️",
"701": "🌫️", "711": "🌫️", "721": "🌫️", "731": "🌫️", "741": "🌫️", "751": "🌫️", "761": "🌫️", "762": "🌫️",
"771": "🌫️",
"781": "🌀",
"800": "☀️",
"801": "🌤️", "802": "🌤️", "803": "☁️", "804": "☁️",
})
($emoji_dict | get $id)
}
# To run this call
# > get_weather
# it will default to location 0 and Fahrenheit degrees
# > get_weather -l 1
# This changes to location 1. Locations are listed in the locations custom command above
# > get_weather -l 2 -u c
# This uses location 2 and Celcius degrees. f = Fahrenheit, c = Celcius
# Since I live in the USA I have not tested outside the country.
# We'll take PRs for things that are broke or augmentations.
# HOW TO USE
# put this in your config.nu file
# use /path/to/get-weather.nu get_weather
#
# then from the nushell commmand prompt type
# get_weather

368
modules/weather/nu.nu Normal file
View file

@ -0,0 +1,368 @@
def signatures [] {
let sig = ([
{ name: "nu-nu"
usage: "signature test for nu-nu"
extra_usage: ""
required_positional: [
{
name: "a"
desc: "required int value"
shape: "Int"
var_id: None
},
{
name: "b"
desc: "required string value"
shape: "String"
var_id: None
}
]
optional_positional: [
{
name: "opt"
desc: "optional number"
shape: "Int"
var_id: None
}
]
rest_positional: {
name: "rest"
desc: "rest value string"
shape: "String"
var_id: None
}
named: [
{
long: "help"
short: "h"
arg: None
required: false
desc: "display this help message"
var_id: None
}
{
long: "flag"
short: "f"
arg: None
required: false
desc: "a flag for the signature"
var_id: None
}
{
long: "named"
short: "n"
arg: "String"
required: false
desc: "named string"
var_id: None
}
]
is_filter: false
creates_scope: false
category: "Experimental"
}
])
let jsonr = ($sig | to json)
$jsonr
}
def process_call [plugin_call] {
let ret = ({
Value: {
List: {
vals: [
{
Record: {
cols: ["one", "two", "three"],
vals: [
{
Int: {
val: 0,
span: {start: 0, end: 1}
}
}
{
Int: {
val: 0,
span: {start: 0, end: 1}
}
}
{
Int: {
val: 0,
span: {start: 0, end: 1}
}
}
],
span: {start: 0, end: 1}
}
}
{
Record: {
cols: ["one", "two", "three"],
vals: [
{
Int: {
val: 0,
span: {start: 0, end: 1}
}
}
{
Int: {
val: 1,
span: { start: 0, end: 1 }
}
}
{
Int: {
val: 2,
span: { start: 0, end: 1 }
}
}
],
span: { start: 0, end: 1 }
}
}
{
Record: {
cols: ["one", "two", "three"],
vals: [
{
Int: {
val: 0,
span: { start: 0, end: 1 }
}
}
{
Int: {
val: 2,
span: { start: 0, end: 1 }
}
}
{
Int: {
val: 4,
span: { start: 0, end: 1 }
}
}
],
span: { start: 0, end: 1 }
}
}
{
Record: {
cols: ["one", "two", "three"],
vals: [
{
Int: {
val: 0,
span: { start: 0, end: 1 }
}
}
{
Int: {
val: 3,
span: { start: 0, end: 1 }
}
}
{
Int: {
val: 6,
span: { start: 0, end: 1 }
}
}
],
span: { start: 0, end: 1 }
}
}
{
Record: {
cols: ["one", "two", "three"],
vals: [
{
Int: {
val: 0,
span: { start: 0, end: 1 }
}
}
{
Int: {
val: 4,
span: { start: 0, end: 1 }
}
}
{
Int: {
val: 8,
span: { start: 0, end: 1 }
}
}
],
span: { start: 0, end: 1 }
}
}
{
Record: {
cols: ["one", "two", "three"],
vals: [
{
Int: {
val: 0,
span: { start: 0, end: 1 }
}
}
{
Int: {
val: 5,
span: { start: 0, end: 1 }
}
}
{
Int: {
val: 10,
span: { start: 0, end: 1 }
}
}
],
span: { start: 0, end: 1 }
}
}
{
Record: {
cols: ["one", "two", "three"],
vals: [
{
Int: {
val: 0,
span: { start: 0, end: 1 }
}
}
{
Int: {
val: 6,
span: { start: 0, end: 1 }
}
}
{
Int: {
val: 12,
span: { start: 0, end: 1 }
}
}
],
span: { start: 0, end: 1 }
}
}
{
Record: {
cols: ["one", "two", "three"],
vals: [
{
Int: {
val: 0,
span: { start: 0, end: 1 }
}
}
{
Int: {
val: 7,
span: { start: 0, end: 1 }
}
}
{
Int: {
val: 14,
span: { start: 0, end: 1 }
}
}
],
span: { start: 0, end: 1 }
}
}
{
Record: {
cols: ["one", "two", "three"],
vals: [
{
Int: {
val: 0,
span: { start: 0, end: 1 }
}
}
{
Int: {
val: 8,
span: { start: 0, end: 1 }
}
}
{
Int: {
val: 16,
span: { start: 0, end: 1 }
}
}
],
span: { start: 0, end: 1 }
}
}
{
Record: {
cols: ["one", "two", "three"],
vals: [
{
Int: {
val: 0,
span: { start: 0, end: 1 }
}
}
{
Int: {
val: 9,
span: { start: 0, end: 1 }
}
}
{
Int: {
val: 18,
span: { start: 0, end: 1 }
}
}
],
span: { start: 0, end: 1 }
}
}
],
span: { start: 0, end: 1 }
}
}
})
let jsonr = ($ret | to json)
$jsonr
}
def plugin [input] {
let plugin_call = $input
if $plugin_call == "Signature" {
signatures
} else if "CallInfo" in $plugin_call {
process_call $plugin_call
} else {
let error = ({
Error: {
label: "ERROR from plugin",
msg: "error message pointing to call head span",
span: {start: 0, end: 1},
}
})
let jsonr = ($error | to json)
$jsonr
}
}
def main [] {
let params = (each {|param| echo $param })
plugin $params
}

View file

@ -0,0 +1,80 @@
# This script will run a command, in this case get_weather, at a prescribed interval
# This is meant to be run from your prompt so that it runs every time you cd or
# run a command. Each time it will check if it's been longer than the interval and
# if so, run the command, otherwise it uses the cached weather information.
# I wrote this so I could have weather in my prompt but not pay the price of hitting
# the web for every prompt. It will need to be tweaked to actually be used in a
# prompt, but for now it just prints the weather in these 3 ways.
# 1. if it's never been run, it runs the weather command and saves the json cache info
# 2. if the interval has not expired yet, it prints the Cached information
# 3. if the interval has expired, it runs the weather command again and caches the info
# this script is depenedent on get-weather
source get-weather.nu
#command to run at interval
def timed_weather_run [
--command(-c): string # The command to run
--interval(-i): duration # The interval duration
] {
# get the type of system we're on
let system_name = ((sys).host | get name)
if $system_name == "Windows" {
# $"The system is Windows(char nl)"
# generate temp file name
let weather_runtime_file = (($env.TMP) | path join weather_runtime_file.json)
# does the temp file already exist, meaning we've written it previously
if ($weather_runtime_file | path exists) {
# $"Weather path exists [($weather_runtime_file)](char nl)"
# open the file and get the last weather data and run time out of it
let last_runtime_data = (open $weather_runtime_file)
# get the last runtime and add my timezone difference
let last_runtime = ($last_runtime_data | get last_run_time | into datetime)
if $last_runtime + $interval > (date now) {
# $"interval not met. last_runtime: [($last_runtime)](char nl)"
let temp = ($last_runtime_data.Temperature)
let emoji = ($last_runtime_data.Emoji)
{
Temperature: ($temp)
Source: "cache"
Emoji: ($emoji)
}
} else {
# save the run time and run the command
# $"interval met, running command: [($command)](char nl)"
# it would be nice to run a dynamic command here but doesn't appear to be possible
# let weather_table = (do { $command })
let weather_table = (if $command == "get_weather" {(get_weather)})
let temp = ($weather_table.Temperature)
let emoji = ($weather_table.Emoji)
{
Temperature: ($temp)
Source: "expired-cache"
Emoji: ($emoji)
}
$weather_table | upsert last_run_time {(date now | date format '%Y-%m-%d %H:%M:%S %z')} | save $weather_runtime_file
}
} else {
# $"Unable to find [($weather_runtime_file)], creating it(char nl)"
let weather_table = (get_weather)
let temp = ($weather_table.Temperature)
let emoji = ($weather_table.Emoji)
{
Temperature: ($temp)
Source: "initial"
Emoji: ($emoji)
}
$weather_table | upsert last_run_time {(date now | date format '%Y-%m-%d %H:%M:%S %z')} | save $weather_runtime_file
}
} else {
echo "Your command did not run because you are not on Windows..."
# ToDo: refactor the info in the Windows section into another def. The only real difference
# is where the temp file will be located. Mac & Linux probably should be in /tmp I guess.
# everything else is linux or mac
}
}
timed_weather_run --command "get_weather" --interval 1min

View file

@ -0,0 +1,44 @@
# This script will run a command, in this case get_weather, at a prescribed interval
# This is meant to be run from your prompt so that it runs every time you cd or
# run a command. Each time it will check if it's been longer than the interval and
# if so, run the command, otherwise it uses the cached weather information.
# I wrote this so I could have weather in my prompt but not pay the price of hitting
# the web for every prompt.
# this script is depenedent on get-weather
use get-weather.nu get_weather
# Create a mutable weather table to hold the weather data
let-env WEATHER = (get_weather | upsert last_run_time { (date now | date format '%Y-%m-%d %H:%M:%S %z')})
#command to run at interval
def-env timed_weather_run [
--interval(-i): duration # The interval duration
] {
# get the last runtime and add my timezone difference
let last_runtime = ($env.WEATHER | get last_run_time | into datetime)
if $last_runtime + $interval > (date now) {
# $"interval not met. last_runtime: [($last_runtime)](char nl)"
let temp = ($env.WEATHER.Temperature)
let emoji = ($env.WEATHER.Emoji)
{
Temperature: ($temp)
Source: "cache"
Emoji: ($emoji)
}
} else {
# $"interval met, running command: [($command)](char nl)"
let-env WEATHER = (get_weather | upsert last_run_time { (date now | date format '%Y-%m-%d %H:%M:%S %z')})
let temp = ($env.WEATHER.Temperature)
let emoji = ($env.WEATHER.Emoji)
{
Temperature: ($temp)
Source: "expired-cache"
Emoji: ($emoji)
}
}
}
timed_weather_run --interval 1min

View file

@ -0,0 +1,192 @@
# Weather Script based on IP Address
# - Weather using dark weather api
# - Air polution condition using airvisual api
# - Street address using google maps api
# - Version 2.0
export def-env weatherds [] {
get_weather (get_location 0)
}
# location functions thanks to https://github.com/nushell/nu_scripts/tree/main/weather
def locations [] {
[
[location city_column state_column country_column lat_column lon_column];
["http://ip-api.com/json/" city region countryCode lat lon]
["https://ipapi.co/json/" city region_code country_code latitude longitude]
["https://freegeoip.app/json/" city region_code country_code latitude longitude]
["https://ipwhois.app/json/" city region country_code latitude longitude]
]
}
def get_location [index: int] {
let wifi = (iwgetid -r)
let loc_json = (http get (locations | select $index).0.location)
# if ip address in your home isn't precise, you can force a location
if $wifi =~ "my_wifi" { "your_lat,your_lon" } else { $"($loc_json.lat),($loc_json.lon)" }
}
# dark sky
def http get_api [loc] {
let apiKey = "ds_api_key"
let options = "?lang=en&units=si&exclude=minutely,hourly,flags"
let url = $"https://api.darksky.net/forecast/($apiKey)/($loc)($options)"
http get $url
}
# street address
def get_address [loc] {
let mapsAPIkey = "maps_api_key"
let url = $"https://maps.googleapis.com/maps/api/geocode/json?latlng=($loc)&sensor=true&key=($mapsAPIkey)"
(http get $url).results.0.formatted_address
}
# wind description (adjust to your liking)
def desc_wind [wind] {
if $wind < 30 { "Normal" } else if $wind < 50 { "Moderate" } else if $wind < 60 { "Strong" } else { "Very Strong" }
}
# uv description (standard)
def uv_class [uvIndex] {
if $uvIndex < 2.9 { "Low" } else if $uvIndex < 5.9 { "Moderate" } else if $uvIndex < 7.9 { "High" } else if $uvIndex < 10.9 { "Very High" } else { "Extreme" }
}
# air pollution
def get_airCond [loc] {
let apiKey = "airvisual_api_key"
let lat = (echo $loc | split row ",").0
let lon = (echo $loc | split row ",").1
let url = $"https://api.airvisual.com/v2/nearest_city?lat=($lat)&lon=($lon)&key=($apiKey)"
let aqius = (http get $url).data.current.pollution.aqius
# clasification (standard)
if $aqius < 51 { "Good" } else if $aqius < 101 { "Moderate" } else if $aqius < 151 { "Unhealthy for some" } else if $aqius < 201 { "Unhealthy" } else if $aqius < 301 { "Very unhealthy" } else { "Hazardous" }
}
# parse all the information
def get_weather [loc] {
let response = (http get_api $loc)
let address = (get_address $loc)
let air_cond = (get_airCond $loc)
## Current conditions
let cond = $response.currently.summary
let temp = $response.currently.temperature
let wind = $response.currently.windSpeed * 3.6
let humi = $response.currently.humidity * 100
let uvIndex = $response.currently.uvIndex
let suntimes = ($response.daily.data | select sunriseTime sunsetTime | select 0 | update cells {|f| $f | into string | into datetime -o -4 | into string})
let sunrise = ($suntimes | get sunriseTime | get 0 | split row ' ' | get 3)
let sunset = ($suntimes | get sunsetTime | get 0 | split row ' ' | get 3)
let vientos = (desc_wind $wind)
let uvClass = (uv_class $uvIndex)
let Vientos = $"($vientos) \(($wind | into string -d 2) Km/h\)"
let humedad = $"($humi)%"
let temperature = $"($temp)°C"
let current = {
"Condition": ($cond)
Temperature: ($temperature)
Humidity: ($humedad)
Wind: ($Vientos)
"UV Index": ($uvClass)
"Air condition": ($air_cond)
Sunrise: ($sunrise)
Sunset: ($sunset)
}
## Forecast
let forecast = ($response.daily.data | select summary temperatureMin temperatureMax windSpeed humidity precipProbability precipIntensity uvIndex | update windSpeed {|f| $f.windSpeed * 3.6} | update precipIntensity {|f| $f.precipIntensity * 24} | update precipProbability {|f| $f.precipProbability * 100} | update humidity {|f| $f.humidity * 100} | update uvIndex {|f| uv_class $f.uvIndex} | update windSpeed {|f| $"(desc_wind $f.windSpeed) \(($f.windSpeed | into string -d 2)\)"} | rename Summary "T° min (°C)" "T° max (°C)" "Wind Speed (Km/h)" "Humidity (%)" "Precip. Prob. (%)" "Precip. Intensity (mm)")
## plots (require custom command gnu-plot)
($forecast | select "Humidity (%)") | gnu-plot
($forecast | select "Precip. Intensity (mm)") | gnu-plot
($forecast | select "Precip. Prob. (%)") | gnu-plot
($forecast | select "T° max (°C)") | gnu-plot
($forecast | select "T° min (°C)") | gnu-plot
## forecast
echo $"Forecast: ($response.daily.summary)"
echo $forecast
## current
echo $"Current conditions: ($address)"
echo $current
}
# Get weather for right command prompt (set in config.nu)
export def-env get_weather_by_interval [INTERVAL_WEATHER] {
let weather_runtime_file = (($env.HOME) | path join .weather_runtime_file.json)
if ($weather_runtime_file | path exists) {
let last_runtime_data = (open $weather_runtime_file)
let LAST_WEATHER_TIME = ($last_runtime_data | get last_weather_time)
if ($LAST_WEATHER_TIME | into datetime) + $INTERVAL_WEATHER < (date now) {
let WEATHER = (get_weather_for_prompt (get_location 0))
let NEW_WEATHER_TIME = (date now | date format '%Y-%m-%d %H:%M:%S %z')
$last_runtime_data | upsert weather $WEATHER | upsert last_weather_time $NEW_WEATHER_TIME | save $weather_runtime_file
$WEATHER
} else {
$last_runtime_data | get weather
}
} else {
let WEATHER = (get_weather_for_prompt (get_location 0))
let LAST_WEATHER_TIME = (date now | date format '%Y-%m-%d %H:%M:%S %z')
let WEATHER_DATA = {
"weather": ($WEATHER)
"last_weather_time": ($LAST_WEATHER_TIME)
}
$WEATHER_DATA | save $weather_runtime_file
$WEATHER
}
}
def get_weather_for_prompt [loc] {
let response = (http get_api $loc)
## current conditions
let cond = $response.currently.summary
let temp = $response.currently.temperature
let temperature = $"($temp)°C"
let icon = (get_weather_icon $response.currently.icon)
let current = {
Condition: ($cond)
Temperature: ($temperature)
Icon: ($icon)
}
echo $"($current.Icon) ($current.Temperature)"
}
def get_weather_icon [icon: string] {
switch $icon {
"clear-day": {"☀️"},
"clear-night": {"🌑"},
"rain": {"🌧️"},
"snow": {"❄️"},
"sleet": {🌨️},
"wind": {"🌬️"},
"fog": {"🌫"},
"cloudy": {"☁️"},
"partly-cloudy-day": {"🌤️"},
"partly-cloudy-night": {"🌑☁️"},
"hail": {🌨},
"thunderstorm": {"🌩️"},
"tornado": {"🌪️"}
}
}