├── .github
└── workflows
│ └── ci.yml
├── LICENSE
├── README.md
├── format.nu
├── languages.ncl
├── languages
└── nu.scm
├── run_test.nu
├── test
├── expected_attribute.nu
├── expected_command.nu
├── expected_comment.nu
├── expected_ctrl.nu
├── expected_data.nu
├── expected_decl.nu
├── expected_exe.nu
├── expected_keyword.nu
├── expected_string.nu
├── input_attribute.nu
├── input_command.nu
├── input_comment.nu
├── input_ctrl.nu
├── input_data.nu
├── input_decl.nu
├── input_exe.nu
├── input_keyword.nu
└── input_string.nu
└── toolkit.nu
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | on:
2 | pull_request:
3 | push:
4 | branches:
5 | - main
6 |
7 | name: continuous-integration
8 |
9 | jobs:
10 | test:
11 | runs-on: ubuntu-latest
12 | name: Nushell formatter test
13 | steps:
14 | - uses: actions/checkout@v4
15 | - uses: hustcer/setup-nu@main
16 | with:
17 | version: "*"
18 | - run: nu --version
19 | - name: Setup Rust toolchain and cache
20 | uses: actions-rust-lang/setup-rust-toolchain@v1.10.1
21 | - name: Clone topiary
22 | uses: GuillaumeFalourd/clone-github-repo-action@v2.3
23 | with:
24 | branch: 'main'
25 | owner: 'tweag'
26 | repository: 'topiary'
27 | - name: Install topiary-cli
28 | run: |
29 | cd topiary
30 | topiary --version || cargo install --path topiary-cli
31 | - name: Run tests
32 | run: nu run_test.nu
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 - 2030 BlindFS
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Format nushell with Topiary
2 |
3 | [](https://github.com/blindfs/topiary-nushell/actions)
4 |
5 | * [Topiary](https://github.com/tweag/topiary): tree-sitter based uniform formatter
6 | * This repo contains:
7 | * languages.ncl: configuration that enables nushell
8 | * nu.scm: tree-sitter query DSL that defines the behavior of the formatter for nushell
9 | * stand-alone tests written in nushell
10 |
11 | ## Status
12 |
13 | * Supposed to work well with all language features of latest nushell (0.103)
14 |
15 | > [!NOTE]
16 | >
17 | > * There're corner cases where `tree-sitter-nu` would fail with parsing errors, if you encounter them, please open an issue [there](https://github.com/nushell/tree-sitter-nu).
18 | > * If you encounter any style/format issue, please report in this repo, any feedback is appreciated.
19 |
20 | ## Setup
21 |
22 | 1. Install topiary-cli using whatever package-manager on your system (0.6.0+ suggested)
23 |
24 | ```nushell
25 | # e.g. installing with cargo
26 | cargo install --git https://github.com/tweag/topiary topiary-cli
27 | ```
28 |
29 | 2. Clone this repo somewhere
30 |
31 | ```nushell
32 | # e.g. to `$env.XDG_CONFIG_HOME/topiary`
33 | git clone https://github.com/blindFS/topiary-nushell ($env.XDG_CONFIG_HOME | path join topiary)
34 | ```
35 |
36 | 3. Setup environment variables (Optional)
37 |
38 | > [!WARNING]
39 | > This is required if you want to do the formatting via vanilla topiary-cli, like in the neovim/helix settings below.
40 | >
41 | > While the [`format.nu`](https://github.com/blindFS/topiary-nushell/blob/main/format.nu) script in this repo just wraps that for you.
42 |
43 | ```nushell
44 | # Set environment variables according to the path of the clone
45 | $env.TOPIARY_CONFIG_FILE = ($env.XDG_CONFIG_HOME | path join topiary languages.ncl)
46 | $env.TOPIARY_LANGUAGE_DIR = ($env.XDG_CONFIG_HOME | path join topiary languages)
47 | ```
48 |
49 | > [!WARNING]
50 | > For windows users, if something went wrong the first time you run the formatter,
51 | > like compiling errors, you might need the following extra steps to make it work.
52 |
53 |
54 | Optional for Windows
55 |
56 | 1. Install the [tree-sitter-cli](https://github.com/tree-sitter/tree-sitter/blob/master/cli/README.md).
57 | 2. Clone [tree-sitter-nu](https://github.com/nushell/tree-sitter-nu) somewhere and cd into it.
58 | 3. Build the parser manually with `tree-sitter build`.
59 | 4. Replace the `languages.ncl` file in this repo with something like:
60 |
61 | ```ncl
62 | {
63 | languages = {
64 | nu = {
65 | extensions = ["nu"],
66 | grammar.source.path = "C:/path/to/tree-sitter-nu/nu.dll",
67 | symbol = "tree_sitter_nu",
68 | },
69 | },
70 | }
71 | ```
72 |
73 |
74 |
75 | ## Usage
76 |
77 |
78 | Using the format.nu wrapper
79 |
80 | ```markdown
81 | Helper to run topiary with the correct environment variables for topiary-nushell
82 |
83 | Usage:
84 | > format.nu {flags} ...(files)
85 |
86 | Flags:
87 | -c, --config_dir : Root of the topiary-nushell repo, defaults to the parent directory of this script
88 | -h, --help: Display the help message for this command
89 |
90 | Parameters:
91 | ...files : Files to format
92 |
93 | Input/output types:
94 | ╭───┬─────────┬─────────╮
95 | │ # │ input │ output │
96 | ├───┼─────────┼─────────┤
97 | │ 0 │ nothing │ nothing │
98 | │ 1 │ string │ string │
99 | ╰───┴─────────┴─────────╯
100 |
101 | Examples:
102 | Read from stdin
103 | > bat foo.nu | format.nu
104 |
105 | Format files (in-place replacement)
106 | > format.nu foo.nu bar.nu
107 |
108 | Path overriding
109 | > format.nu -c /path/to/topiary-nushell foo.nu bar.nu
110 | ```
111 |
112 |
113 |
114 |
115 | Using topiary-cli
116 |
117 | ```nushell
118 | # in-place formatting
119 | topiary format script.nu
120 | # stdin -> stdout
121 | cat foo.nu | topiary format --language nu
122 | ```
123 |
124 |
125 |
126 | ## Editor Integration
127 |
128 |
129 | Neovim
130 | Format on save with conform.nvim:
131 |
132 | ```lua
133 | -- lazy.nvim setup
134 | {
135 | "stevearc/conform.nvim",
136 | dependencies = { "mason.nvim" },
137 | event = "VeryLazy",
138 | opts = {
139 | formatters_by_ft = {
140 | nu = { "topiary_nu" },
141 | },
142 | formatters = {
143 | topiary_nu = {
144 | command = "topiary",
145 | args = { "format", "--language", "nu" },
146 | },
147 | },
148 | },
149 | },
150 | ```
151 |
152 |
153 |
154 |
155 | Helix
156 |
157 | To format on save in Helix, add this configuration to your `helix/languages.toml`.
158 |
159 | ```toml
160 | [[language]]
161 | name = "nu"
162 | auto-format = true
163 | formatter = { command = "topiary", args = ["format", "--language", "nu"] }
164 | ```
165 |
166 |
167 |
168 |
169 | Zed
170 |
171 | ```json
172 | "languages": {
173 | "Nu": {
174 | "formatter": {
175 | "external": {
176 | "command": "/path-to-the-clone/format.nu"
177 | }
178 | },
179 | "format_on_save": "on"
180 | }
181 | }
182 | ```
183 |
184 |
185 |
186 | ## Contribute
187 |
188 | > [!IMPORTANT]
189 | > Help to find format issues with following method
190 | > (dry-run, detects parsing/idempotence/semantic breaking):
191 |
192 | ```nushell
193 | source toolkit.nu
194 | test_format
195 | ```
196 |
--------------------------------------------------------------------------------
/format.nu:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env nu --stdin
2 |
3 | const script_path = path self .
4 |
5 | # Helper to run topiary with the correct environment variables for topiary-nushell
6 | @example "Read from stdin" { bat foo.nu | format.nu }
7 | @example "Format files (in-place replacement)" { format.nu foo.nu bar.nu }
8 | @example "Path overriding" { format.nu -c /path/to/topiary-nushell foo.nu bar.nu }
9 | def main [
10 | --config_dir (-c): path # Root of the topiary-nushell repo, defaults to the parent directory of this script
11 | ...files: path # Files to format
12 | ]: [nothing -> nothing string -> string] {
13 | let config_dir = $config_dir | default $script_path
14 | $env.TOPIARY_CONFIG_FILE = ($config_dir | path join languages.ncl)
15 | $env.TOPIARY_LANGUAGE_DIR = ($config_dir | path join languages)
16 |
17 | if ($files | is-not-empty) {
18 | topiary format ...$files
19 | } else {
20 | topiary format --language nu
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/languages.ncl:
--------------------------------------------------------------------------------
1 | {
2 | languages = {
3 | nu = {
4 | extensions = ["nu"],
5 | grammar.source.git = {
6 | git = "https://github.com/nushell/tree-sitter-nu.git",
7 | rev = "c10340b5bb3789f69182acf8f34c3d4fc24d2fe1",
8 | },
9 | },
10 | },
11 | }
12 |
--------------------------------------------------------------------------------
/languages/nu.scm:
--------------------------------------------------------------------------------
1 | ;; leaf nodes are left intact
2 | [
3 | (cell_path)
4 | (comment)
5 | (shebang)
6 | (unquoted)
7 | (val_binary)
8 | (val_bool)
9 | (val_date)
10 | (val_duration)
11 | (val_filesize)
12 | (val_nothing)
13 | (val_number)
14 | (val_string)
15 | (val_variable)
16 | ] @leaf
17 |
18 | ;; keep empty lines
19 | (_) @allow_blank_line_before
20 |
21 | [
22 | ":"
23 | ";"
24 | "do"
25 | "if"
26 | "match"
27 | "try"
28 | "while"
29 | (env_var)
30 | ] @append_space
31 |
32 | [
33 | "="
34 | (match_guard)
35 | (short_flag)
36 | (long_flag)
37 | ] @prepend_space
38 |
39 | ;; FIXME: temp workaround for the whitespace issue
40 | (short_flag "=" @prepend_antispace)
41 | (long_flag "=" @prepend_antispace)
42 | (param_value "=" @append_space)
43 |
44 | (assignment
45 | opr: _
46 | rhs: (pipeline
47 | (pipe_element
48 | (val_string
49 | (raw_string_begin)
50 | )
51 | )
52 | ) @prepend_space
53 | )
54 |
55 | (
56 | "="
57 | .
58 | (pipeline
59 | (pipe_element
60 | (val_string
61 | (raw_string_begin)
62 | )
63 | )
64 | ) @prepend_space
65 | )
66 |
67 | [
68 | "->"
69 | "=>"
70 | "alias"
71 | "as"
72 | "catch"
73 | "const"
74 | "def"
75 | "else"
76 | "error"
77 | "export"
78 | "export-env"
79 | "extern"
80 | "for"
81 | "hide"
82 | "hide-env"
83 | "in"
84 | "let"
85 | "loop"
86 | "make"
87 | "module"
88 | "mut"
89 | "new"
90 | "overlay"
91 | "return"
92 | "source"
93 | "source-env"
94 | "use"
95 | "where"
96 | ] @prepend_space @append_space
97 |
98 | (pipeline
99 | "|" @append_space @prepend_input_softline
100 | )
101 |
102 | ;; add spaces to left & right sides of operators
103 | (expr_binary
104 | opr: _ @append_input_softline @prepend_input_softline
105 | )
106 |
107 | (assignment opr: _ @prepend_space)
108 |
109 | (where_command
110 | opr: _ @append_input_softline @prepend_input_softline
111 | )
112 |
113 | ;; special flags
114 | (
115 | [
116 | (short_flag)
117 | (long_flag)
118 | ] @append_space
119 | .
120 | (_)
121 | )
122 |
123 | (overlay_hide
124 | overlay: _ @prepend_space
125 | )
126 |
127 | ;; FIXME: temp workaround for the whitespace issue
128 | (hide_env
129 | [
130 | (short_flag)
131 | (long_flag)
132 | ] @append_antispace
133 | .
134 | (_)
135 | )
136 |
137 | (hide_env
138 | (identifier) @append_space
139 | .
140 | (identifier)
141 | )
142 |
143 | (hide_mod
144 | (_) @append_space
145 | .
146 | (_)
147 | )
148 |
149 | ;; indentation
150 | [
151 | "["
152 | "("
153 | "...("
154 | "...["
155 | "...{"
156 | ] @append_indent_start @append_empty_softline
157 |
158 | [
159 | "]"
160 | "}"
161 | ")"
162 | ] @prepend_indent_end @prepend_empty_softline
163 |
164 | ;;; change line happens after || for closure
165 | "{" @append_indent_start
166 | (
167 | "{" @append_empty_softline
168 | .
169 | (parameter_pipes)? @do_nothing
170 | )
171 |
172 | ;; space/newline between parameters
173 | (parameter_pipes
174 | (
175 | (parameter) @append_space
176 | .
177 | (parameter)
178 | )?
179 | ) @append_space @append_spaced_softline
180 |
181 | (parameter_bracks
182 | (parameter) @append_space
183 | .
184 | (parameter) @prepend_empty_softline
185 | )
186 |
187 | (parameter
188 | param_long_flag: _? @prepend_space
189 | .
190 | flag_capsule: _? @prepend_space
191 | )
192 |
193 | ;; attributes
194 | (attribute
195 | (attribute_identifier)
196 | (_)? @prepend_space
197 | ) @append_hardline
198 |
199 | (attribute_list
200 | ";" @delete @append_hardline
201 | )
202 |
203 | ;; declarations
204 | (decl_def
205 | (long_flag)? @prepend_space @append_space
206 | quoted_name: _? @prepend_space @append_space
207 | unquoted_name: _? @prepend_space @append_space
208 | (returns)?
209 | (block) @prepend_space
210 | )
211 |
212 | (returns
213 | ":"? @do_nothing
214 | ) @prepend_space
215 |
216 | (returns
217 | type: _ @append_space
218 | .
219 | type: _
220 | )
221 |
222 | (decl_use (_) @prepend_space)
223 | (decl_extern (_) @prepend_space)
224 | (decl_module (_) @prepend_space)
225 |
226 | ;; newline
227 | (comment) @prepend_input_softline @append_hardline
228 |
229 | ;; TODO: pseudo scope_id to cope with
230 | ;; https://github.com/tree-sitter/tree-sitter/discussions/3967
231 | (nu_script
232 | (_) @append_begin_scope
233 | .
234 | (_) @prepend_end_scope @prepend_input_softline
235 | (#scope_id! "consecutive_scope")
236 | )
237 |
238 | (block
239 | (_) @append_begin_scope
240 | .
241 | (_) @prepend_end_scope @prepend_input_softline
242 | (#scope_id! "consecutive_scope")
243 | )
244 |
245 | (block
246 | "{" @append_space
247 | "}" @prepend_space
248 | )
249 |
250 | (val_closure
251 | (_) @append_begin_scope
252 | .
253 | (_) @prepend_end_scope @prepend_input_softline
254 | (#scope_id! "consecutive_scope")
255 | )
256 |
257 | (val_closure
258 | "{" @append_space
259 | .
260 | (parameter_pipes)? @do_nothing
261 | )
262 |
263 | (val_closure "}" @prepend_space)
264 |
265 | ;; control flow
266 | (ctrl_if
267 | "if" @append_space
268 | condition: _ @append_space
269 | then_branch: _
270 | "else"? @prepend_input_softline
271 | )
272 |
273 | (ctrl_for
274 | "for" @append_space
275 | "in" @prepend_space @append_space
276 | body: _ @prepend_space
277 | )
278 |
279 | (ctrl_while
280 | "while" @append_space
281 | condition: _ @append_space
282 | )
283 |
284 | (ctrl_match
285 | "match" @append_space
286 | scrutinee: _? @append_space
287 | (match_arm)? @prepend_spaced_softline
288 | (default_arm)? @prepend_spaced_softline
289 | )
290 |
291 | (ctrl_do (_) @prepend_input_softline)
292 |
293 | ;; data structures
294 | (command_list
295 | [
296 | (cmd_identifier)
297 | (val_string)
298 | ] @append_space @prepend_spaced_softline
299 | )
300 |
301 | (command
302 | flag: _? @prepend_input_softline
303 | arg_str: _? @prepend_input_softline
304 | arg_spread: _? @prepend_input_softline
305 | arg: _? @prepend_input_softline
306 | )
307 |
308 | (redirection
309 | file_path: _? @prepend_input_softline
310 | ) @prepend_input_softline
311 |
312 | (list_body
313 | entry: _ @append_space
314 | .
315 | entry: _ @prepend_spaced_softline
316 | )
317 |
318 | ;; match_arm
319 | (val_list
320 | entry: _ @append_space
321 | .
322 | entry: _ @prepend_spaced_softline
323 | )
324 |
325 | (val_table
326 | row: _ @prepend_spaced_softline
327 | )
328 |
329 | (val_record
330 | entry: (record_entry) @append_space
331 | .
332 | entry: (record_entry) @prepend_spaced_softline
333 | )
334 |
335 | (record_body
336 | entry: (record_entry) @append_space
337 | .
338 | entry: (record_entry) @prepend_spaced_softline
339 | )
340 |
341 | (collection_type
342 | type: _ @append_delimiter
343 | .
344 | key: _ @prepend_space
345 | (#delimiter! ",")
346 | )
347 |
--------------------------------------------------------------------------------
/run_test.nu:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env nu
2 |
3 | use std assert
4 |
5 | const temp_file = 'test/temp.nu'
6 | let files = glob test/input_*.nu
7 |
8 | $env.TOPIARY_CONFIG_FILE = (pwd | path join languages.ncl)
9 | $env.TOPIARY_LANGUAGE_DIR = (pwd | path join languages)
10 |
11 | for f in $files {
12 | print $"(ansi green)Testing: (ansi yellow)($f)(ansi reset)"
13 | cp $f $temp_file
14 | topiary format $temp_file
15 | let expected_file = $f | str replace --regex '/input_' '/expected_'
16 | try {
17 | assert ((open $temp_file) == (open $expected_file))
18 | } catch {
19 | delta $temp_file $expected_file
20 | }
21 | rm $temp_file
22 | }
23 |
--------------------------------------------------------------------------------
/test/expected_attribute.nu:
--------------------------------------------------------------------------------
1 | @test
2 | def test [] { }
3 | @test
4 | def test [] { }
5 |
6 | @example "adding some dummy paths to an empty PATH" {
7 | with-env {PATH: []} {
8 | path add "returned" --ret
9 | }
10 | } --result [returned]
11 | @example "adding paths based on the operating system" {
12 | path add {linux: "foo"}
13 | }
14 | export def --env "path add" [
15 | --ret (-r) # return $env.PATH, useful in pipelines to avoid scoping.
16 | --append (-a) # append to $env.PATH instead of prepending to.
17 | p
18 | ] { }
19 |
20 | @search-terms multiply times
21 | @example "random" { 2 | double }
22 | @test
23 | def double []: [number -> number] { $in * 2 }
24 |
--------------------------------------------------------------------------------
/test/expected_command.nu:
--------------------------------------------------------------------------------
1 | # local command
2 | ls | get -i name
3 | | length; ls # multiline command
4 | | length
5 | # external command
6 | ^git add (
7 | ls
8 | | get -i name
9 | )
10 | FOO=BAR BAR=BAZ ^$cmd --arg1=val1 -arg2 arg=value arg=($arg3)
11 | cat unknown.txt o+e> (null-device)
12 |
13 | $hash | $in + "\n" out>> $NUENV_FILE
14 |
--------------------------------------------------------------------------------
/test/expected_comment.nu:
--------------------------------------------------------------------------------
1 | # comment at the top
2 |
3 | # comment before command
4 | # multiline
5 | def foo_bar [
6 | # comment at [
7 | foo: string # comment for arg
8 | bar: int # another comment
9 | ] {
10 | # comment at {
11 | # comment in body
12 | [
13 | foo # comment in list
14 | # another comment
15 | bar
16 | ];
17 | {
18 | foo: foo # comment in record
19 | # another comment
20 | bar: bar
21 | } # comment at }
22 | (
23 | # comment at (
24 | foo # comment in parenthesis
25 | bar # another comment
26 | )
27 | }
28 |
29 | # top level comment
30 | # multiline
31 |
--------------------------------------------------------------------------------
/test/expected_ctrl.nu:
--------------------------------------------------------------------------------
1 | # for
2 | for i in [1 2 3] {
3 | # if
4 | if (true or false) {
5 | print "break"; break # break
6 | } else if not false {
7 | print "continue"; continue # continue
8 | }
9 | return 1 # return
10 | }
11 | # alias
12 | alias ll = ls -l # alias comment
13 | # where
14 | ls | where $in.name == 'foo'
15 | | where {|e| $e.item.name !~ $'^($e.index + 1)' }
16 | # match
17 | let foo = {name: 'bar' count: 7}
18 | match $foo {
19 | {name: 'bar' count: $it} if $it < 5 => ($it + 3) # match arm comment
20 | # match comment
21 | {name: 'bar' count: $it} if not ($it >= 5) => ($it + 7)
22 | _ => { exit 0 }
23 | }
24 | # while
25 | mut x = 0; while $x < 10 { $x = $x + 1 }; $x # while comment
26 | # loop
27 | loop {
28 | if $x > 10 { break };
29 | $x = $x + 1
30 | }
31 | # try
32 | try {
33 | # error
34 | error make -u {
35 | msg: 'Some error info'
36 | }
37 | }; print 'Resuming'
38 |
--------------------------------------------------------------------------------
/test/expected_data.nu:
--------------------------------------------------------------------------------
1 | export const config = {
2 | default: {
3 | align: center
4 | updates: when_shown
5 | padding_left: 2
6 | padding_right: 2
7 | icon.font: "Sarasa Term SC Nerd:Bold:17.0"
8 | label.font: "Sarasa Term SC Nerd:Bold:12.0"
9 | icon.color: $colors.white
10 | label.color: $colors.fg
11 | icon.padding_left: 8
12 | icon.padding_right: 2
13 | label.padding_left: 2
14 | label.padding_right: 8
15 | background.corner_radius: 10
16 | background.color: $colors.bg
17 | background.border_width: 1
18 | background.border_color: $colors.bg
19 | }
20 |
21 | workspace_default_args: {
22 | icon.font.size: 12
23 | label.font.size: 17
24 | label.shadow.drawing: on
25 | label.shadow.color: $colors.bg
26 | label.shadow.distance: 3
27 | label.highlight_color: $colors.green
28 | background.drawing: off
29 | background.color: $colors.transparent
30 | background.border_width: 2
31 | background.border_color: $colors.fg
32 | background.shadow.drawing: on
33 | background.shadow.color: $colors.bg
34 | background.shadow.distance: 3
35 | }
36 |
37 | plugin_configs: [
38 | {
39 | name: ws_listener
40 | pos: left
41 | events: [aerospace_workspace_change space_windows_change]
42 | args: {
43 | updates: on
44 | drawing: off
45 | script: '{}/aerospace.nu'
46 | }
47 | }
48 | {
49 | name: front_app
50 | pos: left
51 | events: [front_app_switched aerospace_mode_change]
52 | args: {
53 | label.color: $colors.black
54 | icon.color: $colors.black
55 | icon.font.size: 20
56 | background.color: $colors.blue
57 | background.corner_radius: 3
58 | background.shadow.drawing: on
59 | background.shadow.color: $colors.bg
60 | background.shadow.distance: 3
61 | script: '{}/front_app.nu'
62 | }
63 | }
64 | {
65 | name: media
66 | pos: center
67 | events: [media_change]
68 | args: {
69 | icon: ''
70 | icon.color: $colors.fg
71 | label.color: $colors.fg
72 | background.color: $colors.bg
73 | background.border_color: $colors.fg
74 | script: '{}/media.nu'
75 | }
76 | }
77 | {
78 | name: media_cover
79 | pos: center
80 | events: [media_change]
81 | args: {
82 | icon.drawing: off
83 | label.drawing: off
84 | background.image.drawing: on
85 | background.image: media.artwork
86 | background.image.scale: 0.7
87 | background.color: $colors.transparent
88 | background.border_width: 0
89 | }
90 | }
91 | {
92 | name: clock
93 | args: {
94 | update_freq: 30
95 | icon: ''
96 | script: '{}/clock.nu'
97 | background.corner_radius: 3
98 | padding_right: 1
99 | padding_left: 1
100 | label.font.size: 10
101 | }
102 | }
103 | {
104 | name: volume
105 | events: [volume_change]
106 | args: {
107 | script: '{}/volume.nu'
108 | background.corner_radius: 3
109 | padding_right: 1
110 | padding_left: 1
111 | }
112 | }
113 | {
114 | name: battery
115 | events: [system_woke power_source_change]
116 | args: {
117 | update_freq: 120
118 | script: '{}/battery.nu'
119 | click_script: 'open x-apple.systempreferences:com.apple.preference.battery'
120 | background.corner_radius: 3
121 | padding_right: 1
122 | }
123 | }
124 | {
125 | name: separator_right
126 | args: {
127 | icon: ''
128 | padding_left: 0
129 | label.drawing: off
130 | background.drawing: off
131 | click_script: '{}/toggle_stats.nu'
132 | }
133 | }
134 | {
135 | name: disk
136 | args: {
137 | icon: ''
138 | update_freq: 120
139 | script: '{}/disk.nu'
140 | click_script: 'open -a "Disk Utility"'
141 | icon.color: $colors.green
142 | background.border_color: $colors.green
143 | }
144 | }
145 | {
146 | name: cpu
147 | args: {
148 | icon: ''
149 | update_freq: 10
150 | script: '{}/cpu.nu'
151 | click_script: 'open -a "Activity Monitor"'
152 | icon.color: $colors.yellow
153 | background.border_color: $colors.yellow
154 | }
155 | }
156 | {
157 | name: memory
158 | args: {
159 | icon: ''
160 | update_freq: 10
161 | script: '{}/mem.nu'
162 | click_script: 'open -a "Activity Monitor"'
163 | icon.color: $colors.cyan
164 | background.border_color: $colors.cyan
165 | }
166 | }
167 | {
168 | name: temp_cpu
169 | args: {
170 | icon: ''
171 | label.font.size: 7
172 | label.y_offset: -4
173 | icon.font.size: 16
174 | update_freq: 5
175 | padding_left: -58
176 | script: '{}/temp.nu'
177 | click_script: '{}/popups/temp.nu'
178 | popup.align: left
179 | popup.background.border_width: 2
180 | popup.background.corner_radius: 3
181 | popup.background.border_color: $colors.yellow
182 | popup.background.drawing: on
183 | }
184 | popups: [
185 | {
186 | name: temp_fan1
187 | args: {
188 | label: "unk"
189 | icon: ''
190 | }
191 | }
192 | {
193 | name: temp_fan2
194 | args: {
195 | label: "unk"
196 | icon: ''
197 | }
198 | }
199 | {
200 | name: temp_power
201 | args: {
202 | label: "unk"
203 | icon: ''
204 | }
205 | }
206 | ]
207 | }
208 | {
209 | name: temp_gpu
210 | args: {
211 | label.font.size: 7
212 | padding_left: 0
213 | padding_right: 0
214 | icon.font.size: 16
215 | icon.drawing: off
216 | label.y_offset: 4
217 | background.drawing: off
218 | }
219 | }
220 | {
221 | name: network_down
222 | args: {
223 | icon: ''
224 | label.font.size: 7
225 | label.y_offset: -4
226 | update_freq: 3
227 | padding_left: -73
228 | padding_right: 23
229 | script: '{}/network.nu'
230 | icon.color: $colors.purple
231 | label.highlight_color: $colors.purple
232 | background.border_color: $colors.purple
233 | }
234 | }
235 | {
236 | name: network_up
237 | args: {
238 | label.font.size: 7
239 | padding_left: 0
240 | padding_right: 0
241 | icon.drawing: off
242 | label.y_offset: 4
243 | background.drawing: off
244 | label.highlight_color: $colors.purple
245 | }
246 | }
247 | ]
248 | }
249 |
250 | const table = [[a b]; [1 2] [2 [4 4]]]
251 | const table_no_row = [[a b]; ]
252 |
--------------------------------------------------------------------------------
/test/expected_decl.nu:
--------------------------------------------------------------------------------
1 | # decl_extern
2 | export extern hello [name: string] {
3 | $"hello ($name)!"
4 | }
5 | # decl_extern no body block
6 | extern hi [
7 | name: string
8 | --long (-s) # flags
9 | ]
10 | # env
11 | hide-env ABC
12 | with-env {ABC: 'hello'} {
13 | (
14 | do -i --env {|foo, bar|
15 | print $env.ABC
16 | }
17 | foo bar
18 | )
19 | }
20 |
21 | # closure
22 | let cls = {|foo bar baz|
23 | (
24 | $foo +
25 | $bar + $baz
26 | )
27 | }
28 |
29 | # decl_export
30 | export-env {
31 | $env.hello = 'hello'
32 | }
33 |
34 | # decl_def
35 | def "hi there" [where: string]: nothing -> record, bar: int> {
36 | {
37 | foo: [["baz"]; [1.0]]
38 | bar: 1
39 | }
40 | }
41 |
42 | # decl_use
43 | use greetings.nu hello
44 | export use greetings.nu *
45 | use module [ foo bar ]
46 | use module [ "foo" "bar" ]
47 | use module [ foo "bar" ]
48 | use module [ "foo" bar ]
49 |
50 | # decl_module
51 | module greetings {
52 | export def hello [name: string] {
53 | $"hello ($name)!"
54 | }
55 |
56 | export def hi [where: string] {
57 | $"hi ($where)!"
58 | }
59 | };
60 |
--------------------------------------------------------------------------------
/test/expected_exe.nu:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env nu
2 |
3 | use constants.nu [
4 | colors
5 | get_icon_by_app_name
6 | "foo bar"
7 | ]
8 |
9 | const animate_frames = 30
10 |
11 | def modify_args_per_workspace [
12 | sid: string
13 | focused_sid: string
14 | ]: nothing -> list {
15 | let icons = (
16 | aerospace list-windows --workspace $sid --json
17 | | from json | get app-name
18 | | each { $in | get_icon_by_app_name }
19 | | uniq | sort
20 | | str join ' '
21 | )
22 | let extra = (
23 | if $sid == $focused_sid {
24 | {highlight: on border_color: $colors.green}
25 | } else {
26 | {highlight: off border_color: $colors.fg}
27 | }
28 | )
29 |
30 | ['--set' $"space.($sid)"]
31 | | append (
32 | if (($icons | is-empty) and ($sid != $focused_sid)) {
33 | [
34 | background.drawing=off
35 | label=
36 | padding_left=-2
37 | padding_right=-2
38 | ]
39 | } else {
40 | [
41 | background.drawing=on
42 | label=($icons)
43 | label.highlight=($extra.highlight)
44 | padding_left=2
45 | padding_right=2
46 | ]
47 | }
48 | )
49 | | append $"background.border_color=($extra.border_color)"
50 | }
51 |
52 | def workspace_modification_args [
53 | name: string
54 | last_sid: string
55 | ]: nothing -> list {
56 | # use listener's label to store last focused space id
57 | let focused_sid = (aerospace list-workspaces --focused)
58 | let ids_to_modify = (
59 | if ($last_sid | is-empty) { (aerospace list-workspaces --all | lines) }
60 | else {
61 | [$focused_sid $last_sid]
62 | }
63 | )
64 | $ids_to_modify
65 | | uniq
66 | | each { modify_args_per_workspace $in $focused_sid }
67 | | flatten
68 | | append ["--set" $name $"label=($focused_sid)"]
69 | }
70 |
71 | # remained for other possible signals
72 | match $env.SENDER {
73 | _ => {
74 | # invisible item to store last focused sid
75 | let last_sid = (sketchybar --query $env.NAME | from json | get label.value)
76 | sketchybar --animate tanh $animate_frames ...(workspace_modification_args $env.NAME $last_sid)
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/test/expected_keyword.nu:
--------------------------------------------------------------------------------
1 | overlay use -p --reload __trigger__ as __auto_venv
2 | overlay hide spam
3 | overlay list
4 | overlay new name
5 | overlay hide spam --keep-custom
6 | overlay hide --keep-env [PWD] new_name
7 | hide PWD
8 | hide foo bar
9 | hide-env PWD
10 | hide-env -i PWD PATH
11 | source-env foo.nu
12 |
--------------------------------------------------------------------------------
/test/expected_string.nu:
--------------------------------------------------------------------------------
1 | const dir_preview_cmd = "eza --tree -L 3 --color=always {} | head -200"
2 | const file_preview_cmd = "bat -n --color=always --line-range :200 {}"
3 | const default_preview_cmd = "if ({} | path type) == 'dir'" + $' {($dir_preview_cmd)} else {($file_preview_cmd)}'
4 | # TODO: r#''#r verbatim
5 | const help_preview_cmd = "try {help {}} catch {'custom command or alias'}"
6 | const external_tldr_cmd = "try {tldr -C {}} catch {'No doc yet'}"
7 | const hybrid_help_cmd = (
8 | "Multiline
9 | string" +
10 | ($external_tldr_cmd | str replace '{}' '(foo)') +
11 | "another multiline
12 | string" +
13 | ($help_preview_cmd | str replace '{}' '(bar)')
14 | )
15 | mut foo = 'foo bar'
16 | $foo += 'baz'
17 | $foo += r#'baz'#
18 |
--------------------------------------------------------------------------------
/test/input_attribute.nu:
--------------------------------------------------------------------------------
1 | @test
2 | def test [] {}
3 | @test; def test [] {}
4 |
5 | @example "adding some dummy paths to an empty PATH" {
6 | with-env { PATH: [] } {
7 | path add "returned" --ret
8 | }
9 | } --result [returned]
10 | @example "adding paths based on the operating system" {
11 | path add {linux: "foo"}
12 | }; export def --env "path add" [
13 | --ret (-r) # return $env.PATH, useful in pipelines to avoid scoping.
14 | --append (-a) # append to $env.PATH instead of prepending to.
15 | p
16 | ] {}
17 |
18 | @search-terms multiply times
19 | @example "random" {2 | double}; @test; def double []: [number -> number] { $in * 2 }
20 |
--------------------------------------------------------------------------------
/test/input_command.nu:
--------------------------------------------------------------------------------
1 | # local command
2 | ls | get -i name
3 | | length; ls # multiline command
4 | | length
5 | # external command
6 | ^git add (ls
7 | | get -i name)
8 | FOO=BAR BAR=BAZ ^$cmd --arg1=val1 -arg2 arg=value arg=($arg3)
9 | cat unknown.txt o+e> (null-device)
10 |
11 | $hash | $in + "\n" out>> $NUENV_FILE
12 |
--------------------------------------------------------------------------------
/test/input_comment.nu:
--------------------------------------------------------------------------------
1 | # comment at the top
2 |
3 | # comment before command
4 | # multiline
5 | def foo_bar [ # comment at [
6 | foo: string # comment for arg
7 | bar: int # another comment
8 | ] { # comment at {
9 | # comment in body
10 | [
11 | foo # comment in list
12 | # another comment
13 | bar
14 | ];
15 | {
16 | foo: foo # comment in record
17 | # another comment
18 | bar: bar
19 | } # comment at }
20 | ( # comment at (
21 | foo # comment in parenthesis
22 | bar # another comment
23 | )
24 | }
25 |
26 | # top level comment
27 | # multiline
28 |
--------------------------------------------------------------------------------
/test/input_ctrl.nu:
--------------------------------------------------------------------------------
1 | # for
2 | for i in [1, 2, 3] {
3 | # if
4 | if (true or false) {
5 | print "break"; break # break
6 | } else if not false {
7 | print "continue"; continue # continue
8 | }
9 | return 1 # return
10 | }
11 | # alias
12 | alias ll = ls -l # alias comment
13 | # where
14 | ls | where $in.name == 'foo'
15 | | where {|e| $e.item.name !~ $'^($e.index + 1)' }
16 | # match
17 | let foo = { name: 'bar', count: 7 }
18 | match $foo {
19 | { name: 'bar' count: $it } if $it < 5 => ($it + 3), # match arm comment
20 | # match comment
21 | { name: 'bar' count: $it } if not ($it >= 5) => ($it + 7),
22 | _ => {exit 0}
23 | }
24 | # while
25 | mut x = 0; while $x < 10 { $x = $x + 1 }; $x # while comment
26 | # loop
27 | loop { if $x > 10 { break };
28 | $x = $x + 1 }
29 | # try
30 | try {
31 | # error
32 | error make -u {
33 | msg: 'Some error info' }}; print 'Resuming'
34 |
--------------------------------------------------------------------------------
/test/input_data.nu:
--------------------------------------------------------------------------------
1 | export const config = {
2 | default: {
3 | align: center
4 | updates: when_shown
5 | padding_left: 2
6 | padding_right: 2
7 | icon.font: "Sarasa Term SC Nerd:Bold:17.0"
8 | label.font: "Sarasa Term SC Nerd:Bold:12.0"
9 | icon.color: $colors.white
10 | label.color: $colors.fg
11 | icon.padding_left: 8
12 | icon.padding_right: 2
13 | label.padding_left: 2
14 | label.padding_right: 8
15 | background.corner_radius: 10
16 | background.color: $colors.bg
17 | background.border_width: 1
18 | background.border_color: $colors.bg
19 | }
20 |
21 | workspace_default_args: {
22 | icon.font.size: 12
23 | label.font.size: 17
24 | label.shadow.drawing: on
25 | label.shadow.color: $colors.bg
26 | label.shadow.distance: 3
27 | label.highlight_color: $colors.green
28 | background.drawing: off
29 | background.color: $colors.transparent
30 | background.border_width: 2
31 | background.border_color: $colors.fg
32 | background.shadow.drawing: on
33 | background.shadow.color: $colors.bg
34 | background.shadow.distance: 3
35 | }
36 |
37 | plugin_configs: [
38 | {
39 | name: ws_listener
40 | pos: left
41 | events: [aerospace_workspace_change space_windows_change]
42 | args: {
43 | updates: on
44 | drawing: off
45 | script: '{}/aerospace.nu'
46 | }
47 | }
48 | {
49 | name: front_app
50 | pos: left
51 | events: [front_app_switched aerospace_mode_change]
52 | args: {
53 | label.color: $colors.black
54 | icon.color: $colors.black
55 | icon.font.size: 20
56 | background.color: $colors.blue
57 | background.corner_radius: 3
58 | background.shadow.drawing: on
59 | background.shadow.color: $colors.bg
60 | background.shadow.distance: 3
61 | script: '{}/front_app.nu'
62 | }
63 | }
64 | {
65 | name: media
66 | pos: center
67 | events: [media_change]
68 | args: {
69 | icon: ''
70 | icon.color: $colors.fg
71 | label.color: $colors.fg
72 | background.color: $colors.bg
73 | background.border_color: $colors.fg
74 | script: '{}/media.nu'
75 | }
76 | }
77 | {
78 | name: media_cover
79 | pos: center
80 | events: [media_change]
81 | args: {
82 | icon.drawing: off
83 | label.drawing: off
84 | background.image.drawing: on
85 | background.image: media.artwork
86 | background.image.scale: 0.7
87 | background.color: $colors.transparent
88 | background.border_width: 0
89 | }
90 | }
91 | {
92 | name: clock
93 | args: {
94 | update_freq: 30
95 | icon: ''
96 | script: '{}/clock.nu'
97 | background.corner_radius: 3
98 | padding_right: 1
99 | padding_left: 1
100 | label.font.size: 10
101 | }
102 | }
103 | {
104 | name: volume
105 | events: [volume_change]
106 | args: {
107 | script: '{}/volume.nu'
108 | background.corner_radius: 3
109 | padding_right: 1
110 | padding_left: 1
111 | }
112 | }
113 | {
114 | name: battery
115 | events: [system_woke power_source_change]
116 | args: {
117 | update_freq: 120
118 | script: '{}/battery.nu'
119 | click_script: 'open x-apple.systempreferences:com.apple.preference.battery'
120 | background.corner_radius: 3
121 | padding_right: 1
122 | }
123 | }
124 | {
125 | name: separator_right
126 | args: {
127 | icon: ''
128 | padding_left: 0
129 | label.drawing: off
130 | background.drawing: off
131 | click_script: '{}/toggle_stats.nu'
132 | }
133 | }
134 | {
135 | name: disk
136 | args: {
137 | icon: ''
138 | update_freq: 120
139 | script: '{}/disk.nu'
140 | click_script: 'open -a "Disk Utility"'
141 | icon.color: $colors.green
142 | background.border_color: $colors.green
143 | }
144 | }
145 | {
146 | name: cpu
147 | args: {
148 | icon: ''
149 | update_freq: 10
150 | script: '{}/cpu.nu'
151 | click_script: 'open -a "Activity Monitor"'
152 | icon.color: $colors.yellow
153 | background.border_color: $colors.yellow
154 | }
155 | }
156 | {
157 | name: memory
158 | args: {
159 | icon: ''
160 | update_freq: 10
161 | script: '{}/mem.nu'
162 | click_script: 'open -a "Activity Monitor"'
163 | icon.color: $colors.cyan
164 | background.border_color: $colors.cyan
165 | }
166 | }
167 | {
168 | name: temp_cpu
169 | args: {
170 | icon: ''
171 | label.font.size: 7
172 | label.y_offset: -4
173 | icon.font.size: 16
174 | update_freq: 5
175 | padding_left: -58
176 | script: '{}/temp.nu'
177 | click_script: '{}/popups/temp.nu'
178 | popup.align: left
179 | popup.background.border_width: 2
180 | popup.background.corner_radius: 3
181 | popup.background.border_color: $colors.yellow
182 | popup.background.drawing: on
183 | }
184 | popups: [
185 | {
186 | name: temp_fan1
187 | args: {
188 | label: "unk"
189 | icon: ''
190 | }
191 | }
192 | {
193 | name: temp_fan2
194 | args: {
195 | label: "unk"
196 | icon: ''
197 | }
198 | }
199 | {
200 | name: temp_power
201 | args: {
202 | label: "unk"
203 | icon: ''
204 | }
205 | }
206 | ]
207 | }
208 | {
209 | name: temp_gpu
210 | args: {
211 | label.font.size: 7
212 | padding_left: 0
213 | padding_right: 0
214 | icon.font.size: 16
215 | icon.drawing: off
216 | label.y_offset: 4
217 | background.drawing: off
218 | }
219 | }
220 | {
221 | name: network_down
222 | args: {
223 | icon: ''
224 | label.font.size: 7
225 | label.y_offset: -4
226 | update_freq: 3
227 | padding_left: -73
228 | padding_right: 23
229 | script: '{}/network.nu'
230 | icon.color: $colors.purple
231 | label.highlight_color: $colors.purple
232 | background.border_color: $colors.purple
233 | }
234 | }
235 | {
236 | name: network_up
237 | args: {
238 | label.font.size: 7
239 | padding_left: 0
240 | padding_right: 0
241 | icon.drawing: off
242 | label.y_offset: 4
243 | background.drawing: off
244 | label.highlight_color: $colors.purple
245 | }
246 | }
247 | ]
248 | }
249 |
250 | const table = [[a b]; [1 2] [2 [4 4]]]
251 | const table_no_row = [[a b];]
252 |
--------------------------------------------------------------------------------
/test/input_decl.nu:
--------------------------------------------------------------------------------
1 | # decl_extern
2 | export extern hello [name: string] {
3 | $"hello ($name)!"
4 | }
5 | # decl_extern no body block
6 | extern hi [name: string --long (-s) # flags
7 | ]
8 | # env
9 | hide-env ABC
10 | with-env {ABC: 'hello'} {
11 | (
12 | do -i --env {|foo, bar | print $env.ABC
13 | }
14 | foo bar
15 | )
16 | }
17 |
18 | # closure
19 | let cls = {| foo bar baz|
20 | (
21 | $foo +
22 | $bar + $baz
23 | )
24 | }
25 |
26 | # decl_export
27 | export-env {
28 | $env.hello = 'hello'
29 | }
30 |
31 | # decl_def
32 | def "hi there" [where: string]: nothing -> record, bar: int> {
33 | {foo: [["baz"]; [1.0]],
34 | bar: 1}
35 | }
36 |
37 | # decl_use
38 | use greetings.nu hello
39 | export use greetings.nu *
40 | use module [foo bar]
41 | use module ["foo" "bar"]
42 | use module [foo "bar"]
43 | use module ["foo" bar]
44 |
45 | # decl_module
46 | module greetings {
47 | export def hello [name: string] {
48 | $"hello ($name)!"
49 | }
50 |
51 | export def hi [where: string] {
52 | $"hi ($where)!"
53 | }
54 | };
55 |
--------------------------------------------------------------------------------
/test/input_exe.nu:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env nu
2 |
3 | use constants.nu [
4 | colors
5 | get_icon_by_app_name
6 | "foo bar"
7 | ]
8 |
9 | const animate_frames = 30
10 |
11 | def modify_args_per_workspace [
12 | sid: string
13 | focused_sid: string
14 | ]: nothing -> list {
15 | let icons = (aerospace list-windows --workspace $sid --json
16 | | from json | get app-name
17 | | each { $in | get_icon_by_app_name }
18 | | uniq | sort
19 | | str join ' ')
20 | let extra = (if $sid == $focused_sid
21 | {
22 | { highlight: on border_color: $colors.green }
23 | } else {
24 | { highlight: off border_color: $colors.fg }
25 | })
26 |
27 | ['--set' $"space.($sid)"]
28 | | append (if (($icons | is-empty) and ($sid != $focused_sid)) {
29 | [
30 | background.drawing=off
31 | label=
32 | padding_left=-2
33 | padding_right=-2
34 | ]
35 | } else {
36 | [
37 | background.drawing=on
38 | label=($icons)
39 | label.highlight=($extra.highlight)
40 | padding_left=2
41 | padding_right=2
42 | ]
43 | })
44 | | append $"background.border_color=($extra.border_color)"
45 | }
46 |
47 | def workspace_modification_args [
48 | name: string
49 | last_sid: string
50 | ]: nothing -> list {
51 | # use listener's label to store last focused space id
52 | let focused_sid = (aerospace list-workspaces --focused)
53 | let ids_to_modify = (
54 | if ($last_sid | is-empty)
55 | {(aerospace list-workspaces --all | lines)}
56 | else {
57 | [$focused_sid $last_sid]
58 | })
59 | $ids_to_modify
60 | | uniq
61 | | each { modify_args_per_workspace $in $focused_sid }
62 | | flatten
63 | | append ["--set" $name $"label=($focused_sid)"]
64 | }
65 |
66 | # remained for other possible signals
67 | match $env.SENDER {
68 | _ => {
69 | # invisible item to store last focused sid
70 | let last_sid = (sketchybar --query $env.NAME | from json | get label.value)
71 | sketchybar --animate tanh $animate_frames ...(workspace_modification_args $env.NAME $last_sid)
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/test/input_keyword.nu:
--------------------------------------------------------------------------------
1 | overlay use -p --reload __trigger__ as __auto_venv
2 | overlay hide spam
3 | overlay list
4 | overlay new name
5 | overlay hide spam --keep-custom
6 | overlay hide --keep-env [PWD] new_name
7 | hide PWD
8 | hide foo bar
9 | hide-env PWD
10 | hide-env -i PWD PATH
11 | source-env foo.nu
12 |
--------------------------------------------------------------------------------
/test/input_string.nu:
--------------------------------------------------------------------------------
1 | const dir_preview_cmd = "eza --tree -L 3 --color=always {} | head -200"
2 | const file_preview_cmd = "bat -n --color=always --line-range :200 {}"
3 | const default_preview_cmd = "if ({} | path type) == 'dir'" + $' {($dir_preview_cmd)} else {($file_preview_cmd)}'
4 | # TODO: r#''#r verbatim
5 | const help_preview_cmd = "try {help {}} catch {'custom command or alias'}"
6 | const external_tldr_cmd = "try {tldr -C {}} catch {'No doc yet'}"
7 | const hybrid_help_cmd = ("Multiline
8 | string" +
9 | ($external_tldr_cmd | str replace '{}' '(foo)') +
10 | "another multiline
11 | string" +
12 | ($help_preview_cmd | str replace '{}' '(bar)')
13 | )
14 | mut foo = 'foo bar'
15 | $foo += 'baz'
16 | $foo += r#'baz'#
17 |
--------------------------------------------------------------------------------
/toolkit.nu:
--------------------------------------------------------------------------------
1 | use std assert
2 |
3 | const script_pwd = path self .
4 |
5 | def run_ide_check [
6 | file: path
7 | ] {
8 | nu --ide-check 9999 $file
9 | | lines
10 | | each { $in | from json }
11 | | flatten
12 | | where severity? == Error
13 | | reject start end
14 | }
15 |
16 | def print_progress [
17 | ratio: float
18 | length: int = 20
19 | ] {
20 | let done = '▓'
21 | let empty = '░'
22 | let count = [1 (($ratio * $length) | into int)] | math max
23 | (
24 | print -n
25 | ('' | fill -c $done -w $count)
26 | ('' | fill -c $empty -w ($length - $count))
27 | ($ratio * 100 | into string --decimals 0) %
28 | )
29 | }
30 |
31 | # Test the topiary formatter with all nu files within a directory
32 | # each script should pass the idempotent check as well as the linter
33 | export def test_format [
34 | path: path # path to test
35 | ] {
36 | $env.TOPIARY_CONFIG_FILE = ($script_pwd | path join languages.ncl)
37 | $env.TOPIARY_LANGUAGE_DIR = ($script_pwd | path join languages)
38 | let files = if ($path | path type) == 'file' {
39 | [$path]
40 | } else {
41 | glob $'($path | str trim -r -c '/')/**/*.nu'
42 | }
43 | let len = $files | length
44 | if $len == 0 {
45 | print $"No nu scripts found in (ansi yellow)($path).(ansi reset)"
46 | return
47 | }
48 | let all_passed = 1..$len | par-each {|i|
49 | let file = $files | get ($i - 1)
50 | let target = $"(random uuid).nu"
51 | if ($i mod 10) == 0 {
52 | print -n $"(ansi -e '1000D')"
53 | print_progress ($i / $len)
54 | }
55 | let failed = try {
56 | cp $file $target
57 | let err_before = run_ide_check $target
58 | topiary format $target
59 | let err_after = run_ide_check $target
60 | assert ($err_before == $err_after)
61 | true
62 | } catch {
63 | print $"(ansi red)Error detected: (ansi yellow)($file)(ansi reset)"
64 | false
65 | }
66 | rm $target
67 | $failed
68 | }
69 | | all { $in }
70 | if $all_passed {
71 | print -n $"(ansi -e '1000D')"
72 | print $"(ansi green)All nu scripts successfully passed the check, but style issues are still possible.(ansi reset)"
73 | }
74 | }
75 |
--------------------------------------------------------------------------------