Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions dotnu/commands.nu
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
# Public API is controlled by mod.nu which selectively re-exports user-facing commands.
# To make a command public, add it to the export list in mod.nu.

use std/iter scan
# Regex matching a capture point: a pipeline line ending in `| print $in`.
# Why: `find-capture-points` and `execute-and-parse-results` must agree on what a
# capture point is — they are zipped together in `embeds-update`, so any disagreement
# misaligns outputs against the wrong lines.
const capture_point = '\|\s*print\s+\$in\s*$'

# Check .nu module files to determine which commands depend on other commands.
@example 'Analyze command dependencies in a module' {
Expand Down Expand Up @@ -222,9 +226,12 @@ export def 'embeds-update' [
let prevent_second_replacement = " # to-not-be-replaced-again"

$replacements
| reduce --fold $script {|it|
# Why: prepend a newline so a capture point on the very first line is anchored
# by "\n" on both sides like any interior line; without it the match silently no-ops
| reduce --fold ("\n" + $script) {|it|
str replace ("\n" + $it.0 + "\n") ("\n" + $it.0 + $prevent_second_replacement + "\n" + $it.1 + "\n")
}
| str replace -r '^\n' '' # strip the single leading newline added above
| str replace -a $prevent_second_replacement ''
| str replace -ar '\n{3,}' "\n\n"
| str replace -r "\n*$" "\n"
Expand Down Expand Up @@ -802,8 +809,11 @@ export def 'module-commands-code-to-record' [
}
| merge (
$in.command
| scan --noinit null {|i acc|
if $i == null { $acc } else { $i }
# Why: std `scan` with a null seed now errors — its internal `generate` treats
# null as "no initial value". Forward-fill the last non-null name manually instead.
| reduce --fold [] {|i acc|
let prev = $acc | last
$acc | append (if $i == null { $prev } else { $i })
}
| wrap command
)
Expand Down Expand Up @@ -958,7 +968,7 @@ export def execute-and-parse-results [
| each {
if $in !~ '^\s*#' {
# don't search for `print $in` inside of commented lines
str replace -r '\| *print +\$in *' '| embed-in-script'
str replace -r $capture_point '| embed-in-script'
} else { }
}
| prepend $embed_in_script_src
Expand All @@ -978,7 +988,7 @@ export def execute-and-parse-results [
# Finds lines where embed-in-script is used in the script
export def find-capture-points [] {
lines
| where $it !~ '^\s*#' and $it =~ '\|\s?print \$in *$'
| where $it !~ '^\s*#' and $it =~ $capture_point
}

# Removes annotation lines starting with "# => " from the script
Expand Down
4 changes: 2 additions & 2 deletions tests/assets/dotnu-capture-clean.nu
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
# And and this is the link on dotnu module:
# https://github.com/nushell-prophet/dotnu

ls | sort-by modified -r | last 2 | print $in
40 + 2 | print $in

random int | print $in
[[name type]; [foo file] [bar dir]] | print $in

'Say hello to the core team of the Nushell'
| str replace 'Nushell' 'Best shell'
Expand Down
18 changes: 9 additions & 9 deletions tests/assets/dotnu-capture-updated.nu
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@
# And and this is the link on dotnu module:
# https://github.com/nushell-prophet/dotnu

ls | sort-by modified -r | last 2 | print $in
# => ╭───┬──────────────────────────────┬──────┬───────┬────────────╮
# => │ # │ name │ type │ size │ modified │
# => ├───┼──────────────────────────────┼──────┼───────┼────────────┤
# => │ 0 │ set-x-demo.nu │ file │ 41 B │ a year ago │
# => │ 1 │ parsing-pipe-in-docstring.nu │ file │ 923 B │ a year ago │
# => ╰───┴──────────────────────────────┴──────┴───────┴────────────╯
40 + 2 | print $in
# => 42

random int | print $in
# => 2835756183325042638
[[name type]; [foo file] [bar dir]] | print $in
# => ╭───┬──────┬──────╮
# => │ # │ name │ type │
# => ├───┼──────┼──────┤
# => │ 0 │ foo │ file │
# => │ 1 │ bar │ dir │
# => ╰───┴──────┴──────╯

'Say hello to the core team of the Nushell'
| str replace 'Nushell' 'Best shell'
Expand Down
13 changes: 5 additions & 8 deletions tests/assets/dotnu-capture.nu
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,13 @@
# And and this is the link on dotnu module:
# https://github.com/nushell-prophet/dotnu

ls | sort-by modified -r | last 2 | print $in
# => ╭─#─┬──────name──────┬─type─┬─size──┬───modified───╮
# => │ 0 │ zzz_md_backups │ dir │ 160 B │ 2 months ago │
# => │ 1 │ test.nu │ file │ 45 B │ 3 months ago │
# => ╰─#─┴──────name──────┴─type─┴─size──┴───modified───╯
40 + 2 | print $in
# => 0

random int | print $in
# => 6970240173764648305
[[name type]; [foo file] [bar dir]] | print $in
# => stale

'Say hello to the core team of the Nushell'
| str replace 'Nushell' 'Best shell'
| print $in
# => Say hello to the core team of the Best shell
# => WRONG
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,21 @@
# | where caller in $public_api
# | select caller
#
# # Output as yaml
# # Why: serialize as nuon, not yaml — `to yaml`'s list indentation
# # changed between nushell versions and drifted this snapshot on
# # identical data. nuon compares the parsed structure stably.
# {
# public_api_count: ($public_api | length)
# tested_count: (($public_api | length) - ($untested | length))
# untested: ($untested | get caller)
# }
# | to yaml
public_api_count: 15
tested_count: 12
untested:
- embed-add
- embeds-capture-start
- embeds-capture-stop
# | to nuon --indent 2
{
public_api_count: 15,
tested_count: 12,
untested: [
embed-add,
embeds-capture-start,
embeds-capture-stop
]
}
9 changes: 9 additions & 0 deletions tests/test_commands.nu
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,15 @@ def "embeds-update updates embeds in piped script" [] {
assert ($result =~ '# => HELLO')
}

@test
def "embeds-update annotates a capture point on the first line" [] {
# No leading newline before the capture point
let script = "'hello' | str upcase | print $in\n"
let result = $script | embeds-update

assert ($result =~ '# => HELLO')
}

@test
def "embeds-update preserves script structure" [] {
let script = "# comment\n\n1 + 1 | print $in\n\n# another"
Expand Down
22 changes: 16 additions & 6 deletions toolkit.nu
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export def 'main test-integration' [
}
)
(
run-snapshot-test 'coverage' ([tests output-yaml coverage-untested.yaml] | path join) {
run-snapshot-test 'coverage' ([tests output-yaml coverage-untested.nuon] | path join) {
# Public API from mod.nu
let public_api = open ([dotnu mod.nu] | path join)
| lines
Expand All @@ -119,13 +119,15 @@ export def 'main test-integration' [
| where caller in $public_api
| select caller

# Output as yaml
# Why: serialize as nuon, not yaml — `to yaml`'s list indentation
# changed between nushell versions and drifted this snapshot on
# identical data. nuon compares the parsed structure stably.
{
public_api_count: ($public_api | length)
tested_count: (($public_api | length) - ($untested | length))
untested: ($untested | get caller)
}
| to yaml
| to nuon --indent 2
}
)
]
Expand Down Expand Up @@ -177,9 +179,17 @@ def run-snapshot-test [name: string output_file: string command_src: closure] {
$command_text + (char nl) + (do $command_src)
| save -f $output_file

# Check git diff to determine status
let diff_result = do { ^git diff --quiet $output_file } | complete
let status = if $diff_result.exit_code == 0 { 'passed' } else { 'changed' }
# Check git diff to determine status.
# Why: `git diff` ignores untracked files, so a brand-new snapshot would
# report 'passed' without ever being compared to a baseline. Treat an
# untracked file as 'changed' so it surfaces and `--update` stages it.
let tracked = do { ^git ls-files --error-unmatch $output_file } | complete | get exit_code | $in == 0
let status = if not $tracked {
'changed'
} else {
let diff_result = do { ^git diff --quiet $output_file } | complete
if $diff_result.exit_code == 0 { 'passed' } else { 'changed' }
}

{type: 'integration' name: $name status: $status file: $output_file}
} catch {|err|
Expand Down
Loading