![[MAC0470#06] - Nu scripts pt. 1](/nuscripts.jpg)
[MAC0470#06] - Nu scripts pt. 1
Criando completions para Bend
mac0470
Aqui se inicia a segunda parte da disciplina MAC0470 onde vamos contribuir com os projetos que quisermos (existem certas restrições, mas não vem ao caso). O ecossitema onde eu e minha dupla Laís Nuto vamos fazer nossas contribuições será o Nushell.
O que é Nushell
Nushell é um shell (assim como Bash, Zsh, Fish, etc.) multiplataforma que possui como característica mais marcante a sua linguagem de script nu
que é fortemente inspirada nas modernas linguagens de programação funcionais com suporte a tipos. Diferentemente de um script Bash onde toda entrada e saída é um texto, o Nushell possui tipos e coleções (tabelas e listas) para poder trabalhar com pipelines e processar dados de forma mais eficaz.
Por exemplo, o comando ls
no Nushell me devolve uma tabela ao invés de uma lista de strings com nomes de arquivos.
❯ ls
╭────┬─────────────────────┬──────┬───────────┬───────────────╮
│ # │ name │ type │ size │ modified │
├────┼─────────────────────┼──────┼───────────┼───────────────┤
│ 0 │ README.md │ file │ 2.9 KiB │ 2 months ago │
│ 1 │ astro.config.mjs │ file │ 377 B │ 2 months ago │
│ 2 │ bun.lockb │ file │ 251.6 KiB │ 2 months ago │
│ 3 │ components.json │ file │ 351 B │ 2 months ago │
│ 4 │ node_modules │ dir │ 20.0 KiB │ 2 months ago │
│ 5 │ package.json │ file │ 855 B │ 2 months ago │
│ 6 │ public │ dir │ 4.0 KiB │ 6 minutes ago │
│ 7 │ src │ dir │ 4.0 KiB │ 2 months ago │
│ 8 │ tailwind.config.js │ file │ 2.1 KiB │ 2 months ago │
│ 9 │ tailwind.config.mjs │ file │ 176 B │ 2 months ago │
│ 10 │ tsconfig.json │ file │ 237 B │ 2 months ago │
╰────┴─────────────────────┴──────┴───────────┴───────────────╯
Podemos usar pipes (|
) para manipular a saída do comando ls
para listar apenas diretórios usando o comando where
❯ ls | where type == "dir"
╭───┬──────────────┬──────┬──────────┬───────────────╮
│ # │ name │ type │ size │ modified │
├───┼──────────────┼──────┼──────────┼───────────────┤
│ 0 │ node_modules │ dir │ 20.0 KiB │ 2 months ago │
│ 1 │ public │ dir │ 4.0 KiB │ 8 minutes ago │
│ 2 │ src │ dir │ 4.0 KiB │ 2 months ago │
╰───┴──────────────┴──────┴──────────┴───────────────╯
Podemos pegar apenas a coluna name
❯ ls | where type == "dir" | get name
╭───┬──────────────╮
│ 0 │ node_modules │
│ 1 │ public │
│ 2 │ src │
╰───┴──────────────╯
E por que não deixar tudo em maiúsculas? (O comando each
vai mapear cada elemento usando a lambda function passada {|d| $d | str upcase}
)
❯ ls | where type == "dir" | get name | each {|d| $d | str upcase }
╭───┬──────────────╮
│ 0 │ NODE_MODULES │
│ 1 │ PUBLIC │
│ 2 │ SRC │
╰───┴──────────────╯
Com o que vamos contribuir
Scripts nu
são extremamente poderosos e o shell possui muitas features a serem exploradas e configuradas. Para isso, criaram o repositório nushell/nu_scripts para os usuários compartilharem seus scripts utilitários, como por exemplo: scripts de completion. Scripts de completion são scripts que ajudam o usuário com auto-complete para subcomandos e flags dos executáveis no terminal. O que eu e a Laís vamos fazer é contribuir escrevendo/melhorando scripts de completion para vários programas, como por exemplo: git e o kw.
Apenas como um exemplo, isso é o que o script git-completion.nu
provê quando digito git branch -
e teclo Tab
para receber sugestões:
❯ | git branch -
--abbrev use short commit hash prefixes
--all list both remote and local branches
--color use color in output
--contains show only branches that contain the specified commit
--copy copy branch together with config and reflog
--delete delete branch
--edit-description open editor to edit branch description
--format specify format for listing branches
--list list branches
--merged list reachable branches
--move rename branch
--no-contains show only branches that don't contain specified commit
Diferentemente das completions do Bash, aqui eu posso usar as setas do teclado para escolher uma opção e o Nushell vai completar a linha ao invés de só imprimir as opções e criar uma nova linha embaixo.
Bend
Resolvi começar com algo completamente novo, para entender como funciona o ciclo de contribuição dos Nu Scripts. Cerca de uma semana antes do momento em que escrevo esse blog post, uma companhia brasileira Higher Order Company lançou uma versão beta da linguagem de programação Bend e com ela um utilitário homônimo para permitir que os usuários executem os programas escritos em bend
. De forma simples, a linguagem Bend permite que os desenvolvedores escrevam programas nativamente paralelizáveis para serem executados nos núcleos da CPU ou até mesmo na GPU.
Estudando a própria página de ajuda fornecida com --help
para bend
e cada um dos subcomandos fui capaz de construir o script de completion inteiro. Segue um exemplo de como são as completions para o subcomando bend run-cu
que executa o programa nos CUDA cores da sua placa de vídeo Nvidia:
# Compiles the program and runs it with the Cuda HVM implementation
export extern "bend run-cu" [
-p # Debug and normalization pretty printing
-O: string@"nu-complete bend opts" # Enables or disables the given optimizations
--io # Run with IO enabled
-l # Linear readback (show explicit dups)
--stats(-s) # Show runtime stats and rewrite counts
--warn(-W): string@"nu-complete bend warn" # Show the specified compilation warning
--deny(-D): string@"nu-complete bend warn" # Deny the specified compilation warning
--allow(-A): string@"nu-complete bend warn" # Allow the specified compilation warning
--help(-h) # Print help
--entrypoint(-e): string # Use other entrypoint rather than main or Main
--verbose(-v) # Be verbose
]
Observe que faço menção a dois comandos: nu-complete bend warn
e nu-complete bend opts
. Isso é mais uma das features muito legais das completions no Nushell que me permite escrever uma função que indica um conjunto de valores possíveis para uma opção. Assim estão definidos os dois comandos supracitados:
def "nu-complete bend opts" [] {
[all, no-all, eta, no-eta, prune, no-prune,
linearize-matches, linearize-matches-alt,
no-linearize-matches, float-combinators,
no-float-combinators, merge, no-merge, inline,
no-inline, check-net-size, no-check-net-size]
}
def "nu-complete bend warn" [] {
[all, irrefutable-match, redundant-match, unreachable-match, unused-definition, repeated-bind, recursion-cycle]
}
Com isso, ao digitar bend run-cu -O
e pressionar Tab
eu posso escolher uma opção de otimização dentre as disponíveis:
❯ | bend run-cu -O
all no-all eta no-eta
prune no-prune linearize-matches linearize-matches-alt
no-linearize-matches float-combinators no-float-combinators merge
no-merge inline no-inline check-net-size
no-check-net-size
Enviando minha contribuição
Poucos minutos após abrir o pull request do meu fork para o upstream, um mantenedor me deu um feedback. Ele solicitou que eu escrevesse um pequeno readme.md
para informar sobre o que é bend
e onde as pessoas podem obter ele.
Escrevi o readme
, atualizei o PR e em menos de 10 minutos o mantenador aprovou meu script e integrou com a master.
Com isso, agora estou preparado para, junto com minha dupla, fazer mais contribuições, uma vez que agora já conheço bem como funciona o ciclo de contribuições do projeto.