Browse Source

Move all elvdocs into .d.elv files.

The elvdocs still use the old format (#elvdoc:fn or #elvdoc:var) for now, but
will be changed to "fn" and "var" forms soon.

Also remove the accidentally committed cmd/mvelvdoc. It has been used to perform
the conversion automatically but is not supposed to be committed.
Qi Xiao 1 year ago
parent
commit
a3f4384495
91 changed files with 5919 additions and 6012 deletions
  1. 0 48
      cmd/mvelvdoc/main.go
  2. 221 0
      pkg/edit/buffer_builtins.d.elv
  3. 0 222
      pkg/edit/buffer_builtins.go
  4. 114 0
      pkg/edit/builtins.d.elv
  5. 0 115
      pkg/edit/builtins.go
  6. 17 0
      pkg/edit/command_api.d.elv
  7. 0 18
      pkg/edit/command_api.go
  8. 80 0
      pkg/edit/complete_getopt.d.elv
  9. 0 81
      pkg/edit/complete_getopt.go
  10. 135 0
      pkg/edit/completion.d.elv
  11. 0 136
      pkg/edit/completion.go
  12. 34 0
      pkg/edit/config_api.d.elv
  13. 0 35
      pkg/edit/config_api.go
  14. 4 0
      pkg/edit/editor.d.elv
  15. 0 5
      pkg/edit/editor.go
  16. 49 0
      pkg/edit/histwalk.d.elv
  17. 0 50
      pkg/edit/histwalk.go
  18. 114 0
      pkg/edit/insert_api.d.elv
  19. 0 115
      pkg/edit/insert_api.go
  20. 16 0
      pkg/edit/instant.d.elv
  21. 0 17
      pkg/edit/instant.go
  22. 98 0
      pkg/edit/listing.d.elv
  23. 0 99
      pkg/edit/listing.go
  24. 7 0
      pkg/edit/listing_custom.d.elv
  25. 0 8
      pkg/edit/listing_custom.go
  26. 57 0
      pkg/edit/navigation.d.elv
  27. 0 58
      pkg/edit/navigation.go
  28. 35 0
      pkg/edit/prompt.d.elv
  29. 0 36
      pkg/edit/prompt.go
  30. 25 0
      pkg/edit/repl.d.elv
  31. 0 26
      pkg/edit/repl.go
  32. 29 0
      pkg/edit/state_api.d.elv
  33. 0 30
      pkg/edit/state_api.go
  34. 34 0
      pkg/edit/store_api.d.elv
  35. 0 35
      pkg/edit/store_api.go
  36. 98 0
      pkg/edit/vars.d.elv
  37. 0 99
      pkg/edit/vars.go
  38. 56 0
      pkg/eval/builtin_fn_cmd.d.elv
  39. 0 57
      pkg/eval/builtin_fn_cmd.go
  40. 12 0
      pkg/eval/builtin_fn_cmd_unix.d.elv
  41. 0 13
      pkg/eval/builtin_fn_cmd_unix.go
  42. 189 0
      pkg/eval/builtin_fn_container.d.elv
  43. 0 190
      pkg/eval/builtin_fn_container.go
  44. 69 0
      pkg/eval/builtin_fn_debug.d.elv
  45. 0 70
      pkg/eval/builtin_fn_debug.go
  46. 86 0
      pkg/eval/builtin_fn_env.d.elv
  47. 0 87
      pkg/eval/builtin_fn_env.go
  48. 263 0
      pkg/eval/builtin_fn_flow.d.elv
  49. 0 264
      pkg/eval/builtin_fn_flow.go
  50. 35 0
      pkg/eval/builtin_fn_fs.d.elv
  51. 0 36
      pkg/eval/builtin_fn_fs.go
  52. 472 0
      pkg/eval/builtin_fn_io.d.elv
  53. 0 473
      pkg/eval/builtin_fn_io.go
  54. 382 0
      pkg/eval/builtin_fn_misc.d.elv
  55. 0 383
      pkg/eval/builtin_fn_misc.go
  56. 437 0
      pkg/eval/builtin_fn_num.d.elv
  57. 0 438
      pkg/eval/builtin_fn_num.go
  58. 173 0
      pkg/eval/builtin_fn_pred.d.elv
  59. 0 174
      pkg/eval/builtin_fn_pred.go
  60. 121 0
      pkg/eval/builtin_fn_str.d.elv
  61. 0 122
      pkg/eval/builtin_fn_str.go
  62. 294 0
      pkg/eval/builtin_fn_stream.d.elv
  63. 0 295
      pkg/eval/builtin_fn_stream.go
  64. 126 0
      pkg/eval/builtin_fn_styled.d.elv
  65. 0 127
      pkg/eval/builtin_fn_styled.go
  66. 94 0
      pkg/eval/builtin_ns.d.elv
  67. 0 95
      pkg/eval/builtin_ns.go
  68. 59 0
      pkg/eval/eval.d.elv
  69. 0 60
      pkg/eval/eval.go
  70. 111 0
      pkg/mods/file/file.d.elv
  71. 0 112
      pkg/mods/file/file.go
  72. 158 0
      pkg/mods/flag/flag.d.elv
  73. 0 159
      pkg/mods/flag/flag.go
  74. 538 0
      pkg/mods/math/math.d.elv
  75. 0 539
      pkg/mods/math/math.go
  76. 253 0
      pkg/mods/path/path.d.elv
  77. 0 254
      pkg/mods/path/path.go
  78. 46 0
      pkg/mods/platform/platform.d.elv
  79. 0 47
      pkg/mods/platform/platform.go
  80. 115 0
      pkg/mods/re/re.d.elv
  81. 0 116
      pkg/mods/re/re.go
  82. 44 0
      pkg/mods/runtime/runtime.d.elv
  83. 0 45
      pkg/mods/runtime/runtime.go
  84. 79 0
      pkg/mods/store/store.d.elv
  85. 0 80
      pkg/mods/store/store.go
  86. 452 0
      pkg/mods/str/str.d.elv
  87. 0 453
      pkg/mods/str/str.go
  88. 69 0
      pkg/mods/unix/rlimit.d.elv
  89. 0 70
      pkg/mods/unix/rlimit.go
  90. 19 0
      pkg/mods/unix/umask.d.elv
  91. 0 20
      pkg/mods/unix/umask.go

+ 0 - 48
cmd/mvelvdoc/main.go

@@ -1,48 +0,0 @@
-package main
-
-import (
-	"log"
-	"os"
-	"path/filepath"
-	"strings"
-)
-
-func main() {
-	for _, goFile := range os.Args[1:] {
-		bs, err := os.ReadFile(goFile)
-		handleErr("read file:", err)
-
-		var goLines, elvLines []string
-
-		lines := strings.Split(string(bs), "\n")
-		for i := 0; i < len(lines); i++ {
-			if !strings.HasPrefix(lines[i], "//elvdoc:") {
-				goLines = append(goLines, lines[i])
-				continue
-			}
-			if len(elvLines) > 0 {
-				elvLines = append(elvLines, "")
-			}
-			elvLines = append(elvLines, "#"+lines[i][2:])
-			i++
-			for i < len(lines) && strings.HasPrefix(lines[i], "//") {
-				elvLines = append(elvLines, "#"+lines[i][2:])
-				i++
-			}
-			i--
-		}
-
-		os.WriteFile(goFile, []byte(strings.Join(goLines, "\n")), 0o644)
-		if len(elvLines) > 0 {
-			elvFile := goFile[:len(goFile)-len(filepath.Ext(goFile))] + ".d.elv"
-			elvLines = append(elvLines, "")
-			os.WriteFile(elvFile, []byte(strings.Join(elvLines, "\n")), 0o644)
-		}
-	}
-}
-
-func handleErr(s string, err error) {
-	if err != nil {
-		log.Fatalln(s, err)
-	}
-}

+ 221 - 0
pkg/edit/buffer_builtins.d.elv

@@ -0,0 +1,221 @@
+#elvdoc:fn move-dot-left
+#
+# ```elvish
+# edit:move-dot-left
+# ```
+#
+# Moves the dot left one rune. Does nothing if the dot is at the beginning of
+# the buffer.
+
+#elvdoc:fn kill-rune-left
+#
+# ```elvish
+# edit:kill-rune-left
+# ```
+#
+# Kills one rune left of the dot. Does nothing if the dot is at the beginning of
+# the buffer.
+
+#elvdoc:fn move-dot-right
+#
+# ```elvish
+# edit:move-dot-right
+# ```
+#
+# Moves the dot right one rune. Does nothing if the dot is at the end of the
+# buffer.
+
+#elvdoc:fn kill-rune-left
+#
+# ```elvish
+# edit:kill-rune-left
+# ```
+#
+# Kills one rune right of the dot. Does nothing if the dot is at the end of the
+# buffer.
+
+#elvdoc:fn move-dot-sol
+#
+# ```elvish
+# edit:move-dot-sol
+# ```
+#
+# Moves the dot to the start of the current line.
+
+#elvdoc:fn kill-line-left
+#
+# ```elvish
+# edit:kill-line-left
+# ```
+#
+# Deletes the text between the dot and the start of the current line.
+
+#elvdoc:fn move-dot-eol
+#
+# ```elvish
+# edit:move-dot-eol
+# ```
+#
+# Moves the dot to the end of the current line.
+
+#elvdoc:fn kill-line-right
+#
+# ```elvish
+# edit:kill-line-right
+# ```
+#
+# Deletes the text between the dot and the end of the current line.
+
+#elvdoc:fn move-dot-up
+#
+# ```elvish
+# edit:move-dot-up
+# ```
+#
+# Moves the dot up one line, trying to preserve the visual horizontal position.
+# Does nothing if dot is already on the first line of the buffer.
+
+#elvdoc:fn move-dot-down
+#
+# ```elvish
+# edit:move-dot-down
+# ```
+#
+# Moves the dot down one line, trying to preserve the visual horizontal
+# position. Does nothing if dot is already on the last line of the buffer.
+
+#elvdoc:fn transpose-rune
+#
+# ```elvish
+# edit:transpose-rune
+# ```
+#
+# Swaps the runes to the left and right of the dot. If the dot is at the
+# beginning of the buffer, swaps the first two runes, and if the dot is at the
+# end, it swaps the last two.
+
+#elvdoc:fn move-dot-left-word
+#
+# ```elvish
+# edit:move-dot-left-word
+# ```
+#
+# Moves the dot to the beginning of the last word to the left of the dot.
+
+#elvdoc:fn kill-word-left
+#
+# ```elvish
+# edit:kill-word-left
+# ```
+#
+# Deletes the last word to the left of the dot.
+
+#elvdoc:fn move-dot-right-word
+#
+# ```elvish
+# edit:move-dot-right-word
+# ```
+#
+# Moves the dot to the beginning of the first word to the right of the dot.
+
+#elvdoc:fn kill-word-right
+#
+# ```elvish
+# edit:kill-word-right
+# ```
+#
+# Deletes the first word to the right of the dot.
+
+#elvdoc:fn transpose-word
+#
+# ```elvish
+# edit:transpose-word
+# ```
+#
+# Swaps the words to the left and right of the dot. If the dot is at the
+# beginning of the buffer, swaps the first two words, and the dot is at the
+# end, it swaps the last two.
+
+#elvdoc:fn move-dot-left-small-word
+#
+# ```elvish
+# edit:move-dot-left-small-word
+# ```
+#
+# Moves the dot to the beginning of the last small word to the left of the dot.
+
+#elvdoc:fn kill-small-word-left
+#
+# ```elvish
+# edit:kill-small-word-left
+# ```
+#
+# Deletes the last small word to the left of the dot.
+
+#elvdoc:fn move-dot-right-small-word
+#
+# ```elvish
+# edit:move-dot-right-small-word
+# ```
+#
+# Moves the dot to the beginning of the first small word to the right of the dot.
+
+#elvdoc:fn kill-small-word-right
+#
+# ```elvish
+# edit:kill-small-word-right
+# ```
+#
+# Deletes the first small word to the right of the dot.
+
+#elvdoc:fn transpose-small-word
+#
+# ```elvish
+# edit:transpose-small-word
+# ```
+#
+# Swaps the small words to the left and right of the dot. If the dot is at the
+# beginning of the buffer, it swaps the first two small words, and if the dot
+# is at the end, it swaps the last two.
+
+#elvdoc:fn move-dot-left-alnum-word
+#
+# ```elvish
+# edit:move-dot-left-alnum-word
+# ```
+#
+# Moves the dot to the beginning of the last alnum word to the left of the dot.
+
+#elvdoc:fn kill-alnum-word-left
+#
+# ```elvish
+# edit:kill-alnum-word-left
+# ```
+#
+# Deletes the last alnum word to the left of the dot.
+
+#elvdoc:fn move-dot-right-alnum-word
+#
+# ```elvish
+# edit:move-dot-right-alnum-word
+# ```
+#
+# Moves the dot to the beginning of the first alnum word to the right of the dot.
+
+#elvdoc:fn kill-alnum-word-right
+#
+# ```elvish
+# edit:kill-alnum-word-right
+# ```
+#
+# Deletes the first alnum word to the right of the dot.
+
+#elvdoc:fn transpose-alnum-word
+#
+# ```elvish
+# edit:transpose-alnum-word
+# ```
+#
+# Swaps the alnum words to the left and right of the dot. If the dot is at the
+# beginning of the buffer, it swaps the first two alnum words, and if the dot
+# is at the end, it swaps the last two.

+ 0 - 222
pkg/edit/buffer_builtins.go

@@ -100,101 +100,24 @@ func makeTransform(t pureTransformer) func(*tk.CodeBuffer) {
 
 // Implementation of pure movers.
 
-//elvdoc:fn move-dot-left
-//
-// ```elvish
-// edit:move-dot-left
-// ```
-//
-// Moves the dot left one rune. Does nothing if the dot is at the beginning of
-// the buffer.
-
-//elvdoc:fn kill-rune-left
-//
-// ```elvish
-// edit:kill-rune-left
-// ```
-//
-// Kills one rune left of the dot. Does nothing if the dot is at the beginning of
-// the buffer.
-
 func moveDotLeft(buffer string, dot int) int {
 	_, w := utf8.DecodeLastRuneInString(buffer[:dot])
 	return dot - w
 }
 
-//elvdoc:fn move-dot-right
-//
-// ```elvish
-// edit:move-dot-right
-// ```
-//
-// Moves the dot right one rune. Does nothing if the dot is at the end of the
-// buffer.
-
-//elvdoc:fn kill-rune-left
-//
-// ```elvish
-// edit:kill-rune-left
-// ```
-//
-// Kills one rune right of the dot. Does nothing if the dot is at the end of the
-// buffer.
-
 func moveDotRight(buffer string, dot int) int {
 	_, w := utf8.DecodeRuneInString(buffer[dot:])
 	return dot + w
 }
 
-//elvdoc:fn move-dot-sol
-//
-// ```elvish
-// edit:move-dot-sol
-// ```
-//
-// Moves the dot to the start of the current line.
-
-//elvdoc:fn kill-line-left
-//
-// ```elvish
-// edit:kill-line-left
-// ```
-//
-// Deletes the text between the dot and the start of the current line.
-
 func moveDotSOL(buffer string, dot int) int {
 	return strutil.FindLastSOL(buffer[:dot])
 }
 
-//elvdoc:fn move-dot-eol
-//
-// ```elvish
-// edit:move-dot-eol
-// ```
-//
-// Moves the dot to the end of the current line.
-
-//elvdoc:fn kill-line-right
-//
-// ```elvish
-// edit:kill-line-right
-// ```
-//
-// Deletes the text between the dot and the end of the current line.
-
 func moveDotEOL(buffer string, dot int) int {
 	return strutil.FindFirstEOL(buffer[dot:]) + dot
 }
 
-//elvdoc:fn move-dot-up
-//
-// ```elvish
-// edit:move-dot-up
-// ```
-//
-// Moves the dot up one line, trying to preserve the visual horizontal position.
-// Does nothing if dot is already on the first line of the buffer.
-
 func moveDotUp(buffer string, dot int) int {
 	sol := strutil.FindLastSOL(buffer[:dot])
 	if sol == 0 {
@@ -207,15 +130,6 @@ func moveDotUp(buffer string, dot int) int {
 	return prevSOL + len(wcwidth.Trim(buffer[prevSOL:prevEOL], width))
 }
 
-//elvdoc:fn move-dot-down
-//
-// ```elvish
-// edit:move-dot-down
-// ```
-//
-// Moves the dot down one line, trying to preserve the visual horizontal
-// position. Does nothing if dot is already on the last line of the buffer.
-
 func moveDotDown(buffer string, dot int) int {
 	eol := strutil.FindFirstEOL(buffer[dot:]) + dot
 	if eol == len(buffer) {
@@ -229,16 +143,6 @@ func moveDotDown(buffer string, dot int) int {
 	return nextSOL + len(wcwidth.Trim(buffer[nextSOL:nextEOL], width))
 }
 
-//elvdoc:fn transpose-rune
-//
-// ```elvish
-// edit:transpose-rune
-// ```
-//
-// Swaps the runes to the left and right of the dot. If the dot is at the
-// beginning of the buffer, swaps the first two runes, and if the dot is at the
-// end, it swaps the last two.
-
 func transposeRunes(buffer string, dot int) (string, int) {
 	if len(buffer) == 0 {
 		return buffer, dot
@@ -274,56 +178,14 @@ func transposeRunes(buffer string, dot int) (string, int) {
 	return newBuffer, newDot
 }
 
-//elvdoc:fn move-dot-left-word
-//
-// ```elvish
-// edit:move-dot-left-word
-// ```
-//
-// Moves the dot to the beginning of the last word to the left of the dot.
-
-//elvdoc:fn kill-word-left
-//
-// ```elvish
-// edit:kill-word-left
-// ```
-//
-// Deletes the last word to the left of the dot.
-
 func moveDotLeftWord(buffer string, dot int) int {
 	return moveDotLeftGeneralWord(categorizeWord, buffer, dot)
 }
 
-//elvdoc:fn move-dot-right-word
-//
-// ```elvish
-// edit:move-dot-right-word
-// ```
-//
-// Moves the dot to the beginning of the first word to the right of the dot.
-
-//elvdoc:fn kill-word-right
-//
-// ```elvish
-// edit:kill-word-right
-// ```
-//
-// Deletes the first word to the right of the dot.
-
 func moveDotRightWord(buffer string, dot int) int {
 	return moveDotRightGeneralWord(categorizeWord, buffer, dot)
 }
 
-//elvdoc:fn transpose-word
-//
-// ```elvish
-// edit:transpose-word
-// ```
-//
-// Swaps the words to the left and right of the dot. If the dot is at the
-// beginning of the buffer, swaps the first two words, and the dot is at the
-// end, it swaps the last two.
-
 func transposeWord(buffer string, dot int) (string, int) {
 	return transposeGeneralWord(categorizeWord, buffer, dot)
 }
@@ -337,110 +199,26 @@ func categorizeWord(r rune) int {
 	}
 }
 
-//elvdoc:fn move-dot-left-small-word
-//
-// ```elvish
-// edit:move-dot-left-small-word
-// ```
-//
-// Moves the dot to the beginning of the last small word to the left of the dot.
-
-//elvdoc:fn kill-small-word-left
-//
-// ```elvish
-// edit:kill-small-word-left
-// ```
-//
-// Deletes the last small word to the left of the dot.
-
 func moveDotLeftSmallWord(buffer string, dot int) int {
 	return moveDotLeftGeneralWord(tk.CategorizeSmallWord, buffer, dot)
 }
 
-//elvdoc:fn move-dot-right-small-word
-//
-// ```elvish
-// edit:move-dot-right-small-word
-// ```
-//
-// Moves the dot to the beginning of the first small word to the right of the dot.
-
-//elvdoc:fn kill-small-word-right
-//
-// ```elvish
-// edit:kill-small-word-right
-// ```
-//
-// Deletes the first small word to the right of the dot.
-
 func moveDotRightSmallWord(buffer string, dot int) int {
 	return moveDotRightGeneralWord(tk.CategorizeSmallWord, buffer, dot)
 }
 
-//elvdoc:fn transpose-small-word
-//
-// ```elvish
-// edit:transpose-small-word
-// ```
-//
-// Swaps the small words to the left and right of the dot. If the dot is at the
-// beginning of the buffer, it swaps the first two small words, and if the dot
-// is at the end, it swaps the last two.
-
 func transposeSmallWord(buffer string, dot int) (string, int) {
 	return transposeGeneralWord(tk.CategorizeSmallWord, buffer, dot)
 }
 
-//elvdoc:fn move-dot-left-alnum-word
-//
-// ```elvish
-// edit:move-dot-left-alnum-word
-// ```
-//
-// Moves the dot to the beginning of the last alnum word to the left of the dot.
-
-//elvdoc:fn kill-alnum-word-left
-//
-// ```elvish
-// edit:kill-alnum-word-left
-// ```
-//
-// Deletes the last alnum word to the left of the dot.
-
 func moveDotLeftAlnumWord(buffer string, dot int) int {
 	return moveDotLeftGeneralWord(categorizeAlnum, buffer, dot)
 }
 
-//elvdoc:fn move-dot-right-alnum-word
-//
-// ```elvish
-// edit:move-dot-right-alnum-word
-// ```
-//
-// Moves the dot to the beginning of the first alnum word to the right of the dot.
-
-//elvdoc:fn kill-alnum-word-right
-//
-// ```elvish
-// edit:kill-alnum-word-right
-// ```
-//
-// Deletes the first alnum word to the right of the dot.
-
 func moveDotRightAlnumWord(buffer string, dot int) int {
 	return moveDotRightGeneralWord(categorizeAlnum, buffer, dot)
 }
 
-//elvdoc:fn transpose-alnum-word
-//
-// ```elvish
-// edit:transpose-alnum-word
-// ```
-//
-// Swaps the alnum words to the left and right of the dot. If the dot is at the
-// beginning of the buffer, it swaps the first two alnum words, and if the dot
-// is at the end, it swaps the last two.
-
 func transposeAlnumWord(buffer string, dot int) (string, int) {
 	return transposeGeneralWord(categorizeAlnum, buffer, dot)
 }

+ 114 - 0
pkg/edit/builtins.d.elv

@@ -0,0 +1,114 @@
+#elvdoc:fn binding-table
+#
+# ```elvish
+# edit:binding-table $map
+# ```
+#
+# Converts a normal map into a binding map.
+
+#elvdoc:fn close-mode
+#
+# ```elvish
+# edit:close-mode
+# ```
+#
+# Closes the current active mode.
+
+#elvdoc:fn end-of-history
+#
+# ```elvish
+# edit:end-of-history
+# ```
+#
+# Adds a notification saying "End of history".
+
+#elvdoc:fn redraw
+#
+# ```elvish
+# edit:redraw &full=$false
+# ```
+#
+# Triggers a redraw.
+#
+# The `&full` option controls whether to do a full redraw. By default, all
+# redraws performed by the line editor are incremental redraws, updating only
+# the part of the screen that has changed from the last redraw. A full redraw
+# updates the entire command line.
+
+#elvdoc:fn clear
+#
+# ```elvish
+# edit:clear
+# ```
+#
+# Clears the screen.
+#
+# This command should be used in place of the external `clear` command to clear
+# the screen.
+
+#elvdoc:fn insert-raw
+#
+# ```elvish
+# edit:insert-raw
+# ```
+#
+# Requests the next terminal input to be inserted uninterpreted.
+
+#elvdoc:fn key
+#
+# ```elvish
+# edit:key $string
+# ```
+#
+# Parses a string into a key.
+
+#elvdoc:fn notify
+#
+# ```elvish
+# edit:notify $message
+# ```
+#
+# Prints a notification message. The argument may be a string or a [styled
+# text](builtin.html#styled).
+#
+# If called while the editor is active, this will print the message above the
+# editor, and redraw the editor.
+#
+# If called while the editor is inactive, the message will be queued, and shown
+# once the editor becomes active.
+
+#elvdoc:fn return-line
+#
+# ```elvish
+# edit:return-line
+# ```
+#
+# Causes the Elvish REPL to end the current read iteration and evaluate the
+# code it just read. If called from a key binding, takes effect after the key
+# binding returns.
+
+#elvdoc:fn return-eof
+#
+# ```elvish
+# edit:return-eof
+# ```
+#
+# Causes the Elvish REPL to terminate. If called from a key binding, takes
+# effect after the key binding returns.
+
+#elvdoc:fn smart-enter
+#
+# ```elvish
+# edit:smart-enter
+# ```
+#
+# Inserts a literal newline if the current code is not syntactically complete
+# Elvish code. Accepts the current line otherwise.
+
+#elvdoc:fn wordify
+#
+# ```elvish
+# edit:wordify $code
+# ```
+#
+# Breaks Elvish code into words.

+ 0 - 115
pkg/edit/builtins.go

@@ -15,51 +15,14 @@ import (
 	"src.elv.sh/pkg/ui"
 )
 
-//elvdoc:fn binding-table
-//
-// ```elvish
-// edit:binding-table $map
-// ```
-//
-// Converts a normal map into a binding map.
-
-//elvdoc:fn close-mode
-//
-// ```elvish
-// edit:close-mode
-// ```
-//
-// Closes the current active mode.
-
 func closeMode(app cli.App) {
 	app.PopAddon()
 }
 
-//elvdoc:fn end-of-history
-//
-// ```elvish
-// edit:end-of-history
-// ```
-//
-// Adds a notification saying "End of history".
-
 func endOfHistory(app cli.App) {
 	app.Notify(ui.T("End of history"))
 }
 
-//elvdoc:fn redraw
-//
-// ```elvish
-// edit:redraw &full=$false
-// ```
-//
-// Triggers a redraw.
-//
-// The `&full` option controls whether to do a full redraw. By default, all
-// redraws performed by the line editor are incremental redraws, updating only
-// the part of the screen that has changed from the last redraw. A full redraw
-// updates the entire command line.
-
 type redrawOpts struct{ Full bool }
 
 func (redrawOpts) SetDefaultOptions() {}
@@ -72,17 +35,6 @@ func redraw(app cli.App, opts redrawOpts) {
 	}
 }
 
-//elvdoc:fn clear
-//
-// ```elvish
-// edit:clear
-// ```
-//
-// Clears the screen.
-//
-// This command should be used in place of the external `clear` command to clear
-// the screen.
-
 func clear(app cli.App, tty cli.TTY) {
 	tty.HideCursor()
 	tty.ClearScreen()
@@ -90,14 +42,6 @@ func clear(app cli.App, tty cli.TTY) {
 	tty.ShowCursor()
 }
 
-//elvdoc:fn insert-raw
-//
-// ```elvish
-// edit:insert-raw
-// ```
-//
-// Requests the next terminal input to be inserted uninterpreted.
-
 func insertRaw(app cli.App, tty cli.TTY) {
 	codeArea, ok := focusedCodeArea(app)
 	if !ok {
@@ -122,14 +66,6 @@ func insertRaw(app cli.App, tty cli.TTY) {
 	app.PushAddon(w)
 }
 
-//elvdoc:fn key
-//
-// ```elvish
-// edit:key $string
-// ```
-//
-// Parses a string into a key.
-
 var errMustBeKeyOrString = errors.New("must be key or string")
 
 func toKey(v any) (ui.Key, error) {
@@ -143,21 +79,6 @@ func toKey(v any) (ui.Key, error) {
 	}
 }
 
-//elvdoc:fn notify
-//
-// ```elvish
-// edit:notify $message
-// ```
-//
-// Prints a notification message. The argument may be a string or a [styled
-// text](builtin.html#styled).
-//
-// If called while the editor is active, this will print the message above the
-// editor, and redraw the editor.
-//
-// If called while the editor is inactive, the message will be queued, and shown
-// once the editor becomes active.
-
 func notify(app cli.App, x any) error {
 	// TODO: De-duplicate with the implementation of the styled builtin.
 	var t ui.Text
@@ -174,34 +95,6 @@ func notify(app cli.App, x any) error {
 	return nil
 }
 
-//elvdoc:fn return-line
-//
-// ```elvish
-// edit:return-line
-// ```
-//
-// Causes the Elvish REPL to end the current read iteration and evaluate the
-// code it just read. If called from a key binding, takes effect after the key
-// binding returns.
-
-//elvdoc:fn return-eof
-//
-// ```elvish
-// edit:return-eof
-// ```
-//
-// Causes the Elvish REPL to terminate. If called from a key binding, takes
-// effect after the key binding returns.
-
-//elvdoc:fn smart-enter
-//
-// ```elvish
-// edit:smart-enter
-// ```
-//
-// Inserts a literal newline if the current code is not syntactically complete
-// Elvish code. Accepts the current line otherwise.
-
 func smartEnter(app cli.App) {
 	codeArea, ok := focusedCodeArea(app)
 	if !ok {
@@ -233,14 +126,6 @@ func isSyntaxComplete(code string) bool {
 	return true
 }
 
-//elvdoc:fn wordify
-//
-// ```elvish
-// edit:wordify $code
-// ```
-//
-// Breaks Elvish code into words.
-
 func wordify(fm *eval.Frame, code string) error {
 	out := fm.ValueOutput()
 	for _, s := range parseutil.Wordify(code) {

+ 17 - 0
pkg/edit/command_api.d.elv

@@ -0,0 +1,17 @@
+#elvdoc:var command:binding
+#
+# Key bindings for command mode. This is currently a very small subset of Vi
+# command mode bindings.
+#
+# @cf edit:command:start
+
+#elvdoc:fn command:start
+#
+# ```elvish
+# edit:command:start
+# ```
+#
+# Enter command mode. This mode is intended to emulate Vi's command mode, but
+# it is very incomplete right now.
+#
+# @cf $edit:command:binding

+ 0 - 18
pkg/edit/command_api.go

@@ -7,24 +7,6 @@ import (
 	"src.elv.sh/pkg/eval"
 )
 
-//elvdoc:var command:binding
-//
-// Key bindings for command mode. This is currently a very small subset of Vi
-// command mode bindings.
-//
-// @cf edit:command:start
-
-//elvdoc:fn command:start
-//
-// ```elvish
-// edit:command:start
-// ```
-//
-// Enter command mode. This mode is intended to emulate Vi's command mode, but
-// it is very incomplete right now.
-//
-// @cf $edit:command:binding
-
 func initCommandAPI(ed *Editor, ev *eval.Evaler, nb eval.NsBuilder) {
 	bindingVar := newBindingVar(emptyBindingsMap)
 	bindings := newMapBindings(ed, ev, bindingVar)

+ 80 - 0
pkg/edit/complete_getopt.d.elv

@@ -0,0 +1,80 @@
+#elvdoc:fn complete-getopt
+#
+# ```elvish
+# edit:complete-getopt $args $opt-specs $arg-handlers
+# ```
+# Produces completions according to a specification of accepted command-line
+# options (both short and long options are handled), positional handler
+# functions for each command position, and the current arguments in the command
+# line. The arguments are as follows:
+#
+# * `$args` is an array containing the current arguments in the command line
+#   (without the command itself). These are the arguments as passed to the
+#   [Argument Completer](#argument-completer) function.
+#
+# * `$opt-specs` is an array of maps, each one containing the definition of
+#   one possible command-line option. Matching options will be provided as
+#   completions when the last element of `$args` starts with a dash, but not
+#   otherwise. Each map can contain the following keys (at least one of `short`
+#   or `long` needs to be specified):
+#
+#   - `short` contains the one-letter short option, if any, without the dash.
+#
+#   - `long` contains the long option name, if any, without the initial two
+#     dashes.
+#
+#   - `arg-optional`, if set to `$true`, specifies that the option receives an
+#     optional argument.
+#
+#   - `arg-required`, if set to `$true`, specifies that the option receives a
+#     mandatory argument. Only one of `arg-optional` or `arg-required` can be
+#     set to `$true`.
+#
+#   - `desc` can be set to a human-readable description of the option which
+#     will be displayed in the completion menu.
+#
+#   - `completer` can be set to a function to generate possible completions for
+#     the option argument. The function receives as argument the element at
+#     that position and return zero or more candidates.
+#
+# * `$arg-handlers` is an array of functions, each one returning the possible
+#   completions for that position in the arguments. Each function receives
+#   as argument the last element of `$args`, and should return zero or more
+#   possible values for the completions at that point. The returned values can
+#   be plain strings or the output of `edit:complex-candidate`. If the last
+#   element of the list is the string `...`, then the last handler is reused
+#   for all following arguments.
+#
+# Example:
+#
+# ```elvish-transcript
+# ~> fn complete {|@args|
+#      opt-specs = [ [&short=a &long=all &desc="Show all"]
+#                    [&short=n &desc="Set name" &arg-required=$true
+#                     &completer= {|_| put name1 name2 }] ]
+#      arg-handlers = [ {|_| put first1 first2 }
+#                       {|_| put second1 second2 } ... ]
+#      edit:complete-getopt $args $opt-specs $arg-handlers
+#    }
+# ~> complete ''
+# ▶ first1
+# ▶ first2
+# ~> complete '-'
+# ▶ (edit:complex-candidate -a &display='-a (Show all)')
+# ▶ (edit:complex-candidate --all &display='--all (Show all)')
+# ▶ (edit:complex-candidate -n &display='-n (Set name)')
+# ~> complete -n ''
+# ▶ name1
+# ▶ name2
+# ~> complete -a ''
+# ▶ first1
+# ▶ first2
+# ~> complete arg1 ''
+# ▶ second1
+# ▶ second2
+# ~> complete arg1 arg2 ''
+# ▶ second1
+# ▶ second2
+# ```
+#
+# @cf flag:parse-getopt

+ 0 - 81
pkg/edit/complete_getopt.go

@@ -13,87 +13,6 @@ import (
 	"src.elv.sh/pkg/ui"
 )
 
-//elvdoc:fn complete-getopt
-//
-// ```elvish
-// edit:complete-getopt $args $opt-specs $arg-handlers
-// ```
-// Produces completions according to a specification of accepted command-line
-// options (both short and long options are handled), positional handler
-// functions for each command position, and the current arguments in the command
-// line. The arguments are as follows:
-//
-// * `$args` is an array containing the current arguments in the command line
-//   (without the command itself). These are the arguments as passed to the
-//   [Argument Completer](#argument-completer) function.
-//
-// * `$opt-specs` is an array of maps, each one containing the definition of
-//   one possible command-line option. Matching options will be provided as
-//   completions when the last element of `$args` starts with a dash, but not
-//   otherwise. Each map can contain the following keys (at least one of `short`
-//   or `long` needs to be specified):
-//
-//   - `short` contains the one-letter short option, if any, without the dash.
-//
-//   - `long` contains the long option name, if any, without the initial two
-//     dashes.
-//
-//   - `arg-optional`, if set to `$true`, specifies that the option receives an
-//     optional argument.
-//
-//   - `arg-required`, if set to `$true`, specifies that the option receives a
-//     mandatory argument. Only one of `arg-optional` or `arg-required` can be
-//     set to `$true`.
-//
-//   - `desc` can be set to a human-readable description of the option which
-//     will be displayed in the completion menu.
-//
-//   - `completer` can be set to a function to generate possible completions for
-//     the option argument. The function receives as argument the element at
-//     that position and return zero or more candidates.
-//
-// * `$arg-handlers` is an array of functions, each one returning the possible
-//   completions for that position in the arguments. Each function receives
-//   as argument the last element of `$args`, and should return zero or more
-//   possible values for the completions at that point. The returned values can
-//   be plain strings or the output of `edit:complex-candidate`. If the last
-//   element of the list is the string `...`, then the last handler is reused
-//   for all following arguments.
-//
-// Example:
-//
-// ```elvish-transcript
-// ~> fn complete {|@args|
-//      opt-specs = [ [&short=a &long=all &desc="Show all"]
-//                    [&short=n &desc="Set name" &arg-required=$true
-//                     &completer= {|_| put name1 name2 }] ]
-//      arg-handlers = [ {|_| put first1 first2 }
-//                       {|_| put second1 second2 } ... ]
-//      edit:complete-getopt $args $opt-specs $arg-handlers
-//    }
-// ~> complete ''
-// ▶ first1
-// ▶ first2
-// ~> complete '-'
-// ▶ (edit:complex-candidate -a &display='-a (Show all)')
-// ▶ (edit:complex-candidate --all &display='--all (Show all)')
-// ▶ (edit:complex-candidate -n &display='-n (Set name)')
-// ~> complete -n ''
-// ▶ name1
-// ▶ name2
-// ~> complete -a ''
-// ▶ first1
-// ▶ first2
-// ~> complete arg1 ''
-// ▶ second1
-// ▶ second2
-// ~> complete arg1 arg2 ''
-// ▶ second1
-// ▶ second2
-// ```
-//
-// @cf flag:parse-getopt
-
 func completeGetopt(fm *eval.Frame, vArgs, vOpts, vArgHandlers any) error {
 	args, err := parseGetoptArgs(vArgs)
 	if err != nil {

+ 135 - 0
pkg/edit/completion.d.elv

@@ -0,0 +1,135 @@
+#elvdoc:var completion:arg-completer
+#
+# A map containing argument completers.
+
+#elvdoc:var completion:binding
+#
+# Keybinding for the completion mode.
+
+#elvdoc:var completion:matcher
+#
+# A map mapping from context names to matcher functions. See the
+# [Matcher](#matcher) section.
+
+#elvdoc:fn complete-filename
+#
+# ```elvish
+# edit:complete-filename $args...
+# ```
+#
+# Produces a list of filenames found in the directory of the last argument. All
+# other arguments are ignored. If the last argument does not contain a path
+# (either absolute or relative to the current directory), then the current
+# directory is used. Relevant files are output as `edit:complex-candidate`
+# objects.
+#
+# This function is the default handler for any commands without
+# explicit handlers in `$edit:completion:arg-completer`. See [Argument
+# Completer](#argument-completer).
+#
+# Example:
+#
+# ```elvish-transcript
+# ~> edit:complete-filename ''
+# ▶ (edit:complex-candidate Applications &code-suffix=/ &style='01;34')
+# ▶ (edit:complex-candidate Books &code-suffix=/ &style='01;34')
+# ▶ (edit:complex-candidate Desktop &code-suffix=/ &style='01;34')
+# ▶ (edit:complex-candidate Docsafe &code-suffix=/ &style='01;34')
+# ▶ (edit:complex-candidate Documents &code-suffix=/ &style='01;34')
+# ...
+# ~> edit:complete-filename .elvish/
+# ▶ (edit:complex-candidate .elvish/aliases &code-suffix=/ &style='01;34')
+# ▶ (edit:complex-candidate .elvish/db &code-suffix=' ' &style='')
+# ▶ (edit:complex-candidate .elvish/epm-installed &code-suffix=' ' &style='')
+# ▶ (edit:complex-candidate .elvish/lib &code-suffix=/ &style='01;34')
+# ▶ (edit:complex-candidate .elvish/rc.elv &code-suffix=' ' &style='')
+# ```
+
+#elvdoc:fn complex-candidate
+#
+# ```elvish
+# edit:complex-candidate $stem &display='' &code-suffix=''
+# ```
+#
+# Builds a complex candidate. This is mainly useful in [argument
+# completers](#argument-completer).
+#
+# The `&display` option controls how the candidate is shown in the UI. It can
+# be a string or a [styled](builtin.html#styled) text. If it is empty, `$stem`
+# is used.
+#
+# The `&code-suffix` option affects how the candidate is inserted into the code
+# when it is accepted. By default, a quoted version of `$stem` is inserted. If
+# `$code-suffix` is non-empty, it is added to that text, and the suffix is not
+# quoted.
+
+#elvdoc:fn match-prefix
+#
+# ```elvish
+# edit:match-prefix $seed $inputs?
+# ```
+#
+# For each input, outputs whether the input has $seed as a prefix. Uses the
+# result of `to-string` for non-string inputs.
+#
+# Roughly equivalent to the following Elvish function, but more efficient:
+#
+# ```elvish
+# use str
+# fn match-prefix {|seed @input|
+#   each {|x| str:has-prefix (to-string $x) $seed } $@input
+# }
+# ```
+
+#elvdoc:fn match-subseq
+#
+# ```elvish
+# edit:match-subseq $seed $inputs?
+# ```
+#
+# For each input, outputs whether the input has $seed as a
+# [subsequence](https://en.wikipedia.org/wiki/Subsequence). Uses the result of
+# `to-string` for non-string inputs.
+
+#elvdoc:fn match-substr
+#
+# ```elvish
+# edit:match-substr $seed $inputs?
+# ```
+#
+# For each input, outputs whether the input has $seed as a substring. Uses the
+# result of `to-string` for non-string inputs.
+#
+# Roughly equivalent to the following Elvish function, but more efficient:
+#
+# ```elvish
+# use str
+# fn match-substr {|seed @input|
+#   each {|x| str:has-contains (to-string $x) $seed } $@input
+# }
+# ```
+
+#elvdoc:fn completion:start
+#
+# ```elvish
+# edit:completion:start
+# ```
+#
+# Start the completion mode.
+
+#elvdoc:fn completion:smart-start
+#
+# ```elvish
+# edit:completion:smart-start
+# ```
+#
+# Starts the completion mode. However, if all the candidates share a non-empty
+# prefix and that prefix starts with the seed, inserts the prefix instead.
+
+#elvdoc:fn completion:close
+#
+# ```elvish
+# edit:completion:close
+# ```
+#
+# Closes the completion mode UI.

+ 0 - 136
pkg/edit/completion.go

@@ -23,71 +23,6 @@ import (
 	"src.elv.sh/pkg/ui"
 )
 
-//elvdoc:var completion:arg-completer
-//
-// A map containing argument completers.
-
-//elvdoc:var completion:binding
-//
-// Keybinding for the completion mode.
-
-//elvdoc:var completion:matcher
-//
-// A map mapping from context names to matcher functions. See the
-// [Matcher](#matcher) section.
-
-//elvdoc:fn complete-filename
-//
-// ```elvish
-// edit:complete-filename $args...
-// ```
-//
-// Produces a list of filenames found in the directory of the last argument. All
-// other arguments are ignored. If the last argument does not contain a path
-// (either absolute or relative to the current directory), then the current
-// directory is used. Relevant files are output as `edit:complex-candidate`
-// objects.
-//
-// This function is the default handler for any commands without
-// explicit handlers in `$edit:completion:arg-completer`. See [Argument
-// Completer](#argument-completer).
-//
-// Example:
-//
-// ```elvish-transcript
-// ~> edit:complete-filename ''
-// ▶ (edit:complex-candidate Applications &code-suffix=/ &style='01;34')
-// ▶ (edit:complex-candidate Books &code-suffix=/ &style='01;34')
-// ▶ (edit:complex-candidate Desktop &code-suffix=/ &style='01;34')
-// ▶ (edit:complex-candidate Docsafe &code-suffix=/ &style='01;34')
-// ▶ (edit:complex-candidate Documents &code-suffix=/ &style='01;34')
-// ...
-// ~> edit:complete-filename .elvish/
-// ▶ (edit:complex-candidate .elvish/aliases &code-suffix=/ &style='01;34')
-// ▶ (edit:complex-candidate .elvish/db &code-suffix=' ' &style='')
-// ▶ (edit:complex-candidate .elvish/epm-installed &code-suffix=' ' &style='')
-// ▶ (edit:complex-candidate .elvish/lib &code-suffix=/ &style='01;34')
-// ▶ (edit:complex-candidate .elvish/rc.elv &code-suffix=' ' &style='')
-// ```
-
-//elvdoc:fn complex-candidate
-//
-// ```elvish
-// edit:complex-candidate $stem &display='' &code-suffix=''
-// ```
-//
-// Builds a complex candidate. This is mainly useful in [argument
-// completers](#argument-completer).
-//
-// The `&display` option controls how the candidate is shown in the UI. It can
-// be a string or a [styled](builtin.html#styled) text. If it is empty, `$stem`
-// is used.
-//
-// The `&code-suffix` option affects how the candidate is inserted into the code
-// when it is accepted. By default, a quoted version of `$stem` is inserted. If
-// `$code-suffix` is non-empty, it is added to that text, and the suffix is not
-// quoted.
-
 type complexCandidateOpts struct {
 	CodeSuffix string
 	Display    any
@@ -115,69 +50,6 @@ func complexCandidate(fm *eval.Frame, opts complexCandidateOpts, stem string) (c
 	}, nil
 }
 
-//elvdoc:fn match-prefix
-//
-// ```elvish
-// edit:match-prefix $seed $inputs?
-// ```
-//
-// For each input, outputs whether the input has $seed as a prefix. Uses the
-// result of `to-string` for non-string inputs.
-//
-// Roughly equivalent to the following Elvish function, but more efficient:
-//
-// ```elvish
-// use str
-// fn match-prefix {|seed @input|
-//   each {|x| str:has-prefix (to-string $x) $seed } $@input
-// }
-// ```
-
-//elvdoc:fn match-subseq
-//
-// ```elvish
-// edit:match-subseq $seed $inputs?
-// ```
-//
-// For each input, outputs whether the input has $seed as a
-// [subsequence](https://en.wikipedia.org/wiki/Subsequence). Uses the result of
-// `to-string` for non-string inputs.
-
-//elvdoc:fn match-substr
-//
-// ```elvish
-// edit:match-substr $seed $inputs?
-// ```
-//
-// For each input, outputs whether the input has $seed as a substring. Uses the
-// result of `to-string` for non-string inputs.
-//
-// Roughly equivalent to the following Elvish function, but more efficient:
-//
-// ```elvish
-// use str
-// fn match-substr {|seed @input|
-//   each {|x| str:has-contains (to-string $x) $seed } $@input
-// }
-// ```
-
-//elvdoc:fn completion:start
-//
-// ```elvish
-// edit:completion:start
-// ```
-//
-// Start the completion mode.
-
-//elvdoc:fn completion:smart-start
-//
-// ```elvish
-// edit:completion:smart-start
-// ```
-//
-// Starts the completion mode. However, if all the candidates share a non-empty
-// prefix and that prefix starts with the seed, inserts the prefix instead.
-
 func completionStart(app cli.App, bindings tk.Bindings, ev *eval.Evaler, cfg complete.Config, smart bool) {
 	codeArea, ok := focusedCodeArea(app)
 	if !ok {
@@ -231,14 +103,6 @@ func completionStart(app cli.App, bindings tk.Bindings, ev *eval.Evaler, cfg com
 	}
 }
 
-//elvdoc:fn completion:close
-//
-// ```elvish
-// edit:completion:close
-// ```
-//
-// Closes the completion mode UI.
-
 func initCompletion(ed *Editor, ev *eval.Evaler, nb eval.NsBuilder) {
 	bindingVar := newBindingVar(emptyBindingsMap)
 	bindings := newMapBindings(ed, ev, bindingVar)

+ 34 - 0
pkg/edit/config_api.d.elv

@@ -0,0 +1,34 @@
+#elvdoc:var max-height
+#
+# Maximum height the editor is allowed to use, defaults to `+Inf`.
+#
+# By default, the height of the editor is only restricted by the terminal
+# height. Some modes like location mode can use a lot of lines; as a result,
+# it can often occupy the entire terminal, and push up your scrollback buffer.
+# Change this variable to a finite number to restrict the height of the editor.
+
+#elvdoc:var before-readline
+#
+# A list of functions to call before each readline cycle. Each function is
+# called without any arguments.
+
+#elvdoc:var after-readline
+#
+# A list of functions to call after each readline cycle. Each function is
+# called with a single string argument containing the code that has been read.
+
+#elvdoc:var add-cmd-filters
+#
+# List of filters to run before adding a command to history.
+#
+# A filter is a function that takes a command as argument and outputs
+# a boolean value. If any of the filters outputs `$false`, the
+# command is not saved to history, and the rest of the filters are
+# not run. The default value of this list contains a filter which
+# ignores command starts with space.
+
+#elvdoc:var global-binding
+#
+# Global keybindings, consulted for keys not handled by mode-specific bindings.
+#
+# See [Keybindings](#keybindings).

+ 0 - 35
pkg/edit/config_api.go

@@ -14,15 +14,6 @@ import (
 	"src.elv.sh/pkg/store/storedefs"
 )
 
-//elvdoc:var max-height
-//
-// Maximum height the editor is allowed to use, defaults to `+Inf`.
-//
-// By default, the height of the editor is only restricted by the terminal
-// height. Some modes like location mode can use a lot of lines; as a result,
-// it can often occupy the entire terminal, and push up your scrollback buffer.
-// Change this variable to a finite number to restrict the height of the editor.
-
 func initMaxHeight(appSpec *cli.AppSpec, nb eval.NsBuilder) {
 	maxHeight := newIntVar(-1)
 	appSpec.MaxHeight = func() int { return maxHeight.GetRaw().(int) }
@@ -34,11 +25,6 @@ func initReadlineHooks(appSpec *cli.AppSpec, ev *eval.Evaler, nb eval.NsBuilder)
 	initAfterReadline(appSpec, ev, nb)
 }
 
-//elvdoc:var before-readline
-//
-// A list of functions to call before each readline cycle. Each function is
-// called without any arguments.
-
 func initBeforeReadline(appSpec *cli.AppSpec, ev *eval.Evaler, nb eval.NsBuilder) {
 	hook := newListVar(vals.EmptyList)
 	nb.AddVar("before-readline", hook)
@@ -47,11 +33,6 @@ func initBeforeReadline(appSpec *cli.AppSpec, ev *eval.Evaler, nb eval.NsBuilder
 	})
 }
 
-//elvdoc:var after-readline
-//
-// A list of functions to call after each readline cycle. Each function is
-// called with a single string argument containing the code that has been read.
-
 func initAfterReadline(appSpec *cli.AppSpec, ev *eval.Evaler, nb eval.NsBuilder) {
 	hook := newListVar(vals.EmptyList)
 	nb.AddVar("after-readline", hook)
@@ -60,16 +41,6 @@ func initAfterReadline(appSpec *cli.AppSpec, ev *eval.Evaler, nb eval.NsBuilder)
 	})
 }
 
-//elvdoc:var add-cmd-filters
-//
-// List of filters to run before adding a command to history.
-//
-// A filter is a function that takes a command as argument and outputs
-// a boolean value. If any of the filters outputs `$false`, the
-// command is not saved to history, and the rest of the filters are
-// not run. The default value of this list contains a filter which
-// ignores command starts with space.
-
 func initAddCmdFilters(appSpec *cli.AppSpec, ev *eval.Evaler, nb eval.NsBuilder, s histutil.Store) {
 	ignoreLeadingSpace := eval.NewGoFn("<ignore-cmd-with-leading-space>",
 		func(s string) bool { return !strings.HasPrefix(s, " ") })
@@ -86,12 +57,6 @@ func initAddCmdFilters(appSpec *cli.AppSpec, ev *eval.Evaler, nb eval.NsBuilder,
 	})
 }
 
-//elvdoc:var global-binding
-//
-// Global keybindings, consulted for keys not handled by mode-specific bindings.
-//
-// See [Keybindings](#keybindings).
-
 func initGlobalBindings(appSpec *cli.AppSpec, nt notifier, ev *eval.Evaler, nb eval.NsBuilder) {
 	bindingVar := newBindingVar(emptyBindingsMap)
 	appSpec.GlobalBindings = newMapBindings(nt, ev, bindingVar)

+ 4 - 0
pkg/edit/editor.d.elv

@@ -0,0 +1,4 @@
+#elvdoc:var exceptions
+#
+# A list of exceptions thrown from callbacks such as prompts. Useful for
+# examining tracebacks and other metadata.

+ 0 - 5
pkg/edit/editor.go

@@ -87,11 +87,6 @@ func NewEditor(tty cli.TTY, ev *eval.Evaler, st storedefs.Store) *Editor {
 	return ed
 }
 
-//elvdoc:var exceptions
-//
-// A list of exceptions thrown from callbacks such as prompts. Useful for
-// examining tracebacks and other metadata.
-
 func initExceptionsAPI(ed *Editor, nb eval.NsBuilder) {
 	nb.AddVar("exceptions", vars.FromPtrWithMutex(&ed.excList, &ed.excMutex))
 }

+ 49 - 0
pkg/edit/histwalk.d.elv

@@ -0,0 +1,49 @@
+#elvdoc:var history:binding
+#
+# ```elvish
+# edit:history:binding
+# ```
+#
+# Binding table for the history mode.
+
+#elvdoc:fn history:start
+#
+# ```elvish
+# edit:history:start
+# ```
+#
+# Starts the history mode.
+
+#elvdoc:fn history:up
+#
+# ```elvish
+# edit:history:up
+# ```
+#
+# Walks to the previous entry in history mode.
+
+#elvdoc:fn history:down
+#
+# ```elvish
+# edit:history:down
+# ```
+#
+# Walks to the next entry in history mode.
+
+#elvdoc:fn history:down-or-quit
+#
+# ```elvish
+# edit:history:down-or-quit
+# ```
+#
+# Walks to the next entry in history mode, or quit the history mode if already
+# at the newest entry.
+
+#elvdoc:fn history:fast-forward
+#
+# ```elvish
+# edit:history:fast-forward
+# ```
+#
+# Import command history entries that happened after the current session
+# started.

+ 0 - 50
pkg/edit/histwalk.go

@@ -10,56 +10,6 @@ import (
 	"src.elv.sh/pkg/eval"
 )
 
-//elvdoc:var history:binding
-//
-// ```elvish
-// edit:history:binding
-// ```
-//
-// Binding table for the history mode.
-
-//elvdoc:fn history:start
-//
-// ```elvish
-// edit:history:start
-// ```
-//
-// Starts the history mode.
-
-//elvdoc:fn history:up
-//
-// ```elvish
-// edit:history:up
-// ```
-//
-// Walks to the previous entry in history mode.
-
-//elvdoc:fn history:down
-//
-// ```elvish
-// edit:history:down
-// ```
-//
-// Walks to the next entry in history mode.
-
-//elvdoc:fn history:down-or-quit
-//
-// ```elvish
-// edit:history:down-or-quit
-// ```
-//
-// Walks to the next entry in history mode, or quit the history mode if already
-// at the newest entry.
-
-//elvdoc:fn history:fast-forward
-//
-// ```elvish
-// edit:history:fast-forward
-// ```
-//
-// Import command history entries that happened after the current session
-// started.
-
 func initHistWalk(ed *Editor, ev *eval.Evaler, hs *histStore, nb eval.NsBuilder) {
 	bindingVar := newBindingVar(emptyBindingsMap)
 	bindings := newMapBindings(ed, ev, bindingVar)

+ 114 - 0
pkg/edit/insert_api.d.elv

@@ -0,0 +1,114 @@
+#elvdoc:var abbr
+#
+# A map from simple abbreviations to their expansions.
+#
+# An abbreviation is replaced by its expansion when it is typed in full
+# and consecutively, without being interrupted by the use of other editing
+# functionalities, such as cursor movements.
+#
+# If more than one abbreviations would match, the longest one is used.
+#
+# Examples:
+#
+# ```elvish
+# set edit:abbr['||'] = '| less'
+# set edit:abbr['>dn'] = '2>/dev/null'
+# ```
+#
+# With the definitions above, typing `||` anywhere expands to `| less`, and
+# typing `>dn` anywhere expands to `2>/dev/null`. However, typing a `|`, moving
+# the cursor left, and typing another `|` does **not** expand to `| less`,
+# since the abbreviation `||` was not typed consecutively.
+#
+# @cf $edit:command-abbr $edit:small-word-abbr
+
+#elvdoc:var command-abbr
+#
+# A map from command abbreviations to their expansions.
+#
+# A command abbreviation is replaced by its expansion when seen in the command
+# position followed by a [whitespace](language.html#whitespace). This is
+# similar to the Fish shell's
+# [abbreviations](https://fishshell.com/docs/current/cmds/abbr.html), but does
+# not trigger when executing a command with Enter -- you must type a space
+# first.
+#
+# Examples:
+#
+# ```elvish
+# set edit:command-abbr['l'] = 'less'
+# set edit:command-abbr['gc'] = 'git commit'
+# ```
+#
+# @cf $edit:abbr $edit:small-word-abbr
+
+#elvdoc:var small-word-abbr
+#
+# A map from small-word abbreviations to their expansions.
+#
+# A small-word abbreviation is replaced by its expansion after it is typed in
+# full and consecutively, and followed by another character (the *trigger*
+# character). Furthermore, the expansion requires the following conditions to
+# be satisfied:
+#
+# -   The end of the abbreviation must be adjacent to a small-word boundary,
+#     i.e. the last character of the abbreviation and the trigger character
+#     must be from two different small-word categories.
+#
+# -   The start of the abbreviation must also be adjacent to a small-word
+#     boundary, unless it appears at the beginning of the code buffer.
+#
+# -   The cursor must be at the end of the buffer.
+#
+# If more than one abbreviations would match, the longest one is used. See the description of
+# [small words](#word-types) for more information.
+#
+# As an example, with the following configuration:
+#
+# ```elvish
+# set edit:small-word-abbr['gcm'] = 'git checkout master'
+# ```
+#
+# In the following scenarios, the `gcm` abbreviation is expanded:
+#
+# -   With an empty buffer, typing `gcm` and a space or semicolon;
+#
+# -   When the buffer ends with a space, typing `gcm` and a space or semicolon.
+#
+# The space or semicolon after `gcm` is preserved in both cases.
+#
+# In the following scenarios, the `gcm` abbreviation is **not** expanded:
+#
+# -   With an empty buffer, typing `Xgcm` and a space or semicolon (start of
+#     abbreviation is not adjacent to a small-word boundary);
+#
+# -   When the buffer ends with `X`, typing `gcm` and a space or semicolon (end
+#     of abbreviation is not adjacent to a small-word boundary);
+#
+# -   When the buffer is non-empty, move the cursor to the beginning, and typing
+#     `gcm` and a space (cursor not at the end of the buffer).
+#
+# This example shows the case where the abbreviation consists of a single small
+# word of alphanumerical characters, but that doesn't have to be the case. For
+# example, with the following configuration:
+#
+# ```elvish
+# set edit:small-word-abbr['>dn'] = ' 2>/dev/null'
+# ```
+#
+# The abbreviation `>dn` starts with a punctuation character, and ends with an
+# alphanumerical character. This means that it is expanded when it borders
+# a whitespace or alphanumerical character to the left, and a whitespace or
+# punctuation to the right; for example, typing `ls>dn;` will expand it.
+#
+# Some extra examples of small-word abbreviations:
+#
+# ```elvish
+# set edit:small-word-abbr['gcp'] = 'git cherry-pick -x'
+# set edit:small-word-abbr['ll'] = 'ls -ltr'
+# ```
+#
+# If both a [simple abbreviation](#edit:abbr) and a small-word abbreviation can
+# be expanded, the simple abbreviation has priority.
+#
+# @cf $edit:abbr $edit:command-abbr

+ 0 - 115
pkg/edit/insert_api.go

@@ -7,121 +7,6 @@ import (
 	"src.elv.sh/pkg/eval/vars"
 )
 
-//elvdoc:var abbr
-//
-// A map from simple abbreviations to their expansions.
-//
-// An abbreviation is replaced by its expansion when it is typed in full
-// and consecutively, without being interrupted by the use of other editing
-// functionalities, such as cursor movements.
-//
-// If more than one abbreviations would match, the longest one is used.
-//
-// Examples:
-//
-// ```elvish
-// set edit:abbr['||'] = '| less'
-// set edit:abbr['>dn'] = '2>/dev/null'
-// ```
-//
-// With the definitions above, typing `||` anywhere expands to `| less`, and
-// typing `>dn` anywhere expands to `2>/dev/null`. However, typing a `|`, moving
-// the cursor left, and typing another `|` does **not** expand to `| less`,
-// since the abbreviation `||` was not typed consecutively.
-//
-// @cf $edit:command-abbr $edit:small-word-abbr
-
-//elvdoc:var command-abbr
-//
-// A map from command abbreviations to their expansions.
-//
-// A command abbreviation is replaced by its expansion when seen in the command
-// position followed by a [whitespace](language.html#whitespace). This is
-// similar to the Fish shell's
-// [abbreviations](https://fishshell.com/docs/current/cmds/abbr.html), but does
-// not trigger when executing a command with Enter -- you must type a space
-// first.
-//
-// Examples:
-//
-// ```elvish
-// set edit:command-abbr['l'] = 'less'
-// set edit:command-abbr['gc'] = 'git commit'
-// ```
-//
-// @cf $edit:abbr $edit:small-word-abbr
-
-//elvdoc:var small-word-abbr
-//
-// A map from small-word abbreviations to their expansions.
-//
-// A small-word abbreviation is replaced by its expansion after it is typed in
-// full and consecutively, and followed by another character (the *trigger*
-// character). Furthermore, the expansion requires the following conditions to
-// be satisfied:
-//
-// -   The end of the abbreviation must be adjacent to a small-word boundary,
-//     i.e. the last character of the abbreviation and the trigger character
-//     must be from two different small-word categories.
-//
-// -   The start of the abbreviation must also be adjacent to a small-word
-//     boundary, unless it appears at the beginning of the code buffer.
-//
-// -   The cursor must be at the end of the buffer.
-//
-// If more than one abbreviations would match, the longest one is used. See the description of
-// [small words](#word-types) for more information.
-//
-// As an example, with the following configuration:
-//
-// ```elvish
-// set edit:small-word-abbr['gcm'] = 'git checkout master'
-// ```
-//
-// In the following scenarios, the `gcm` abbreviation is expanded:
-//
-// -   With an empty buffer, typing `gcm` and a space or semicolon;
-//
-// -   When the buffer ends with a space, typing `gcm` and a space or semicolon.
-//
-// The space or semicolon after `gcm` is preserved in both cases.
-//
-// In the following scenarios, the `gcm` abbreviation is **not** expanded:
-//
-// -   With an empty buffer, typing `Xgcm` and a space or semicolon (start of
-//     abbreviation is not adjacent to a small-word boundary);
-//
-// -   When the buffer ends with `X`, typing `gcm` and a space or semicolon (end
-//     of abbreviation is not adjacent to a small-word boundary);
-//
-// -   When the buffer is non-empty, move the cursor to the beginning, and typing
-//     `gcm` and a space (cursor not at the end of the buffer).
-//
-// This example shows the case where the abbreviation consists of a single small
-// word of alphanumerical characters, but that doesn't have to be the case. For
-// example, with the following configuration:
-//
-// ```elvish
-// set edit:small-word-abbr['>dn'] = ' 2>/dev/null'
-// ```
-//
-// The abbreviation `>dn` starts with a punctuation character, and ends with an
-// alphanumerical character. This means that it is expanded when it borders
-// a whitespace or alphanumerical character to the left, and a whitespace or
-// punctuation to the right; for example, typing `ls>dn;` will expand it.
-//
-// Some extra examples of small-word abbreviations:
-//
-// ```elvish
-// set edit:small-word-abbr['gcp'] = 'git cherry-pick -x'
-// set edit:small-word-abbr['ll'] = 'ls -ltr'
-// ```
-//
-// If both a [simple abbreviation](#edit:abbr) and a small-word abbreviation can
-// be expanded, the simple abbreviation has priority.
-//
-// @cf $edit:abbr $edit:command-abbr
-
 func initInsertAPI(appSpec *cli.AppSpec, nt notifier, ev *eval.Evaler, nb eval.NsBuilder) {
 	simpleAbbr := vals.EmptyMap
 	simpleAbbrVar := vars.FromPtr(&simpleAbbr)

+ 16 - 0
pkg/edit/instant.d.elv

@@ -0,0 +1,16 @@
+#elvdoc:var -instant:binding
+#
+# Binding for the instant mode.
+
+#elvdoc:fn -instant:start
+#
+# ```elvish
+# edit:-instant:start
+# ```
+#
+# Starts the instant mode. In instant mode, any text entered at the command
+# line is evaluated immediately, with the output displayed.
+#
+# **WARNING**: Beware of unintended consequences when using destructive
+# commands. For example, if you type `sudo rm -rf /tmp/*` in the instant mode,
+# Elvish will attempt to evaluate `sudo rm -rf /` when you typed that far.

+ 0 - 17
pkg/edit/instant.go

@@ -8,23 +8,6 @@ import (
 	"src.elv.sh/pkg/parse"
 )
 
-//elvdoc:var -instant:binding
-//
-// Binding for the instant mode.
-
-//elvdoc:fn -instant:start
-//
-// ```elvish
-// edit:-instant:start
-// ```
-//
-// Starts the instant mode. In instant mode, any text entered at the command
-// line is evaluated immediately, with the output displayed.
-//
-// **WARNING**: Beware of unintended consequences when using destructive
-// commands. For example, if you type `sudo rm -rf /tmp/*` in the instant mode,
-// Elvish will attempt to evaluate `sudo rm -rf /` when you typed that far.
-
 func initInstant(ed *Editor, ev *eval.Evaler, nb eval.NsBuilder) {
 	bindingVar := newBindingVar(emptyBindingsMap)
 	bindings := newMapBindings(ed, ev, bindingVar)

+ 98 - 0
pkg/edit/listing.d.elv

@@ -0,0 +1,98 @@
+#elvdoc:fn listing:accept
+#
+# ```elvish
+# edit:listing:accept
+# ```
+#
+# Accepts the current selected listing item.
+
+#elvdoc:fn listing:up
+#
+# ```elvish
+# edit:listing:up
+# ```
+#
+# Moves the cursor up in listing mode.
+
+#elvdoc:fn listing:down
+#
+# ```elvish
+# edit:listing:down
+# ```
+#
+# Moves the cursor down in listing mode.
+
+#elvdoc:fn listing:up-cycle
+#
+# ```elvish
+# edit:listing:up-cycle
+# ```
+#
+# Moves the cursor up in listing mode, or to the last item if the first item is
+# currently selected.
+
+#elvdoc:fn listing:down-cycle
+#
+# ```elvish
+# edit:listing:down-cycle
+# ```
+#
+# Moves the cursor down in listing mode, or to the first item if the last item is
+# currently selected.
+
+#elvdoc:fn listing:page-up
+#
+# ```elvish
+# edit:listing:page-up
+# ```
+#
+# Moves the cursor up one page.
+
+#elvdoc:fn listing:page-down
+#
+# ```elvish
+# edit:listing:page-down
+# ```
+#
+# Moves the cursor down one page.
+
+#elvdoc:fn listing:left
+#
+# ```elvish
+# edit:listing:left
+# ```
+#
+# Moves the cursor left in listing mode.
+
+#elvdoc:fn listing:right
+#
+# ```elvish
+# edit:listing:right
+# ```
+#
+# Moves the cursor right in listing mode.
+
+#elvdoc:var location:hidden
+#
+# ```elvish
+# edit:location:hidden
+# ```
+#
+# A list of directories to hide in the location addon.
+
+#elvdoc:var location:pinned
+#
+# ```elvish
+# edit:location:pinned
+# ```
+#
+# A list of directories to always show at the top of the list of the location
+# addon.
+
+#elvdoc:var location:workspaces
+#
+# ```elvish
+# edit:location:workspaces
+# ```
+#
+# A map mapping types of workspaces to their patterns.

+ 0 - 99
pkg/edit/listing.go

@@ -134,100 +134,26 @@ func initLocation(ed *Editor, ev *eval.Evaler, st storedefs.Store, commonBinding
 	})
 }
 
-//elvdoc:fn listing:accept
-//
-// ```elvish
-// edit:listing:accept
-// ```
-//
-// Accepts the current selected listing item.
-
 func listingAccept(app cli.App) {
 	if w, ok := activeComboBox(app); ok {
 		w.ListBox().Accept()
 	}
 }
 
-//elvdoc:fn listing:up
-//
-// ```elvish
-// edit:listing:up
-// ```
-//
-// Moves the cursor up in listing mode.
-
 func listingUp(app cli.App) { listingSelect(app, tk.Prev) }
 
-//elvdoc:fn listing:down
-//
-// ```elvish
-// edit:listing:down
-// ```
-//
-// Moves the cursor down in listing mode.
-
 func listingDown(app cli.App) { listingSelect(app, tk.Next) }
 
-//elvdoc:fn listing:up-cycle
-//
-// ```elvish
-// edit:listing:up-cycle
-// ```
-//
-// Moves the cursor up in listing mode, or to the last item if the first item is
-// currently selected.
-
 func listingUpCycle(app cli.App) { listingSelect(app, tk.PrevWrap) }
 
-//elvdoc:fn listing:down-cycle
-//
-// ```elvish
-// edit:listing:down-cycle
-// ```
-//
-// Moves the cursor down in listing mode, or to the first item if the last item is
-// currently selected.
-
 func listingDownCycle(app cli.App) { listingSelect(app, tk.NextWrap) }
 
-//elvdoc:fn listing:page-up
-//
-// ```elvish
-// edit:listing:page-up
-// ```
-//
-// Moves the cursor up one page.
-
 func listingPageUp(app cli.App) { listingSelect(app, tk.PrevPage) }
 
-//elvdoc:fn listing:page-down
-//
-// ```elvish
-// edit:listing:page-down
-// ```
-//
-// Moves the cursor down one page.
-
 func listingPageDown(app cli.App) { listingSelect(app, tk.NextPage) }
 
-//elvdoc:fn listing:left
-//
-// ```elvish
-// edit:listing:left
-// ```
-//
-// Moves the cursor left in listing mode.
-
 func listingLeft(app cli.App) { listingSelect(app, tk.Left) }
 
-//elvdoc:fn listing:right
-//
-// ```elvish
-// edit:listing:right
-// ```
-//
-// Moves the cursor right in listing mode.
-
 func listingRight(app cli.App) { listingSelect(app, tk.Right) }
 
 func listingSelect(app cli.App, f func(tk.ListBoxState) int) {
@@ -242,31 +168,6 @@ func listingRefilter(app cli.App) {
 	}
 }
 
-//elvdoc:var location:hidden
-//
-// ```elvish
-// edit:location:hidden
-// ```
-//
-// A list of directories to hide in the location addon.
-
-//elvdoc:var location:pinned
-//
-// ```elvish
-// edit:location:pinned
-// ```
-//
-// A list of directories to always show at the top of the list of the location
-// addon.
-
-//elvdoc:var location:workspaces
-//
-// ```elvish
-// edit:location:workspaces
-// ```
-//
-// A map mapping types of workspaces to their patterns.
-
 func adaptToIterateString(variable vars.Var) func(func(string)) {
 	return func(f func(s string)) {
 		vals.Iterate(variable.Get(), func(v any) bool {

+ 7 - 0
pkg/edit/listing_custom.d.elv

@@ -0,0 +1,7 @@
+#elvdoc:fn listing:start-custom
+#
+# ```elvish
+# edit:listing:start-custom
+# ```
+#
+# Starts a custom listing addon.

+ 0 - 8
pkg/edit/listing_custom.go

@@ -25,14 +25,6 @@ type customListingOpts struct {
 
 func (*customListingOpts) SetDefaultOptions() {}
 
-//elvdoc:fn listing:start-custom
-//
-// ```elvish
-// edit:listing:start-custom
-// ```
-//
-// Starts a custom listing addon.
-
 func listingStartCustom(ed *Editor, fm *eval.Frame, opts customListingOpts, items any) {
 	var bindings tk.Bindings
 	if opts.Binding.Map != nil {

+ 57 - 0
pkg/edit/navigation.d.elv

@@ -0,0 +1,57 @@
+#elvdoc:var navigation:selected-file
+#
+# Name of the currently selected file in navigation mode. $nil if not in
+# navigation mode.
+
+#elvdoc:var navigation:binding
+#
+# ```elvish
+# edit:navigation:binding
+# ```
+#
+# Keybinding for the navigation mode.
+
+#elvdoc:fn navigation:start
+#
+# ```elvish
+# edit:navigation:start
+# ```
+#
+# Start the navigation mode.
+
+#elvdoc:fn navigation:insert-selected
+#
+# ```elvish
+# edit:navigation:insert-selected
+# ```
+#
+# Inserts the selected filename.
+
+#elvdoc:fn navigation:insert-selected-and-quit
+#
+# ```elvish
+# edit:navigation:insert-selected-and-quit
+# ```
+#
+# Inserts the selected filename and closes the navigation addon.
+
+#elvdoc:fn navigation:trigger-filter
+#
+# ```elvish
+# edit:navigation:trigger-filter
+# ```
+#
+# Toggles the filtering status of the navigation addon.
+
+#elvdoc:fn navigation:trigger-shown-hidden
+#
+# ```elvish
+# edit:navigation:trigger-shown-hidden
+# ```
+#
+# Toggles whether the navigation addon should be showing hidden files.
+
+#elvdoc:var navigation:width-ratio
+#
+# A list of 3 integers, used for specifying the width ratio of the 3 columns in
+# navigation mode.

+ 0 - 58
pkg/edit/navigation.go

@@ -12,35 +12,6 @@ import (
 	"src.elv.sh/pkg/parse"
 )
 
-//elvdoc:var navigation:selected-file
-//
-// Name of the currently selected file in navigation mode. $nil if not in
-// navigation mode.
-
-//elvdoc:var navigation:binding
-//
-// ```elvish
-// edit:navigation:binding
-// ```
-//
-// Keybinding for the navigation mode.
-
-//elvdoc:fn navigation:start
-//
-// ```elvish
-// edit:navigation:start
-// ```
-//
-// Start the navigation mode.
-
-//elvdoc:fn navigation:insert-selected
-//
-// ```elvish
-// edit:navigation:insert-selected
-// ```
-//
-// Inserts the selected filename.
-
 func navInsertSelected(app cli.App) {
 	w, ok := activeNavigation(app)
 	if !ok {
@@ -69,40 +40,11 @@ func navInsertSelected(app cli.App) {
 	})
 }
 
-//elvdoc:fn navigation:insert-selected-and-quit
-//
-// ```elvish
-// edit:navigation:insert-selected-and-quit
-// ```
-//
-// Inserts the selected filename and closes the navigation addon.
-
 func navInsertSelectedAndQuit(app cli.App) {
 	navInsertSelected(app)
 	closeMode(app)
 }
 
-//elvdoc:fn navigation:trigger-filter
-//
-// ```elvish
-// edit:navigation:trigger-filter
-// ```
-//
-// Toggles the filtering status of the navigation addon.
-
-//elvdoc:fn navigation:trigger-shown-hidden
-//
-// ```elvish
-// edit:navigation:trigger-shown-hidden
-// ```
-//
-// Toggles whether the navigation addon should be showing hidden files.
-
-//elvdoc:var navigation:width-ratio
-//
-// A list of 3 integers, used for specifying the width ratio of the 3 columns in
-// navigation mode.
-
 func convertNavWidthRatio(v any) [3]int {
 	var (
 		numbers []int

+ 35 - 0
pkg/edit/prompt.d.elv

@@ -0,0 +1,35 @@
+#elvdoc:var prompt
+#
+# See [Prompts](#prompts).
+
+#elvdoc:var -prompt-eagerness
+#
+# See [Prompt Eagerness](#prompt-eagerness).
+
+#elvdoc:var prompt-stale-threshold
+#
+# See [Stale Prompt](#stale-prompt).
+
+#elvdoc:var prompt-stale-transformer.
+#
+# See [Stale Prompt](#stale-prompt).
+
+#elvdoc:var rprompt
+#
+# See [Prompts](#prompts).
+
+#elvdoc:var -rprompt-eagerness
+#
+# See [Prompt Eagerness](#prompt-eagerness).
+
+#elvdoc:var rprompt-stale-threshold
+#
+# See [Stale Prompt](#stale-prompt).
+
+#elvdoc:var rprompt-stale-transformer.
+#
+# See [Stale Prompt](#stale-prompt).
+
+#elvdoc:var rprompt-persistent
+#
+# See [RPrompt Persistency](#rprompt-persistency).

+ 0 - 36
pkg/edit/prompt.go

@@ -16,42 +16,6 @@ import (
 	"src.elv.sh/pkg/ui"
 )
 
-//elvdoc:var prompt
-//
-// See [Prompts](#prompts).
-
-//elvdoc:var -prompt-eagerness
-//
-// See [Prompt Eagerness](#prompt-eagerness).
-
-//elvdoc:var prompt-stale-threshold
-//
-// See [Stale Prompt](#stale-prompt).
-
-//elvdoc:var prompt-stale-transformer.
-//
-// See [Stale Prompt](#stale-prompt).
-
-//elvdoc:var rprompt
-//
-// See [Prompts](#prompts).
-
-//elvdoc:var -rprompt-eagerness
-//
-// See [Prompt Eagerness](#prompt-eagerness).
-
-//elvdoc:var rprompt-stale-threshold
-//
-// See [Stale Prompt](#stale-prompt).
-
-//elvdoc:var rprompt-stale-transformer.
-//
-// See [Stale Prompt](#stale-prompt).
-
-//elvdoc:var rprompt-persistent
-//
-// See [RPrompt Persistency](#rprompt-persistency).
-
 func initPrompts(appSpec *cli.AppSpec, nt notifier, ev *eval.Evaler, nb eval.NsBuilder) {
 	promptVal, rpromptVal := getDefaultPromptVals()
 	initPrompt(&appSpec.Prompt, "prompt", promptVal, nt, ev, nb)

+ 25 - 0
pkg/edit/repl.d.elv

@@ -0,0 +1,25 @@
+#elvdoc:var after-command
+#
+# A list of functions to call after each interactive command completes. There is one pre-defined
+# function used to populate the [`$edit:command-duration`](./edit.html#edit:command-duration)
+# variable. Each function is called with a single [map](https://elv.sh/ref/language.html#map)
+# argument containing the following keys:
+#
+# * `src`: Information about the source that was executed, same as what
+#   [`src`](builtin.html#src) would output inside the code.
+#
+# * `duration`: A [floating-point number](https://elv.sh/ref/language.html#number) representing the
+# command execution duration in seconds.
+#
+# * `error`: An [exception](../ref/language.html#exception) object if the command terminated with
+# an exception, else [`$nil`](../ref/language.html#nil).
+#
+# @cf $edit:command-duration
+
+#elvdoc:var command-duration
+#
+# Duration, in seconds, of the most recent interactive command. This can be useful in your prompt
+# to provide feedback on how long a command took to run. The initial value of this variable is the
+# time to evaluate your [`rc.elv`](command.html#rc-file) before printing the first prompt.
+#
+# @cf $edit:after-command

+ 0 - 26
pkg/edit/repl.go

@@ -10,32 +10,6 @@ import (
 	"src.elv.sh/pkg/parse"
 )
 
-//elvdoc:var after-command
-//
-// A list of functions to call after each interactive command completes. There is one pre-defined
-// function used to populate the [`$edit:command-duration`](./edit.html#edit:command-duration)
-// variable. Each function is called with a single [map](https://elv.sh/ref/language.html#map)
-// argument containing the following keys:
-//
-// * `src`: Information about the source that was executed, same as what
-//   [`src`](builtin.html#src) would output inside the code.
-//
-// * `duration`: A [floating-point number](https://elv.sh/ref/language.html#number) representing the
-// command execution duration in seconds.
-//
-// * `error`: An [exception](../ref/language.html#exception) object if the command terminated with
-// an exception, else [`$nil`](../ref/language.html#nil).
-//
-// @cf $edit:command-duration
-
-//elvdoc:var command-duration
-//
-// Duration, in seconds, of the most recent interactive command. This can be useful in your prompt
-// to provide feedback on how long a command took to run. The initial value of this variable is the
-// time to evaluate your [`rc.elv`](command.html#rc-file) before printing the first prompt.
-//
-// @cf $edit:after-command
-
 func initRepl(ed *Editor, ev *eval.Evaler, nb eval.NsBuilder) {
 	var commandDuration float64
 	// TODO: Ensure that this variable can only be written from the Elvish code

+ 29 - 0
pkg/edit/state_api.d.elv

@@ -0,0 +1,29 @@
+#elvdoc:fn insert-at-dot
+#
+# ```elvish
+# edit:insert-at-dot $text
+# ```
+#
+# Inserts the given text at the dot, moving the dot after the newly
+# inserted text.
+
+#elvdoc:fn replace-input
+#
+# ```elvish
+# edit:replace-input $text
+# ```
+#
+# Equivalent to assigning `$text` to `$edit:current-command`.
+
+#elvdoc:var -dot
+#
+# Contains the current position of the cursor, as a byte position within
+# `$edit:current-command`.
+
+#elvdoc:var current-command
+#
+# Contains the content of the current input. Setting the variable will
+# cause the cursor to move to the very end, as if `edit-dot = (count
+# $edit:current-command)` has been invoked.
+#
+# This API is subject to change.

+ 0 - 30
pkg/edit/state_api.go

@@ -8,15 +8,6 @@ import (
 	"src.elv.sh/pkg/eval/vars"
 )
 
-//elvdoc:fn insert-at-dot
-//
-// ```elvish
-// edit:insert-at-dot $text
-// ```
-//
-// Inserts the given text at the dot, moving the dot after the newly
-// inserted text.
-
 func insertAtDot(app cli.App, text string) {
 	codeArea, ok := focusedCodeArea(app)
 	if !ok {
@@ -27,14 +18,6 @@ func insertAtDot(app cli.App, text string) {
 	})
 }
 
-//elvdoc:fn replace-input
-//
-// ```elvish
-// edit:replace-input $text
-// ```
-//
-// Equivalent to assigning `$text` to `$edit:current-command`.
-
 func replaceInput(app cli.App, text string) {
 	codeArea, ok := focusedCodeArea(app)
 	if !ok {
@@ -45,19 +28,6 @@ func replaceInput(app cli.App, text string) {
 	})
 }
 
-//elvdoc:var -dot
-//
-// Contains the current position of the cursor, as a byte position within
-// `$edit:current-command`.
-
-//elvdoc:var current-command
-//
-// Contains the content of the current input. Setting the variable will
-// cause the cursor to move to the very end, as if `edit-dot = (count
-// $edit:current-command)` has been invoked.
-//
-// This API is subject to change.
-
 func initStateAPI(app cli.App, nb eval.NsBuilder) {
 	// State API always operates on the root CodeArea widget
 	codeArea := app.ActiveWidget().(tk.CodeArea)

+ 34 - 0
pkg/edit/store_api.d.elv

@@ -0,0 +1,34 @@
+#elvdoc:fn command-history
+#
+# ```elvish
+# edit:command-history &cmd-only=$false &dedup=$false &newest-first
+# ```
+#
+# Outputs the command history.
+#
+# By default, each entry is represented as a map, with an `id` key key for the
+# sequence number of the command, and a `cmd` key for the text of the command.
+# If `&cmd-only` is `$true`, only the text of each command is output.
+#
+# All entries are output by default. If `&dedup` is `$true`, only the most
+# recent instance of each command (when comparing just the `cmd` key) is
+# output.
+#
+# Commands are are output in oldest to newest order by default. If
+# `&newest-first` is `$true` the output is in newest to oldest order instead.
+#
+# As an example, either of the following extracts the text of the most recent
+# command:
+#
+# ```elvish
+# edit:command-history | put [(all)][-1][cmd]
+# edit:command-history &cmd-only &newest-first | take 1
+# ```
+
+#elvdoc:fn insert-last-word
+#
+# ```elvish
+# edit:insert-last-word
+# ```
+#
+# Inserts the last word of the last command.

+ 0 - 35
pkg/edit/store_api.go

@@ -14,33 +14,6 @@ import (
 
 var errStoreOffline = errors.New("store offline")
 
-//elvdoc:fn command-history
-//
-// ```elvish
-// edit:command-history &cmd-only=$false &dedup=$false &newest-first
-// ```
-//
-// Outputs the command history.
-//
-// By default, each entry is represented as a map, with an `id` key key for the
-// sequence number of the command, and a `cmd` key for the text of the command.
-// If `&cmd-only` is `$true`, only the text of each command is output.
-//
-// All entries are output by default. If `&dedup` is `$true`, only the most
-// recent instance of each command (when comparing just the `cmd` key) is
-// output.
-//
-// Commands are are output in oldest to newest order by default. If
-// `&newest-first` is `$true` the output is in newest to oldest order instead.
-//
-// As an example, either of the following extracts the text of the most recent
-// command:
-//
-// ```elvish
-// edit:command-history | put [(all)][-1][cmd]
-// edit:command-history &cmd-only &newest-first | take 1
-// ```
-
 type cmdhistOpt struct{ CmdOnly, Dedup, NewestFirst bool }
 
 func (o *cmdhistOpt) SetDefaultOptions() {}
@@ -100,14 +73,6 @@ func reverseCmds(cmds []storedefs.Cmd) {
 	}
 }
 
-//elvdoc:fn insert-last-word
-//
-// ```elvish
-// edit:insert-last-word
-// ```
-//
-// Inserts the last word of the last command.
-
 func insertLastWord(app cli.App, histStore histutil.Store) error {
 	codeArea, ok := focusedCodeArea(app)
 	if !ok {

+ 98 - 0
pkg/edit/vars.d.elv

@@ -0,0 +1,98 @@
+#elvdoc:fn add-var
+#
+# ```elvish
+# edit:add-var $name $init
+# ```
+#
+# Defines a new variable in the interactive REPL with an initial value. The new variable becomes
+# available during the next REPL cycle.
+#
+# Equivalent to running `var $name = $init` at a REPL prompt, but `$name` can be
+# dynamic.
+#
+# This is most useful for modules to modify the REPL namespace. Example:
+#
+# ```elvish-transcript
+# ~> cat .config/elvish/lib/a.elv
+# for i [(range 10)] {
+#   edit:add-var foo$i $i
+# }
+# ~> use a
+# ~> put $foo1 $foo2
+# ▶ (num 1)
+# ▶ (num 2)
+# ```
+#
+# Note that if you use a variable as the `$init` argument, `edit:add-var`
+# doesn't add the variable "itself" to the REPL namespace. The variable in the
+# REPL namespace will have the initial value set to the variable's value, but
+# it is not an alias of the original variable:
+#
+# ```elvish-transcript
+# ~> cat .config/elvish/lib/b.elv
+# var foo = foo
+# edit:add-var foo $foo
+# ~> use b
+# ~> put $foo
+# ▶ foo
+# ~> set foo = bar
+# ~> echo $b:foo
+# foo
+# ```
+#
+# ### Importing definition from a module into the REPL
+#
+# One common use of this command is to put the definitions of functions intended for REPL use in a
+# module instead of your [`rc.elv`](command.html#rc-file). For example, if you want to define `ll`
+# as `ls -l`, you can do so in your `rc.elv` directly:
+#
+# ```elvish
+# fn ll {|@a| ls -l $@a }
+# ```
+#
+# But if you move the definition into a module (say `util.elv` in one of the
+# [module search directories](command.html#module-search-directories), this
+# function can only be used as `util:ll` (after `use util`). To make it usable
+# directly as `ll`, you can add the following to `util.elv`:
+#
+# ```elvish
+# edit:add-var ll~ $ll~
+# ```
+#
+# ### Conditionally importing a module
+#
+# Another use case is to add a module or function to the REPL namespace
+# conditionally. For example, to only import [the `unix` module](unix.html)
+# when actually running on UNIX, a straightforward solution is to do the
+# following in `rc.elv`:
+#
+# ```elvish
+# use platform
+# if $platform:is-unix {
+#   use unix
+# }
+# ```
+#
+# This doesn't work however, since what `use` does is introducing a variable
+# named `$unix:`. Since all variables in Elvish are lexically scoped, the
+# `$unix:` variable is only valid inside the `if` block.
+#
+# This can be fixed by explicitly introducing the `$unix:` variable to the REPL
+# namespace. The following works both from `rc.elv` and from a module:
+#
+# ```elvish
+# use platform
+# if $platform:is-unix {
+#   use unix
+#   edit:add-var unix: $unix:
+# }
+# ```
+
+#elvdoc:fn add-vars
+#
+# ```elvish
+# edit:add-vars $map
+# ```
+#
+# Takes a map from strings to arbitrary values. Equivalent to calling
+# `edit:add-var` for each key-value pair in the map.

+ 0 - 99
pkg/edit/vars.go

@@ -16,96 +16,6 @@ func initVarsAPI(ed *Editor, nb eval.NsBuilder) {
 	})
 }
 
-//elvdoc:fn add-var
-//
-// ```elvish
-// edit:add-var $name $init
-// ```
-//
-// Defines a new variable in the interactive REPL with an initial value. The new variable becomes
-// available during the next REPL cycle.
-//
-// Equivalent to running `var $name = $init` at a REPL prompt, but `$name` can be
-// dynamic.
-//
-// This is most useful for modules to modify the REPL namespace. Example:
-//
-// ```elvish-transcript
-// ~> cat .config/elvish/lib/a.elv
-// for i [(range 10)] {
-//   edit:add-var foo$i $i
-// }
-// ~> use a
-// ~> put $foo1 $foo2
-// ▶ (num 1)
-// ▶ (num 2)
-// ```
-//
-// Note that if you use a variable as the `$init` argument, `edit:add-var`
-// doesn't add the variable "itself" to the REPL namespace. The variable in the
-// REPL namespace will have the initial value set to the variable's value, but
-// it is not an alias of the original variable:
-//
-// ```elvish-transcript
-// ~> cat .config/elvish/lib/b.elv
-// var foo = foo
-// edit:add-var foo $foo
-// ~> use b
-// ~> put $foo
-// ▶ foo
-// ~> set foo = bar
-// ~> echo $b:foo
-// foo
-// ```
-//
-// ### Importing definition from a module into the REPL
-//
-// One common use of this command is to put the definitions of functions intended for REPL use in a
-// module instead of your [`rc.elv`](command.html#rc-file). For example, if you want to define `ll`
-// as `ls -l`, you can do so in your `rc.elv` directly:
-//
-// ```elvish
-// fn ll {|@a| ls -l $@a }
-// ```
-//
-// But if you move the definition into a module (say `util.elv` in one of the
-// [module search directories](command.html#module-search-directories), this
-// function can only be used as `util:ll` (after `use util`). To make it usable
-// directly as `ll`, you can add the following to `util.elv`:
-//
-// ```elvish
-// edit:add-var ll~ $ll~
-// ```
-//
-// ### Conditionally importing a module
-//
-// Another use case is to add a module or function to the REPL namespace
-// conditionally. For example, to only import [the `unix` module](unix.html)
-// when actually running on UNIX, a straightforward solution is to do the
-// following in `rc.elv`:
-//
-// ```elvish
-// use platform
-// if $platform:is-unix {
-//   use unix
-// }
-// ```
-//
-// This doesn't work however, since what `use` does is introducing a variable
-// named `$unix:`. Since all variables in Elvish are lexically scoped, the
-// `$unix:` variable is only valid inside the `if` block.
-//
-// This can be fixed by explicitly introducing the `$unix:` variable to the REPL
-// namespace. The following works both from `rc.elv` and from a module:
-//
-// ```elvish
-// use platform
-// if $platform:is-unix {
-//   use unix
-//   edit:add-var unix: $unix:
-// }
-// ```
-
 func addVar(fm *eval.Frame, name string, val any) error {
 	if !isUnqualified(name) {
 		return errs.BadValue{
@@ -121,15 +31,6 @@ func addVar(fm *eval.Frame, name string, val any) error {
 	return nil
 }
 
-//elvdoc:fn add-vars
-//
-// ```elvish
-// edit:add-vars $map
-// ```
-//
-// Takes a map from strings to arbitrary values. Equivalent to calling
-// `edit:add-var` for each key-value pair in the map.
-
 func addVars(fm *eval.Frame, m vals.Map) error {
 	nb := eval.BuildNs()
 	for it := m.Iterator(); it.HasElem(); it.Next() {

+ 56 - 0
pkg/eval/builtin_fn_cmd.d.elv

@@ -0,0 +1,56 @@
+#elvdoc:fn external
+#
+# ```elvish
+# external $program
+# ```
+#
+# Construct a callable value for the external program `$program`. Example:
+#
+# ```elvish-transcript
+# ~> var x = (external man)
+# ~> $x ls # opens the manpage for ls
+# ```
+#
+# @cf has-external search-external
+
+#elvdoc:fn has-external
+#
+# ```elvish
+# has-external $command
+# ```
+#
+# Test whether `$command` names a valid external command. Examples (your output
+# might differ):
+#
+# ```elvish-transcript
+# ~> has-external cat
+# ▶ $true
+# ~> has-external lalala
+# ▶ $false
+# ```
+#
+# @cf external search-external
+
+#elvdoc:fn search-external
+#
+# ```elvish
+# search-external $command
+# ```
+#
+# Output the full path of the external `$command`. Throws an exception when not
+# found. Example (your output might vary):
+#
+# ```elvish-transcript
+# ~> search-external cat
+# ▶ /bin/cat
+# ```
+#
+# @cf external has-external
+
+#elvdoc:fn exit
+#
+# ```elvish
+# exit $status?
+# ```
+#
+# Exit the Elvish process with `$status` (defaulting to 0).

+ 0 - 57
pkg/eval/builtin_fn_cmd.go

@@ -25,76 +25,19 @@ func init() {
 	})
 }
 
-//elvdoc:fn external
-//
-// ```elvish
-// external $program
-// ```
-//
-// Construct a callable value for the external program `$program`. Example:
-//
-// ```elvish-transcript
-// ~> var x = (external man)
-// ~> $x ls # opens the manpage for ls
-// ```
-//
-// @cf has-external search-external
-
 func external(cmd string) Callable {
 	return NewExternalCmd(cmd)
 }
 
-//elvdoc:fn has-external
-//
-// ```elvish
-// has-external $command
-// ```
-//
-// Test whether `$command` names a valid external command. Examples (your output
-// might differ):
-//
-// ```elvish-transcript
-// ~> has-external cat
-// ▶ $true
-// ~> has-external lalala
-// ▶ $false
-// ```
-//
-// @cf external search-external
-
 func hasExternal(cmd string) bool {
 	_, err := exec.LookPath(cmd)
 	return err == nil
 }
 
-//elvdoc:fn search-external
-//
-// ```elvish
-// search-external $command
-// ```
-//
-// Output the full path of the external `$command`. Throws an exception when not
-// found. Example (your output might vary):
-//
-// ```elvish-transcript
-// ~> search-external cat
-// ▶ /bin/cat
-// ```
-//
-// @cf external has-external
-
 func searchExternal(cmd string) (string, error) {
 	return exec.LookPath(cmd)
 }
 
-//elvdoc:fn exit
-//
-// ```elvish
-// exit $status?
-// ```
-//
-// Exit the Elvish process with `$status` (defaulting to 0).
-
 // Can be overridden in tests.
 var osExit = os.Exit
 

+ 12 - 0
pkg/eval/builtin_fn_cmd_unix.d.elv

@@ -0,0 +1,12 @@
+#elvdoc:fn exec
+#
+# ```elvish
+# exec $command? $args...
+# ```
+#
+# Replace the Elvish process with an external `$command`, defaulting to
+# `elvish`, passing the given arguments. This decrements `$E:SHLVL` before
+# starting the new process.
+#
+# This command always raises an exception on Windows with the message "not
+# supported on Windows".

+ 0 - 13
pkg/eval/builtin_fn_cmd_unix.go

@@ -19,19 +19,6 @@ import (
 // in the same process group.
 var ErrNotInSameProcessGroup = errors.New("not in the same process group")
 
-//elvdoc:fn exec
-//
-// ```elvish
-// exec $command? $args...
-// ```
-//
-// Replace the Elvish process with an external `$command`, defaulting to
-// `elvish`, passing the given arguments. This decrements `$E:SHLVL` before
-// starting the new process.
-//
-// This command always raises an exception on Windows with the message "not
-// supported on Windows".
-
 // Reference to syscall.Exec. Can be overridden in tests.
 var syscallExec = syscall.Exec
 

+ 189 - 0
pkg/eval/builtin_fn_container.d.elv

@@ -0,0 +1,189 @@
+#elvdoc:fn ns
+#
+# ```elvish
+# ns $map
+# ```
+#
+# Constructs a namespace from `$map`, using the keys as variable names and the
+# values as their values. Examples:
+#
+# ```elvish-transcript
+# ~> var n = (ns [&name=value])
+# ~> put $n[name]
+# ▶ value
+# ~> var n: = (ns [&name=value])
+# ~> put $n:name
+# ▶ value
+# ```
+
+#elvdoc:fn make-map
+#
+# ```elvish
+# make-map $input?
+# ```
+#
+# Outputs a map from the [value inputs](#value-inputs), each of which must be
+# an iterable value with with two elements. The first element of each value
+# is used as the key, and the second element is used as the value.
+#
+# If the same key appears multiple times, the last value is used.
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> make-map [[k v]]
+# ▶ [&k=v]
+# ~> make-map [[k v1] [k v2]]
+# ▶ [&k=v2]
+# ~> put [k1 v1] [k2 v2] | make-map
+# ▶ [&k1=v1 &k2=v2]
+# ~> put aA bB | make-map
+# ▶ [&a=A &b=B]
+# ```
+
+#elvdoc:fn assoc
+#
+# ```elvish
+# assoc $container $k $v
+# ```
+#
+# Output a slightly modified version of `$container`, such that its value at `$k`
+# is `$v`. Applies to both lists and to maps.
+#
+# When `$container` is a list, `$k` may be a negative index. However, slice is not
+# yet supported.
+#
+# ```elvish-transcript
+# ~> assoc [foo bar quux] 0 lorem
+# ▶ [lorem bar quux]
+# ~> assoc [foo bar quux] -1 ipsum
+# ▶ [foo bar ipsum]
+# ~> assoc [&k=v] k v2
+# ▶ [&k=v2]
+# ~> assoc [&k=v] k2 v2
+# ▶ [&k2=v2 &k=v]
+# ```
+#
+# Etymology: [Clojure](https://clojuredocs.org/clojure.core/assoc).
+#
+# @cf dissoc
+
+#elvdoc:fn dissoc
+#
+# ```elvish
+# dissoc $map $k
+# ```
+#
+# Output a slightly modified version of `$map`, with the key `$k` removed. If
+# `$map` does not contain `$k` as a key, the same map is returned.
+#
+# ```elvish-transcript
+# ~> dissoc [&foo=bar &lorem=ipsum] foo
+# ▶ [&lorem=ipsum]
+# ~> dissoc [&foo=bar &lorem=ipsum] k
+# ▶ [&lorem=ipsum &foo=bar]
+# ```
+#
+# @cf assoc
+
+#elvdoc:fn has-value
+#
+# ```elvish
+# has-value $container $value
+# ```
+#
+# Determine whether `$value` is a value in `$container`.
+#
+# Examples, maps:
+#
+# ```elvish-transcript
+# ~> has-value [&k1=v1 &k2=v2] v1
+# ▶ $true
+# ~> has-value [&k1=v1 &k2=v2] k1
+# ▶ $false
+# ```
+#
+# Examples, lists:
+#
+# ```elvish-transcript
+# ~> has-value [v1 v2] v1
+# ▶ $true
+# ~> has-value [v1 v2] k1
+# ▶ $false
+# ```
+#
+# Examples, strings:
+#
+# ```elvish-transcript
+# ~> has-value ab b
+# ▶ $true
+# ~> has-value ab c
+# ▶ $false
+# ```
+
+#elvdoc:fn has-key
+#
+# ```elvish
+# has-key $container $key
+# ```
+#
+# Determine whether `$key` is a key in `$container`. A key could be a map key or
+# an index on a list or string. This includes a range of indexes.
+#
+# Examples, maps:
+#
+# ```elvish-transcript
+# ~> has-key [&k1=v1 &k2=v2] k1
+# ▶ $true
+# ~> has-key [&k1=v1 &k2=v2] v1
+# ▶ $false
+# ```
+#
+# Examples, lists:
+#
+# ```elvish-transcript
+# ~> has-key [v1 v2] 0
+# ▶ $true
+# ~> has-key [v1 v2] 1
+# ▶ $true
+# ~> has-key [v1 v2] 2
+# ▶ $false
+# ~> has-key [v1 v2] 0:2
+# ▶ $true
+# ~> has-key [v1 v2] 0:3
+# ▶ $false
+# ```
+#
+# Examples, strings:
+#
+# ```elvish-transcript
+# ~> has-key ab 0
+# ▶ $true
+# ~> has-key ab 1
+# ▶ $true
+# ~> has-key ab 2
+# ▶ $false
+# ~> has-key ab 0:2
+# ▶ $true
+# ~> has-key ab 0:3
+# ▶ $false
+# ```
+
+#elvdoc:fn keys
+#
+# ```elvish
+# keys $map
+# ```
+#
+# Put all keys of `$map` on the structured stdout.
+#
+# Example:
+#
+# ```elvish-transcript
+# ~> keys [&a=foo &b=bar &c=baz]
+# ▶ a
+# ▶ c
+# ▶ b
+# ```
+#
+# Note that there is no guaranteed order for the keys of a map.

+ 0 - 190
pkg/eval/builtin_fn_container.go

@@ -27,24 +27,6 @@ func init() {
 	})
 }
 
-//elvdoc:fn ns
-//
-// ```elvish
-// ns $map
-// ```
-//
-// Constructs a namespace from `$map`, using the keys as variable names and the
-// values as their values. Examples:
-//
-// ```elvish-transcript
-// ~> var n = (ns [&name=value])
-// ~> put $n[name]
-// ▶ value
-// ~> var n: = (ns [&name=value])
-// ~> put $n:name
-// ▶ value
-// ```
-
 func nsFn(m vals.Map) (*Ns, error) {
 	nb := BuildNs()
 	for it := m.Iterator(); it.HasElem(); it.Next() {
@@ -60,31 +42,6 @@ func nsFn(m vals.Map) (*Ns, error) {
 	return nb.Ns(), nil
 }
 
-//elvdoc:fn make-map
-//
-// ```elvish
-// make-map $input?
-// ```
-//
-// Outputs a map from the [value inputs](#value-inputs), each of which must be
-// an iterable value with with two elements. The first element of each value
-// is used as the key, and the second element is used as the value.
-//
-// If the same key appears multiple times, the last value is used.
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> make-map [[k v]]
-// ▶ [&k=v]
-// ~> make-map [[k v1] [k v2]]
-// ▶ [&k=v2]
-// ~> put [k1 v1] [k2 v2] | make-map
-// ▶ [&k1=v1 &k2=v2]
-// ~> put aA bB | make-map
-// ▶ [&a=A &b=B]
-// ```
-
 func makeMap(input Inputs) (vals.Map, error) {
 	m := vals.EmptyMap
 	var errMakeMap error
@@ -117,57 +74,12 @@ func makeMap(input Inputs) (vals.Map, error) {
 	return m, errMakeMap
 }
 
-//elvdoc:fn assoc
-//
-// ```elvish
-// assoc $container $k $v
-// ```
-//
-// Output a slightly modified version of `$container`, such that its value at `$k`
-// is `$v`. Applies to both lists and to maps.
-//
-// When `$container` is a list, `$k` may be a negative index. However, slice is not
-// yet supported.
-//
-// ```elvish-transcript
-// ~> assoc [foo bar quux] 0 lorem
-// ▶ [lorem bar quux]
-// ~> assoc [foo bar quux] -1 ipsum
-// ▶ [foo bar ipsum]
-// ~> assoc [&k=v] k v2
-// ▶ [&k=v2]
-// ~> assoc [&k=v] k2 v2
-// ▶ [&k2=v2 &k=v]
-// ```
-//
-// Etymology: [Clojure](https://clojuredocs.org/clojure.core/assoc).
-//
-// @cf dissoc
-
 func assoc(a, k, v any) (any, error) {
 	return vals.Assoc(a, k, v)
 }
 
 var errCannotDissoc = errors.New("cannot dissoc")
 
-//elvdoc:fn dissoc
-//
-// ```elvish
-// dissoc $map $k
-// ```
-//
-// Output a slightly modified version of `$map`, with the key `$k` removed. If
-// `$map` does not contain `$k` as a key, the same map is returned.
-//
-// ```elvish-transcript
-// ~> dissoc [&foo=bar &lorem=ipsum] foo
-// ▶ [&lorem=ipsum]
-// ~> dissoc [&foo=bar &lorem=ipsum] k
-// ▶ [&lorem=ipsum &foo=bar]
-// ```
-//
-// @cf assoc
-
 func dissoc(a, k any) (any, error) {
 	a2 := vals.Dissoc(a, k)
 	if a2 == nil {
@@ -176,41 +88,6 @@ func dissoc(a, k any) (any, error) {
 	return a2, nil
 }
 
-//elvdoc:fn has-value
-//
-// ```elvish
-// has-value $container $value
-// ```
-//
-// Determine whether `$value` is a value in `$container`.
-//
-// Examples, maps:
-//
-// ```elvish-transcript
-// ~> has-value [&k1=v1 &k2=v2] v1
-// ▶ $true
-// ~> has-value [&k1=v1 &k2=v2] k1
-// ▶ $false
-// ```
-//
-// Examples, lists:
-//
-// ```elvish-transcript
-// ~> has-value [v1 v2] v1
-// ▶ $true
-// ~> has-value [v1 v2] k1
-// ▶ $false
-// ```
-//
-// Examples, strings:
-//
-// ```elvish-transcript
-// ~> has-value ab b
-// ▶ $true
-// ~> has-value ab c
-// ▶ $false
-// ```
-
 func hasValue(container, value any) (bool, error) {
 	switch container := container.(type) {
 	case vals.Map:
@@ -231,77 +108,10 @@ func hasValue(container, value any) (bool, error) {
 	}
 }
 
-//elvdoc:fn has-key
-//
-// ```elvish
-// has-key $container $key
-// ```
-//
-// Determine whether `$key` is a key in `$container`. A key could be a map key or
-// an index on a list or string. This includes a range of indexes.
-//
-// Examples, maps:
-//
-// ```elvish-transcript
-// ~> has-key [&k1=v1 &k2=v2] k1
-// ▶ $true
-// ~> has-key [&k1=v1 &k2=v2] v1
-// ▶ $false
-// ```
-//
-// Examples, lists:
-//
-// ```elvish-transcript
-// ~> has-key [v1 v2] 0
-// ▶ $true
-// ~> has-key [v1 v2] 1
-// ▶ $true
-// ~> has-key [v1 v2] 2
-// ▶ $false
-// ~> has-key [v1 v2] 0:2
-// ▶ $true
-// ~> has-key [v1 v2] 0:3
-// ▶ $false
-// ```
-//
-// Examples, strings:
-//
-// ```elvish-transcript
-// ~> has-key ab 0
-// ▶ $true
-// ~> has-key ab 1
-// ▶ $true
-// ~> has-key ab 2
-// ▶ $false
-// ~> has-key ab 0:2
-// ▶ $true
-// ~> has-key ab 0:3
-// ▶ $false
-// ```
-
 func hasKey(container, key any) bool {
 	return vals.HasKey(container, key)
 }
 
-//elvdoc:fn keys
-//
-// ```elvish
-// keys $map
-// ```
-//
-// Put all keys of `$map` on the structured stdout.
-//
-// Example:
-//
-// ```elvish-transcript
-// ~> keys [&a=foo &b=bar &c=baz]
-// ▶ a
-// ▶ c
-// ▶ b
-// ```
-//
-// Note that there is no guaranteed order for the keys of a map.
-
 func keys(fm *Frame, v any) error {
 	out := fm.ValueOutput()
 	var errPut error

+ 69 - 0
pkg/eval/builtin_fn_debug.d.elv

@@ -0,0 +1,69 @@
+#elvdoc:fn src
+#
+# ```elvish
+# src
+# ```
+#
+# Output a map-like value describing the current source being evaluated. The value
+# contains the following fields:
+#
+# -   `name`, a unique name of the current source. If the source originates from a
+#     file, it is the full path of the file.
+#
+# -   `code`, the full body of the current source.
+#
+# -   `is-file`, whether the source originates from a file.
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> put (src)[name code is-file]
+# ▶ '[tty]'
+# ▶ 'put (src)[name code is-file]'
+# ▶ $false
+# ~> echo 'put (src)[name code is-file]' > show-src.elv
+# ~> elvish show-src.elv
+# ▶ /home/elf/show-src.elv
+# ▶ "put (src)[name code is-file]\n"
+# ▶ $true
+# ```
+#
+# Note: this builtin always returns information of the source of the function
+# calling `src`. Consider the following example:
+#
+# ```elvish-transcript
+# ~> echo 'fn show { put (src)[name] }' > ~/.elvish/lib/src-fsutil.elv
+# ~> use src-util
+# ~> src-util:show
+# ▶ /home/elf/.elvish/lib/src-fsutil.elv
+# ```
+
+#elvdoc:fn -gc
+#
+# ```elvish
+# -gc
+# ```
+#
+# Force the Go garbage collector to run.
+#
+# This is only useful for debug purposes.
+
+#elvdoc:fn -stack
+#
+# ```elvish
+# -stack
+# ```
+#
+# Print a stack trace.
+#
+# This is only useful for debug purposes.
+
+#elvdoc:fn -log
+#
+# ```elvish
+# -log $filename
+# ```
+#
+# Direct internal debug logs to the named file.
+#
+# This is only useful for debug purposes.

+ 0 - 70
pkg/eval/builtin_fn_debug.go

@@ -16,74 +16,14 @@ func init() {
 	})
 }
 
-//elvdoc:fn src
-//
-// ```elvish
-// src
-// ```
-//
-// Output a map-like value describing the current source being evaluated. The value
-// contains the following fields:
-//
-// -   `name`, a unique name of the current source. If the source originates from a
-//     file, it is the full path of the file.
-//
-// -   `code`, the full body of the current source.
-//
-// -   `is-file`, whether the source originates from a file.
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> put (src)[name code is-file]
-// ▶ '[tty]'
-// ▶ 'put (src)[name code is-file]'
-// ▶ $false
-// ~> echo 'put (src)[name code is-file]' > show-src.elv
-// ~> elvish show-src.elv
-// ▶ /home/elf/show-src.elv
-// ▶ "put (src)[name code is-file]\n"
-// ▶ $true
-// ```
-//
-// Note: this builtin always returns information of the source of the function
-// calling `src`. Consider the following example:
-//
-// ```elvish-transcript
-// ~> echo 'fn show { put (src)[name] }' > ~/.elvish/lib/src-fsutil.elv
-// ~> use src-util
-// ~> src-util:show
-// ▶ /home/elf/.elvish/lib/src-fsutil.elv
-// ```
-
 func src(fm *Frame) parse.Source {
 	return fm.srcMeta
 }
 
-//elvdoc:fn -gc
-//
-// ```elvish
-// -gc
-// ```
-//
-// Force the Go garbage collector to run.
-//
-// This is only useful for debug purposes.
-
 func _gc() {
 	runtime.GC()
 }
 
-//elvdoc:fn -stack
-//
-// ```elvish
-// -stack
-// ```
-//
-// Print a stack trace.
-//
-// This is only useful for debug purposes.
-
 func _stack(fm *Frame) error {
 	// TODO(xiaq): Dup with main.go.
 	buf := make([]byte, 1024)
@@ -94,16 +34,6 @@ func _stack(fm *Frame) error {
 	return err
 }
 
-//elvdoc:fn -log
-//
-// ```elvish
-// -log $filename
-// ```
-//
-// Direct internal debug logs to the named file.
-//
-// This is only useful for debug purposes.
-
 func _log(fname string) error {
 	return logutil.SetOutputFile(fname)
 }

+ 86 - 0
pkg/eval/builtin_fn_env.d.elv

@@ -0,0 +1,86 @@
+#elvdoc:fn set-env
+#
+# ```elvish
+# set-env $name $value
+# ```
+#
+# Sets an environment variable to the given value. Calling `set-env VAR_NAME
+# value` is similar to `set E:VAR_NAME = value`, but allows the variable name
+# to be dynamic.
+#
+# Example:
+#
+# ```elvish-transcript
+# ~> set-env X foobar
+# ~> put $E:X
+# ▶ foobar
+# ```
+#
+# @cf get-env has-env unset-env
+
+#elvdoc:fn unset-env
+#
+# ```elvish
+# unset-env $name
+# ```
+#
+# Unset an environment variable. Calling `unset-env VAR_NAME` is similar to
+# `del E:VAR_NAME`, but allows the variable name to be dynamic.
+#
+# Example:
+#
+# ```elvish-transcript
+# ~> set E:X = foo
+# ~> unset-env X
+# ~> has-env X
+# ▶ $false
+# ~> put $E:X
+# ▶ ''
+# ```
+#
+# @cf has-env get-env set-env
+
+#elvdoc:fn has-env
+#
+# ```elvish
+# has-env $name
+# ```
+#
+# Test whether an environment variable exists. This command has no equivalent
+# operation using the `E:` namespace (but see https://b.elv.sh/1026).
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> has-env PATH
+# ▶ $true
+# ~> has-env NO_SUCH_ENV
+# ▶ $false
+# ```
+#
+# @cf get-env set-env unset-env
+
+#elvdoc:fn get-env
+#
+# ```elvish
+# get-env $name
+# ```
+#
+# Gets the value of an environment variable. Throws an exception if the
+# environment variable does not exist.
+#
+# Calling `get-env VAR_NAME` is similar to `put $E:VAR_NAME`, but allows the
+# variable name to be dynamic, and throws an exception instead of producing an
+# empty string for nonexistent environment variables.
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> get-env LANG
+# ▶ zh_CN.UTF-8
+# ~> get-env NO_SUCH_ENV
+# Exception: non-existent environment variable
+# [tty], line 1: get-env NO_SUCH_ENV
+# ```
+#
+# @cf has-env set-env unset-env

+ 0 - 87
pkg/eval/builtin_fn_env.go

@@ -9,48 +9,6 @@ import (
 // variable does not exist.
 var ErrNonExistentEnvVar = errors.New("non-existent environment variable")
 
-//elvdoc:fn set-env
-//
-// ```elvish
-// set-env $name $value
-// ```
-//
-// Sets an environment variable to the given value. Calling `set-env VAR_NAME
-// value` is similar to `set E:VAR_NAME = value`, but allows the variable name
-// to be dynamic.
-//
-// Example:
-//
-// ```elvish-transcript
-// ~> set-env X foobar
-// ~> put $E:X
-// ▶ foobar
-// ```
-//
-// @cf get-env has-env unset-env
-
-//elvdoc:fn unset-env
-//
-// ```elvish
-// unset-env $name
-// ```
-//
-// Unset an environment variable. Calling `unset-env VAR_NAME` is similar to
-// `del E:VAR_NAME`, but allows the variable name to be dynamic.
-//
-// Example:
-//
-// ```elvish-transcript
-// ~> set E:X = foo
-// ~> unset-env X
-// ~> has-env X
-// ▶ $false
-// ~> put $E:X
-// ▶ ''
-// ```
-//
-// @cf has-env get-env set-env
-
 func init() {
 	addBuiltinFns(map[string]any{
 		"has-env":   hasEnv,
@@ -60,56 +18,11 @@ func init() {
 	})
 }
 
-//elvdoc:fn has-env
-//
-// ```elvish
-// has-env $name
-// ```
-//
-// Test whether an environment variable exists. This command has no equivalent
-// operation using the `E:` namespace (but see https://b.elv.sh/1026).
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> has-env PATH
-// ▶ $true
-// ~> has-env NO_SUCH_ENV
-// ▶ $false
-// ```
-//
-// @cf get-env set-env unset-env
-
 func hasEnv(key string) bool {
 	_, ok := os.LookupEnv(key)
 	return ok
 }
 
-//elvdoc:fn get-env
-//
-// ```elvish
-// get-env $name
-// ```
-//
-// Gets the value of an environment variable. Throws an exception if the
-// environment variable does not exist.
-//
-// Calling `get-env VAR_NAME` is similar to `put $E:VAR_NAME`, but allows the
-// variable name to be dynamic, and throws an exception instead of producing an
-// empty string for nonexistent environment variables.
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> get-env LANG
-// ▶ zh_CN.UTF-8
-// ~> get-env NO_SUCH_ENV
-// Exception: non-existent environment variable
-// [tty], line 1: get-env NO_SUCH_ENV
-// ```
-//
-// @cf has-env set-env unset-env
-
 func getEnv(key string) (string, error) {
 	value, ok := os.LookupEnv(key)
 	if !ok {

+ 263 - 0
pkg/eval/builtin_fn_flow.d.elv

@@ -0,0 +1,263 @@
+#elvdoc:fn run-parallel
+#
+# ```elvish
+# run-parallel $callable ...
+# ```
+#
+# Run several callables in parallel, and wait for all of them to finish.
+#
+# If one or more callables throw exceptions, the other callables continue running,
+# and a composite exception is thrown when all callables finish execution.
+#
+# The behavior of `run-parallel` is consistent with the behavior of pipelines,
+# except that it does not perform any redirections.
+#
+# Here is an example that lets you pipe the stdout and stderr of a command to two
+# different commands in order to independently capture the output of each byte stream:
+#
+# ```elvish-transcript
+# ~> fn capture {|f|
+#      var pout = (file:pipe)
+#      var perr = (file:pipe)
+#      var out err
+#      run-parallel {
+#        $f > $pout[w] 2> $perr[w]
+#        file:close $pout[w]
+#        file:close $perr[w]
+#      } {
+#        set out = (slurp < $pout[r])
+#        file:close $pout[r]
+#      } {
+#        set err = (slurp < $perr[r])
+#        file:close $perr[r]
+#      }
+#      put $out $err
+#    }
+# ~> capture { echo stdout-test; echo stderr-test >&2 }
+# ▶ "stdout-test\n"
+# ▶ "stderr-test\n"
+# ```
+#
+# This command is intended for doing a fixed number of heterogeneous things in
+# parallel. If you need homogeneous parallel processing of possibly unbound data,
+# use `peach` instead.
+#
+# @cf peach
+
+#elvdoc:fn each
+#
+# ```elvish
+# each $f $inputs?
+# ```
+#
+# Calls `$f` on each [value input](#value-inputs).
+#
+# An exception raised from [`break`](#break) is caught by `each`, and will
+# cause it to terminate early.
+#
+# An exception raised from [`continue`](#continue) is swallowed and can be used
+# to terminate a single iteration early.
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> range 5 8 | each {|x| * $x $x }
+# ▶ 25
+# ▶ 36
+# ▶ 49
+# ~> each {|x| put $x[:3] } [lorem ipsum]
+# ▶ lor
+# ▶ ips
+# ```
+#
+# @cf peach
+#
+# Etymology: Various languages, as `for each`. Happens to have the same name as
+# the iteration construct of
+# [Factor](http://docs.factorcode.org/content/word-each,sequences.html).
+
+#elvdoc:fn peach
+#
+# ```elvish
+# peach $f $inputs?
+# ```
+#
+# Calls `$f` for each [value input](#value-inputs), possibly in parallel.
+#
+# Like `each`, an exception raised from [`break`](#break) will cause `peach`
+# to terminate early. However due to the parallel nature of `peach`, the exact
+# time of termination is non-deterministic, and termination is not guaranteed.
+#
+# An exception raised from [`continue`](#continue) is swallowed and can be used
+# to terminate a single iteration early.
+#
+# Example (your output will differ):
+#
+# ```elvish-transcript
+# ~> range 1 10 | peach {|x| + $x 10 }
+# ▶ (num 12)
+# ▶ (num 13)
+# ▶ (num 11)
+# ▶ (num 16)
+# ▶ (num 18)
+# ▶ (num 14)
+# ▶ (num 17)
+# ▶ (num 15)
+# ▶ (num 19)
+# ~> range 1 101 |
+#    peach {|x| if (== 50 $x) { break } else { put $x } } |
+#    + (all) # 1+...+49 = 1225; 1+...+100 = 5050
+# ▶ (num 1328)
+# ```
+#
+# This command is intended for homogeneous processing of possibly unbound data. If
+# you need to do a fixed number of heterogeneous things in parallel, use
+# `run-parallel`.
+#
+# @cf each run-parallel
+
+#elvdoc:fn fail
+#
+# ```elvish
+# fail $v
+# ```
+#
+# Throws an exception; `$v` may be any type. If `$v` is already an exception,
+# `fail` rethrows it.
+#
+# ```elvish-transcript
+# ~> fail bad
+# Exception: bad
+# [tty 9], line 1: fail bad
+# ~> put ?(fail bad)
+# ▶ ?(fail bad)
+# ~> fn f { fail bad }
+# ~> fail ?(f)
+# Exception: bad
+# Traceback:
+#   [tty 7], line 1:
+#     fn f { fail bad }
+#   [tty 8], line 1:
+#     fail ?(f)
+# ```
+
+#elvdoc:fn return
+#
+# ```elvish
+# return
+# ```
+#
+# Raises the special "return" exception. When raised inside a named function
+# (defined by the [`fn` keyword](language.html#fn)) it is captured by the
+# function and causes the function to terminate. It is not captured by an
+# ordinary anonymous function.
+#
+# Because `return` raises an exception it can be caught by a
+# [`try`](language.html#try) block. If not caught, either implicitly by a
+# named function or explicitly, it causes a failure like any other uncaught
+# exception.
+#
+# See the discussion about [flow commands and
+# exceptions](language.html#exception-and-flow-commands)
+#
+# **Note**: If you want to shadow the builtin `return` function with a local
+# wrapper, do not define it with `fn` as `fn` swallows the special exception
+# raised by return. Consider this example:
+#
+# ```elvish-transcript
+# ~> use builtin
+# ~> fn return { put return; builtin:return }
+# ~> fn test-return { put before; return; put after }
+# ~> test-return
+# ▶ before
+# ▶ return
+# ▶ after
+# ```
+#
+# Instead, shadow the function by directly assigning to `return~`:
+#
+# ```elvish-transcript
+# ~> use builtin
+# ~> var return~ = { put return; builtin:return }
+# ~> fn test-return { put before; return; put after }
+# ~> test-return
+# ▶ before
+# ▶ return
+# ```
+
+#elvdoc:fn break
+#
+# ```elvish
+# break
+# ```
+#
+# Raises the special "break" exception. When raised inside a loop it is
+# captured and causes the loop to terminate.
+#
+# Because `break` raises an exception it can be caught by a
+# [`try`](language.html#try) block. If not caught, either implicitly by a loop
+# or explicitly, it causes a failure like any other uncaught exception.
+#
+# See the discussion about [flow commands and exceptions](language.html#exception-and-flow-commands)
+#
+# **Note**: You can create a `break` function and it will shadow the builtin
+# command. If you do so you should explicitly invoke the builtin. For example:
+#
+# ```elvish-transcript
+# ~> use builtin
+# ~> fn break { put 'break'; builtin:break; put 'should not appear' }
+# ~> for x [a b c] { put $x; break; put 'unexpected' }
+# ▶ a
+# ▶ break
+# ```
+
+#elvdoc:fn continue
+#
+# ```elvish
+# continue
+# ```
+#
+# Raises the special "continue" exception. When raised inside a loop it is
+# captured and causes the loop to begin its next iteration.
+#
+# Because `continue` raises an exception it can be caught by a
+# [`try`](language.html#try) block. If not caught, either implicitly by a loop
+# or explicitly, it causes a failure like any other uncaught exception.
+#
+# See the discussion about [flow commands and exceptions](language.html#exception-and-flow-commands)
+#
+# **Note**: You can create a `continue` function and it will shadow the builtin
+# command. If you do so you should explicitly invoke the builtin. For example:
+#
+# ```elvish-transcript
+# ~> use builtin
+# ~> fn continue { put 'continue'; builtin:continue; put 'should not appear' }
+# ~> for x [a b c] { put $x; continue; put 'unexpected' }
+# ▶ a
+# ▶ continue
+# ▶ b
+# ▶ continue
+# ▶ c
+# ▶ continue
+# ```
+
+#elvdoc:fn defer
+#
+# ```elvish
+# defer $fn
+# ```
+#
+# Schedules a function to be called when execution reaches the end of the
+# current closure. The function is called with no arguments or options, and any
+# exception it throws gets propagated.
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> { defer { put foo }; put bar }
+# ▶ bar
+# ▶ foo
+# ~> defer { put foo }
+# Exception: defer must be called from within a closure
+# [tty 2], line 1: defer { put foo }
+# ```

+ 0 - 264
pkg/eval/builtin_fn_flow.go

@@ -29,52 +29,6 @@ func init() {
 	})
 }
 
-//elvdoc:fn run-parallel
-//
-// ```elvish
-// run-parallel $callable ...
-// ```
-//
-// Run several callables in parallel, and wait for all of them to finish.
-//
-// If one or more callables throw exceptions, the other callables continue running,
-// and a composite exception is thrown when all callables finish execution.
-//
-// The behavior of `run-parallel` is consistent with the behavior of pipelines,
-// except that it does not perform any redirections.
-//
-// Here is an example that lets you pipe the stdout and stderr of a command to two
-// different commands in order to independently capture the output of each byte stream:
-//
-// ```elvish-transcript
-// ~> fn capture {|f|
-//      var pout = (file:pipe)
-//      var perr = (file:pipe)
-//      var out err
-//      run-parallel {
-//        $f > $pout[w] 2> $perr[w]
-//        file:close $pout[w]
-//        file:close $perr[w]
-//      } {
-//        set out = (slurp < $pout[r])
-//        file:close $pout[r]
-//      } {
-//        set err = (slurp < $perr[r])
-//        file:close $perr[r]
-//      }
-//      put $out $err
-//    }
-// ~> capture { echo stdout-test; echo stderr-test >&2 }
-// ▶ "stdout-test\n"
-// ▶ "stderr-test\n"
-// ```
-//
-// This command is intended for doing a fixed number of heterogeneous things in
-// parallel. If you need homogeneous parallel processing of possibly unbound data,
-// use `peach` instead.
-//
-// @cf peach
-
 func runParallel(fm *Frame, functions ...Callable) error {
 	var wg sync.WaitGroup
 	wg.Add(len(functions))
@@ -93,38 +47,6 @@ func runParallel(fm *Frame, functions ...Callable) error {
 	return MakePipelineError(exceptions)
 }
 
-//elvdoc:fn each
-//
-// ```elvish
-// each $f $inputs?
-// ```
-//
-// Calls `$f` on each [value input](#value-inputs).
-//
-// An exception raised from [`break`](#break) is caught by `each`, and will
-// cause it to terminate early.
-//
-// An exception raised from [`continue`](#continue) is swallowed and can be used
-// to terminate a single iteration early.
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> range 5 8 | each {|x| * $x $x }
-// ▶ 25
-// ▶ 36
-// ▶ 49
-// ~> each {|x| put $x[:3] } [lorem ipsum]
-// ▶ lor
-// ▶ ips
-// ```
-//
-// @cf peach
-//
-// Etymology: Various languages, as `for each`. Happens to have the same name as
-// the iteration construct of
-// [Factor](http://docs.factorcode.org/content/word-each,sequences.html).
-
 func each(fm *Frame, f Callable, inputs Inputs) error {
 	broken := false
 	var err error
@@ -151,46 +73,6 @@ func each(fm *Frame, f Callable, inputs Inputs) error {
 	return err
 }
 
-//elvdoc:fn peach
-//
-// ```elvish
-// peach $f $inputs?
-// ```
-//
-// Calls `$f` for each [value input](#value-inputs), possibly in parallel.
-//
-// Like `each`, an exception raised from [`break`](#break) will cause `peach`
-// to terminate early. However due to the parallel nature of `peach`, the exact
-// time of termination is non-deterministic, and termination is not guaranteed.
-//
-// An exception raised from [`continue`](#continue) is swallowed and can be used
-// to terminate a single iteration early.
-//
-// Example (your output will differ):
-//
-// ```elvish-transcript
-// ~> range 1 10 | peach {|x| + $x 10 }
-// ▶ (num 12)
-// ▶ (num 13)
-// ▶ (num 11)
-// ▶ (num 16)
-// ▶ (num 18)
-// ▶ (num 14)
-// ▶ (num 17)
-// ▶ (num 15)
-// ▶ (num 19)
-// ~> range 1 101 |
-//    peach {|x| if (== 50 $x) { break } else { put $x } } |
-//    + (all) # 1+...+49 = 1225; 1+...+100 = 5050
-// ▶ (num 1328)
-// ```
-//
-// This command is intended for homogeneous processing of possibly unbound data. If
-// you need to do a fixed number of heterogeneous things in parallel, use
-// `run-parallel`.
-//
-// @cf each run-parallel
-
 func peach(fm *Frame, f Callable, inputs Inputs) error {
 	var wg sync.WaitGroup
 	var broken int32
@@ -244,31 +126,6 @@ func (failFields) IsStructMap() {}
 func (f failFields) Type() string { return "fail" }
 func (f failFields) Content() any { return f.e.Content }
 
-//elvdoc:fn fail
-//
-// ```elvish
-// fail $v
-// ```
-//
-// Throws an exception; `$v` may be any type. If `$v` is already an exception,
-// `fail` rethrows it.
-//
-// ```elvish-transcript
-// ~> fail bad
-// Exception: bad
-// [tty 9], line 1: fail bad
-// ~> put ?(fail bad)
-// ▶ ?(fail bad)
-// ~> fn f { fail bad }
-// ~> fail ?(f)
-// Exception: bad
-// Traceback:
-//   [tty 7], line 1:
-//     fn f { fail bad }
-//   [tty 8], line 1:
-//     fail ?(f)
-// ```
-
 func fail(v any) error {
 	if e, ok := v.(error); ok {
 		// MAYBE TODO: if v is an exception, attach a "rethrown" stack trace,
@@ -282,139 +139,18 @@ func multiErrorFn(excs ...Exception) error {
 	return PipelineError{excs}
 }
 
-//elvdoc:fn return
-//
-// ```elvish
-// return
-// ```
-//
-// Raises the special "return" exception. When raised inside a named function
-// (defined by the [`fn` keyword](language.html#fn)) it is captured by the
-// function and causes the function to terminate. It is not captured by an
-// ordinary anonymous function.
-//
-// Because `return` raises an exception it can be caught by a
-// [`try`](language.html#try) block. If not caught, either implicitly by a
-// named function or explicitly, it causes a failure like any other uncaught
-// exception.
-//
-// See the discussion about [flow commands and
-// exceptions](language.html#exception-and-flow-commands)
-//
-// **Note**: If you want to shadow the builtin `return` function with a local
-// wrapper, do not define it with `fn` as `fn` swallows the special exception
-// raised by return. Consider this example:
-//
-// ```elvish-transcript
-// ~> use builtin
-// ~> fn return { put return; builtin:return }
-// ~> fn test-return { put before; return; put after }
-// ~> test-return
-// ▶ before
-// ▶ return
-// ▶ after
-// ```
-//
-// Instead, shadow the function by directly assigning to `return~`:
-//
-// ```elvish-transcript
-// ~> use builtin
-// ~> var return~ = { put return; builtin:return }
-// ~> fn test-return { put before; return; put after }
-// ~> test-return
-// ▶ before
-// ▶ return
-// ```
-
 func returnFn() error {
 	return Return
 }
 
-//elvdoc:fn break
-//
-// ```elvish
-// break
-// ```
-//
-// Raises the special "break" exception. When raised inside a loop it is
-// captured and causes the loop to terminate.
-//
-// Because `break` raises an exception it can be caught by a
-// [`try`](language.html#try) block. If not caught, either implicitly by a loop
-// or explicitly, it causes a failure like any other uncaught exception.
-//
-// See the discussion about [flow commands and exceptions](language.html#exception-and-flow-commands)
-//
-// **Note**: You can create a `break` function and it will shadow the builtin
-// command. If you do so you should explicitly invoke the builtin. For example:
-//
-// ```elvish-transcript
-// ~> use builtin
-// ~> fn break { put 'break'; builtin:break; put 'should not appear' }
-// ~> for x [a b c] { put $x; break; put 'unexpected' }
-// ▶ a
-// ▶ break
-// ```
-
 func breakFn() error {
 	return Break
 }
 
-//elvdoc:fn continue
-//
-// ```elvish
-// continue
-// ```
-//
-// Raises the special "continue" exception. When raised inside a loop it is
-// captured and causes the loop to begin its next iteration.
-//
-// Because `continue` raises an exception it can be caught by a
-// [`try`](language.html#try) block. If not caught, either implicitly by a loop
-// or explicitly, it causes a failure like any other uncaught exception.
-//
-// See the discussion about [flow commands and exceptions](language.html#exception-and-flow-commands)
-//
-// **Note**: You can create a `continue` function and it will shadow the builtin
-// command. If you do so you should explicitly invoke the builtin. For example:
-//
-// ```elvish-transcript
-// ~> use builtin
-// ~> fn continue { put 'continue'; builtin:continue; put 'should not appear' }
-// ~> for x [a b c] { put $x; continue; put 'unexpected' }
-// ▶ a
-// ▶ continue
-// ▶ b
-// ▶ continue
-// ▶ c
-// ▶ continue
-// ```
-
 func continueFn() error {
 	return Continue
 }
 
-//elvdoc:fn defer
-//
-// ```elvish
-// defer $fn
-// ```
-//
-// Schedules a function to be called when execution reaches the end of the
-// current closure. The function is called with no arguments or options, and any
-// exception it throws gets propagated.
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> { defer { put foo }; put bar }
-// ▶ bar
-// ▶ foo
-// ~> defer { put foo }
-// Exception: defer must be called from within a closure
-// [tty 2], line 1: defer { put foo }
-// ```
-
 var errDeferNotInClosure = errors.New("defer must be called from within a closure")
 
 func deferFn(fm *Frame, fn Callable) error {

+ 35 - 0
pkg/eval/builtin_fn_fs.d.elv

@@ -0,0 +1,35 @@
+#elvdoc:fn cd
+#
+# ```elvish
+# cd $dirname
+# ```
+#
+# Changes directory.
+#
+# This affects the entire process, including parallel tasks that are started
+# implicitly (such as prompt functions) or explicitly (such as one started by
+# [`peach`](#peach)).
+#
+# Note that Elvish's `cd` does not support `cd -`.
+#
+# @cf pwd
+
+#elvdoc:fn tilde-abbr
+#
+# ```elvish
+# tilde-abbr $path
+# ```
+#
+# If `$path` represents a path under the home directory, replace the home
+# directory with `~`. Examples:
+#
+# ```elvish-transcript
+# ~> echo $E:HOME
+# /Users/foo
+# ~> tilde-abbr /Users/foo
+# ▶ '~'
+# ~> tilde-abbr /Users/foobar
+# ▶ /Users/foobar
+# ~> tilde-abbr /Users/foo/a/b
+# ▶ '~/a/b'
+# ```

+ 0 - 36
pkg/eval/builtin_fn_fs.go

@@ -17,22 +17,6 @@ func init() {
 	})
 }
 
-//elvdoc:fn cd
-//
-// ```elvish
-// cd $dirname
-// ```
-//
-// Changes directory.
-//
-// This affects the entire process, including parallel tasks that are started
-// implicitly (such as prompt functions) or explicitly (such as one started by
-// [`peach`](#peach)).
-//
-// Note that Elvish's `cd` does not support `cd -`.
-//
-// @cf pwd
-
 func cd(fm *Frame, args ...string) error {
 	var dir string
 	switch len(args) {
@@ -51,26 +35,6 @@ func cd(fm *Frame, args ...string) error {
 	return fm.Evaler.Chdir(dir)
 }
 
-//elvdoc:fn tilde-abbr
-//
-// ```elvish
-// tilde-abbr $path
-// ```
-//
-// If `$path` represents a path under the home directory, replace the home
-// directory with `~`. Examples:
-//
-// ```elvish-transcript
-// ~> echo $E:HOME
-// /Users/foo
-// ~> tilde-abbr /Users/foo
-// ▶ '~'
-// ~> tilde-abbr /Users/foobar
-// ▶ /Users/foobar
-// ~> tilde-abbr /Users/foo/a/b
-// ▶ '~/a/b'
-// ```
-
 func tildeAbbr(path string) string {
 	return fsutil.TildeAbbr(path)
 }

+ 472 - 0
pkg/eval/builtin_fn_io.d.elv

@@ -0,0 +1,472 @@
+#elvdoc:fn put
+#
+# ```elvish
+# put $value...
+# ```
+#
+# Takes arbitrary arguments and write them to the structured stdout.
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> put a
+# ▶ a
+# ~> put lorem ipsum [a b] { ls }
+# ▶ lorem
+# ▶ ipsum
+# ▶ [a b]
+# ▶ <closure 0xc4202607e0>
+# ```
+#
+# **Note**: It is almost never necessary to use `put (...)` - just write the
+# `...` part. For example, `put (eq a b)` is the equivalent to just `eq a b`.
+#
+# Etymology: Various languages, in particular
+# [C](https://manpages.debian.org/stretch/manpages-dev/puts.3.en.html) and
+# [Ruby](https://ruby-doc.org/core-2.2.2/IO.html#method-i-puts) as `puts`.
+
+#elvdoc:fn repeat
+#
+# ```elvish
+# repeat $n $value
+# ```
+#
+# Output `$value` for `$n` times. Example:
+#
+# ```elvish-transcript
+# ~> repeat 0 lorem
+# ~> repeat 4 NAN
+# ▶ NAN
+# ▶ NAN
+# ▶ NAN
+# ▶ NAN
+# ```
+#
+# Etymology: [Clojure](https://clojuredocs.org/clojure.core/repeat).
+
+#elvdoc:fn read-upto
+#
+# ```elvish
+# read-upto $terminator
+# ```
+#
+# Reads byte input until `$terminator` or end-of-file is encountered. It outputs the part of the
+# input read as a string value. The output contains the trailing `$terminator`, unless `read-upto`
+# terminated at end-of-file.
+#
+# The `$terminator` must be a single ASCII character such as `"\x00"` (NUL).
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> echo "a,b,c" | read-upto ","
+# ▶ 'a,'
+# ~> echo "foo\nbar" | read-upto "\n"
+# ▶ "foo\n"
+# ~> echo "a.elv\x00b.elv" | read-upto "\x00"
+# ▶ "a.elv\x00"
+# ~> print "foobar" | read-upto "\n"
+# ▶ foobar
+# ```
+
+#elvdoc:fn read-line
+#
+# ```elvish
+# read-line
+# ```
+#
+# Reads a single line from byte input, and writes the line to the value output,
+# stripping the line ending. A line can end with `"\r\n"`, `"\n"`, or end of
+# file. Examples:
+#
+# ```elvish-transcript
+# ~> print line | read-line
+# ▶ line
+# ~> print "line\n" | read-line
+# ▶ line
+# ~> print "line\r\n" | read-line
+# ▶ line
+# ~> print "line-with-extra-cr\r\r\n" | read-line
+# ▶ "line-with-extra-cr\r"
+# ```
+
+#elvdoc:fn print
+#
+# ```elvish
+# print &sep=' ' $value...
+# ```
+#
+# Like `echo`, just without the newline.
+#
+# @cf echo
+#
+# Etymology: Various languages, in particular
+# [Perl](https://perldoc.perl.org/functions/print.html) and
+# [zsh](http://zsh.sourceforge.net/Doc/Release/Shell-Builtin-Commands.html), whose
+# `print`s do not print a trailing newline.
+
+#elvdoc:fn printf
+#
+# ```elvish
+# printf $template $value...
+# ```
+#
+# Prints values to the byte stream according to a template. If you need to inject the output into
+# the value stream use this pattern: `printf .... | slurp`. That ensures that any newlines in the
+# output of `printf` do not cause its output to be broken into multiple values, thus eliminating
+# the newlines, which will occur if you do `put (printf ....)`.
+#
+# Like [`print`](#print), this command does not add an implicit newline; include an explicit `"\n"`
+# in the formatting template instead. For example, `printf "%.1f\n" (/ 10.0 3)`.
+#
+# See Go's [`fmt`](https://golang.org/pkg/fmt/#hdr-Printing) package for
+# details about the formatting verbs and the various flags that modify the
+# default behavior, such as padding and justification.
+#
+# Unlike Go, each formatting verb has a single associated internal type, and
+# accepts any argument that can reasonably be converted to that type:
+#
+# - The verbs `%s`, `%q` and `%v` convert the corresponding argument to a
+#   string in different ways:
+#
+#     - `%s` uses [to-string](#to-string) to convert a value to string.
+#
+#     - `%q` uses [repr](#repr) to convert a value to string.
+#
+#     - `%v` is equivalent to `%s`, and `%#v` is equivalent to `%q`.
+#
+# - The verb `%t` first convert the corresponding argument to a boolean using
+#   [bool](#bool), and then uses its Go counterpart to format the boolean.
+#
+# - The verbs `%b`, `%c`, `%d`, `%o`, `%O`, `%x`, `%X` and `%U` first convert
+#   the corresponding argument to an integer using an internal algorithm, and
+#   use their Go counterparts to format the integer.
+#
+# - The verbs `%e`, `%E`, `%f`, `%F`, `%g` and `%G` first convert the
+#   corresponding argument to a floating-point number using
+#   [float64](#float64), and then use their Go counterparts to format the
+#   number.
+#
+# The special verb `%%` prints a literal `%` and consumes no argument.
+#
+# Verbs not documented above are not supported.
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> printf "%10s %.2f\n" Pi $math:pi
+#         Pi 3.14
+# ~> printf "%-10s %.2f %s\n" Pi $math:pi $math:pi
+# Pi         3.14 3.141592653589793
+# ~> printf "%d\n" 0b11100111
+# 231
+# ~> printf "%08b\n" 231
+# 11100111
+# ~> printf "list is: %q\n" [foo bar 'foo bar']
+# list is: [foo bar 'foo bar']
+# ```
+#
+# **Note**: Compared to the [POSIX `printf`
+# command](https://pubs.opengroup.org/onlinepubs/007908799/xcu/printf.html)
+# found in other shells, there are 3 key differences:
+#
+# - The behavior of the formatting verbs are based on Go's
+#   [`fmt`](https://golang.org/pkg/fmt/) package instead of the POSIX
+#   specification.
+#
+# - The number of arguments after the formatting template must match the number
+#   of formatting verbs. The POSIX command will repeat the template string to
+#   consume excess values; this command does not have that behavior.
+#
+# - This command does not interpret escape sequences such as `\n`; just use
+#   [double-quoted strings](language.html#double-quoted-string).
+#
+# @cf print echo pprint repr
+
+#elvdoc:fn echo
+#
+# ```elvish
+# echo &sep=' ' $value...
+# ```
+#
+# Print all arguments, joined by the `sep` option, and followed by a newline.
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> echo Hello   elvish
+# Hello elvish
+# ~> echo "Hello   elvish"
+# Hello   elvish
+# ~> echo &sep=, lorem ipsum
+# lorem,ipsum
+# ```
+#
+# Notes: The `echo` builtin does not treat `-e` or `-n` specially. For instance,
+# `echo -n` just prints `-n`. Use double-quoted strings to print special
+# characters, and `print` to suppress the trailing newline.
+#
+# @cf print
+#
+# Etymology: Bourne sh.
+
+#elvdoc:fn pprint
+#
+# ```elvish
+# pprint $value...
+# ```
+#
+# Pretty-print representations of Elvish values. Examples:
+#
+# ```elvish-transcript
+# ~> pprint [foo bar]
+# [
+# foo
+# bar
+# ]
+# ~> pprint [&k1=v1 &k2=v2]
+# [
+# &k2=
+# v2
+# &k1=
+# v1
+# ]
+# ```
+#
+# The output format is subject to change.
+#
+# @cf repr
+
+#elvdoc:fn repr
+#
+# ```elvish
+# repr $value...
+# ```
+#
+# Writes representation of `$value`s, separated by space and followed by a
+# newline. Example:
+#
+# ```elvish-transcript
+# ~> repr [foo 'lorem ipsum'] "aha\n"
+# [foo 'lorem ipsum'] "aha\n"
+# ```
+#
+# @cf pprint
+#
+# Etymology: [Python](https://docs.python.org/3/library/functions.html#repr).
+
+#elvdoc:fn show
+#
+# ```elvish
+# show $e
+# ```
+#
+# Shows the value to the output, which is assumed to be a VT-100-compatible
+# terminal.
+#
+# Currently, the only type of value that can be showed is exceptions, but this
+# will likely expand in future.
+#
+# Example:
+#
+# ```elvish-transcript
+# ~> var e = ?(fail lorem-ipsum)
+# ~> show $e
+# Exception: lorem-ipsum
+# [tty 3], line 1: var e = ?(fail lorem-ipsum)
+# ```
+
+#elvdoc:fn only-bytes
+#
+# ```elvish
+# only-bytes
+# ```
+#
+# Passes byte input to output, and discards value inputs.
+#
+# Example:
+#
+# ```elvish-transcript
+# ~> { put value; echo bytes } | only-bytes
+# bytes
+# ```
+
+#elvdoc:fn only-values
+#
+# ```elvish
+# only-values
+# ```
+#
+# Passes value input to output, and discards byte inputs.
+#
+# Example:
+#
+# ```elvish-transcript
+# ~> { put value; echo bytes } | only-values
+# ▶ value
+# ```
+
+#elvdoc:fn slurp
+#
+# ```elvish
+# slurp
+# ```
+#
+# Reads bytes input into a single string, and put this string on structured
+# stdout.
+#
+# Example:
+#
+# ```elvish-transcript
+# ~> echo "a\nb" | slurp
+# ▶ "a\nb\n"
+# ```
+#
+# Etymology: Perl, as
+# [`File::Slurp`](http://search.cpan.org/~uri/File-Slurp-9999.19/lib/File/Slurp.pm).
+
+#elvdoc:fn from-lines
+#
+# ```elvish
+# from-lines
+# ```
+#
+# Splits byte input into lines, and writes them to the value output. Value
+# input is ignored.
+#
+# ```elvish-transcript
+# ~> { echo a; echo b } | from-lines
+# ▶ a
+# ▶ b
+# ~> { echo a; put b } | from-lines
+# ▶ a
+# ```
+#
+# @cf from-terminated read-upto to-lines
+
+#elvdoc:fn from-json
+#
+# ```elvish
+# from-json
+# ```
+#
+# Takes bytes stdin, parses it as JSON and puts the result on structured stdout.
+# The input can contain multiple JSONs, and whitespace between them are ignored.
+#
+# Note that JSON's only number type corresponds to Elvish's floating-point
+# number type, and is always considered [inexact](language.html#exactness).
+# It may be necessary to coerce JSON numbers to exact numbers using
+# [exact-num](#exact-num).
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> echo '"a"' | from-json
+# ▶ a
+# ~> echo '["lorem", "ipsum"]' | from-json
+# ▶ [lorem ipsum]
+# ~> echo '{"lorem": "ipsum"}' | from-json
+# ▶ [&lorem=ipsum]
+# ~> # multiple JSONs running together
+# echo '"a""b"["x"]' | from-json
+# ▶ a
+# ▶ b
+# ▶ [x]
+# ~> # multiple JSONs separated by newlines
+# echo '"a"
+# {"k": "v"}' | from-json
+# ▶ a
+# ▶ [&k=v]
+# ```
+#
+# @cf to-json
+
+#elvdoc:fn from-terminated
+#
+# ```elvish
+# from-terminated $terminator
+# ```
+#
+# Splits byte input into lines at each `$terminator` character, and writes
+# them to the value output. If the byte input ends with `$terminator`, it is
+# dropped. Value input is ignored.
+#
+# The `$terminator` must be a single ASCII character such as `"\x00"` (NUL).
+#
+# ```elvish-transcript
+# ~> { echo a; echo b } | from-terminated "\x00"
+# ▶ "a\nb\n"
+# ~> print "a\x00b" | from-terminated "\x00"
+# ▶ a
+# ▶ b
+# ~> print "a\x00b\x00" | from-terminated "\x00"
+# ▶ a
+# ▶ b
+# ```
+#
+# @cf from-lines read-upto to-terminated
+
+#elvdoc:fn to-lines
+#
+# ```elvish
+# to-lines $inputs?
+# ```
+#
+# Writes each [value input](#value-inputs) to a separate line in the byte
+# output. Byte input is ignored.
+#
+# ```elvish-transcript
+# ~> put a b | to-lines
+# a
+# b
+# ~> to-lines [a b]
+# a
+# b
+# ~> { put a; echo b } | to-lines
+# b
+# a
+# ```
+#
+# @cf from-lines to-terminated
+
+#elvdoc:fn to-terminated
+#
+# ```elvish
+# to-terminated $terminator $inputs?
+# ```
+#
+# Writes each [value input](#value-inputs) to the byte output with the
+# specified terminator character. Byte input is ignored. This behavior is
+# useful, for example, when feeding output into a program that accepts NUL
+# terminated lines to avoid ambiguities if the values contains newline
+# characters.
+#
+# The `$terminator` must be a single ASCII character such as `"\x00"` (NUL).
+#
+# ```elvish-transcript
+# ~> put a b | to-terminated "\x00" | slurp
+# ▶ "a\x00b\x00"
+# ~> to-terminated "\x00" [a b] | slurp
+# ▶ "a\x00b\x00"
+# ```
+#
+# @cf from-terminated to-lines
+
+#elvdoc:fn to-json
+#
+# ```elvish
+# to-json
+# ```
+#
+# Takes structured stdin, convert it to JSON and puts the result on bytes stdout.
+#
+# ```elvish-transcript
+# ~> put a | to-json
+# "a"
+# ~> put [lorem ipsum] | to-json
+# ["lorem","ipsum"]
+# ~> put [&lorem=ipsum] | to-json
+# {"lorem":"ipsum"}
+# ```
+#
+# @cf from-json

+ 0 - 473
pkg/eval/builtin_fn_io.go

@@ -57,33 +57,6 @@ func init() {
 	})
 }
 
-//elvdoc:fn put
-//
-// ```elvish
-// put $value...
-// ```
-//
-// Takes arbitrary arguments and write them to the structured stdout.
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> put a
-// ▶ a
-// ~> put lorem ipsum [a b] { ls }
-// ▶ lorem
-// ▶ ipsum
-// ▶ [a b]
-// ▶ <closure 0xc4202607e0>
-// ```
-//
-// **Note**: It is almost never necessary to use `put (...)` - just write the
-// `...` part. For example, `put (eq a b)` is the equivalent to just `eq a b`.
-//
-// Etymology: Various languages, in particular
-// [C](https://manpages.debian.org/stretch/manpages-dev/puts.3.en.html) and
-// [Ruby](https://ruby-doc.org/core-2.2.2/IO.html#method-i-puts) as `puts`.
-
 func put(fm *Frame, args ...any) error {
 	out := fm.ValueOutput()
 	for _, a := range args {
@@ -95,25 +68,6 @@ func put(fm *Frame, args ...any) error {
 	return nil
 }
 
-//elvdoc:fn repeat
-//
-// ```elvish
-// repeat $n $value
-// ```
-//
-// Output `$value` for `$n` times. Example:
-//
-// ```elvish-transcript
-// ~> repeat 0 lorem
-// ~> repeat 4 NAN
-// ▶ NAN
-// ▶ NAN
-// ▶ NAN
-// ▶ NAN
-// ```
-//
-// Etymology: [Clojure](https://clojuredocs.org/clojure.core/repeat).
-
 func repeat(fm *Frame, n int, v any) error {
 	out := fm.ValueOutput()
 	for i := 0; i < n; i++ {
@@ -125,31 +79,6 @@ func repeat(fm *Frame, n int, v any) error {
 	return nil
 }
 
-//elvdoc:fn read-upto
-//
-// ```elvish
-// read-upto $terminator
-// ```
-//
-// Reads byte input until `$terminator` or end-of-file is encountered. It outputs the part of the
-// input read as a string value. The output contains the trailing `$terminator`, unless `read-upto`
-// terminated at end-of-file.
-//
-// The `$terminator` must be a single ASCII character such as `"\x00"` (NUL).
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> echo "a,b,c" | read-upto ","
-// ▶ 'a,'
-// ~> echo "foo\nbar" | read-upto "\n"
-// ▶ "foo\n"
-// ~> echo "a.elv\x00b.elv" | read-upto "\x00"
-// ▶ "a.elv\x00"
-// ~> print "foobar" | read-upto "\n"
-// ▶ foobar
-// ```
-
 func readUpto(fm *Frame, terminator string) (string, error) {
 	if err := checkTerminator(terminator); err != nil {
 		return "", err
@@ -181,27 +110,6 @@ func checkTerminator(s string) error {
 	return nil
 }
 
-//elvdoc:fn read-line
-//
-// ```elvish
-// read-line
-// ```
-//
-// Reads a single line from byte input, and writes the line to the value output,
-// stripping the line ending. A line can end with `"\r\n"`, `"\n"`, or end of
-// file. Examples:
-//
-// ```elvish-transcript
-// ~> print line | read-line
-// ▶ line
-// ~> print "line\n" | read-line
-// ▶ line
-// ~> print "line\r\n" | read-line
-// ▶ line
-// ~> print "line-with-extra-cr\r\r\n" | read-line
-// ▶ "line-with-extra-cr\r"
-// ```
-
 func readLine(fm *Frame) (string, error) {
 	s, err := readUpto(fm, "\n")
 	if err != nil {
@@ -210,21 +118,6 @@ func readLine(fm *Frame) (string, error) {
 	return strutil.ChopLineEnding(s), nil
 }
 
-//elvdoc:fn print
-//
-// ```elvish
-// print &sep=' ' $value...
-// ```
-//
-// Like `echo`, just without the newline.
-//
-// @cf echo
-//
-// Etymology: Various languages, in particular
-// [Perl](https://perldoc.perl.org/functions/print.html) and
-// [zsh](http://zsh.sourceforge.net/Doc/Release/Shell-Builtin-Commands.html), whose
-// `print`s do not print a trailing newline.
-
 type printOpts struct{ Sep string }
 
 func (o *printOpts) SetDefaultOptions() { o.Sep = " " }
@@ -246,84 +139,6 @@ func print(fm *Frame, opts printOpts, args ...any) error {
 	return nil
 }
 
-//elvdoc:fn printf
-//
-// ```elvish
-// printf $template $value...
-// ```
-//
-// Prints values to the byte stream according to a template. If you need to inject the output into
-// the value stream use this pattern: `printf .... | slurp`. That ensures that any newlines in the
-// output of `printf` do not cause its output to be broken into multiple values, thus eliminating
-// the newlines, which will occur if you do `put (printf ....)`.
-//
-// Like [`print`](#print), this command does not add an implicit newline; include an explicit `"\n"`
-// in the formatting template instead. For example, `printf "%.1f\n" (/ 10.0 3)`.
-//
-// See Go's [`fmt`](https://golang.org/pkg/fmt/#hdr-Printing) package for
-// details about the formatting verbs and the various flags that modify the
-// default behavior, such as padding and justification.
-//
-// Unlike Go, each formatting verb has a single associated internal type, and
-// accepts any argument that can reasonably be converted to that type:
-//
-// - The verbs `%s`, `%q` and `%v` convert the corresponding argument to a
-//   string in different ways:
-//
-//     - `%s` uses [to-string](#to-string) to convert a value to string.
-//
-//     - `%q` uses [repr](#repr) to convert a value to string.
-//
-//     - `%v` is equivalent to `%s`, and `%#v` is equivalent to `%q`.
-//
-// - The verb `%t` first convert the corresponding argument to a boolean using
-//   [bool](#bool), and then uses its Go counterpart to format the boolean.
-//
-// - The verbs `%b`, `%c`, `%d`, `%o`, `%O`, `%x`, `%X` and `%U` first convert
-//   the corresponding argument to an integer using an internal algorithm, and
-//   use their Go counterparts to format the integer.
-//
-// - The verbs `%e`, `%E`, `%f`, `%F`, `%g` and `%G` first convert the
-//   corresponding argument to a floating-point number using
-//   [float64](#float64), and then use their Go counterparts to format the
-//   number.
-//
-// The special verb `%%` prints a literal `%` and consumes no argument.
-//
-// Verbs not documented above are not supported.
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> printf "%10s %.2f\n" Pi $math:pi
-//         Pi 3.14
-// ~> printf "%-10s %.2f %s\n" Pi $math:pi $math:pi
-// Pi         3.14 3.141592653589793
-// ~> printf "%d\n" 0b11100111
-// 231
-// ~> printf "%08b\n" 231
-// 11100111
-// ~> printf "list is: %q\n" [foo bar 'foo bar']
-// list is: [foo bar 'foo bar']
-// ```
-//
-// **Note**: Compared to the [POSIX `printf`
-// command](https://pubs.opengroup.org/onlinepubs/007908799/xcu/printf.html)
-// found in other shells, there are 3 key differences:
-//
-// - The behavior of the formatting verbs are based on Go's
-//   [`fmt`](https://golang.org/pkg/fmt/) package instead of the POSIX
-//   specification.
-//
-// - The number of arguments after the formatting template must match the number
-//   of formatting verbs. The POSIX command will repeat the template string to
-//   consume excess values; this command does not have that behavior.
-//
-// - This command does not interpret escape sequences such as `\n`; just use
-//   [double-quoted strings](language.html#double-quoted-string).
-//
-// @cf print echo pprint repr
-
 func printf(fm *Frame, template string, args ...any) error {
 	wrappedArgs := make([]any, len(args))
 	for i, arg := range args {
@@ -398,33 +213,6 @@ func writeFmt(state fmt.State, v rune, val any) {
 	fmt.Fprintf(state, sb.String(), val)
 }
 
-//elvdoc:fn echo
-//
-// ```elvish
-// echo &sep=' ' $value...
-// ```
-//
-// Print all arguments, joined by the `sep` option, and followed by a newline.
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> echo Hello   elvish
-// Hello elvish
-// ~> echo "Hello   elvish"
-// Hello   elvish
-// ~> echo &sep=, lorem ipsum
-// lorem,ipsum
-// ```
-//
-// Notes: The `echo` builtin does not treat `-e` or `-n` specially. For instance,
-// `echo -n` just prints `-n`. Use double-quoted strings to print special
-// characters, and `print` to suppress the trailing newline.
-//
-// @cf print
-//
-// Etymology: Bourne sh.
-
 func echo(fm *Frame, opts printOpts, args ...any) error {
 	err := print(fm, opts, args...)
 	if err != nil {
@@ -434,33 +222,6 @@ func echo(fm *Frame, opts printOpts, args ...any) error {
 	return err
 }
 
-//elvdoc:fn pprint
-//
-// ```elvish
-// pprint $value...
-// ```
-//
-// Pretty-print representations of Elvish values. Examples:
-//
-// ```elvish-transcript
-// ~> pprint [foo bar]
-// [
-// foo
-// bar
-// ]
-// ~> pprint [&k1=v1 &k2=v2]
-// [
-// &k2=
-// v2
-// &k1=
-// v1
-// ]
-// ```
-//
-// The output format is subject to change.
-//
-// @cf repr
-
 func pprint(fm *Frame, args ...any) error {
 	out := fm.ByteOutput()
 	for _, arg := range args {
@@ -476,24 +237,6 @@ func pprint(fm *Frame, args ...any) error {
 	return nil
 }
 
-//elvdoc:fn repr
-//
-// ```elvish
-// repr $value...
-// ```
-//
-// Writes representation of `$value`s, separated by space and followed by a
-// newline. Example:
-//
-// ```elvish-transcript
-// ~> repr [foo 'lorem ipsum'] "aha\n"
-// [foo 'lorem ipsum'] "aha\n"
-// ```
-//
-// @cf pprint
-//
-// Etymology: [Python](https://docs.python.org/3/library/functions.html#repr).
-
 func repr(fm *Frame, args ...any) error {
 	out := fm.ByteOutput()
 	for i, arg := range args {
@@ -512,27 +255,6 @@ func repr(fm *Frame, args ...any) error {
 	return err
 }
 
-//elvdoc:fn show
-//
-// ```elvish
-// show $e
-// ```
-//
-// Shows the value to the output, which is assumed to be a VT-100-compatible
-// terminal.
-//
-// Currently, the only type of value that can be showed is exceptions, but this
-// will likely expand in future.
-//
-// Example:
-//
-// ```elvish-transcript
-// ~> var e = ?(fail lorem-ipsum)
-// ~> show $e
-// Exception: lorem-ipsum
-// [tty 3], line 1: var e = ?(fail lorem-ipsum)
-// ```
-
 func show(fm *Frame, v diag.Shower) error {
 	out := fm.ByteOutput()
 	_, err := out.WriteString(v.Show(""))
@@ -543,21 +265,6 @@ func show(fm *Frame, v diag.Shower) error {
 	return err
 }
 
-//elvdoc:fn only-bytes
-//
-// ```elvish
-// only-bytes
-// ```
-//
-// Passes byte input to output, and discards value inputs.
-//
-// Example:
-//
-// ```elvish-transcript
-// ~> { put value; echo bytes } | only-bytes
-// bytes
-// ```
-
 func onlyBytes(fm *Frame) error {
 	// Discard values in a goroutine.
 	valuesDone := make(chan struct{})
@@ -573,21 +280,6 @@ func onlyBytes(fm *Frame) error {
 	return err
 }
 
-//elvdoc:fn only-values
-//
-// ```elvish
-// only-values
-// ```
-//
-// Passes value input to output, and discards byte inputs.
-//
-// Example:
-//
-// ```elvish-transcript
-// ~> { put value; echo bytes } | only-values
-// ▶ value
-// ```
-
 func onlyValues(fm *Frame) error {
 	// Discard bytes in a goroutine.
 	bytesDone := make(chan struct{})
@@ -614,49 +306,11 @@ type blackholeWriter struct{}
 
 func (blackholeWriter) Write(p []byte) (int, error) { return len(p), nil }
 
-//elvdoc:fn slurp
-//
-// ```elvish
-// slurp
-// ```
-//
-// Reads bytes input into a single string, and put this string on structured
-// stdout.
-//
-// Example:
-//
-// ```elvish-transcript
-// ~> echo "a\nb" | slurp
-// ▶ "a\nb\n"
-// ```
-//
-// Etymology: Perl, as
-// [`File::Slurp`](http://search.cpan.org/~uri/File-Slurp-9999.19/lib/File/Slurp.pm).
-
 func slurp(fm *Frame) (string, error) {
 	b, err := io.ReadAll(fm.InputFile())
 	return string(b), err
 }
 
-//elvdoc:fn from-lines
-//
-// ```elvish
-// from-lines
-// ```
-//
-// Splits byte input into lines, and writes them to the value output. Value
-// input is ignored.
-//
-// ```elvish-transcript
-// ~> { echo a; echo b } | from-lines
-// ▶ a
-// ▶ b
-// ~> { echo a; put b } | from-lines
-// ▶ a
-// ```
-//
-// @cf from-terminated read-upto to-lines
-
 func fromLines(fm *Frame) error {
 	filein := bufio.NewReader(fm.InputFile())
 	out := fm.ValueOutput()
@@ -677,43 +331,6 @@ func fromLines(fm *Frame) error {
 	}
 }
 
-//elvdoc:fn from-json
-//
-// ```elvish
-// from-json
-// ```
-//
-// Takes bytes stdin, parses it as JSON and puts the result on structured stdout.
-// The input can contain multiple JSONs, and whitespace between them are ignored.
-//
-// Note that JSON's only number type corresponds to Elvish's floating-point
-// number type, and is always considered [inexact](language.html#exactness).
-// It may be necessary to coerce JSON numbers to exact numbers using
-// [exact-num](#exact-num).
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> echo '"a"' | from-json
-// ▶ a
-// ~> echo '["lorem", "ipsum"]' | from-json
-// ▶ [lorem ipsum]
-// ~> echo '{"lorem": "ipsum"}' | from-json
-// ▶ [&lorem=ipsum]
-// ~> # multiple JSONs running together
-// echo '"a""b"["x"]' | from-json
-// ▶ a
-// ▶ b
-// ▶ [x]
-// ~> # multiple JSONs separated by newlines
-// echo '"a"
-// {"k": "v"}' | from-json
-// ▶ a
-// ▶ [&k=v]
-// ```
-//
-// @cf to-json
-
 func fromJSON(fm *Frame) error {
 	in := fm.InputFile()
 	out := fm.ValueOutput()
@@ -771,31 +388,6 @@ func fromJSONInterface(v any) (any, error) {
 	}
 }
 
-//elvdoc:fn from-terminated
-//
-// ```elvish
-// from-terminated $terminator
-// ```
-//
-// Splits byte input into lines at each `$terminator` character, and writes
-// them to the value output. If the byte input ends with `$terminator`, it is
-// dropped. Value input is ignored.
-//
-// The `$terminator` must be a single ASCII character such as `"\x00"` (NUL).
-//
-// ```elvish-transcript
-// ~> { echo a; echo b } | from-terminated "\x00"
-// ▶ "a\nb\n"
-// ~> print "a\x00b" | from-terminated "\x00"
-// ▶ a
-// ▶ b
-// ~> print "a\x00b\x00" | from-terminated "\x00"
-// ▶ a
-// ▶ b
-// ```
-//
-// @cf from-lines read-upto to-terminated
-
 func fromTerminated(fm *Frame, terminator string) error {
 	if err := checkTerminator(terminator); err != nil {
 		return err
@@ -821,29 +413,6 @@ func fromTerminated(fm *Frame, terminator string) error {
 	}
 }
 
-//elvdoc:fn to-lines
-//
-// ```elvish
-// to-lines $inputs?
-// ```
-//
-// Writes each [value input](#value-inputs) to a separate line in the byte
-// output. Byte input is ignored.
-//
-// ```elvish-transcript
-// ~> put a b | to-lines
-// a
-// b
-// ~> to-lines [a b]
-// a
-// b
-// ~> { put a; echo b } | to-lines
-// b
-// a
-// ```
-//
-// @cf from-lines to-terminated
-
 func toLines(fm *Frame, inputs Inputs) error {
 	out := fm.ByteOutput()
 	var errOut error
@@ -858,29 +427,6 @@ func toLines(fm *Frame, inputs Inputs) error {
 	return errOut
 }
 
-//elvdoc:fn to-terminated
-//
-// ```elvish
-// to-terminated $terminator $inputs?
-// ```
-//
-// Writes each [value input](#value-inputs) to the byte output with the
-// specified terminator character. Byte input is ignored. This behavior is
-// useful, for example, when feeding output into a program that accepts NUL
-// terminated lines to avoid ambiguities if the values contains newline
-// characters.
-//
-// The `$terminator` must be a single ASCII character such as `"\x00"` (NUL).
-//
-// ```elvish-transcript
-// ~> put a b | to-terminated "\x00" | slurp
-// ▶ "a\x00b\x00"
-// ~> to-terminated "\x00" [a b] | slurp
-// ▶ "a\x00b\x00"
-// ```
-//
-// @cf from-terminated to-lines
-
 func toTerminated(fm *Frame, terminator string, inputs Inputs) error {
 	if err := checkTerminator(terminator); err != nil {
 		return err
@@ -897,25 +443,6 @@ func toTerminated(fm *Frame, terminator string, inputs Inputs) error {
 	return errOut
 }
 
-//elvdoc:fn to-json
-//
-// ```elvish
-// to-json
-// ```
-//
-// Takes structured stdin, convert it to JSON and puts the result on bytes stdout.
-//
-// ```elvish-transcript
-// ~> put a | to-json
-// "a"
-// ~> put [lorem ipsum] | to-json
-// ["lorem","ipsum"]
-// ~> put [&lorem=ipsum] | to-json
-// {"lorem":"ipsum"}
-// ```
-//
-// @cf from-json
-
 func toJSON(fm *Frame, inputs Inputs) error {
 	encoder := json.NewEncoder(fm.ByteOutput())
 

+ 382 - 0
pkg/eval/builtin_fn_misc.d.elv

@@ -0,0 +1,382 @@
+#elvdoc:fn nop
+#
+# ```elvish
+# nop &any-opt= $value...
+# ```
+#
+# Accepts arbitrary arguments and options and does exactly nothing.
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> nop
+# ~> nop a b c
+# ~> nop &k=v
+# ```
+#
+# Etymology: Various languages, in particular NOP in
+# [assembly languages](https://en.wikipedia.org/wiki/NOP).
+
+#elvdoc:fn kind-of
+#
+# ```elvish
+# kind-of $value...
+# ```
+#
+# Output the kinds of `$value`s. Example:
+#
+# ```elvish-transcript
+# ~> kind-of lorem [] [&]
+# ▶ string
+# ▶ list
+# ▶ map
+# ```
+#
+# The terminology and definition of "kind" is subject to change.
+
+#elvdoc:fn constantly
+#
+# ```elvish
+# constantly $value...
+# ```
+#
+# Output a function that takes no arguments and outputs `$value`s when called.
+# Examples:
+#
+# ```elvish-transcript
+# ~> var f = (constantly lorem ipsum)
+# ~> $f
+# ▶ lorem
+# ▶ ipsum
+# ```
+#
+# The above example is equivalent to simply `var f = { put lorem ipsum }`;
+# it is most useful when the argument is **not** a literal value, e.g.
+#
+# ```elvish-transcript
+# ~> var f = (constantly (uname))
+# ~> $f
+# ▶ Darwin
+# ~> $f
+# ▶ Darwin
+# ```
+#
+# The above code only calls `uname` once when defining `$f`. In contrast, if
+# `$f` is defined as `var f = { put (uname) }`, every time you invoke `$f`,
+# `uname` will be called.
+#
+# Etymology: [Clojure](https://clojuredocs.org/clojure.core/constantly).
+
+#elvdoc:fn call
+#
+# ```elvish
+# call $fn $args $opts
+# ```
+#
+# Calls `$fn` with `$args` as the arguments, and `$opts` as the option. Useful
+# for calling a function with dynamic option keys.
+#
+# Example:
+#
+# ```elvish-transcript
+# ~> var f = {|a &k1=v1 &k2=v2| put $a $k1 $k2 }
+# ~> call $f [foo] [&k1=bar]
+# ▶ foo
+# ▶ bar
+# ▶ v2
+# ```
+
+#elvdoc:fn resolve
+#
+# ```elvish
+# resolve $command
+# ```
+#
+# Output what `$command` resolves to in symbolic form. Command resolution is
+# described in the [language reference](language.html#ordinary-command).
+#
+# Example:
+#
+# ```elvish-transcript
+# ~> resolve echo
+# ▶ <builtin echo>
+# ~> fn f { }
+# ~> resolve f
+# ▶ <closure 0xc4201c24d0>
+# ~> resolve cat
+# ▶ <external cat>
+# ```
+
+#elvdoc:fn eval
+#
+# ```elvish
+# eval $code &ns=$nil &on-end=$nil
+# ```
+#
+# Evaluates `$code`, which should be a string. The evaluation happens in a
+# new, restricted namespace, whose initial set of variables can be specified by
+# the `&ns` option. After evaluation completes, the new namespace is passed to
+# the callback specified by `&on-end` if it is not nil.
+#
+# The namespace specified by `&ns` is never modified; it will not be affected
+# by the creation or deletion of variables by `$code`. However, the values of
+# the variables may be mutated by `$code`.
+#
+# If the `&ns` option is `$nil` (the default), a temporary namespace built by
+# amalgamating the local and upvalue scopes of the caller is used.
+#
+# If `$code` fails to parse or compile, the parse error or compilation error is
+# raised as an exception.
+#
+# Basic examples that do not modify the namespace or any variable:
+#
+# ```elvish-transcript
+# ~> eval 'put x'
+# ▶ x
+# ~> var x = foo
+# ~> eval 'put $x'
+# ▶ foo
+# ~> var ns = (ns [&x=bar])
+# ~> eval &ns=$ns 'put $x'
+# ▶ bar
+# ```
+#
+# Examples that modify existing variables:
+#
+# ```elvish-transcript
+# ~> var y = foo
+# ~> eval 'set y = bar'
+# ~> put $y
+# ▶ bar
+# ```
+#
+# Examples that creates new variables and uses the callback to access it:
+#
+# ```elvish-transcript
+# ~> eval 'var z = lorem'
+# ~> put $z
+# compilation error: variable $z not found
+# [ttz 2], line 1: put $z
+# ~> var saved-ns = $nil
+# ~> eval &on-end={|ns| set saved-ns = $ns } 'var z = lorem'
+# ~> put $saved-ns[z]
+# ▶ lorem
+# ```
+#
+# Note that when using variables from an outer scope, only those
+# that have been referenced are captured as upvalues (see [closure
+# semantics](language.html#closure-semantics)) and thus accessible to `eval`:
+#
+# ```elvish-transcript
+# ~> var a b
+# ~> fn f {|code| nop $a; eval $code }
+# ~> f 'echo $a'
+# $nil
+# ~> f 'echo $b'
+# Exception: compilation error: variable $b not found
+# [eval 2], line 1: echo $b
+# Traceback: [... omitted ...]
+# ```
+
+#elvdoc:fn use-mod
+#
+# ```elvish
+# use-mod $use-spec
+# ```
+#
+# Imports a module, and outputs the namespace for the module.
+#
+# Most code should use the [use](language.html#importing-modules-with-use)
+# special command instead.
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> echo 'var x = value' > a.elv
+# ~> put (use-mod ./a)[x]
+# ▶ value
+# ```
+
+#elvdoc:fn deprecate
+#
+# ```elvish
+# deprecate $msg
+# ```
+#
+# Shows the given deprecation message to stderr. If called from a function
+# or module, also shows the call site of the function or import site of the
+# module. Does nothing if the combination of the call site and the message has
+# been shown before.
+#
+# ```elvish-transcript
+# ~> deprecate msg
+# deprecation: msg
+# ~> fn f { deprecate msg }
+# ~> f
+# deprecation: msg
+# [tty 19], line 1: f
+# ~> exec
+# ~> deprecate msg
+# deprecation: msg
+# ~> fn f { deprecate msg }
+# ~> f
+# deprecation: msg
+# [tty 3], line 1: f
+# ~> f # a different call site; shows deprecate message
+# deprecation: msg
+# [tty 4], line 1: f
+# ~> fn g { f }
+# ~> g
+# deprecation: msg
+# [tty 5], line 1: fn g { f }
+# ~> g # same call site, no more deprecation message
+# ```
+
+#elvdoc:fn sleep
+#
+# ```elvish
+# sleep $duration
+# ```
+#
+# Pauses for at least the specified duration. The actual pause duration depends
+# on the system.
+#
+# This only affects the current Elvish context. It does not affect any other
+# contexts that might be executing in parallel as a consequence of a command
+# such as [`peach`](#peach).
+#
+# A duration can be a simple [number](language.html#number) (with optional
+# fractional value) without an explicit unit suffix, with an implicit unit of
+# seconds.
+#
+# A duration can also be a string written as a sequence of decimal numbers,
+# each with optional fraction, plus a unit suffix. For example, "300ms",
+# "1.5h" or "1h45m7s". Valid time units are "ns", "us" (or "µs"), "ms", "s",
+# "m", "h".
+#
+# Passing a negative duration causes an exception; this is different from the
+# typical BSD or GNU `sleep` command that silently exits with a success status
+# without pausing when given a negative duration.
+#
+# See the [Go documentation](https://golang.org/pkg/time/#ParseDuration) for
+# more information about how durations are parsed.
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> sleep 0.1    # sleeps 0.1 seconds
+# ~> sleep 100ms  # sleeps 0.1 seconds
+# ~> sleep 1.5m   # sleeps 1.5 minutes
+# ~> sleep 1m30s  # sleeps 1.5 minutes
+# ~> sleep -1
+# Exception: sleep duration must be >= zero
+# [tty 8], line 1: sleep -1
+# ```
+
+#elvdoc:fn time
+#
+# ```elvish
+# time &on-end=$nil $callable
+# ```
+#
+# Runs the callable, and call `$on-end` with the duration it took, as a
+# number in seconds. If `$on-end` is `$nil` (the default), prints the
+# duration in human-readable form.
+#
+# If `$callable` throws an exception, the exception is propagated after the
+# on-end or default printing is done.
+#
+# If `$on-end` throws an exception, it is propagated, unless `$callable` has
+# already thrown an exception.
+#
+# Example:
+#
+# ```elvish-transcript
+# ~> time { sleep 1 }
+# 1.006060647s
+# ~> time { sleep 0.01 }
+# 1.288977ms
+# ~> var t = ''
+# ~> time &on-end={|x| set t = $x } { sleep 1 }
+# ~> put $t
+# ▶ (num 1.000925004)
+# ~> time &on-end={|x| set t = $x } { sleep 0.01 }
+# ~> put $t
+# ▶ (num 0.011030208)
+# ```
+#
+# @cf benchmark
+
+#elvdoc:fn benchmark
+#
+# ```elvish
+# benchmark &min-runs=5 &min-time=1s &on-end=$nil &on-run-end=$nil $callable
+# ```
+#
+# Runs `$callable` repeatedly, and reports statistics about how long each run
+# takes.
+#
+# If the `&on-end` callback is not given, `benchmark` prints the average,
+# standard deviation, minimum and maximum of the time it took to run
+# `$callback`, and the number of runs. If the `&on-end` callback is given,
+# `benchmark` instead calls it with a map containing these metrics, keyed by
+# `avg`, `stddev`, `min`, `max` and `runs`. Each duration value (i.e. all
+# except `runs`) is given as the number of seconds.
+#
+# The number of runs is controlled by `&min-runs` and `&min-time`. The
+# `$callable` is run at least `&min-runs` times, **and** when the total
+# duration is at least `&min-time`.
+#
+# The `&min-runs` option must be a non-negative integer within the range of the
+# machine word.
+#
+# The `&min-time` option must be a string representing a non-negative duration,
+# specified as a sequence of decimal numbers with a unit suffix (the numbers
+# may have fractional parts), such as "300ms", "1.5h" and "1h45m7s". Valid time
+# units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
+#
+# If `&on-run-end` is given, it is called after each call to `$callable`, with
+# the time that call took, given as the number of seconds.
+#
+# If `$callable` throws an exception, `benchmark` terminates and propagates the
+# exception after the `&on-end` callback (or the default printing behavior)
+# finishes. The duration of the call that throws an exception is not passed to
+# `&on-run-end`, nor is it included when calculating the statistics for
+# `&on-end`. If the first call to `$callable` throws an exception and `&on-end`
+# is `$nil`, nothing is printed and any `&on-end` callback is not called.
+#
+# If `&on-run-end` is given and throws an exception, `benchmark` terminates and
+# propagates the exception after the `&on-end` callback (or the default
+# printing behavior) finishes, unless `$callable` has already thrown an
+# exception
+#
+# If `&on-end` throws an exception, the exception is propagated, unless
+# `$callable` or `&on-run-end` has already thrown an exception.
+#
+# Example:
+#
+# ```elvish-transcript
+# ~> benchmark { }
+# 98ns ± 382ns (min 0s, max 210.417µs, 10119226 runs)
+# ~> benchmark &on-end={|m| put $m[avg]} { }
+# ▶ (num 9.8e-08)
+# ~> benchmark &on-run-end={|d| echo $d} { sleep 0.3 }
+# 0.301123625
+# 0.30123775
+# 0.30119075
+# 0.300629166
+# 0.301260333
+# 301.088324ms ± 234.298µs (min 300.629166ms, max 301.260333ms, 5 runs)
+# ```
+#
+# @cf time
+
+#elvdoc:fn -ifaddrs
+#
+# ```elvish
+# -ifaddrs
+# ```
+#
+# Output all IP addresses of the current host.
+#
+# This should be part of a networking module instead of the builtin module.

+ 0 - 383
pkg/eval/builtin_fn_misc.go

@@ -48,48 +48,12 @@ func init() {
 
 }
 
-//elvdoc:fn nop
-//
-// ```elvish
-// nop &any-opt= $value...
-// ```
-//
-// Accepts arbitrary arguments and options and does exactly nothing.
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> nop
-// ~> nop a b c
-// ~> nop &k=v
-// ```
-//
-// Etymology: Various languages, in particular NOP in
-// [assembly languages](https://en.wikipedia.org/wiki/NOP).
-
 var nopGoFn = NewGoFn("nop", nop)
 
 func nop(opts RawOptions, args ...any) {
 	// Do nothing
 }
 
-//elvdoc:fn kind-of
-//
-// ```elvish
-// kind-of $value...
-// ```
-//
-// Output the kinds of `$value`s. Example:
-//
-// ```elvish-transcript
-// ~> kind-of lorem [] [&]
-// ▶ string
-// ▶ list
-// ▶ map
-// ```
-//
-// The terminology and definition of "kind" is subject to change.
-
 func kindOf(fm *Frame, args ...any) error {
 	out := fm.ValueOutput()
 	for _, a := range args {
@@ -101,39 +65,6 @@ func kindOf(fm *Frame, args ...any) error {
 	return nil
 }
 
-//elvdoc:fn constantly
-//
-// ```elvish
-// constantly $value...
-// ```
-//
-// Output a function that takes no arguments and outputs `$value`s when called.
-// Examples:
-//
-// ```elvish-transcript
-// ~> var f = (constantly lorem ipsum)
-// ~> $f
-// ▶ lorem
-// ▶ ipsum
-// ```
-//
-// The above example is equivalent to simply `var f = { put lorem ipsum }`;
-// it is most useful when the argument is **not** a literal value, e.g.
-//
-// ```elvish-transcript
-// ~> var f = (constantly (uname))
-// ~> $f
-// ▶ Darwin
-// ~> $f
-// ▶ Darwin
-// ```
-//
-// The above code only calls `uname` once when defining `$f`. In contrast, if
-// `$f` is defined as `var f = { put (uname) }`, every time you invoke `$f`,
-// `uname` will be called.
-//
-// Etymology: [Clojure](https://clojuredocs.org/clojure.core/constantly).
-
 func constantly(args ...any) Callable {
 	// TODO(xiaq): Repr of this function is not right.
 	return NewGoFn(
@@ -151,25 +82,6 @@ func constantly(args ...any) Callable {
 	)
 }
 
-//elvdoc:fn call
-//
-// ```elvish
-// call $fn $args $opts
-// ```
-//
-// Calls `$fn` with `$args` as the arguments, and `$opts` as the option. Useful
-// for calling a function with dynamic option keys.
-//
-// Example:
-//
-// ```elvish-transcript
-// ~> var f = {|a &k1=v1 &k2=v2| put $a $k1 $k2 }
-// ~> call $f [foo] [&k1=bar]
-// ▶ foo
-// ▶ bar
-// ▶ v2
-// ```
-
 func call(fm *Frame, fn Callable, argsVal vals.List, optsVal vals.Map) error {
 	args := make([]any, 0, argsVal.Len())
 	for it := argsVal.Iterator(); it.HasElem(); it.Next() {
@@ -188,27 +100,6 @@ func call(fm *Frame, fn Callable, argsVal vals.List, optsVal vals.Map) error {
 	return fn.Call(fm.Fork("-call"), args, opts)
 }
 
-//elvdoc:fn resolve
-//
-// ```elvish
-// resolve $command
-// ```
-//
-// Output what `$command` resolves to in symbolic form. Command resolution is
-// described in the [language reference](language.html#ordinary-command).
-//
-// Example:
-//
-// ```elvish-transcript
-// ~> resolve echo
-// ▶ <builtin echo>
-// ~> fn f { }
-// ~> resolve f
-// ▶ <closure 0xc4201c24d0>
-// ~> resolve cat
-// ▶ <external cat>
-// ```
-
 func resolve(fm *Frame, head string) string {
 	special, fnRef := resolveCmdHeadInternally(fm, head, nil)
 	switch {
@@ -221,77 +112,6 @@ func resolve(fm *Frame, head string) string {
 	}
 }
 
-//elvdoc:fn eval
-//
-// ```elvish
-// eval $code &ns=$nil &on-end=$nil
-// ```
-//
-// Evaluates `$code`, which should be a string. The evaluation happens in a
-// new, restricted namespace, whose initial set of variables can be specified by
-// the `&ns` option. After evaluation completes, the new namespace is passed to
-// the callback specified by `&on-end` if it is not nil.
-//
-// The namespace specified by `&ns` is never modified; it will not be affected
-// by the creation or deletion of variables by `$code`. However, the values of
-// the variables may be mutated by `$code`.
-//
-// If the `&ns` option is `$nil` (the default), a temporary namespace built by
-// amalgamating the local and upvalue scopes of the caller is used.
-//
-// If `$code` fails to parse or compile, the parse error or compilation error is
-// raised as an exception.
-//
-// Basic examples that do not modify the namespace or any variable:
-//
-// ```elvish-transcript
-// ~> eval 'put x'
-// ▶ x
-// ~> var x = foo
-// ~> eval 'put $x'
-// ▶ foo
-// ~> var ns = (ns [&x=bar])
-// ~> eval &ns=$ns 'put $x'
-// ▶ bar
-// ```
-//
-// Examples that modify existing variables:
-//
-// ```elvish-transcript
-// ~> var y = foo
-// ~> eval 'set y = bar'
-// ~> put $y
-// ▶ bar
-// ```
-//
-// Examples that creates new variables and uses the callback to access it:
-//
-// ```elvish-transcript
-// ~> eval 'var z = lorem'
-// ~> put $z
-// compilation error: variable $z not found
-// [ttz 2], line 1: put $z
-// ~> var saved-ns = $nil
-// ~> eval &on-end={|ns| set saved-ns = $ns } 'var z = lorem'
-// ~> put $saved-ns[z]
-// ▶ lorem
-// ```
-//
-// Note that when using variables from an outer scope, only those
-// that have been referenced are captured as upvalues (see [closure
-// semantics](language.html#closure-semantics)) and thus accessible to `eval`:
-//
-// ```elvish-transcript
-// ~> var a b
-// ~> fn f {|code| nop $a; eval $code }
-// ~> f 'echo $a'
-// $nil
-// ~> f 'echo $b'
-// Exception: compilation error: variable $b not found
-// [eval 2], line 1: echo $b
-// Traceback: [... omitted ...]
-// ```
-
 type evalOpts struct {
 	Ns    *Ns
 	OnEnd Callable
@@ -331,64 +151,10 @@ func nextEvalCount() int {
 	return evalCount
 }
 
-//elvdoc:fn use-mod
-//
-// ```elvish
-// use-mod $use-spec
-// ```
-//
-// Imports a module, and outputs the namespace for the module.
-//
-// Most code should use the [use](language.html#importing-modules-with-use)
-// special command instead.
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> echo 'var x = value' > a.elv
-// ~> put (use-mod ./a)[x]
-// ▶ value
-// ```
-
 func useMod(fm *Frame, spec string) (*Ns, error) {
 	return use(fm, spec, nil)
 }
 
-//elvdoc:fn deprecate
-//
-// ```elvish
-// deprecate $msg
-// ```
-//
-// Shows the given deprecation message to stderr. If called from a function
-// or module, also shows the call site of the function or import site of the
-// module. Does nothing if the combination of the call site and the message has
-// been shown before.
-//
-// ```elvish-transcript
-// ~> deprecate msg
-// deprecation: msg
-// ~> fn f { deprecate msg }
-// ~> f
-// deprecation: msg
-// [tty 19], line 1: f
-// ~> exec
-// ~> deprecate msg
-// deprecation: msg
-// ~> fn f { deprecate msg }
-// ~> f
-// deprecation: msg
-// [tty 3], line 1: f
-// ~> f # a different call site; shows deprecate message
-// deprecation: msg
-// [tty 4], line 1: f
-// ~> fn g { f }
-// ~> g
-// deprecation: msg
-// [tty 5], line 1: fn g { f }
-// ~> g # same call site, no more deprecation message
-// ```
-
 func deprecate(fm *Frame, msg string) {
 	var ctx *diag.Context
 	if fm.traceback.Next != nil {
@@ -401,47 +167,6 @@ func deprecate(fm *Frame, msg string) {
 // Frame argument to allow inspection of the value of d in tests.
 var timeAfter = func(fm *Frame, d time.Duration) <-chan time.Time { return time.After(d) }
 
-//elvdoc:fn sleep
-//
-// ```elvish
-// sleep $duration
-// ```
-//
-// Pauses for at least the specified duration. The actual pause duration depends
-// on the system.
-//
-// This only affects the current Elvish context. It does not affect any other
-// contexts that might be executing in parallel as a consequence of a command
-// such as [`peach`](#peach).
-//
-// A duration can be a simple [number](language.html#number) (with optional
-// fractional value) without an explicit unit suffix, with an implicit unit of
-// seconds.
-//
-// A duration can also be a string written as a sequence of decimal numbers,
-// each with optional fraction, plus a unit suffix. For example, "300ms",
-// "1.5h" or "1h45m7s". Valid time units are "ns", "us" (or "µs"), "ms", "s",
-// "m", "h".
-//
-// Passing a negative duration causes an exception; this is different from the
-// typical BSD or GNU `sleep` command that silently exits with a success status
-// without pausing when given a negative duration.
-//
-// See the [Go documentation](https://golang.org/pkg/time/#ParseDuration) for
-// more information about how durations are parsed.
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> sleep 0.1    # sleeps 0.1 seconds
-// ~> sleep 100ms  # sleeps 0.1 seconds
-// ~> sleep 1.5m   # sleeps 1.5 minutes
-// ~> sleep 1m30s  # sleeps 1.5 minutes
-// ~> sleep -1
-// Exception: sleep duration must be >= zero
-// [tty 8], line 1: sleep -1
-// ```
-
 func sleep(fm *Frame, duration any) error {
 	var f float64
 	var d time.Duration
@@ -473,40 +198,6 @@ func sleep(fm *Frame, duration any) error {
 	}
 }
 
-//elvdoc:fn time
-//
-// ```elvish
-// time &on-end=$nil $callable
-// ```
-//
-// Runs the callable, and call `$on-end` with the duration it took, as a
-// number in seconds. If `$on-end` is `$nil` (the default), prints the
-// duration in human-readable form.
-//
-// If `$callable` throws an exception, the exception is propagated after the
-// on-end or default printing is done.
-//
-// If `$on-end` throws an exception, it is propagated, unless `$callable` has
-// already thrown an exception.
-//
-// Example:
-//
-// ```elvish-transcript
-// ~> time { sleep 1 }
-// 1.006060647s
-// ~> time { sleep 0.01 }
-// 1.288977ms
-// ~> var t = ''
-// ~> time &on-end={|x| set t = $x } { sleep 1 }
-// ~> put $t
-// ▶ (num 1.000925004)
-// ~> time &on-end={|x| set t = $x } { sleep 0.01 }
-// ~> put $t
-// ▶ (num 0.011030208)
-// ```
-//
-// @cf benchmark
-
 type timeOpt struct{ OnEnd Callable }
 
 func (o *timeOpt) SetDefaultOptions() {}
@@ -533,70 +224,6 @@ func timeCmd(fm *Frame, opts timeOpt, f Callable) error {
 	return err
 }
 
-//elvdoc:fn benchmark
-//
-// ```elvish
-// benchmark &min-runs=5 &min-time=1s &on-end=$nil &on-run-end=$nil $callable
-// ```
-//
-// Runs `$callable` repeatedly, and reports statistics about how long each run
-// takes.
-//
-// If the `&on-end` callback is not given, `benchmark` prints the average,
-// standard deviation, minimum and maximum of the time it took to run
-// `$callback`, and the number of runs. If the `&on-end` callback is given,
-// `benchmark` instead calls it with a map containing these metrics, keyed by
-// `avg`, `stddev`, `min`, `max` and `runs`. Each duration value (i.e. all
-// except `runs`) is given as the number of seconds.
-//
-// The number of runs is controlled by `&min-runs` and `&min-time`. The
-// `$callable` is run at least `&min-runs` times, **and** when the total
-// duration is at least `&min-time`.
-//
-// The `&min-runs` option must be a non-negative integer within the range of the
-// machine word.
-//
-// The `&min-time` option must be a string representing a non-negative duration,
-// specified as a sequence of decimal numbers with a unit suffix (the numbers
-// may have fractional parts), such as "300ms", "1.5h" and "1h45m7s". Valid time
-// units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
-//
-// If `&on-run-end` is given, it is called after each call to `$callable`, with
-// the time that call took, given as the number of seconds.
-//
-// If `$callable` throws an exception, `benchmark` terminates and propagates the
-// exception after the `&on-end` callback (or the default printing behavior)
-// finishes. The duration of the call that throws an exception is not passed to
-// `&on-run-end`, nor is it included when calculating the statistics for
-// `&on-end`. If the first call to `$callable` throws an exception and `&on-end`
-// is `$nil`, nothing is printed and any `&on-end` callback is not called.
-//
-// If `&on-run-end` is given and throws an exception, `benchmark` terminates and
-// propagates the exception after the `&on-end` callback (or the default
-// printing behavior) finishes, unless `$callable` has already thrown an
-// exception
-//
-// If `&on-end` throws an exception, the exception is propagated, unless
-// `$callable` or `&on-run-end` has already thrown an exception.
-//
-// Example:
-//
-// ```elvish-transcript
-// ~> benchmark { }
-// 98ns ± 382ns (min 0s, max 210.417µs, 10119226 runs)
-// ~> benchmark &on-end={|m| put $m[avg]} { }
-// ▶ (num 9.8e-08)
-// ~> benchmark &on-run-end={|d| echo $d} { sleep 0.3 }
-// 0.301123625
-// 0.30123775
-// 0.30119075
-// 0.300629166
-// 0.301260333
-// 301.088324ms ± 234.298µs (min 300.629166ms, max 301.260333ms, 5 runs)
-// ```
-//
-// @cf time
-
 type benchmarkOpts struct {
 	OnEnd    Callable
 	OnRunEnd Callable
@@ -720,16 +347,6 @@ func int64ToElv(i int64) any {
 	}
 }
 
-//elvdoc:fn -ifaddrs
-//
-// ```elvish
-// -ifaddrs
-// ```
-//
-// Output all IP addresses of the current host.
-//
-// This should be part of a networking module instead of the builtin module.
-
 func _ifaddrs(fm *Frame) error {
 	addrs, err := net.InterfaceAddrs()
 	if err != nil {

+ 437 - 0
pkg/eval/builtin_fn_num.d.elv

@@ -0,0 +1,437 @@
+#elvdoc:fn rand
+#
+# ```elvish
+# rand
+# ```
+#
+# Output a pseudo-random number in the interval [0, 1). Example:
+#
+# ```elvish-transcript
+# ~> rand
+# ▶ 0.17843564133528436
+# ```
+
+#elvdoc:fn num
+#
+# ```elvish
+# num $string-or-number
+# ```
+#
+# Constructs a [typed number](./language.html#number).
+#
+# If the argument is a string, this command outputs the typed number the
+# argument represents, or raises an exception if the argument is not a valid
+# representation of a number. If the argument is already a typed number, this
+# command outputs it as is.
+#
+# This command is usually not needed for working with numbers; see the
+# discussion of [numeric commands](#numeric-commands).
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> num 10
+# ▶ (num 10)
+# ~> num 0x10
+# ▶ (num 16)
+# ~> num 1/12
+# ▶ (num 1/12)
+# ~> num 3.14
+# ▶ (num 3.14)
+# ~> num (num 10)
+# ▶ (num 10)
+# ```
+#
+# @cf exact-num inexact-num
+
+#elvdoc:fn exact-num
+#
+# ```elvish
+# exact-num $string-or-number
+# ```
+#
+# Coerces the argument to an exact number. If the argument is infinity or NaN,
+# an exception is thrown.
+#
+# If the argument is a string, it is converted to a typed number first. If the
+# argument is already an exact number, it is returned as is.
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> exact-num (num 0.125)
+# ▶ (num 1/8)
+# ~> exact-num 0.125
+# ▶ (num 1/8)
+# ~> exact-num (num 1)
+# ▶ (num 1)
+# ```
+#
+# Beware that seemingly simple fractions that can't be represented precisely in
+# binary can result in the denominator being a very large power of 2:
+#
+# ```elvish-transcript
+# ~> exact-num 0.1
+# ▶ (num 3602879701896397/36028797018963968)
+# ```
+#
+# @cf num inexact-num
+
+#elvdoc:fn inexact-num
+#
+# ```elvish
+# inexact-num $string-or-number
+# ```
+#
+# Coerces the argument to an inexact number.
+#
+# If the argument is a string, it is converted to a typed number first. If the
+# argument is already an inexact number, it is returned as is.
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> inexact-num (num 1)
+# ▶ (num 1.0)
+# ~> inexact-num (num 0.5)
+# ▶ (num 0.5)
+# ~> inexact-num (num 1/2)
+# ▶ (num 0.5)
+# ~> inexact-num 1/2
+# ▶ (num 0.5)
+# ```
+#
+# Since the underlying representation for inexact numbers has limited range,
+# numbers with very large magnitudes may be converted to an infinite value:
+#
+# ```elvish-transcript
+# ~> inexact-num 1000000000000000000
+# ▶ (num 1e+18)
+# ~> inexact-num 10000000000000000000
+# ▶ (num +Inf)
+# ~> inexact-num -10000000000000000000
+# ▶ (num -Inf)
+# ```
+#
+# Likewise, numbers with very small magnitudes may be converted to 0:
+#
+# ```elvish-transcript
+# ~> use math
+# ~> math:pow 10 -323
+# ▶ (num 1/100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000)
+# ~> inexact-num (math:pow 10 -323)
+# ▶ (num 1e-323)
+# ~> math:pow 10 -324
+# ▶ (num 1/1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000)
+# ~> inexact-num (math:pow 10 -324)
+# ▶ (num 0.0)
+# ```
+#
+# @cf num exact-num
+
+#elvdoc:fn float64
+#
+# ```elvish
+# float64 $string-or-number
+# ```
+#
+# Constructs a floating-point number.
+#
+# This command is deprecated; use [`num`](#num) to construct a typed number, or
+# [`inexact-num`](#inexact-num) to construct an inexact number.
+
+#elvdoc:fn &lt; &lt;= == != &gt; &gt;= {#num-cmp}
+#
+# ```elvish
+# <  $number... # less
+# <= $number... # less or equal
+# == $number... # equal
+# != $number... # not equal
+# >  $number... # greater
+# >= $number... # greater or equal
+# ```
+#
+# Number comparisons. All of them accept an arbitrary number of arguments:
+#
+# 1.  When given fewer than two arguments, all output `$true`.
+#
+# 2.  When given two arguments, output whether the two arguments satisfy the named
+# relationship.
+#
+# 3.  When given more than two arguments, output whether every adjacent pair of
+# numbers satisfy the named relationship.
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> == 3 3.0
+# ▶ $true
+# ~> < 3 4
+# ▶ $true
+# ~> < 3 4 10
+# ▶ $true
+# ~> < 6 9 1
+# ▶ $false
+# ```
+#
+# As a consequence of rule 3, the `!=` command outputs `$true` as long as any
+# _adjacent_ pair of numbers are not equal, even if some numbers that are not
+# adjacent are equal:
+#
+# ```elvish-transcript
+# ~> != 5 5 4
+# ▶ $false
+# ~> != 5 6 5
+# ▶ $true
+# ```
+
+#elvdoc:fn + {#add}
+#
+# ```elvish
+# + $num...
+# ```
+#
+# Outputs the sum of all arguments, or 0 when there are no arguments.
+#
+# This command is [exactness-preserving](#exactness-preserving).
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> + 5 2 7
+# ▶ (num 14)
+# ~> + 1/2 1/3 1/4
+# ▶ (num 13/12)
+# ~> + 1/2 0.5
+# ▶ (num 1.0)
+# ```
+
+#elvdoc:fn - {#sub}
+#
+# ```elvish
+# - $x-num $y-num...
+# ```
+#
+# Outputs the result of subtracting from `$x-num` all the `$y-num`s, working
+# from left to right. When no `$y-num` is given, outputs the negation of
+# `$x-num` instead (in other words, `- $x-num` is equivalent to `- 0 $x-num`).
+#
+# This command is [exactness-preserving](#exactness-preserving).
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> - 5
+# ▶ (num -5)
+# ~> - 5 2
+# ▶ (num 3)
+# ~> - 5 2 7
+# ▶ (num -4)
+# ~> - 1/2 1/3
+# ▶ (num 1/6)
+# ~> - 1/2 0.3
+# ▶ (num 0.2)
+# ~> - 10
+# ▶ (num -10)
+# ```
+
+#elvdoc:fn * {#mul}
+#
+# ```elvish
+# * $num...
+# ```
+#
+# Outputs the product of all arguments, or 1 when there are no arguments.
+#
+# This command is [exactness-preserving](#exactness-preserving). Additionally,
+# when any argument is exact 0 and no other argument is a floating-point
+# infinity, the result is exact 0.
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> * 2 5 7
+# ▶ (num 70)
+# ~> * 1/2 0.5
+# ▶ (num 0.25)
+# ~> * 0 0.5
+# ▶ (num 0)
+# ```
+
+#elvdoc:fn / {#div}
+#
+# ```elvish
+# / $x-num $y-num...
+# ```
+#
+# Outputs the result of dividing `$x-num` with all the `$y-num`s, working from
+# left to right. When no `$y-num` is given, outputs the reciprocal of `$x-num`
+# instead (in other words, `/ $y-num` is equivalent to `/ 1 $y-num`).
+#
+# Dividing by exact 0 raises an exception. Dividing by inexact 0 results with
+# either infinity or NaN according to floating-point semantics.
+#
+# This command is [exactness-preserving](#exactness-preserving). Additionally,
+# when `$x-num` is exact 0 and no `$y-num` is exact 0, the result is exact 0.
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> / 2
+# ▶ (num 1/2)
+# ~> / 2.0
+# ▶ (num 0.5)
+# ~> / 10 5
+# ▶ (num 2)
+# ~> / 2 5
+# ▶ (num 2/5)
+# ~> / 2 5 7
+# ▶ (num 2/35)
+# ~> / 0 1.0
+# ▶ (num 0)
+# ~> / 2 0
+# Exception: bad value: divisor must be number other than exact 0, but is exact 0
+# [tty 6], line 1: / 2 0
+# ~> / 2 0.0
+# ▶ (num +Inf)
+# ```
+#
+# When given no argument, this command is equivalent to `cd /`, due to the
+# implicit cd feature. (The implicit cd feature will probably change to avoid
+# this oddity).
+
+#elvdoc:fn % {#rem}
+#
+# ```elvish
+# % $x $y
+# ```
+#
+# Outputs the remainder after dividing `$x` by `$y`. The result has the same
+# sign as `$x`.
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> % 10 3
+# ▶ (num 1)
+# ~> % -10 3
+# ▶ (num -1)
+# ~> % 10 -3
+# ▶ (num 1)
+# ```
+#
+# Note that `%` requires both arguments to be within the range of signed
+# integers the size of a [machine
+# word](https://en.wikipedia.org/wiki/Word_(computer_architecture)), and throws
+# an exception otherwise:
+#
+# ```elvish-transcript
+# ~> % (math:pow 2 63) 3
+# Exception: wrong type for arg #0: must be integer
+# ```
+#
+# This limit may be lifted in the future.
+
+#elvdoc:fn randint
+#
+# ```elvish
+# randint $low? $high
+# ```
+#
+# Output a pseudo-random integer N such that `$low <= N < $high`. If not given,
+# `$low` defaults to 0. Examples:
+#
+# ```elvish-transcript
+# ~> # Emulate dice
+# randint 1 7
+# ▶ 6
+# ```
+
+#elvdoc:fn -randseed
+#
+# ```elvish
+# -randseed $seed
+# ```
+#
+# Sets the seed for the random number generator.
+
+#elvdoc:fn range
+#
+# ```elvish
+# range &step $start=0 $end
+# ```
+#
+# Outputs numbers, starting from `$start` and ending before `$end`, using
+# `&step` as the increment.
+#
+# - If `$start` <= `$end`, `&step` defaults to 1, and `range` outputs values as
+#   long as they are smaller than `$end`. An exception is thrown if `&step` is
+#   given a negative value.
+#
+# - If `$start` > `$end`, `&step` defaults to -1, and `range` outputs values as
+#   long as they are greater than `$end`. An exception is thrown if `&step` is
+#   given a positive value.
+#
+# As a special case, if the outputs are floating point numbers, `range` also
+# terminates if the values stop changing.
+#
+# This command is [exactness-preserving](#exactness-preserving).
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> range 4
+# ▶ (num 0)
+# ▶ (num 1)
+# ▶ (num 2)
+# ▶ (num 3)
+# ~> range 4 0
+# ▶ (num 4)
+# ▶ (num 3)
+# ▶ (num 2)
+# ▶ (num 1)
+# ~> range -3 3 &step=2
+# ▶ (num -3)
+# ▶ (num -1)
+# ▶ (num 1)
+# ~> range 3 -3 &step=-2
+# ▶ (num 3)
+# ▶ (num 1)
+# ▶ (num -1)
+# ~> range (- (math:pow 2 53) 1) +inf
+# ▶ (num 9007199254740991.0)
+# ▶ (num 9007199254740992.0)
+# ```
+#
+# When using floating-point numbers, beware that numerical errors can result in
+# an incorrect number of outputs:
+#
+# ```elvish-transcript
+# ~> range 0.9 &step=0.3
+# ▶ (num 0.0)
+# ▶ (num 0.3)
+# ▶ (num 0.6)
+# ▶ (num 0.8999999999999999)
+# ```
+#
+# Avoid this problem by using exact rationals:
+#
+# ```elvish-transcript
+# ~> range 9/10 &step=3/10
+# ▶ (num 0)
+# ▶ (num 3/10)
+# ▶ (num 3/5)
+# ```
+#
+# One usage of this command is to execute something a fixed number of times by
+# combining with [each](#each):
+#
+# ```elvish-transcript
+# ~> range 3 | each {|_| echo foo }
+# foo
+# foo
+# foo
+# ```
+#
+# Etymology:
+# [Python](https://docs.python.org/3/library/functions.html#func-range).

+ 0 - 438
pkg/eval/builtin_fn_num.go

@@ -14,19 +14,6 @@ import (
 
 // Numerical operations.
 
-//elvdoc:fn rand
-//
-// ```elvish
-// rand
-// ```
-//
-// Output a pseudo-random number in the interval [0, 1). Example:
-//
-// ```elvish-transcript
-// ~> rand
-// ▶ 0.17843564133528436
-// ```
-
 func init() {
 	addBuiltinFns(map[string]any{
 		// Constructor
@@ -63,77 +50,11 @@ func init() {
 	rand.Seed(time.Now().UTC().UnixNano())
 }
 
-//elvdoc:fn num
-//
-// ```elvish
-// num $string-or-number
-// ```
-//
-// Constructs a [typed number](./language.html#number).
-//
-// If the argument is a string, this command outputs the typed number the
-// argument represents, or raises an exception if the argument is not a valid
-// representation of a number. If the argument is already a typed number, this
-// command outputs it as is.
-//
-// This command is usually not needed for working with numbers; see the
-// discussion of [numeric commands](#numeric-commands).
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> num 10
-// ▶ (num 10)
-// ~> num 0x10
-// ▶ (num 16)
-// ~> num 1/12
-// ▶ (num 1/12)
-// ~> num 3.14
-// ▶ (num 3.14)
-// ~> num (num 10)
-// ▶ (num 10)
-// ```
-//
-// @cf exact-num inexact-num
-
 func num(n vals.Num) vals.Num {
 	// Conversion is actually handled in vals/conversion.go.
 	return n
 }
 
-//elvdoc:fn exact-num
-//
-// ```elvish
-// exact-num $string-or-number
-// ```
-//
-// Coerces the argument to an exact number. If the argument is infinity or NaN,
-// an exception is thrown.
-//
-// If the argument is a string, it is converted to a typed number first. If the
-// argument is already an exact number, it is returned as is.
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> exact-num (num 0.125)
-// ▶ (num 1/8)
-// ~> exact-num 0.125
-// ▶ (num 1/8)
-// ~> exact-num (num 1)
-// ▶ (num 1)
-// ```
-//
-// Beware that seemingly simple fractions that can't be represented precisely in
-// binary can result in the denominator being a very large power of 2:
-//
-// ```elvish-transcript
-// ~> exact-num 0.1
-// ▶ (num 3602879701896397/36028797018963968)
-// ```
-//
-// @cf num inexact-num
-
 func exactNum(n vals.Num) (vals.Num, error) {
 	if f, ok := n.(float64); ok {
 		r := new(big.Rat).SetFloat64(f)
@@ -146,122 +67,14 @@ func exactNum(n vals.Num) (vals.Num, error) {
 	return n, nil
 }
 
-//elvdoc:fn inexact-num
-//
-// ```elvish
-// inexact-num $string-or-number
-// ```
-//
-// Coerces the argument to an inexact number.
-//
-// If the argument is a string, it is converted to a typed number first. If the
-// argument is already an inexact number, it is returned as is.
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> inexact-num (num 1)
-// ▶ (num 1.0)
-// ~> inexact-num (num 0.5)
-// ▶ (num 0.5)
-// ~> inexact-num (num 1/2)
-// ▶ (num 0.5)
-// ~> inexact-num 1/2
-// ▶ (num 0.5)
-// ```
-//
-// Since the underlying representation for inexact numbers has limited range,
-// numbers with very large magnitudes may be converted to an infinite value:
-//
-// ```elvish-transcript
-// ~> inexact-num 1000000000000000000
-// ▶ (num 1e+18)
-// ~> inexact-num 10000000000000000000
-// ▶ (num +Inf)
-// ~> inexact-num -10000000000000000000
-// ▶ (num -Inf)
-// ```
-//
-// Likewise, numbers with very small magnitudes may be converted to 0:
-//
-// ```elvish-transcript
-// ~> use math
-// ~> math:pow 10 -323
-// ▶ (num 1/100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000)
-// ~> inexact-num (math:pow 10 -323)
-// ▶ (num 1e-323)
-// ~> math:pow 10 -324
-// ▶ (num 1/1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000)
-// ~> inexact-num (math:pow 10 -324)
-// ▶ (num 0.0)
-// ```
-//
-// @cf num exact-num
-
 func inexactNum(f float64) float64 {
 	return f
 }
 
-//elvdoc:fn float64
-//
-// ```elvish
-// float64 $string-or-number
-// ```
-//
-// Constructs a floating-point number.
-//
-// This command is deprecated; use [`num`](#num) to construct a typed number, or
-// [`inexact-num`](#inexact-num) to construct an inexact number.
-
 func toFloat64(f float64) float64 {
 	return f
 }
 
-//elvdoc:fn &lt; &lt;= == != &gt; &gt;= {#num-cmp}
-//
-// ```elvish
-// <  $number... # less
-// <= $number... # less or equal
-// == $number... # equal
-// != $number... # not equal
-// >  $number... # greater
-// >= $number... # greater or equal
-// ```
-//
-// Number comparisons. All of them accept an arbitrary number of arguments:
-//
-// 1.  When given fewer than two arguments, all output `$true`.
-//
-// 2.  When given two arguments, output whether the two arguments satisfy the named
-// relationship.
-//
-// 3.  When given more than two arguments, output whether every adjacent pair of
-// numbers satisfy the named relationship.
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> == 3 3.0
-// ▶ $true
-// ~> < 3 4
-// ▶ $true
-// ~> < 3 4 10
-// ▶ $true
-// ~> < 6 9 1
-// ▶ $false
-// ```
-//
-// As a consequence of rule 3, the `!=` command outputs `$true` as long as any
-// _adjacent_ pair of numbers are not equal, even if some numbers that are not
-// adjacent are equal:
-//
-// ```elvish-transcript
-// ~> != 5 5 4
-// ▶ $false
-// ~> != 5 6 5
-// ▶ $true
-// ```
-
 func lt(nums ...vals.Num) bool {
 	return chainCompare(nums,
 		func(a, b int) bool { return a < b },
@@ -335,27 +148,6 @@ func chainCompare(nums []vals.Num,
 	return true
 }
 
-//elvdoc:fn + {#add}
-//
-// ```elvish
-// + $num...
-// ```
-//
-// Outputs the sum of all arguments, or 0 when there are no arguments.
-//
-// This command is [exactness-preserving](#exactness-preserving).
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> + 5 2 7
-// ▶ (num 14)
-// ~> + 1/2 1/3 1/4
-// ▶ (num 13/12)
-// ~> + 1/2 0.5
-// ▶ (num 1.0)
-// ```
-
 func add(rawNums ...vals.Num) vals.Num {
 	nums := vals.UnifyNums(rawNums, vals.BigInt)
 	switch nums := nums.(type) {
@@ -382,35 +174,6 @@ func add(rawNums ...vals.Num) vals.Num {
 	}
 }
 
-//elvdoc:fn - {#sub}
-//
-// ```elvish
-// - $x-num $y-num...
-// ```
-//
-// Outputs the result of subtracting from `$x-num` all the `$y-num`s, working
-// from left to right. When no `$y-num` is given, outputs the negation of
-// `$x-num` instead (in other words, `- $x-num` is equivalent to `- 0 $x-num`).
-//
-// This command is [exactness-preserving](#exactness-preserving).
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> - 5
-// ▶ (num -5)
-// ~> - 5 2
-// ▶ (num 3)
-// ~> - 5 2 7
-// ▶ (num -4)
-// ~> - 1/2 1/3
-// ▶ (num 1/6)
-// ~> - 1/2 0.3
-// ▶ (num 0.2)
-// ~> - 10
-// ▶ (num -10)
-// ```
-
 func sub(rawNums ...vals.Num) (vals.Num, error) {
 	if len(rawNums) == 0 {
 		return nil, errs.ArityMismatch{What: "arguments", ValidLow: 1, ValidHigh: -1, Actual: 0}
@@ -454,29 +217,6 @@ func sub(rawNums ...vals.Num) (vals.Num, error) {
 	}
 }
 
-//elvdoc:fn * {#mul}
-//
-// ```elvish
-// * $num...
-// ```
-//
-// Outputs the product of all arguments, or 1 when there are no arguments.
-//
-// This command is [exactness-preserving](#exactness-preserving). Additionally,
-// when any argument is exact 0 and no other argument is a floating-point
-// infinity, the result is exact 0.
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> * 2 5 7
-// ▶ (num 70)
-// ~> * 1/2 0.5
-// ▶ (num 0.25)
-// ~> * 0 0.5
-// ▶ (num 0)
-// ```
-
 func mul(rawNums ...vals.Num) vals.Num {
 	hasExact0 := false
 	hasInf := false
@@ -518,48 +258,6 @@ func mul(rawNums ...vals.Num) vals.Num {
 	}
 }
 
-//elvdoc:fn / {#div}
-//
-// ```elvish
-// / $x-num $y-num...
-// ```
-//
-// Outputs the result of dividing `$x-num` with all the `$y-num`s, working from
-// left to right. When no `$y-num` is given, outputs the reciprocal of `$x-num`
-// instead (in other words, `/ $y-num` is equivalent to `/ 1 $y-num`).
-//
-// Dividing by exact 0 raises an exception. Dividing by inexact 0 results with
-// either infinity or NaN according to floating-point semantics.
-//
-// This command is [exactness-preserving](#exactness-preserving). Additionally,
-// when `$x-num` is exact 0 and no `$y-num` is exact 0, the result is exact 0.
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> / 2
-// ▶ (num 1/2)
-// ~> / 2.0
-// ▶ (num 0.5)
-// ~> / 10 5
-// ▶ (num 2)
-// ~> / 2 5
-// ▶ (num 2/5)
-// ~> / 2 5 7
-// ▶ (num 2/35)
-// ~> / 0 1.0
-// ▶ (num 0)
-// ~> / 2 0
-// Exception: bad value: divisor must be number other than exact 0, but is exact 0
-// [tty 6], line 1: / 2 0
-// ~> / 2 0.0
-// ▶ (num +Inf)
-// ```
-//
-// When given no argument, this command is equivalent to `cd /`, due to the
-// implicit cd feature. (The implicit cd feature will probably change to avoid
-// this oddity).
-
 func slash(fm *Frame, args ...vals.Num) error {
 	if len(args) == 0 {
 		// cd /
@@ -613,38 +311,6 @@ func div(rawNums ...vals.Num) (vals.Num, error) {
 	}
 }
 
-//elvdoc:fn % {#rem}
-//
-// ```elvish
-// % $x $y
-// ```
-//
-// Outputs the remainder after dividing `$x` by `$y`. The result has the same
-// sign as `$x`.
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> % 10 3
-// ▶ (num 1)
-// ~> % -10 3
-// ▶ (num -1)
-// ~> % 10 -3
-// ▶ (num 1)
-// ```
-//
-// Note that `%` requires both arguments to be within the range of signed
-// integers the size of a [machine
-// word](https://en.wikipedia.org/wiki/Word_(computer_architecture)), and throws
-// an exception otherwise:
-//
-// ```elvish-transcript
-// ~> % (math:pow 2 63) 3
-// Exception: wrong type for arg #0: must be integer
-// ```
-//
-// This limit may be lifted in the future.
-
 func rem(a, b int) (int, error) {
 	// TODO: Support other number types
 	if b == 0 {
@@ -653,21 +319,6 @@ func rem(a, b int) (int, error) {
 	return a % b, nil
 }
 
-//elvdoc:fn randint
-//
-// ```elvish
-// randint $low? $high
-// ```
-//
-// Output a pseudo-random integer N such that `$low <= N < $high`. If not given,
-// `$low` defaults to 0. Examples:
-//
-// ```elvish-transcript
-// ~> # Emulate dice
-// randint 1 7
-// ▶ 6
-// ```
-
 func randint(args ...int) (int, error) {
 	var low, high int
 	switch len(args) {
@@ -686,97 +337,8 @@ func randint(args ...int) (int, error) {
 	return low + rand.Intn(high-low), nil
 }
 
-//elvdoc:fn -randseed
-//
-// ```elvish
-// -randseed $seed
-// ```
-//
-// Sets the seed for the random number generator.
-
 func randseed(x int) { rand.Seed(int64(x)) }
 
-//elvdoc:fn range
-//
-// ```elvish
-// range &step $start=0 $end
-// ```
-//
-// Outputs numbers, starting from `$start` and ending before `$end`, using
-// `&step` as the increment.
-//
-// - If `$start` <= `$end`, `&step` defaults to 1, and `range` outputs values as
-//   long as they are smaller than `$end`. An exception is thrown if `&step` is
-//   given a negative value.
-//
-// - If `$start` > `$end`, `&step` defaults to -1, and `range` outputs values as
-//   long as they are greater than `$end`. An exception is thrown if `&step` is
-//   given a positive value.
-//
-// As a special case, if the outputs are floating point numbers, `range` also
-// terminates if the values stop changing.
-//
-// This command is [exactness-preserving](#exactness-preserving).
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> range 4
-// ▶ (num 0)
-// ▶ (num 1)
-// ▶ (num 2)
-// ▶ (num 3)
-// ~> range 4 0
-// ▶ (num 4)
-// ▶ (num 3)
-// ▶ (num 2)
-// ▶ (num 1)
-// ~> range -3 3 &step=2
-// ▶ (num -3)
-// ▶ (num -1)
-// ▶ (num 1)
-// ~> range 3 -3 &step=-2
-// ▶ (num 3)
-// ▶ (num 1)
-// ▶ (num -1)
-// ~> range (- (math:pow 2 53) 1) +inf
-// ▶ (num 9007199254740991.0)
-// ▶ (num 9007199254740992.0)
-// ```
-//
-// When using floating-point numbers, beware that numerical errors can result in
-// an incorrect number of outputs:
-//
-// ```elvish-transcript
-// ~> range 0.9 &step=0.3
-// ▶ (num 0.0)
-// ▶ (num 0.3)
-// ▶ (num 0.6)
-// ▶ (num 0.8999999999999999)
-// ```
-//
-// Avoid this problem by using exact rationals:
-//
-// ```elvish-transcript
-// ~> range 9/10 &step=3/10
-// ▶ (num 0)
-// ▶ (num 3/10)
-// ▶ (num 3/5)
-// ```
-//
-// One usage of this command is to execute something a fixed number of times by
-// combining with [each](#each):
-//
-// ```elvish-transcript
-// ~> range 3 | each {|_| echo foo }
-// foo
-// foo
-// foo
-// ```
-//
-// Etymology:
-// [Python](https://docs.python.org/3/library/functions.html#func-range).
-
 type rangeOpts struct{ Step vals.Num }
 
 // TODO: The default value can only be used implicitly; passing "range

+ 173 - 0
pkg/eval/builtin_fn_pred.d.elv

@@ -0,0 +1,173 @@
+#elvdoc:fn bool
+#
+# ```elvish
+# bool $value
+# ```
+#
+# Convert a value to boolean. In Elvish, only `$false` and errors are booleanly
+# false. Everything else, including 0, empty strings and empty lists, is booleanly
+# true:
+#
+# ```elvish-transcript
+# ~> bool $true
+# ▶ $true
+# ~> bool $false
+# ▶ $false
+# ~> bool $ok
+# ▶ $true
+# ~> bool ?(fail haha)
+# ▶ $false
+# ~> bool ''
+# ▶ $true
+# ~> bool []
+# ▶ $true
+# ~> bool abc
+# ▶ $true
+# ```
+#
+# @cf not
+
+#elvdoc:fn not
+#
+# ```elvish
+# not $value
+# ```
+#
+# Boolean negation. Examples:
+#
+# ```elvish-transcript
+# ~> not $true
+# ▶ $false
+# ~> not $false
+# ▶ $true
+# ~> not $ok
+# ▶ $false
+# ~> not ?(fail error)
+# ▶ $true
+# ```
+#
+# **Note**: The related logical commands `and` and `or` are implemented as
+# [special commands](language.html#special-commands) instead, since they do not
+# always evaluate all their arguments. The `not` command always evaluates its
+# only argument, and is thus a normal command.
+#
+# @cf bool
+
+#elvdoc:fn is
+#
+# ```elvish
+# is $values...
+# ```
+#
+# Determine whether all `$value`s have the same identity. Writes `$true` when
+# given no or one argument.
+#
+# The definition of identity is subject to change. Do not rely on its behavior.
+#
+# ```elvish-transcript
+# ~> is a a
+# ▶ $true
+# ~> is a b
+# ▶ $false
+# ~> is [] []
+# ▶ $true
+# ~> is [a] [a]
+# ▶ $false
+# ```
+#
+# @cf eq
+#
+# Etymology: [Python](https://docs.python.org/3/reference/expressions.html#is).
+
+#elvdoc:fn eq
+#
+# ```elvish
+# eq $values...
+# ```
+#
+# Determines whether all `$value`s are equal. Writes `$true` when
+# given no or one argument.
+#
+# Two values are equal when they have the same type and value.
+#
+# For complex data structures like lists and maps, comparison is done
+# recursively. A pseudo-map is equal to another pseudo-map with the same
+# internal type (which is not exposed to Elvish code now) and value.
+#
+# ```elvish-transcript
+# ~> eq a a
+# ▶ $true
+# ~> eq [a] [a]
+# ▶ $true
+# ~> eq [&k=v] [&k=v]
+# ▶ $true
+# ~> eq a [b]
+# ▶ $false
+# ```
+#
+# @cf is not-eq
+#
+# Etymology: [Perl](https://perldoc.perl.org/perlop.html#Equality-Operators).
+
+#elvdoc:fn not-eq
+#
+# ```elvish
+# not-eq $values...
+# ```
+#
+# Determines whether every adjacent pair of `$value`s are not equal. Note that
+# this does not imply that `$value`s are all distinct. Examples:
+#
+# ```elvish-transcript
+# ~> not-eq 1 2 3
+# ▶ $true
+# ~> not-eq 1 2 1
+# ▶ $true
+# ~> not-eq 1 1 2
+# ▶ $false
+# ```
+#
+# @cf eq
+
+#elvdoc:fn compare
+#
+# ```elvish
+# compare $a $b
+# ```
+#
+# Outputs -1 if `$a` < `$b`, 0 if `$a` = `$b`, and 1 if `$a` > `$b`.
+#
+# The following comparison algorithm is used:
+#
+# - Typed numbers are compared numerically. The comparison is consistent with
+#   the [number comparison commands](#num-cmp), except that `NaN` values are
+#   considered equal to each other and smaller than all other numbers.
+#
+# - Strings are compared lexicographically by bytes, consistent with the
+#   [string comparison commands](#str-cmp). For UTF-8 encoded strings, this is
+#   equivalent to comparing by codepoints.
+#
+# - Lists are compared lexicographically by elements, if the elements at the
+#   same positions are comparable.
+#
+# If the ordering between two elements is not defined by the conditions above,
+# i.e. if the value of `$a` or `$b` is not covered by any of the cases above or
+# if they belong to different cases, a "bad value" exception is thrown.
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> compare a b
+# ▶ (num 1)
+# ~> compare b a
+# ▶ (num -1)
+# ~> compare x x
+# ▶ (num 0)
+# ~> compare (num 10) (num 1)
+# ▶ (num 1)
+# ```
+#
+# Beware that strings that look like numbers are treated as strings, not
+# numbers.
+#
+# @cf order

+ 0 - 174
pkg/eval/builtin_fn_pred.go

@@ -10,35 +10,6 @@ import (
 
 // Basic predicate commands.
 
-//elvdoc:fn bool
-//
-// ```elvish
-// bool $value
-// ```
-//
-// Convert a value to boolean. In Elvish, only `$false` and errors are booleanly
-// false. Everything else, including 0, empty strings and empty lists, is booleanly
-// true:
-//
-// ```elvish-transcript
-// ~> bool $true
-// ▶ $true
-// ~> bool $false
-// ▶ $false
-// ~> bool $ok
-// ▶ $true
-// ~> bool ?(fail haha)
-// ▶ $false
-// ~> bool ''
-// ▶ $true
-// ~> bool []
-// ▶ $true
-// ~> bool abc
-// ▶ $true
-// ```
-//
-// @cf not
-
 func init() {
 	addBuiltinFns(map[string]any{
 		"bool":    vals.Bool,
@@ -50,62 +21,10 @@ func init() {
 	})
 }
 
-//elvdoc:fn not
-//
-// ```elvish
-// not $value
-// ```
-//
-// Boolean negation. Examples:
-//
-// ```elvish-transcript
-// ~> not $true
-// ▶ $false
-// ~> not $false
-// ▶ $true
-// ~> not $ok
-// ▶ $false
-// ~> not ?(fail error)
-// ▶ $true
-// ```
-//
-// **Note**: The related logical commands `and` and `or` are implemented as
-// [special commands](language.html#special-commands) instead, since they do not
-// always evaluate all their arguments. The `not` command always evaluates its
-// only argument, and is thus a normal command.
-//
-// @cf bool
-
 func not(v any) bool {
 	return !vals.Bool(v)
 }
 
-//elvdoc:fn is
-//
-// ```elvish
-// is $values...
-// ```
-//
-// Determine whether all `$value`s have the same identity. Writes `$true` when
-// given no or one argument.
-//
-// The definition of identity is subject to change. Do not rely on its behavior.
-//
-// ```elvish-transcript
-// ~> is a a
-// ▶ $true
-// ~> is a b
-// ▶ $false
-// ~> is [] []
-// ▶ $true
-// ~> is [a] [a]
-// ▶ $false
-// ```
-//
-// @cf eq
-//
-// Etymology: [Python](https://docs.python.org/3/reference/expressions.html#is).
-
 func is(args ...any) bool {
 	for i := 0; i+1 < len(args); i++ {
 		if args[i] != args[i+1] {
@@ -115,36 +34,6 @@ func is(args ...any) bool {
 	return true
 }
 
-//elvdoc:fn eq
-//
-// ```elvish
-// eq $values...
-// ```
-//
-// Determines whether all `$value`s are equal. Writes `$true` when
-// given no or one argument.
-//
-// Two values are equal when they have the same type and value.
-//
-// For complex data structures like lists and maps, comparison is done
-// recursively. A pseudo-map is equal to another pseudo-map with the same
-// internal type (which is not exposed to Elvish code now) and value.
-//
-// ```elvish-transcript
-// ~> eq a a
-// ▶ $true
-// ~> eq [a] [a]
-// ▶ $true
-// ~> eq [&k=v] [&k=v]
-// ▶ $true
-// ~> eq a [b]
-// ▶ $false
-// ```
-//
-// @cf is not-eq
-//
-// Etymology: [Perl](https://perldoc.perl.org/perlop.html#Equality-Operators).
-
 func eq(args ...any) bool {
 	for i := 0; i+1 < len(args); i++ {
 		if !vals.Equal(args[i], args[i+1]) {
@@ -154,26 +43,6 @@ func eq(args ...any) bool {
 	return true
 }
 
-//elvdoc:fn not-eq
-//
-// ```elvish
-// not-eq $values...
-// ```
-//
-// Determines whether every adjacent pair of `$value`s are not equal. Note that
-// this does not imply that `$value`s are all distinct. Examples:
-//
-// ```elvish-transcript
-// ~> not-eq 1 2 3
-// ▶ $true
-// ~> not-eq 1 2 1
-// ▶ $true
-// ~> not-eq 1 1 2
-// ▶ $false
-// ```
-//
-// @cf eq
-
 func notEq(args ...any) bool {
 	for i := 0; i+1 < len(args); i++ {
 		if vals.Equal(args[i], args[i+1]) {
@@ -183,49 +52,6 @@ func notEq(args ...any) bool {
 	return true
 }
 
-//elvdoc:fn compare
-//
-// ```elvish
-// compare $a $b
-// ```
-//
-// Outputs -1 if `$a` < `$b`, 0 if `$a` = `$b`, and 1 if `$a` > `$b`.
-//
-// The following comparison algorithm is used:
-//
-// - Typed numbers are compared numerically. The comparison is consistent with
-//   the [number comparison commands](#num-cmp), except that `NaN` values are
-//   considered equal to each other and smaller than all other numbers.
-//
-// - Strings are compared lexicographically by bytes, consistent with the
-//   [string comparison commands](#str-cmp). For UTF-8 encoded strings, this is
-//   equivalent to comparing by codepoints.
-//
-// - Lists are compared lexicographically by elements, if the elements at the
-//   same positions are comparable.
-//
-// If the ordering between two elements is not defined by the conditions above,
-// i.e. if the value of `$a` or `$b` is not covered by any of the cases above or
-// if they belong to different cases, a "bad value" exception is thrown.
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> compare a b
-// ▶ (num 1)
-// ~> compare b a
-// ▶ (num -1)
-// ~> compare x x
-// ▶ (num 0)
-// ~> compare (num 10) (num 1)
-// ▶ (num 1)
-// ```
-//
-// Beware that strings that look like numbers are treated as strings, not
-// numbers.
-//
-// @cf order
-
 // ErrUncomparable is raised by the compare and order commands when inputs contain
 // uncomparable values.
 var ErrUncomparable = errs.BadValue{

+ 121 - 0
pkg/eval/builtin_fn_str.d.elv

@@ -0,0 +1,121 @@
+#elvdoc:fn &lt;s &lt;=s ==s !=s &gt;s &gt;=s {#str-cmp}
+#
+# ```elvish
+# <s  $string... # less
+# <=s $string... # less or equal
+# ==s $string... # equal
+# !=s $string... # not equal
+# >s  $string... # greater
+# >=s $string... # greater or equal
+# ```
+#
+# String comparisons. They behave similarly to their number counterparts when
+# given multiple arguments. Examples:
+#
+# ```elvish-transcript
+# ~> >s lorem ipsum
+# ▶ $true
+# ~> ==s 1 1.0
+# ▶ $false
+# ~> >s 8 12
+# ▶ $true
+# ```
+
+#elvdoc:fn wcswidth
+#
+# ```elvish
+# wcswidth $string
+# ```
+#
+# Output the width of `$string` when displayed on the terminal. Examples:
+#
+# ```elvish-transcript
+# ~> wcswidth a
+# ▶ 1
+# ~> wcswidth lorem
+# ▶ 5
+# ~> wcswidth 你好,世界
+# ▶ 10
+# ```
+
+#elvdoc:fn to-string
+#
+# ```elvish
+# to-string $value...
+# ```
+#
+# Convert arguments to string values.
+#
+# ```elvish-transcript
+# ~> to-string foo [a] [&k=v]
+# ▶ foo
+# ▶ '[a]'
+# ▶ '[&k=v]'
+# ```
+
+#elvdoc:fn base
+#
+# ```elvish
+# base $base $number...
+# ```
+#
+# Outputs a string for each `$number` written in `$base`. The `$base` must be
+# between 2 and 36, inclusive. Examples:
+#
+# ```elvish-transcript
+# ~> base 2 1 3 4 16 255
+# ▶ 1
+# ▶ 11
+# ▶ 100
+# ▶ 10000
+# ▶ 11111111
+# ~> base 16 1 3 4 16 255
+# ▶ 1
+# ▶ 3
+# ▶ 4
+# ▶ 10
+# ▶ ff
+# ```
+
+#elvdoc:fn eawk
+#
+# ```elvish
+# eawk $f $inputs?
+# ```
+#
+# For each [value input](#value-inputs), calls `$f` with the input followed by
+# all its fields. A [`break`](./builtin.html#break) command will cause `eawk`
+# to stop processing inputs. A [`continue`](./builtin.html#continue) command
+# will exit $f, but is ignored by `eawk`.
+#
+# It should behave the same as the following functions:
+#
+# ```elvish
+# fn eawk {|f @rest|
+#   each {|line|
+#     var @fields = (re:split '[ \t]+' (str:trim $line " \t"))
+#     $f $line $@fields
+#   } $@rest
+# }
+# ```
+#
+# This command allows you to write code very similar to `awk` scripts using
+# anonymous functions. Example:
+#
+# ```elvish-transcript
+# ~> echo " lorem ipsum\n1 2" | awk '{ print $1 }'
+# lorem
+# 1
+# ~> echo " lorem ipsum\n1 2" | eawk {|line a b| put $a }
+# ▶ lorem
+# ▶ 1
+# ```
+#
+# **Note**: Since Elvish allows variable names consisting solely of digits, you
+# can also do the following:
+#
+# ```elvish-transcript
+# ~> echo " lorem ipsum\n1 2" | eawk {|0 1 2| put $1 }
+# ▶ lorem
+# ▶ 1
+# ```

+ 0 - 122
pkg/eval/builtin_fn_str.go

@@ -15,46 +15,6 @@ import (
 // ErrInputOfEawkMustBeString is thrown when eawk gets a non-string input.
 var ErrInputOfEawkMustBeString = errors.New("input of eawk must be string")
 
-//elvdoc:fn &lt;s &lt;=s ==s !=s &gt;s &gt;=s {#str-cmp}
-//
-// ```elvish
-// <s  $string... # less
-// <=s $string... # less or equal
-// ==s $string... # equal
-// !=s $string... # not equal
-// >s  $string... # greater
-// >=s $string... # greater or equal
-// ```
-//
-// String comparisons. They behave similarly to their number counterparts when
-// given multiple arguments. Examples:
-//
-// ```elvish-transcript
-// ~> >s lorem ipsum
-// ▶ $true
-// ~> ==s 1 1.0
-// ▶ $false
-// ~> >s 8 12
-// ▶ $true
-// ```
-
-//elvdoc:fn wcswidth
-//
-// ```elvish
-// wcswidth $string
-// ```
-//
-// Output the width of `$string` when displayed on the terminal. Examples:
-//
-// ```elvish-transcript
-// ~> wcswidth a
-// ▶ 1
-// ~> wcswidth lorem
-// ▶ 5
-// ~> wcswidth 你好,世界
-// ▶ 10
-// ```
-
 // TODO(xiaq): Document -override-wcswidth.
 
 func init() {
@@ -77,21 +37,6 @@ func init() {
 	})
 }
 
-//elvdoc:fn to-string
-//
-// ```elvish
-// to-string $value...
-// ```
-//
-// Convert arguments to string values.
-//
-// ```elvish-transcript
-// ~> to-string foo [a] [&k=v]
-// ▶ foo
-// ▶ '[a]'
-// ▶ '[&k=v]'
-// ```
-
 func toString(fm *Frame, args ...any) error {
 	out := fm.ValueOutput()
 	for _, a := range args {
@@ -103,30 +48,6 @@ func toString(fm *Frame, args ...any) error {
 	return nil
 }
 
-//elvdoc:fn base
-//
-// ```elvish
-// base $base $number...
-// ```
-//
-// Outputs a string for each `$number` written in `$base`. The `$base` must be
-// between 2 and 36, inclusive. Examples:
-//
-// ```elvish-transcript
-// ~> base 2 1 3 4 16 255
-// ▶ 1
-// ▶ 11
-// ▶ 100
-// ▶ 10000
-// ▶ 11111111
-// ~> base 16 1 3 4 16 255
-// ▶ 1
-// ▶ 3
-// ▶ 4
-// ▶ 10
-// ▶ ff
-// ```
-
 // ErrBadBase is thrown by the "base" builtin if the base is smaller than 2 or
 // greater than 36.
 var ErrBadBase = errors.New("bad base")
@@ -148,49 +69,6 @@ func base(fm *Frame, b int, nums ...int) error {
 
 var eawkWordSep = regexp.MustCompile("[ \t]+")
 
-//elvdoc:fn eawk
-//
-// ```elvish
-// eawk $f $inputs?
-// ```
-//
-// For each [value input](#value-inputs), calls `$f` with the input followed by
-// all its fields. A [`break`](./builtin.html#break) command will cause `eawk`
-// to stop processing inputs. A [`continue`](./builtin.html#continue) command
-// will exit $f, but is ignored by `eawk`.
-//
-// It should behave the same as the following functions:
-//
-// ```elvish
-// fn eawk {|f @rest|
-//   each {|line|
-//     var @fields = (re:split '[ \t]+' (str:trim $line " \t"))
-//     $f $line $@fields
-//   } $@rest
-// }
-// ```
-//
-// This command allows you to write code very similar to `awk` scripts using
-// anonymous functions. Example:
-//
-// ```elvish-transcript
-// ~> echo " lorem ipsum\n1 2" | awk '{ print $1 }'
-// lorem
-// 1
-// ~> echo " lorem ipsum\n1 2" | eawk {|line a b| put $a }
-// ▶ lorem
-// ▶ 1
-// ```
-//
-// **Note**: Since Elvish allows variable names consisting solely of digits, you
-// can also do the following:
-//
-// ```elvish-transcript
-// ~> echo " lorem ipsum\n1 2" | eawk {|0 1 2| put $1 }
-// ▶ lorem
-// ▶ 1
-// ```
-
 func eawk(fm *Frame, f Callable, inputs Inputs) error {
 	broken := false
 	var err error

+ 294 - 0
pkg/eval/builtin_fn_stream.d.elv

@@ -0,0 +1,294 @@
+#elvdoc:fn all
+#
+# ```elvish
+# all $inputs?
+# ```
+#
+# Takes [value inputs](#value-inputs), and outputs those values unchanged.
+#
+# This is an [identity
+# function](https://en.wikipedia.org/wiki/Identity_function) for the value
+# channel; in other words, `a | all` is equivalent to just `a` if `a` only has
+# value output.
+#
+# This command can be used inside output capture (i.e. `(all)`) to turn value
+# inputs into arguments. For example:
+#
+# ```elvish-transcript
+# ~> echo '["foo","bar"] ["lorem","ipsum"]' | from-json
+# ▶ [foo bar]
+# ▶ [lorem ipsum]
+# ~> echo '["foo","bar"] ["lorem","ipsum"]' | from-json | put (all)[0]
+# ▶ foo
+# ▶ lorem
+# ```
+#
+# The latter pipeline is equivalent to the following:
+#
+# ```elvish-transcript
+# ~> put (echo '["foo","bar"] ["lorem","ipsum"]' | from-json)[0]
+# ▶ foo
+# ▶ lorem
+# ```
+#
+# In general, when `(all)` appears in the last command of a pipeline, it is
+# equivalent to just moving the previous commands of the pipeline into `()`.
+# The choice is a stylistic one; the `(all)` variant is longer overall, but can
+# be more readable since it allows you to avoid putting an excessively long
+# pipeline inside an output capture, and keeps the data flow within the
+# pipeline.
+#
+# Putting the value capture inside `[]` (i.e. `[(all)]`) is useful for storing
+# all value inputs in a list for further processing:
+#
+# ```elvish-transcript
+# ~> fn f { var inputs = [(all)]; put $inputs[1] }
+# ~> put foo bar baz | f
+# ▶ bar
+# ```
+#
+# The `all` command can also take "inputs" from an iterable argument. This can
+# be used to flatten lists or strings (although not recursively):
+#
+# ```elvish-transcript
+# ~> all [foo [lorem ipsum]]
+# ▶ foo
+# ▶ [lorem ipsum]
+# ~> all foo
+# ▶ f
+# ▶ o
+# ▶ o
+# ```
+#
+# This can be used together with `(one)` to turn a single iterable value in the
+# pipeline into its elements:
+#
+# ```elvish-transcript
+# ~> echo '["foo","bar","lorem"]' | from-json
+# ▶ [foo bar lorem]
+# ~> echo '["foo","bar","lorem"]' | from-json | all (one)
+# ▶ foo
+# ▶ bar
+# ▶ lorem
+# ```
+#
+# When given byte inputs, the `all` command currently functions like
+# [`from-lines`](#from-lines), although this behavior is subject to change:
+#
+# ```elvish-transcript
+# ~> print "foo\nbar\n" | all
+# ▶ foo
+# ▶ bar
+# ```
+#
+# @cf one
+
+#elvdoc:fn one
+#
+# ```elvish
+# one $inputs?
+# ```
+#
+# Takes exactly one [value input](#value-inputs) and outputs it. If there are
+# more than one value inputs, raises an exception.
+#
+# This function can be used in a similar way to [`all`](#all), but is a better
+# choice when you expect that there is exactly one output.
+#
+# @cf all
+
+#elvdoc:fn take
+#
+# ```elvish
+# take $n $inputs?
+# ```
+#
+# Outputs the first `$n` [value inputs](#value-inputs). If `$n` is larger than
+# the number of value inputs, outputs everything.
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> range 2 | take 10
+# ▶ 0
+# ▶ 1
+# ~> take 3 [a b c d e]
+# ▶ a
+# ▶ b
+# ▶ c
+# ~> use str
+# ~> str:split ' ' 'how are you?' | take 1
+# ▶ how
+# ```
+#
+# Etymology: Haskell.
+#
+# @cf drop
+
+#elvdoc:fn drop
+#
+# ```elvish
+# drop $n $inputs?
+# ```
+#
+# Ignores the first `$n` [value inputs](#value-inputs) and outputs the rest.
+# If `$n` is larger than the number of value inputs, outputs nothing.
+#
+# Example:
+#
+# ```elvish-transcript
+# ~> range 10 | drop 8
+# ▶ (num 8)
+# ▶ (num 9)
+# ~> range 2 | drop 10
+# ~> drop 2 [a b c d e]
+# ▶ c
+# ▶ d
+# ▶ e
+# ~> use str
+# ~> str:split ' ' 'how are you?' | drop 1
+# ▶ are
+# ▶ 'you?'
+# ```
+#
+# Etymology: Haskell.
+#
+# @cf take
+
+#elvdoc:fn compact
+#
+# ```elvish
+# compact $inputs?
+# ```
+#
+# Replaces consecutive runs of equal values with a single copy. Similar to the
+# `uniq` command on Unix.
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> put a a b b c | compact
+# ▶ a
+# ▶ b
+# ▶ c
+# ~> compact [a a b b c]
+# ▶ a
+# ▶ b
+# ▶ c
+# ~> put a b a | compact
+# ▶ a
+# ▶ b
+# ▶ a
+# ```
+
+#elvdoc:fn count
+#
+# ```elvish
+# count $input-list?
+# ```
+#
+# Count the number of inputs.
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> count lorem # count bytes in a string
+# ▶ 5
+# ~> count [lorem ipsum]
+# ▶ 2
+# ~> range 100 | count
+# ▶ 100
+# ~> seq 100 | count
+# ▶ 100
+# ```
+
+#elvdoc:fn order
+#
+# ```elvish
+# order &less-than=$nil &key=$nil &reverse=$false $inputs?
+# ```
+#
+# Outputs the [value inputs](#value-inputs) sorted in ascending order. The
+# sorting process is guaranteed to be
+# [stable](https://en.wikipedia.org/wiki/Sorting_algorithm#Stability).
+#
+# The `&less-than` option, if given, establishes the ordering of the items. Its
+# value should be a function that takes two arguments and outputs a single
+# boolean indicating whether the first argument is less than the second
+# argument. If the function throws an exception, `order` rethrows the exception
+# without outputting any value.
+#
+# If `&less-than` is `$nil` (the default), a builtin comparator equivalent to
+# `{|a b| == -1 (compare $a $b) }` is used.
+#
+# The `&key` option, if given, is a function that gets called with each item to
+# be sorted. It must output a single value, which is used for comparison in
+# place of the original value. If the function throws an exception, `order`
+# rethrows the exception.
+#
+# Use of `&key` can usually be rewritten to use `&less-than` instead, but using
+# `&key` is usually faster because the callback is only called once for each
+# element, whereas the `&less-than` callback is called O(n*lg(n)) times on
+# average.
+#
+# If `&key` and `&less-than` are both specified, the output of the `&key`
+# callback for each element is passed to the `&less-than` callback.
+#
+# The `&reverse` option, if true, reverses the order of output.
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> put foo bar ipsum | order
+# ▶ bar
+# ▶ foo
+# ▶ ipsum
+# ~> order [(num 10) (num 1) (num 5)]
+# ▶ (num 1)
+# ▶ (num 5)
+# ▶ (num 10)
+# ~> order [[a b] [a] [b b] [a c]]
+# ▶ [a]
+# ▶ [a b]
+# ▶ [a c]
+# ▶ [b b]
+# ~> order &reverse [a c b]
+# ▶ c
+# ▶ b
+# ▶ a
+# ~> put [0 x] [1 a] [2 b] | order &key={|l| put $l[1]}
+# ▶ [1 a]
+# ▶ [2 b]
+# ▶ [0 x]
+# ~> order &less-than={|a b| eq $a x } [l x o r x e x m]
+# ▶ x
+# ▶ x
+# ▶ x
+# ▶ l
+# ▶ o
+# ▶ r
+# ▶ e
+# ▶ m
+# ```
+#
+# Beware that strings that look like numbers are treated as strings, not
+# numbers. To sort strings as numbers, use an explicit `&key` or `&less-than`:
+#
+# ```elvish-transcript
+# ~> order [5 1 10]
+# ▶ 1
+# ▶ 10
+# ▶ 5
+# ~> order &key=$num~ [5 1 10]
+# ▶ 1
+# ▶ 5
+# ▶ 10
+# ~> order &less-than=$"<~" [5 1 10]
+# ▶ 1
+# ▶ 5
+# ▶ 10
+# ```
+#
+# (The `$"<~"` syntax is a reference to [the `<` function](#num-cmp).)
+#
+# @cf compare

+ 0 - 295
pkg/eval/builtin_fn_stream.go

@@ -25,91 +25,6 @@ func init() {
 	})
 }
 
-//elvdoc:fn all
-//
-// ```elvish
-// all $inputs?
-// ```
-//
-// Takes [value inputs](#value-inputs), and outputs those values unchanged.
-//
-// This is an [identity
-// function](https://en.wikipedia.org/wiki/Identity_function) for the value
-// channel; in other words, `a | all` is equivalent to just `a` if `a` only has
-// value output.
-//
-// This command can be used inside output capture (i.e. `(all)`) to turn value
-// inputs into arguments. For example:
-//
-// ```elvish-transcript
-// ~> echo '["foo","bar"] ["lorem","ipsum"]' | from-json
-// ▶ [foo bar]
-// ▶ [lorem ipsum]
-// ~> echo '["foo","bar"] ["lorem","ipsum"]' | from-json | put (all)[0]
-// ▶ foo
-// ▶ lorem
-// ```
-//
-// The latter pipeline is equivalent to the following:
-//
-// ```elvish-transcript
-// ~> put (echo '["foo","bar"] ["lorem","ipsum"]' | from-json)[0]
-// ▶ foo
-// ▶ lorem
-// ```
-//
-// In general, when `(all)` appears in the last command of a pipeline, it is
-// equivalent to just moving the previous commands of the pipeline into `()`.
-// The choice is a stylistic one; the `(all)` variant is longer overall, but can
-// be more readable since it allows you to avoid putting an excessively long
-// pipeline inside an output capture, and keeps the data flow within the
-// pipeline.
-//
-// Putting the value capture inside `[]` (i.e. `[(all)]`) is useful for storing
-// all value inputs in a list for further processing:
-//
-// ```elvish-transcript
-// ~> fn f { var inputs = [(all)]; put $inputs[1] }
-// ~> put foo bar baz | f
-// ▶ bar
-// ```
-//
-// The `all` command can also take "inputs" from an iterable argument. This can
-// be used to flatten lists or strings (although not recursively):
-//
-// ```elvish-transcript
-// ~> all [foo [lorem ipsum]]
-// ▶ foo
-// ▶ [lorem ipsum]
-// ~> all foo
-// ▶ f
-// ▶ o
-// ▶ o
-// ```
-//
-// This can be used together with `(one)` to turn a single iterable value in the
-// pipeline into its elements:
-//
-// ```elvish-transcript
-// ~> echo '["foo","bar","lorem"]' | from-json
-// ▶ [foo bar lorem]
-// ~> echo '["foo","bar","lorem"]' | from-json | all (one)
-// ▶ foo
-// ▶ bar
-// ▶ lorem
-// ```
-//
-// When given byte inputs, the `all` command currently functions like
-// [`from-lines`](#from-lines), although this behavior is subject to change:
-//
-// ```elvish-transcript
-// ~> print "foo\nbar\n" | all
-// ▶ foo
-// ▶ bar
-// ```
-//
-// @cf one
-
 func all(fm *Frame, inputs Inputs) error {
 	out := fm.ValueOutput()
 	var errOut error
@@ -122,20 +37,6 @@ func all(fm *Frame, inputs Inputs) error {
 	return errOut
 }
 
-//elvdoc:fn one
-//
-// ```elvish
-// one $inputs?
-// ```
-//
-// Takes exactly one [value input](#value-inputs) and outputs it. If there are
-// more than one value inputs, raises an exception.
-//
-// This function can be used in a similar way to [`all`](#all), but is a better
-// choice when you expect that there is exactly one output.
-//
-// @cf all
-
 func one(fm *Frame, inputs Inputs) error {
 	var val any
 	n := 0
@@ -151,34 +52,6 @@ func one(fm *Frame, inputs Inputs) error {
 	return errs.ArityMismatch{What: "values", ValidLow: 1, ValidHigh: 1, Actual: n}
 }
 
-//elvdoc:fn take
-//
-// ```elvish
-// take $n $inputs?
-// ```
-//
-// Outputs the first `$n` [value inputs](#value-inputs). If `$n` is larger than
-// the number of value inputs, outputs everything.
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> range 2 | take 10
-// ▶ 0
-// ▶ 1
-// ~> take 3 [a b c d e]
-// ▶ a
-// ▶ b
-// ▶ c
-// ~> use str
-// ~> str:split ' ' 'how are you?' | take 1
-// ▶ how
-// ```
-//
-// Etymology: Haskell.
-//
-// @cf drop
-
 func take(fm *Frame, n int, inputs Inputs) error {
 	out := fm.ValueOutput()
 	var errOut error
@@ -195,36 +68,6 @@ func take(fm *Frame, n int, inputs Inputs) error {
 	return errOut
 }
 
-//elvdoc:fn drop
-//
-// ```elvish
-// drop $n $inputs?
-// ```
-//
-// Ignores the first `$n` [value inputs](#value-inputs) and outputs the rest.
-// If `$n` is larger than the number of value inputs, outputs nothing.
-//
-// Example:
-//
-// ```elvish-transcript
-// ~> range 10 | drop 8
-// ▶ (num 8)
-// ▶ (num 9)
-// ~> range 2 | drop 10
-// ~> drop 2 [a b c d e]
-// ▶ c
-// ▶ d
-// ▶ e
-// ~> use str
-// ~> str:split ' ' 'how are you?' | drop 1
-// ▶ are
-// ▶ 'you?'
-// ```
-//
-// Etymology: Haskell.
-//
-// @cf take
-
 func drop(fm *Frame, n int, inputs Inputs) error {
 	out := fm.ValueOutput()
 	var errOut error
@@ -241,32 +84,6 @@ func drop(fm *Frame, n int, inputs Inputs) error {
 	return errOut
 }
 
-//elvdoc:fn compact
-//
-// ```elvish
-// compact $inputs?
-// ```
-//
-// Replaces consecutive runs of equal values with a single copy. Similar to the
-// `uniq` command on Unix.
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> put a a b b c | compact
-// ▶ a
-// ▶ b
-// ▶ c
-// ~> compact [a a b b c]
-// ▶ a
-// ▶ b
-// ▶ c
-// ~> put a b a | compact
-// ▶ a
-// ▶ b
-// ▶ a
-// ```
-
 func compact(fm *Frame, inputs Inputs) error {
 	out := fm.ValueOutput()
 	first := true
@@ -286,27 +103,6 @@ func compact(fm *Frame, inputs Inputs) error {
 	return errOut
 }
 
-//elvdoc:fn count
-//
-// ```elvish
-// count $input-list?
-// ```
-//
-// Count the number of inputs.
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> count lorem # count bytes in a string
-// ▶ 5
-// ~> count [lorem ipsum]
-// ▶ 2
-// ~> range 100 | count
-// ▶ 100
-// ~> seq 100 | count
-// ▶ 100
-// ```
-
 // The count implementation uses a custom varargs based implementation rather
 // than the more common `Inputs` API (see pkg/eval/go_fn.go) because this
 // allows the implementation to be O(1) for the common cases rather than O(n).
@@ -340,97 +136,6 @@ func count(fm *Frame, args ...any) (int, error) {
 	return n, nil
 }
 
-//elvdoc:fn order
-//
-// ```elvish
-// order &less-than=$nil &key=$nil &reverse=$false $inputs?
-// ```
-//
-// Outputs the [value inputs](#value-inputs) sorted in ascending order. The
-// sorting process is guaranteed to be
-// [stable](https://en.wikipedia.org/wiki/Sorting_algorithm#Stability).
-//
-// The `&less-than` option, if given, establishes the ordering of the items. Its
-// value should be a function that takes two arguments and outputs a single
-// boolean indicating whether the first argument is less than the second
-// argument. If the function throws an exception, `order` rethrows the exception
-// without outputting any value.
-//
-// If `&less-than` is `$nil` (the default), a builtin comparator equivalent to
-// `{|a b| == -1 (compare $a $b) }` is used.
-//
-// The `&key` option, if given, is a function that gets called with each item to
-// be sorted. It must output a single value, which is used for comparison in
-// place of the original value. If the function throws an exception, `order`
-// rethrows the exception.
-//
-// Use of `&key` can usually be rewritten to use `&less-than` instead, but using
-// `&key` is usually faster because the callback is only called once for each
-// element, whereas the `&less-than` callback is called O(n*lg(n)) times on
-// average.
-//
-// If `&key` and `&less-than` are both specified, the output of the `&key`
-// callback for each element is passed to the `&less-than` callback.
-//
-// The `&reverse` option, if true, reverses the order of output.
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> put foo bar ipsum | order
-// ▶ bar
-// ▶ foo
-// ▶ ipsum
-// ~> order [(num 10) (num 1) (num 5)]
-// ▶ (num 1)
-// ▶ (num 5)
-// ▶ (num 10)
-// ~> order [[a b] [a] [b b] [a c]]
-// ▶ [a]
-// ▶ [a b]
-// ▶ [a c]
-// ▶ [b b]
-// ~> order &reverse [a c b]
-// ▶ c
-// ▶ b
-// ▶ a
-// ~> put [0 x] [1 a] [2 b] | order &key={|l| put $l[1]}
-// ▶ [1 a]
-// ▶ [2 b]
-// ▶ [0 x]
-// ~> order &less-than={|a b| eq $a x } [l x o r x e x m]
-// ▶ x
-// ▶ x
-// ▶ x
-// ▶ l
-// ▶ o
-// ▶ r
-// ▶ e
-// ▶ m
-// ```
-//
-// Beware that strings that look like numbers are treated as strings, not
-// numbers. To sort strings as numbers, use an explicit `&key` or `&less-than`:
-//
-// ```elvish-transcript
-// ~> order [5 1 10]
-// ▶ 1
-// ▶ 10
-// ▶ 5
-// ~> order &key=$num~ [5 1 10]
-// ▶ 1
-// ▶ 5
-// ▶ 10
-// ~> order &less-than=$"<~" [5 1 10]
-// ▶ 1
-// ▶ 5
-// ▶ 10
-// ```
-//
-// (The `$"<~"` syntax is a reference to [the `<` function](#num-cmp).)
-//
-// @cf compare
-
 type orderOptions struct {
 	Reverse  bool
 	Key      Callable

+ 126 - 0
pkg/eval/builtin_fn_styled.d.elv

@@ -0,0 +1,126 @@
+#elvdoc:fn styled-segment
+#
+# ```elvish
+# styled-segment $object &fg-color=default &bg-color=default &bold=$false &dim=$false &italic=$false &underlined=$false &blink=$false &inverse=$false
+# ```
+#
+# Constructs a styled segment, a building block for styled texts.
+#
+# - If `$object` is a string, constructs a styled segment with `$object` as the
+#   content, and the properties specified by the options.
+#
+# - If `$object` is a styled segment, constructs a styled segment that is a
+#   copy of `$object`, with the properties specified by the options overridden.
+#
+# The properties of styled segments can be inspected by indexing into it. Valid
+# keys are the same as the options to `styled-segment`, plus `text` for the
+# string content:
+#
+# ```elvish-transcript
+# ~> var s = (styled-segment abc &bold)
+# ~> put $s[text]
+# ▶ abc
+# ~> put $s[fg-color]
+# ▶ default
+# ~> put $s[bold]
+# ▶ $true
+# ```
+#
+# Prefer the high-level [`styled`](#styled) command to build and transform
+# styled texts. Styled segments are a low-level construct, and you only have to
+# deal with it when building custom style transformers.
+#
+# In the following example, a custom transformer sets the `inverse` property
+# for every bold segment:
+#
+# ```elvish
+# styled foo(styled bar bold) {|x| styled-segment $x &inverse=$x[bold] }
+# # transforms "foo" + bold "bar" into "foo" + bold and inverse "bar"
+# ```
+
+#elvdoc:fn styled
+#
+# ```elvish
+# styled $object $style-transformer...
+# ```
+#
+# Constructs a **styled text** by applying the supplied transformers to the
+# supplied `$object`, which may be a string, a [styled
+# segment](#styled-segment), or an existing styled text.
+#
+# Each `$style-transformer` can be one of the following:
+#
+# - A boolean attribute name:
+#
+#   - One of `bold`, `dim`, `italic`, `underlined`, `blink` and `inverse` for
+#     setting the corresponding attribute.
+#
+#   - An attribute name prefixed by `no-` for unsetting the attribute.
+#
+#   - An attribute name prefixed by `toggle-` for toggling the attribute
+#     between set and unset.
+#
+# - A color name for setting the text color, which may be one of the
+#   following:
+#
+#   - One of the 8 basic ANSI colors: `black`, `red`, `green`, `yellow`,
+#     `blue`, `magenta`, `cyan` and `white`.
+#
+#   - The bright variant of the 8 basic ANSI colors, with a `bright-` prefix.
+#
+#   - Any color from the xterm 256-color palette, as `colorX` (such as
+#     `color12`).
+#
+#   - A 24-bit RGB color written as `#RRGGBB` (such as `'#778899'`).
+#
+#     **Note**: You need to quote such values, since an unquoted `#` introduces
+#     a comment (e.g. use `'bg-#778899'` instead of `bg-#778899`).
+#
+# - A color name prefixed by `fg-` to set the foreground color. This has
+#   the same effect as specifying the color name without the `fg-` prefix.
+#
+# - A color name prefixed by `bg-` to set the background color.
+#
+# - A function that receives a styled segment as the only argument and outputs
+#   a single styled segment, which will be applied to all the segments.
+#
+# When a styled text is converted to a string the corresponding
+# [ANSI SGR code](https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_.28Select_Graphic_Rendition.29_parameters)
+# is built to render the style.
+#
+# Examples:
+#
+# ```elvish
+# echo (styled foo red bold) # prints red bold "foo"
+# echo (styled (styled foo red bold) green) # prints green bold "foo"
+# ```
+#
+# A styled text can contain multiple [segments](#styled-segment) with different
+# styles. Such styled texts can be constructed by concatenating multiple styled
+# texts with the [compounding](language.html#compounding) syntax. Strings and
+# styled segments are automatically "promoted" to styled texts when
+# concatenating. Examples:
+#
+# ```elvish
+# echo foo(styled bar red) # prints "foo" + red "bar"
+# echo (styled foo bold)(styled bar red) # prints bold "foo" + red "bar"
+# ```
+#
+# The individual segments in a styled text can be extracted by indexing:
+#
+# ```elvish
+# var s = (styled abc red)(styled def green)
+# put $s[0] $s[1]
+# ```
+#
+# When printed to the terminal, a styled text is not affected by any existing
+# SGR styles in effect, and it will always reset the SGR style afterwards. For
+# example:
+#
+# ```elvish
+# print "\e[1m"
+# echo (styled foo red)
+# echo bar
+# # "foo" will be printed as red, but not bold
+# # "bar" will be printed without any style
+# ```

+ 0 - 127
pkg/eval/builtin_fn_styled.go

@@ -18,46 +18,6 @@ func init() {
 	})
 }
 
-//elvdoc:fn styled-segment
-//
-// ```elvish
-// styled-segment $object &fg-color=default &bg-color=default &bold=$false &dim=$false &italic=$false &underlined=$false &blink=$false &inverse=$false
-// ```
-//
-// Constructs a styled segment, a building block for styled texts.
-//
-// - If `$object` is a string, constructs a styled segment with `$object` as the
-//   content, and the properties specified by the options.
-//
-// - If `$object` is a styled segment, constructs a styled segment that is a
-//   copy of `$object`, with the properties specified by the options overridden.
-//
-// The properties of styled segments can be inspected by indexing into it. Valid
-// keys are the same as the options to `styled-segment`, plus `text` for the
-// string content:
-//
-// ```elvish-transcript
-// ~> var s = (styled-segment abc &bold)
-// ~> put $s[text]
-// ▶ abc
-// ~> put $s[fg-color]
-// ▶ default
-// ~> put $s[bold]
-// ▶ $true
-// ```
-//
-// Prefer the high-level [`styled`](#styled) command to build and transform
-// styled texts. Styled segments are a low-level construct, and you only have to
-// deal with it when building custom style transformers.
-//
-// In the following example, a custom transformer sets the `inverse` property
-// for every bold segment:
-//
-// ```elvish
-// styled foo(styled bar bold) {|x| styled-segment $x &inverse=$x[bold] }
-// # transforms "foo" + bold "bar" into "foo" + bold and inverse "bar"
-// ```
-
 // Turns a string or ui.Segment into a new ui.Segment with the attributes
 // from the supplied options applied to it. If the input is already a Segment its
 // attributes are copied and modified.
@@ -85,93 +45,6 @@ func styledSegment(options RawOptions, input any) (*ui.Segment, error) {
 	}, nil
 }
 
-//elvdoc:fn styled
-//
-// ```elvish
-// styled $object $style-transformer...
-// ```
-//
-// Constructs a **styled text** by applying the supplied transformers to the
-// supplied `$object`, which may be a string, a [styled
-// segment](#styled-segment), or an existing styled text.
-//
-// Each `$style-transformer` can be one of the following:
-//
-// - A boolean attribute name:
-//
-//   - One of `bold`, `dim`, `italic`, `underlined`, `blink` and `inverse` for
-//     setting the corresponding attribute.
-//
-//   - An attribute name prefixed by `no-` for unsetting the attribute.
-//
-//   - An attribute name prefixed by `toggle-` for toggling the attribute
-//     between set and unset.
-//
-// - A color name for setting the text color, which may be one of the
-//   following:
-//
-//   - One of the 8 basic ANSI colors: `black`, `red`, `green`, `yellow`,
-//     `blue`, `magenta`, `cyan` and `white`.
-//
-//   - The bright variant of the 8 basic ANSI colors, with a `bright-` prefix.
-//
-//   - Any color from the xterm 256-color palette, as `colorX` (such as
-//     `color12`).
-//
-//   - A 24-bit RGB color written as `#RRGGBB` (such as `'#778899'`).
-//
-//     **Note**: You need to quote such values, since an unquoted `#` introduces
-//     a comment (e.g. use `'bg-#778899'` instead of `bg-#778899`).
-//
-// - A color name prefixed by `fg-` to set the foreground color. This has
-//   the same effect as specifying the color name without the `fg-` prefix.
-//
-// - A color name prefixed by `bg-` to set the background color.
-//
-// - A function that receives a styled segment as the only argument and outputs
-//   a single styled segment, which will be applied to all the segments.
-//
-// When a styled text is converted to a string the corresponding
-// [ANSI SGR code](https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_.28Select_Graphic_Rendition.29_parameters)
-// is built to render the style.
-//
-// Examples:
-//
-// ```elvish
-// echo (styled foo red bold) # prints red bold "foo"
-// echo (styled (styled foo red bold) green) # prints green bold "foo"
-// ```
-//
-// A styled text can contain multiple [segments](#styled-segment) with different
-// styles. Such styled texts can be constructed by concatenating multiple styled
-// texts with the [compounding](language.html#compounding) syntax. Strings and
-// styled segments are automatically "promoted" to styled texts when
-// concatenating. Examples:
-//
-// ```elvish
-// echo foo(styled bar red) # prints "foo" + red "bar"
-// echo (styled foo bold)(styled bar red) # prints bold "foo" + red "bar"
-// ```
-//
-// The individual segments in a styled text can be extracted by indexing:
-//
-// ```elvish
-// var s = (styled abc red)(styled def green)
-// put $s[0] $s[1]
-// ```
-//
-// When printed to the terminal, a styled text is not affected by any existing
-// SGR styles in effect, and it will always reset the SGR style afterwards. For
-// example:
-//
-// ```elvish
-// print "\e[1m"
-// echo (styled foo red)
-// echo bar
-// # "foo" will be printed as red, but not bold
-// # "bar" will be printed without any style
-// ```
-
 func styled(fm *Frame, input any, stylings ...any) (ui.Text, error) {
 	var text ui.Text
 

+ 94 - 0
pkg/eval/builtin_ns.d.elv

@@ -0,0 +1,94 @@
+#elvdoc:var _
+#
+# A blackhole variable.
+#
+# Values assigned to it will be discarded. Referencing it always results in $nil.
+
+#elvdoc:var args
+#
+# A list containing command-line arguments. Analogous to `argv` in some other
+# languages. Examples:
+#
+# ```elvish-transcript
+# ~> echo 'put $args' > args.elv
+# ~> elvish args.elv foo -bar
+# ▶ [foo -bar]
+# ~> elvish -c 'put $args' foo -bar
+# ▶ [foo -bar]
+# ```
+#
+# As demonstrated above, this variable does not contain the name of the script
+# used to invoke it. For that information, use the `src` command.
+#
+# @cf src
+
+#elvdoc:var false
+#
+# The boolean false value.
+
+#elvdoc:var ok
+#
+# The special value used by `?()` to signal absence of exceptions.
+
+#elvdoc:var nil
+#
+# A special value useful for representing the lack of values.
+
+#elvdoc:var paths
+#
+# A list of search paths, kept in sync with `$E:PATH`. It is easier to use than
+# `$E:PATH`.
+
+#elvdoc:var pid
+#
+# The process ID of the current Elvish process.
+
+#elvdoc:var pwd
+#
+# The present working directory. Setting this variable has the same effect as
+# `cd`. This variable is most useful in a temporary assignment.
+#
+# Example:
+#
+# ```elvish
+# ## Updates all git repositories
+# for x [*/] {
+#   pwd=$x {
+#     if ?(test -d .git) {
+#       git pull
+#     }
+#   }
+# }
+# ```
+#
+# Etymology: the `pwd` command.
+#
+# @cf cd
+
+#elvdoc:var true
+#
+# The boolean true value.
+
+#elvdoc:var buildinfo
+#
+# A [psuedo-map](./language.html#pseudo-map) that exposes information about the Elvish binary.
+# Running `put $buildinfo | to-json` will produce the same output as `elvish -buildinfo -json`.
+#
+# @cf $version
+
+#elvdoc:var version
+#
+# The full version of the Elvish binary as a string. This is the same information reported by
+# `elvish -version` and the value of `$buildinfo[version]`.
+#
+# **Note:** In general it is better to perform functionality tests rather than testing `$version`.
+# For example, do something like
+#
+# ```elvish
+# has-key $builtin: new-var
+# ```
+#
+# to test if variable `new-var` is available rather than comparing against `$version` to see if the
+# elvish version is equal to or newer than the version that introduced `new-var`.
+#
+# @cf $buildinfo

+ 0 - 95
pkg/eval/builtin_ns.go

@@ -8,101 +8,6 @@ import (
 	"src.elv.sh/pkg/eval/vars"
 )
 
-//elvdoc:var _
-//
-// A blackhole variable.
-//
-// Values assigned to it will be discarded. Referencing it always results in $nil.
-
-//elvdoc:var args
-//
-// A list containing command-line arguments. Analogous to `argv` in some other
-// languages. Examples:
-//
-// ```elvish-transcript
-// ~> echo 'put $args' > args.elv
-// ~> elvish args.elv foo -bar
-// ▶ [foo -bar]
-// ~> elvish -c 'put $args' foo -bar
-// ▶ [foo -bar]
-// ```
-//
-// As demonstrated above, this variable does not contain the name of the script
-// used to invoke it. For that information, use the `src` command.
-//
-// @cf src
-
-//elvdoc:var false
-//
-// The boolean false value.
-
-//elvdoc:var ok
-//
-// The special value used by `?()` to signal absence of exceptions.
-
-//elvdoc:var nil
-//
-// A special value useful for representing the lack of values.
-
-//elvdoc:var paths
-//
-// A list of search paths, kept in sync with `$E:PATH`. It is easier to use than
-// `$E:PATH`.
-
-//elvdoc:var pid
-//
-// The process ID of the current Elvish process.
-
-//elvdoc:var pwd
-//
-// The present working directory. Setting this variable has the same effect as
-// `cd`. This variable is most useful in a temporary assignment.
-//
-// Example:
-//
-// ```elvish
-// ## Updates all git repositories
-// for x [*/] {
-//   pwd=$x {
-//     if ?(test -d .git) {
-//       git pull
-//     }
-//   }
-// }
-// ```
-//
-// Etymology: the `pwd` command.
-//
-// @cf cd
-
-//elvdoc:var true
-//
-// The boolean true value.
-
-//elvdoc:var buildinfo
-//
-// A [psuedo-map](./language.html#pseudo-map) that exposes information about the Elvish binary.
-// Running `put $buildinfo | to-json` will produce the same output as `elvish -buildinfo -json`.
-//
-// @cf $version
-
-//elvdoc:var version
-//
-// The full version of the Elvish binary as a string. This is the same information reported by
-// `elvish -version` and the value of `$buildinfo[version]`.
-//
-// **Note:** In general it is better to perform functionality tests rather than testing `$version`.
-// For example, do something like
-//
-// ```elvish
-// has-key $builtin: new-var
-// ```
-//
-// to test if variable `new-var` is available rather than comparing against `$version` to see if the
-// elvish version is equal to or newer than the version that introduced `new-var`.
-//
-// @cf $buildinfo
-
 var builtinNs = BuildNsNamed("").AddVars(map[string]vars.Var{
 	"_":              vars.NewBlackhole(),
 	"pid":            vars.NewReadOnly(strconv.Itoa(syscall.Getpid())),

+ 59 - 0
pkg/eval/eval.d.elv

@@ -0,0 +1,59 @@
+#elvdoc:var after-chdir
+#
+# A list of functions to run after changing directory. These functions are always
+# called with directory to change it, which might be a relative path. The
+# following example also shows `$before-chdir`:
+#
+# ```elvish-transcript
+# ~> set before-chdir = [{|dir| echo "Going to change to "$dir", pwd is "$pwd }]
+# ~> set after-chdir = [{|dir| echo "Changed to "$dir", pwd is "$pwd }]
+# ~> cd /usr
+# Going to change to /usr, pwd is /Users/xiaq
+# Changed to /usr, pwd is /usr
+# /usr> cd local
+# Going to change to local, pwd is /usr
+# Changed to local, pwd is /usr/local
+# /usr/local>
+# ```
+#
+# **Note**: The use of `echo` above is for illustrative purposes. When Elvish
+# is used interactively, the working directory may be changed in location mode
+# or navigation mode, and outputs from `echo` can garble the terminal. If you
+# are writing a plugin that works with the interactive mode, it's better to use
+# [`edit:notify`](edit.html#edit:notify).
+#
+# @cf $before-chdir
+
+#elvdoc:var before-chdir
+#
+# A list of functions to run before changing directory. These functions are always
+# called with the new working directory.
+#
+# @cf $after-chdir
+
+#elvdoc:var num-bg-jobs
+#
+# Number of background jobs.
+
+#elvdoc:var notify-bg-job-success
+#
+# Whether to notify success of background jobs, defaulting to `$true`.
+#
+# Failures of background jobs are always notified.
+
+#elvdoc:var value-out-indicator
+#
+# A string put before value outputs (such as those of `put`). Defaults to
+# `'▶ '`. Example:
+#
+# ```elvish-transcript
+# ~> put lorem ipsum
+# ▶ lorem
+# ▶ ipsum
+# ~> set value-out-indicator = 'val> '
+# ~> put lorem ipsum
+# val> lorem
+# val> ipsum
+# ```
+#
+# Note that you almost always want some trailing whitespace for readability.

+ 0 - 60
pkg/eval/eval.go

@@ -88,66 +88,6 @@ type Evaler struct {
 	numBgJobs int
 }
 
-//elvdoc:var after-chdir
-//
-// A list of functions to run after changing directory. These functions are always
-// called with directory to change it, which might be a relative path. The
-// following example also shows `$before-chdir`:
-//
-// ```elvish-transcript
-// ~> set before-chdir = [{|dir| echo "Going to change to "$dir", pwd is "$pwd }]
-// ~> set after-chdir = [{|dir| echo "Changed to "$dir", pwd is "$pwd }]
-// ~> cd /usr
-// Going to change to /usr, pwd is /Users/xiaq
-// Changed to /usr, pwd is /usr
-// /usr> cd local
-// Going to change to local, pwd is /usr
-// Changed to local, pwd is /usr/local
-// /usr/local>
-// ```
-//
-// **Note**: The use of `echo` above is for illustrative purposes. When Elvish
-// is used interactively, the working directory may be changed in location mode
-// or navigation mode, and outputs from `echo` can garble the terminal. If you
-// are writing a plugin that works with the interactive mode, it's better to use
-// [`edit:notify`](edit.html#edit:notify).
-//
-// @cf $before-chdir
-
-//elvdoc:var before-chdir
-//
-// A list of functions to run before changing directory. These functions are always
-// called with the new working directory.
-//
-// @cf $after-chdir
-
-//elvdoc:var num-bg-jobs
-//
-// Number of background jobs.
-
-//elvdoc:var notify-bg-job-success
-//
-// Whether to notify success of background jobs, defaulting to `$true`.
-//
-// Failures of background jobs are always notified.
-
-//elvdoc:var value-out-indicator
-//
-// A string put before value outputs (such as those of `put`). Defaults to
-// `'▶ '`. Example:
-//
-// ```elvish-transcript
-// ~> put lorem ipsum
-// ▶ lorem
-// ▶ ipsum
-// ~> set value-out-indicator = 'val> '
-// ~> put lorem ipsum
-// val> lorem
-// val> ipsum
-// ```
-//
-// Note that you almost always want some trailing whitespace for readability.
-
 // NewEvaler creates a new Evaler.
 func NewEvaler() *Evaler {
 	builtin := builtinNs.Ns()

+ 111 - 0
pkg/mods/file/file.d.elv

@@ -0,0 +1,111 @@
+#elvdoc:fn is-tty
+#
+# ```elvish
+# file:is-tty $file
+# ```
+#
+# Outputs whether `$file` is a terminal device.
+#
+# The `$file` can be a file object or a number. If it's a number, it's
+# interpreted as a numerical file descriptor.
+#
+# ```elvish-transcript
+# ~> var f = (file:open /dev/tty)
+# ~> file:is-tty $f
+# ▶ $true
+# ~> file:close $f
+# ~> var f = (file:open /dev/null)
+# ~> file:is-tty $f
+# ▶ $false
+# ~> file:close $f
+# ~> var p = (file:pipe)
+# ~> file:is-tty $p[r]
+# ▶ $false
+# ~> file:is-tty $p[w]
+# ▶ $false
+# ~> file:close $p[r]
+# ~> file:close $p[w]
+# ~> file:is-tty 0
+# ▶ $true
+# ~> file:is-tty 1
+# ▶ $true
+# ~> file:is-tty 2
+# ▶ $true
+# ```
+
+#elvdoc:fn open
+#
+# ```elvish
+# file:open $filename
+# ```
+#
+# Opens a file. Currently, `open` only supports opening a file for reading.
+# File must be closed with `close` explicitly. Example:
+#
+# ```elvish-transcript
+# ~> cat a.txt
+# This is
+# a file.
+# ~> use file
+# ~> var f = (file:open a.txt)
+# ~> cat < $f
+# This is
+# a file.
+# ~> close $f
+# ```
+#
+# @cf file:close
+
+#elvdoc:fn close
+#
+# ```elvish
+# file:close $file
+# ```
+#
+# Closes a file opened with `open`.
+#
+# @cf file:open
+
+#elvdoc:fn pipe
+#
+# ```elvish
+# file:pipe
+# ```
+#
+# Create a new pipe that can be used in redirections. A pipe contains a read-end and write-end.
+# Each pipe object is a [pseudo-map](language.html#pseudo-map) with fields `r` (the read-end [file
+# object](./language.html#file)) and `w` (the write-end).
+#
+# When redirecting command input from a pipe with `<`, the read-end is used. When redirecting
+# command output to a pipe with `>`, the write-end is used. Redirecting both input and output with
+# `<>` to a pipe is not supported.
+#
+# Pipes have an OS-dependent buffer, so writing to a pipe without an active reader
+# does not necessarily block. Pipes **must** be explicitly closed with `file:close`.
+#
+# Putting values into pipes will cause those values to be discarded.
+#
+# Examples (assuming the pipe has a large enough buffer):
+#
+# ```elvish-transcript
+# ~> var p = (file:pipe)
+# ~> echo 'lorem ipsum' > $p
+# ~> head -n1 < $p
+# lorem ipsum
+# ~> put 'lorem ipsum' > $p
+# ~> file:close $p[w] # close the write-end
+# ~> head -n1 < $p # blocks unless the write-end is closed
+# ~> file:close $p[r] # close the read-end
+# ```
+#
+# @cf file:close
+
+#elvdoc:fn truncate
+#
+# ```elvish
+# file:truncate $filename $size
+# ```
+#
+# changes the size of the named file. If the file is a symbolic link, it
+# changes the size of the link's target. The size must be an integer between 0
+# and 2^64-1.

+ 0 - 112
pkg/mods/file/file.go

@@ -21,41 +21,6 @@ var Ns = eval.BuildNsNamed("file").
 		"truncate": truncate,
 	}).Ns()
 
-//elvdoc:fn is-tty
-//
-// ```elvish
-// file:is-tty $file
-// ```
-//
-// Outputs whether `$file` is a terminal device.
-//
-// The `$file` can be a file object or a number. If it's a number, it's
-// interpreted as a numerical file descriptor.
-//
-// ```elvish-transcript
-// ~> var f = (file:open /dev/tty)
-// ~> file:is-tty $f
-// ▶ $true
-// ~> file:close $f
-// ~> var f = (file:open /dev/null)
-// ~> file:is-tty $f
-// ▶ $false
-// ~> file:close $f
-// ~> var p = (file:pipe)
-// ~> file:is-tty $p[r]
-// ▶ $false
-// ~> file:is-tty $p[w]
-// ▶ $false
-// ~> file:close $p[r]
-// ~> file:close $p[w]
-// ~> file:is-tty 0
-// ▶ $true
-// ~> file:is-tty 1
-// ▶ $true
-// ~> file:is-tty 2
-// ▶ $true
-// ```
-
 func isTTY(fm *eval.Frame, file any) (bool, error) {
 	switch file := file.(type) {
 	case *os.File:
@@ -75,96 +40,19 @@ func isTTY(fm *eval.Frame, file any) (bool, error) {
 	}
 }
 
-//elvdoc:fn open
-//
-// ```elvish
-// file:open $filename
-// ```
-//
-// Opens a file. Currently, `open` only supports opening a file for reading.
-// File must be closed with `close` explicitly. Example:
-//
-// ```elvish-transcript
-// ~> cat a.txt
-// This is
-// a file.
-// ~> use file
-// ~> var f = (file:open a.txt)
-// ~> cat < $f
-// This is
-// a file.
-// ~> close $f
-// ```
-//
-// @cf file:close
-
 func open(name string) (vals.File, error) {
 	return os.Open(name)
 }
 
-//elvdoc:fn close
-//
-// ```elvish
-// file:close $file
-// ```
-//
-// Closes a file opened with `open`.
-//
-// @cf file:open
-
 func close(f vals.File) error {
 	return f.Close()
 }
 
-//elvdoc:fn pipe
-//
-// ```elvish
-// file:pipe
-// ```
-//
-// Create a new pipe that can be used in redirections. A pipe contains a read-end and write-end.
-// Each pipe object is a [pseudo-map](language.html#pseudo-map) with fields `r` (the read-end [file
-// object](./language.html#file)) and `w` (the write-end).
-//
-// When redirecting command input from a pipe with `<`, the read-end is used. When redirecting
-// command output to a pipe with `>`, the write-end is used. Redirecting both input and output with
-// `<>` to a pipe is not supported.
-//
-// Pipes have an OS-dependent buffer, so writing to a pipe without an active reader
-// does not necessarily block. Pipes **must** be explicitly closed with `file:close`.
-//
-// Putting values into pipes will cause those values to be discarded.
-//
-// Examples (assuming the pipe has a large enough buffer):
-//
-// ```elvish-transcript
-// ~> var p = (file:pipe)
-// ~> echo 'lorem ipsum' > $p
-// ~> head -n1 < $p
-// lorem ipsum
-// ~> put 'lorem ipsum' > $p
-// ~> file:close $p[w] # close the write-end
-// ~> head -n1 < $p # blocks unless the write-end is closed
-// ~> file:close $p[r] # close the read-end
-// ```
-//
-// @cf file:close
-
 func pipe() (vals.Pipe, error) {
 	r, w, err := os.Pipe()
 	return vals.NewPipe(r, w), err
 }
 
-//elvdoc:fn truncate
-//
-// ```elvish
-// file:truncate $filename $size
-// ```
-//
-// changes the size of the named file. If the file is a symbolic link, it
-// changes the size of the link's target. The size must be an integer between 0
-// and 2^64-1.
-
 func truncate(name string, rawSize vals.Num) error {
 	var size int64
 	switch rawSize := rawSize.(type) {

+ 158 - 0
pkg/mods/flag/flag.d.elv

@@ -0,0 +1,158 @@
+#elvdoc:fn call
+#
+# ```elvish
+# flag:call $fn $args
+# ```
+#
+# Parses flags from `$args` according to the signature of the
+# `$fn`, using the [Go convention](#go-convention), and calls `$fn`.
+#
+# The `$fn` must be a user-defined function (i.e. not a builtin
+# function or external command). Each option corresponds to a flag; see
+# [`flag:parse`](#flag:parse) for how the default value affects the behavior of
+# flags. After parsing, the non-flag arguments are used as function arguments.
+#
+# Example:
+#
+# ```elvish-transcript
+# ~> use flag
+# ~> fn f {|&verbose=$false &port=(num 8000) name| put $verbose $port $name }
+# ~> flag:call $f [-verbose -port 80 a.c]
+# ▶ $true
+# ▶ (num 80)
+# ▶ a.c
+# ```
+#
+# @cf flag:parse
+
+#elvdoc:fn parse
+#
+# ```elvish
+# flag:parse $args $specs
+# ```
+#
+# Parses flags from `$args` according to the `$specs`, using the [Go
+# convention](#go-convention).
+#
+# The `$args` must be a list of strings containing the command-line arguments
+# to parse.
+#
+# The `$specs` must be a list of flag specs:
+#
+# ```elvish
+# [
+#   [flag default-value 'description of the flag']
+#   ...
+# ]
+# ```
+#
+# Each flag spec consists of the name of the flag (without the leading `-`),
+# its default value, and a description. The default value influences the how
+# the flag gets converted from string:
+#
+# -   If it is boolean, the flag is a boolean flag (see [Go
+#     convention](#go-convention) for implications). Flag values `0`, `f`, `F`,
+#     `false`, `False` and `FALSE` are converted to `$false`, and `1`, `t`,
+#     `T`, `true`, `True` and `TRUE` to `$true`. Other values are invalid.
+#
+# -   If it is a string, no conversion is done.
+#
+# -   If it is a [typed number](language.html#number), the flag value is
+#     converted using [`num`](builtin.html#num).
+#
+# -   If it is a list, the flag value is split at `,` (equivalent to `{|s| put
+#     [(str:split , $s)] }`).
+#
+# -   If it is none of the above, an exception is thrown.
+#
+# On success, this command outputs two values: a map containing the value of
+# flags defined in `$specs` (whether they appear in `$args` or not), and a list
+# containing non-flag arguments.
+#
+# Example:
+#
+# ```elvish-transcript
+# ~> flag:parse [-v -times 10 foo] [
+#      [v $false 'Verbose']
+#      [times (num 1) 'How many times']
+#    ]
+# ▶ [&v=$true &times=(num 10)]
+# ▶ [foo]
+# ~> flag:parse [] [
+#      [v $false 'Verbose']
+#      [times (num 1) 'How many times']
+#    ]
+# ▶ [&v=$false &times=(num 1)]
+# ▶ []
+# ```
+#
+# @cf flag:call flag:parse-getopt
+
+#elvdoc:fn parse-getopt
+#
+# ```elvish
+# flag:parse-getopt $args $specs ^
+#   &stop-after-double-dash=$true &stop-before-non-flag=$false &long-only=$false
+# ```
+#
+# Parses flags from `$args` according to the `$specs`, using the [getopt
+# convention](#getopt-convention) (see there for the semantics of the options),
+# and outputs the result.
+#
+# The `$args` must be a list of strings containing the command-line arguments
+# to parse.
+#
+# The `$specs` must be a list of flag specs:
+#
+# ```elvish
+# [
+#   [&short=f &long=flag &arg-optional=$false &arg-required=$false]
+#   ...
+# ]
+# ```
+#
+# Each flag spec can contain the following:
+#
+# -   The short and long form of the flag, without the leading `-` or `--`. The
+#     short form, if non-empty, must be one character. At least one of `&short`
+#     and `&long` must be non-empty.
+#
+# -   Whether the flag takes an optional argument or a required argument. At
+#     most one of `&arg-optional` and `&arg-required` may be true.
+#
+# It is not an error for a flag spec to contain more keys.
+#
+# On success, this command outputs two values: a list describing all flags
+# parsed from `$args`, and a list containing non-flag arguments. The former
+# list looks like:
+#
+# ```elvish
+# [
+#   [&spec=... &arg=value &long=$false]
+#   ...
+# ]
+# ```
+#
+# Each entry contains the original spec for the flag, its argument, and whether
+# the flag appeared in its long form.
+#
+# Example (some output reformatted for readability):
+#
+# ```elvish-transcript
+# ~> var specs = [
+#      [&short=v &long=verbose]
+#      [&short=p &long=port &arg-required]
+#    ]
+# ~> flag:parse-getopt [-v -p 80 foo] $specs
+# ▶ [[&spec=[&short=v &long=verbose] &long=$false &arg='']
+#    [&spec=[&arg-required=$true &short=p &long=port] &long=$false &arg=80]]
+# ▶ [foo]
+# ~> flag:parse-getopt [--verbose] $specs
+# ▶ [[&spec=[&short=v &long=verbose] &long=$true &arg='']]
+# ▶ []
+# ~> flag:parse-getopt [-v] [[&short=v &extra-info=foo]] # extra key in spec
+# ▶ [[&spec=[&extra-info=foo &short=v] &long=$false &arg='']]
+# ▶ []
+# ```
+#
+# @cf flag:parse edit:complete-getopt

+ 0 - 159
pkg/mods/flag/flag.go

@@ -21,33 +21,6 @@ var Ns = eval.BuildNsNamed("flag").
 		"parse-getopt": parseGetopt,
 	}).Ns()
 
-//elvdoc:fn call
-//
-// ```elvish
-// flag:call $fn $args
-// ```
-//
-// Parses flags from `$args` according to the signature of the
-// `$fn`, using the [Go convention](#go-convention), and calls `$fn`.
-//
-// The `$fn` must be a user-defined function (i.e. not a builtin
-// function or external command). Each option corresponds to a flag; see
-// [`flag:parse`](#flag:parse) for how the default value affects the behavior of
-// flags. After parsing, the non-flag arguments are used as function arguments.
-//
-// Example:
-//
-// ```elvish-transcript
-// ~> use flag
-// ~> fn f {|&verbose=$false &port=(num 8000) name| put $verbose $port $name }
-// ~> flag:call $f [-verbose -port 80 a.c]
-// ▶ $true
-// ▶ (num 80)
-// ▶ a.c
-// ```
-//
-// @cf flag:parse
-
 func call(fm *eval.Frame, fn *eval.Closure, argsVal vals.List) error {
 	var args []string
 	err := vals.ScanListToGo(argsVal, &args)
@@ -78,69 +51,6 @@ func callArgs(ss []string) []any {
 	return vs
 }
 
-//elvdoc:fn parse
-//
-// ```elvish
-// flag:parse $args $specs
-// ```
-//
-// Parses flags from `$args` according to the `$specs`, using the [Go
-// convention](#go-convention).
-//
-// The `$args` must be a list of strings containing the command-line arguments
-// to parse.
-//
-// The `$specs` must be a list of flag specs:
-//
-// ```elvish
-// [
-//   [flag default-value 'description of the flag']
-//   ...
-// ]
-// ```
-//
-// Each flag spec consists of the name of the flag (without the leading `-`),
-// its default value, and a description. The default value influences the how
-// the flag gets converted from string:
-//
-// -   If it is boolean, the flag is a boolean flag (see [Go
-//     convention](#go-convention) for implications). Flag values `0`, `f`, `F`,
-//     `false`, `False` and `FALSE` are converted to `$false`, and `1`, `t`,
-//     `T`, `true`, `True` and `TRUE` to `$true`. Other values are invalid.
-//
-// -   If it is a string, no conversion is done.
-//
-// -   If it is a [typed number](language.html#number), the flag value is
-//     converted using [`num`](builtin.html#num).
-//
-// -   If it is a list, the flag value is split at `,` (equivalent to `{|s| put
-//     [(str:split , $s)] }`).
-//
-// -   If it is none of the above, an exception is thrown.
-//
-// On success, this command outputs two values: a map containing the value of
-// flags defined in `$specs` (whether they appear in `$args` or not), and a list
-// containing non-flag arguments.
-//
-// Example:
-//
-// ```elvish-transcript
-// ~> flag:parse [-v -times 10 foo] [
-//      [v $false 'Verbose']
-//      [times (num 1) 'How many times']
-//    ]
-// ▶ [&v=$true &times=(num 10)]
-// ▶ [foo]
-// ~> flag:parse [] [
-//      [v $false 'Verbose']
-//      [times (num 1) 'How many times']
-//    ]
-// ▶ [&v=$false &times=(num 1)]
-// ▶ []
-// ```
-//
-// @cf flag:call flag:parse-getopt
-
 func parse(argsVal vals.List, specsVal vals.List) (vals.Map, vals.List, error) {
 	var args []string
 	err := vals.ScanListToGo(argsVal, &args)
@@ -217,75 +127,6 @@ func (lf *listFlag) Set(s string) error {
 	return nil
 }
 
-//elvdoc:fn parse-getopt
-//
-// ```elvish
-// flag:parse-getopt $args $specs ^
-//   &stop-after-double-dash=$true &stop-before-non-flag=$false &long-only=$false
-// ```
-//
-// Parses flags from `$args` according to the `$specs`, using the [getopt
-// convention](#getopt-convention) (see there for the semantics of the options),
-// and outputs the result.
-//
-// The `$args` must be a list of strings containing the command-line arguments
-// to parse.
-//
-// The `$specs` must be a list of flag specs:
-//
-// ```elvish
-// [
-//   [&short=f &long=flag &arg-optional=$false &arg-required=$false]
-//   ...
-// ]
-// ```
-//
-// Each flag spec can contain the following:
-//
-// -   The short and long form of the flag, without the leading `-` or `--`. The
-//     short form, if non-empty, must be one character. At least one of `&short`
-//     and `&long` must be non-empty.
-//
-// -   Whether the flag takes an optional argument or a required argument. At
-//     most one of `&arg-optional` and `&arg-required` may be true.
-//
-// It is not an error for a flag spec to contain more keys.
-//
-// On success, this command outputs two values: a list describing all flags
-// parsed from `$args`, and a list containing non-flag arguments. The former
-// list looks like:
-//
-// ```elvish
-// [
-//   [&spec=... &arg=value &long=$false]
-//   ...
-// ]
-// ```
-//
-// Each entry contains the original spec for the flag, its argument, and whether
-// the flag appeared in its long form.
-//
-// Example (some output reformatted for readability):
-//
-// ```elvish-transcript
-// ~> var specs = [
-//      [&short=v &long=verbose]
-//      [&short=p &long=port &arg-required]
-//    ]
-// ~> flag:parse-getopt [-v -p 80 foo] $specs
-// ▶ [[&spec=[&short=v &long=verbose] &long=$false &arg='']
-//    [&spec=[&arg-required=$true &short=p &long=port] &long=$false &arg=80]]
-// ▶ [foo]
-// ~> flag:parse-getopt [--verbose] $specs
-// ▶ [[&spec=[&short=v &long=verbose] &long=$true &arg='']]
-// ▶ []
-// ~> flag:parse-getopt [-v] [[&short=v &extra-info=foo]] # extra key in spec
-// ▶ [[&spec=[&extra-info=foo &short=v] &long=$false &arg='']]
-// ▶ []
-// ```
-//
-// @cf flag:parse edit:complete-getopt
-
 type specStruct struct {
 	Short       rune
 	Long        string

+ 538 - 0
pkg/mods/math/math.d.elv

@@ -0,0 +1,538 @@
+#elvdoc:var e
+#
+# Approximate value of
+# [`e`](https://en.wikipedia.org/wiki/E_(mathematical_constant)):
+# 2.718281.... This variable is read-only.
+
+#elvdoc:var pi
+#
+# Approximate value of [`π`](https://en.wikipedia.org/wiki/Pi): 3.141592.... This
+# variable is read-only.
+
+#elvdoc:fn abs
+#
+# ```elvish
+# math:abs $number
+# ```
+#
+# Computes the absolute value `$number`. This function is exactness-preserving.
+# Examples:
+#
+# ```elvish-transcript
+# ~> math:abs 2
+# ▶ (num 2)
+# ~> math:abs -2
+# ▶ (num 2)
+# ~> math:abs 10000000000000000000
+# ▶ (num 10000000000000000000)
+# ~> math:abs -10000000000000000000
+# ▶ (num 10000000000000000000)
+# ~> math:abs 1/2
+# ▶ (num 1/2)
+# ~> math:abs -1/2
+# ▶ (num 1/2)
+# ~> math:abs 1.23
+# ▶ (num 1.23)
+# ~> math:abs -1.23
+# ▶ (num 1.23)
+# ```
+
+#elvdoc:fn acos
+#
+# ```elvish
+# math:acos $number
+# ```
+#
+# Outputs the arccosine of `$number`, in radians (not degrees). Examples:
+#
+# ```elvish-transcript
+# ~> math:acos 1
+# ▶ (num 1)
+# ~> math:acos 1.00001
+# ▶ (num NaN)
+# ```
+
+#elvdoc:fn acosh
+#
+# ```elvish
+# math:acosh $number
+# ```
+#
+# Outputs the inverse hyperbolic cosine of `$number`. Examples:
+#
+# ```elvish-transcript
+# ~> math:acosh 1
+# ▶ (num 0)
+# ~> math:acosh 0
+# ▶ (num NaN)
+# ```
+
+#elvdoc:fn asin
+#
+# ```elvish
+# math:asin $number
+# ```
+#
+# Outputs the arcsine of `$number`, in radians (not degrees). Examples:
+#
+# ```elvish-transcript
+# ~> math:asin 0
+# ▶ (num 0)
+# ~> math:asin 1
+# ▶ (num 1.5707963267948966)
+# ~> math:asin 1.00001
+# ▶ (num NaN)
+# ```
+
+#elvdoc:fn asinh
+#
+# ```elvish
+# math:asinh $number
+# ```
+#
+# Outputs the inverse hyperbolic sine of `$number`. Examples:
+#
+# ```elvish-transcript
+# ~> math:asinh 0
+# ▶ (num 0)
+# ~> math:asinh inf
+# ▶ (num +Inf)
+# ```
+
+#elvdoc:fn atan
+#
+# ```elvish
+# math:atan $number
+# ```
+#
+# Outputs the arctangent of `$number`, in radians (not degrees). Examples:
+#
+# ```elvish-transcript
+# ~> math:atan 0
+# ▶ (num 0)
+# ~> math:atan $math:inf
+# ▶ (num 1.5707963267948966)
+# ```
+
+#elvdoc:fn atanh
+#
+# ```elvish
+# math:atanh $number
+# ```
+#
+# Outputs the inverse hyperbolic tangent of `$number`. Examples:
+#
+# ```elvish-transcript
+# ~> math:atanh 0
+# ▶ (num 0)
+# ~> math:atanh 1
+# ▶ (num +Inf)
+# ```
+
+#elvdoc:fn ceil
+#
+# ```elvish
+# math:ceil $number
+# ```
+#
+# Computes the least integer greater than or equal to `$number`. This function
+# is exactness-preserving.
+#
+# The results for the special floating-point values -0.0, +0.0, -Inf, +Inf and
+# NaN are themselves.
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> math:floor 1
+# ▶ (num 1)
+# ~> math:floor 3/2
+# ▶ (num 1)
+# ~> math:floor -3/2
+# ▶ (num -2)
+# ~> math:floor 1.1
+# ▶ (num 1.0)
+# ~> math:floor -1.1
+# ▶ (num -2.0)
+# ```
+
+#elvdoc:fn cos
+#
+# ```elvish
+# math:cos $number
+# ```
+#
+# Computes the cosine of `$number` in units of radians (not degrees).
+# Examples:
+#
+# ```elvish-transcript
+# ~> math:cos 0
+# ▶ (num 1)
+# ~> math:cos 3.14159265
+# ▶ (num -1)
+# ```
+
+#elvdoc:fn cosh
+#
+# ```elvish
+# math:cosh $number
+# ```
+#
+# Computes the hyperbolic cosine of `$number`. Example:
+#
+# ```elvish-transcript
+# ~> math:cosh 0
+# ▶ (num 1)
+# ```
+
+#elvdoc:fn floor
+#
+# ```elvish
+# math:floor $number
+# ```
+#
+# Computes the greatest integer less than or equal to `$number`. This function
+# is exactness-preserving.
+#
+# The results for the special floating-point values -0.0, +0.0, -Inf, +Inf and
+# NaN are themselves.
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> math:floor 1
+# ▶ (num 1)
+# ~> math:floor 3/2
+# ▶ (num 1)
+# ~> math:floor -3/2
+# ▶ (num -2)
+# ~> math:floor 1.1
+# ▶ (num 1.0)
+# ~> math:floor -1.1
+# ▶ (num -2.0)
+# ```
+
+#elvdoc:fn is-inf
+#
+# ```elvish
+# math:is-inf &sign=0 $number
+# ```
+#
+# Tests whether the number is infinity. If sign > 0, tests whether `$number`
+# is positive infinity. If sign < 0, tests whether `$number` is negative
+# infinity. If sign == 0, tests whether `$number` is either infinity.
+#
+# ```elvish-transcript
+# ~> math:is-inf 123
+# ▶ $false
+# ~> math:is-inf inf
+# ▶ $true
+# ~> math:is-inf -inf
+# ▶ $true
+# ~> math:is-inf &sign=1 inf
+# ▶ $true
+# ~> math:is-inf &sign=-1 inf
+# ▶ $false
+# ~> math:is-inf &sign=-1 -inf
+# ▶ $true
+# ```
+
+#elvdoc:fn is-nan
+#
+# ```elvish
+# math:is-nan $number
+# ```
+#
+# Tests whether the number is a NaN (not-a-number).
+#
+# ```elvish-transcript
+# ~> math:is-nan 123
+# ▶ $false
+# ~> math:is-nan (num inf)
+# ▶ $false
+# ~> math:is-nan (num nan)
+# ▶ $true
+# ```
+
+#elvdoc:fn log
+#
+# ```elvish
+# math:log $number
+# ```
+#
+# Computes the natural (base *e*) logarithm of `$number`. Examples:
+#
+# ```elvish-transcript
+# ~> math:log 1.0
+# ▶ (num 1)
+# ~> math:log -2.3
+# ▶ (num NaN)
+# ```
+
+#elvdoc:fn log10
+#
+# ```elvish
+# math:log10 $number
+# ```
+#
+# Computes the base 10 logarithm of `$number`. Examples:
+#
+# ```elvish-transcript
+# ~> math:log10 100.0
+# ▶ (num 2)
+# ~> math:log10 -1.7
+# ▶ (num NaN)
+# ```
+
+#elvdoc:fn log2
+#
+# ```elvish
+# math:log2 $number
+# ```
+#
+# Computes the base 2 logarithm of `$number`. Examples:
+#
+# ```elvish-transcript
+# ~> math:log2 8
+# ▶ (num 3)
+# ~> math:log2 -5.3
+# ▶ (num NaN)
+# ```
+
+#elvdoc:fn max
+#
+# ```elvish
+# math:max $number...
+# ```
+#
+# Outputs the maximum number in the arguments. If there are no arguments,
+# an exception is thrown. If any number is NaN then NaN is output. This
+# function is exactness-preserving.
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> math:max 3 5 2
+# ▶ (num 5)
+# ~> math:max (range 100)
+# ▶ (num 99)
+# ~> math:max 1/2 1/3 2/3
+# ▶ (num 2/3)
+# ```
+
+#elvdoc:fn min
+#
+# ```elvish
+# math:min $number...
+# ```
+#
+# Outputs the minimum number in the arguments. If there are no arguments
+# an exception is thrown. If any number is NaN then NaN is output. This
+# function is exactness-preserving.
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> math:min
+# Exception: arity mismatch: arguments must be 1 or more values, but is 0 values
+# [tty 17], line 1: math:min
+# ~> math:min 3 5 2
+# ▶ (num 2)
+# ~> math:min 1/2 1/3 2/3
+# ▶ (num 1/3)
+# ```
+
+#elvdoc:fn pow
+#
+# ```elvish
+# math:pow $base $exponent
+# ```
+#
+# Outputs the result of raising `$base` to the power of `$exponent`.
+#
+# This function produces an exact result when `$base` is exact and `$exponent`
+# is an exact integer. Otherwise it produces an inexact result.
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> math:pow 3 2
+# ▶ (num 9)
+# ~> math:pow -2 2
+# ▶ (num 4)
+# ~> math:pow 1/2 3
+# ▶ (num 1/8)
+# ~> math:pow 1/2 -3
+# ▶ (num 8)
+# ~> math:pow 9 1/2
+# ▶ (num 3.0)
+# ~> math:pow 12 1.1
+# ▶ (num 15.38506624784179)
+# ```
+
+#elvdoc:fn round
+#
+# ```elvish
+# math:round $number
+# ```
+#
+# Outputs the nearest integer, rounding half away from zero. This function is
+# exactness-preserving.
+#
+# The results for the special floating-point values -0.0, +0.0, -Inf, +Inf and
+# NaN are themselves.
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> math:round 2
+# ▶ (num 2)
+# ~> math:round 1/3
+# ▶ (num 0)
+# ~> math:round 1/2
+# ▶ (num 1)
+# ~> math:round 2/3
+# ▶ (num 1)
+# ~> math:round -1/3
+# ▶ (num 0)
+# ~> math:round -1/2
+# ▶ (num -1)
+# ~> math:round -2/3
+# ▶ (num -1)
+# ~> math:round 2.5
+# ▶ (num 3.0)
+# ```
+
+#elvdoc:fn round-to-even
+#
+# ```elvish
+# math:round-to-even $number
+# ```
+#
+# Outputs the nearest integer, rounding ties to even. This function is
+# exactness-preserving.
+#
+# The results for the special floating-point values -0.0, +0.0, -Inf, +Inf and
+# NaN are themselves.
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> math:round-to-even 2
+# ▶ (num 2)
+# ~> math:round-to-even 1/2
+# ▶ (num 0)
+# ~> math:round-to-even 3/2
+# ▶ (num 2)
+# ~> math:round-to-even 5/2
+# ▶ (num 2)
+# ~> math:round-to-even -5/2
+# ▶ (num -2)
+# ~> math:round-to-even 2.5
+# ▶ (num 2.0)
+# ~> math:round-to-even 1.5
+# ▶ (num 2.0)
+# ```
+
+#elvdoc:fn sin
+#
+# ```elvish
+# math:sin $number
+# ```
+#
+# Computes the sine of `$number` in units of radians (not degrees). Examples:
+#
+# ```elvish-transcript
+# ~> math:sin 0
+# ▶ (num 0)
+# ~> math:sin 3.14159265
+# ▶ (num 3.5897930298416118e-09)
+# ```
+
+#elvdoc:fn sinh
+#
+# ```elvish
+# math:sinh $number
+# ```
+#
+# Computes the hyperbolic sine of `$number`. Example:
+#
+# ```elvish-transcript
+# ~> math:sinh 0
+# ▶ (num 0)
+# ```
+
+#elvdoc:fn sqrt
+#
+# ```elvish
+# math:sqrt $number
+# ```
+#
+# Computes the square-root of `$number`. Examples:
+#
+# ```elvish-transcript
+# ~> math:sqrt 0
+# ▶ (num 0)
+# ~> math:sqrt 4
+# ▶ (num 2)
+# ~> math:sqrt -4
+# ▶ (num NaN)
+# ```
+
+#elvdoc:fn tan
+#
+# ```elvish
+# math:tan $number
+# ```
+#
+# Computes the tangent of `$number` in units of radians (not degrees). Examples:
+#
+# ```elvish-transcript
+# ~> math:tan 0
+# ▶ (num 0)
+# ~> math:tan 3.14159265
+# ▶ (num -0.0000000035897930298416118)
+# ```
+
+#elvdoc:fn tanh
+#
+# ```elvish
+# math:tanh $number
+# ```
+#
+# Computes the hyperbolic tangent of `$number`. Example:
+#
+# ```elvish-transcript
+# ~> math:tanh 0
+# ▶ (num 0)
+# ```
+
+#elvdoc:fn trunc
+#
+# ```elvish
+# math:trunc $number
+# ```
+#
+# Outputs the integer portion of `$number`. This function is exactness-preserving.
+#
+# The results for the special floating-point values -0.0, +0.0, -Inf, +Inf and
+# NaN are themselves.
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> math:trunc 1
+# ▶ (num 1)
+# ~> math:trunc 3/2
+# ▶ (num 1)
+# ~> math:trunc 5/3
+# ▶ (num 1)
+# ~> math:trunc -3/2
+# ▶ (num -1)
+# ~> math:trunc -5/3
+# ▶ (num -1)
+# ~> math:trunc 1.7
+# ▶ (num 1.0)
+# ~> math:trunc -1.7
+# ▶ (num -1.0)
+# ```

+ 0 - 539
pkg/mods/math/math.go

@@ -48,45 +48,6 @@ var Ns = eval.BuildNsNamed("math").
 		"trunc":         trunc,
 	}).Ns()
 
-//elvdoc:var e
-//
-// Approximate value of
-// [`e`](https://en.wikipedia.org/wiki/E_(mathematical_constant)):
-// 2.718281.... This variable is read-only.
-
-//elvdoc:var pi
-//
-// Approximate value of [`π`](https://en.wikipedia.org/wiki/Pi): 3.141592.... This
-// variable is read-only.
-
-//elvdoc:fn abs
-//
-// ```elvish
-// math:abs $number
-// ```
-//
-// Computes the absolute value `$number`. This function is exactness-preserving.
-// Examples:
-//
-// ```elvish-transcript
-// ~> math:abs 2
-// ▶ (num 2)
-// ~> math:abs -2
-// ▶ (num 2)
-// ~> math:abs 10000000000000000000
-// ▶ (num 10000000000000000000)
-// ~> math:abs -10000000000000000000
-// ▶ (num 10000000000000000000)
-// ~> math:abs 1/2
-// ▶ (num 1/2)
-// ~> math:abs -1/2
-// ▶ (num 1/2)
-// ~> math:abs 1.23
-// ▶ (num 1.23)
-// ~> math:abs -1.23
-// ▶ (num 1.23)
-// ```
-
 const (
 	maxInt = int(^uint(0) >> 1)
 	minInt = -maxInt - 1
@@ -121,125 +82,6 @@ func abs(n vals.Num) vals.Num {
 	}
 }
 
-//elvdoc:fn acos
-//
-// ```elvish
-// math:acos $number
-// ```
-//
-// Outputs the arccosine of `$number`, in radians (not degrees). Examples:
-//
-// ```elvish-transcript
-// ~> math:acos 1
-// ▶ (num 1)
-// ~> math:acos 1.00001
-// ▶ (num NaN)
-// ```
-
-//elvdoc:fn acosh
-//
-// ```elvish
-// math:acosh $number
-// ```
-//
-// Outputs the inverse hyperbolic cosine of `$number`. Examples:
-//
-// ```elvish-transcript
-// ~> math:acosh 1
-// ▶ (num 0)
-// ~> math:acosh 0
-// ▶ (num NaN)
-// ```
-
-//elvdoc:fn asin
-//
-// ```elvish
-// math:asin $number
-// ```
-//
-// Outputs the arcsine of `$number`, in radians (not degrees). Examples:
-//
-// ```elvish-transcript
-// ~> math:asin 0
-// ▶ (num 0)
-// ~> math:asin 1
-// ▶ (num 1.5707963267948966)
-// ~> math:asin 1.00001
-// ▶ (num NaN)
-// ```
-
-//elvdoc:fn asinh
-//
-// ```elvish
-// math:asinh $number
-// ```
-//
-// Outputs the inverse hyperbolic sine of `$number`. Examples:
-//
-// ```elvish-transcript
-// ~> math:asinh 0
-// ▶ (num 0)
-// ~> math:asinh inf
-// ▶ (num +Inf)
-// ```
-
-//elvdoc:fn atan
-//
-// ```elvish
-// math:atan $number
-// ```
-//
-// Outputs the arctangent of `$number`, in radians (not degrees). Examples:
-//
-// ```elvish-transcript
-// ~> math:atan 0
-// ▶ (num 0)
-// ~> math:atan $math:inf
-// ▶ (num 1.5707963267948966)
-// ```
-
-//elvdoc:fn atanh
-//
-// ```elvish
-// math:atanh $number
-// ```
-//
-// Outputs the inverse hyperbolic tangent of `$number`. Examples:
-//
-// ```elvish-transcript
-// ~> math:atanh 0
-// ▶ (num 0)
-// ~> math:atanh 1
-// ▶ (num +Inf)
-// ```
-
-//elvdoc:fn ceil
-//
-// ```elvish
-// math:ceil $number
-// ```
-//
-// Computes the least integer greater than or equal to `$number`. This function
-// is exactness-preserving.
-//
-// The results for the special floating-point values -0.0, +0.0, -Inf, +Inf and
-// NaN are themselves.
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> math:floor 1
-// ▶ (num 1)
-// ~> math:floor 3/2
-// ▶ (num 1)
-// ~> math:floor -3/2
-// ▶ (num -2)
-// ~> math:floor 1.1
-// ▶ (num 1.0)
-// ~> math:floor -1.1
-// ▶ (num -2.0)
-// ```
-
 var (
 	big1 = big.NewInt(1)
 	big2 = big.NewInt(2)
@@ -254,62 +96,6 @@ func ceil(n vals.Num) vals.Num {
 		})
 }
 
-//elvdoc:fn cos
-//
-// ```elvish
-// math:cos $number
-// ```
-//
-// Computes the cosine of `$number` in units of radians (not degrees).
-// Examples:
-//
-// ```elvish-transcript
-// ~> math:cos 0
-// ▶ (num 1)
-// ~> math:cos 3.14159265
-// ▶ (num -1)
-// ```
-
-//elvdoc:fn cosh
-//
-// ```elvish
-// math:cosh $number
-// ```
-//
-// Computes the hyperbolic cosine of `$number`. Example:
-//
-// ```elvish-transcript
-// ~> math:cosh 0
-// ▶ (num 1)
-// ```
-
-//elvdoc:fn floor
-//
-// ```elvish
-// math:floor $number
-// ```
-//
-// Computes the greatest integer less than or equal to `$number`. This function
-// is exactness-preserving.
-//
-// The results for the special floating-point values -0.0, +0.0, -Inf, +Inf and
-// NaN are themselves.
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> math:floor 1
-// ▶ (num 1)
-// ~> math:floor 3/2
-// ▶ (num 1)
-// ~> math:floor -3/2
-// ▶ (num -2)
-// ~> math:floor 1.1
-// ▶ (num 1.0)
-// ~> math:floor -1.1
-// ▶ (num -2.0)
-// ```
-
 func floor(n vals.Num) vals.Num {
 	return integerize(n,
 		math.Floor,
@@ -318,31 +104,6 @@ func floor(n vals.Num) vals.Num {
 		})
 }
 
-//elvdoc:fn is-inf
-//
-// ```elvish
-// math:is-inf &sign=0 $number
-// ```
-//
-// Tests whether the number is infinity. If sign > 0, tests whether `$number`
-// is positive infinity. If sign < 0, tests whether `$number` is negative
-// infinity. If sign == 0, tests whether `$number` is either infinity.
-//
-// ```elvish-transcript
-// ~> math:is-inf 123
-// ▶ $false
-// ~> math:is-inf inf
-// ▶ $true
-// ~> math:is-inf -inf
-// ▶ $true
-// ~> math:is-inf &sign=1 inf
-// ▶ $true
-// ~> math:is-inf &sign=-1 inf
-// ▶ $false
-// ~> math:is-inf &sign=-1 -inf
-// ▶ $true
-// ```
-
 type isInfOpts struct{ Sign int }
 
 func (opts *isInfOpts) SetDefaultOptions() { opts.Sign = 0 }
@@ -354,23 +115,6 @@ func isInf(opts isInfOpts, n vals.Num) bool {
 	return false
 }
 
-//elvdoc:fn is-nan
-//
-// ```elvish
-// math:is-nan $number
-// ```
-//
-// Tests whether the number is a NaN (not-a-number).
-//
-// ```elvish-transcript
-// ~> math:is-nan 123
-// ▶ $false
-// ~> math:is-nan (num inf)
-// ▶ $false
-// ~> math:is-nan (num nan)
-// ▶ $true
-// ```
-
 func isNaN(n vals.Num) bool {
 	if f, ok := n.(float64); ok {
 		return math.IsNaN(f)
@@ -378,72 +122,6 @@ func isNaN(n vals.Num) bool {
 	return false
 }
 
-//elvdoc:fn log
-//
-// ```elvish
-// math:log $number
-// ```
-//
-// Computes the natural (base *e*) logarithm of `$number`. Examples:
-//
-// ```elvish-transcript
-// ~> math:log 1.0
-// ▶ (num 1)
-// ~> math:log -2.3
-// ▶ (num NaN)
-// ```
-
-//elvdoc:fn log10
-//
-// ```elvish
-// math:log10 $number
-// ```
-//
-// Computes the base 10 logarithm of `$number`. Examples:
-//
-// ```elvish-transcript
-// ~> math:log10 100.0
-// ▶ (num 2)
-// ~> math:log10 -1.7
-// ▶ (num NaN)
-// ```
-
-//elvdoc:fn log2
-//
-// ```elvish
-// math:log2 $number
-// ```
-//
-// Computes the base 2 logarithm of `$number`. Examples:
-//
-// ```elvish-transcript
-// ~> math:log2 8
-// ▶ (num 3)
-// ~> math:log2 -5.3
-// ▶ (num NaN)
-// ```
-
-//elvdoc:fn max
-//
-// ```elvish
-// math:max $number...
-// ```
-//
-// Outputs the maximum number in the arguments. If there are no arguments,
-// an exception is thrown. If any number is NaN then NaN is output. This
-// function is exactness-preserving.
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> math:max 3 5 2
-// ▶ (num 5)
-// ~> math:max (range 100)
-// ▶ (num 99)
-// ~> math:max 1/2 1/3 2/3
-// ▶ (num 2/3)
-// ```
-
 func max(rawNums ...vals.Num) (vals.Num, error) {
 	if len(rawNums) == 0 {
 		return nil, errs.ArityMismatch{What: "arguments", ValidLow: 1, ValidHigh: -1, Actual: 0}
@@ -485,28 +163,6 @@ func max(rawNums ...vals.Num) (vals.Num, error) {
 	}
 }
 
-//elvdoc:fn min
-//
-// ```elvish
-// math:min $number...
-// ```
-//
-// Outputs the minimum number in the arguments. If there are no arguments
-// an exception is thrown. If any number is NaN then NaN is output. This
-// function is exactness-preserving.
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> math:min
-// Exception: arity mismatch: arguments must be 1 or more values, but is 0 values
-// [tty 17], line 1: math:min
-// ~> math:min 3 5 2
-// ▶ (num 2)
-// ~> math:min 1/2 1/3 2/3
-// ▶ (num 1/3)
-// ```
-
 func min(rawNums ...vals.Num) (vals.Num, error) {
 	if len(rawNums) == 0 {
 		return nil, errs.ArityMismatch{What: "arguments", ValidLow: 1, ValidHigh: -1, Actual: 0}
@@ -548,34 +204,6 @@ func min(rawNums ...vals.Num) (vals.Num, error) {
 	}
 }
 
-//elvdoc:fn pow
-//
-// ```elvish
-// math:pow $base $exponent
-// ```
-//
-// Outputs the result of raising `$base` to the power of `$exponent`.
-//
-// This function produces an exact result when `$base` is exact and `$exponent`
-// is an exact integer. Otherwise it produces an inexact result.
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> math:pow 3 2
-// ▶ (num 9)
-// ~> math:pow -2 2
-// ▶ (num 4)
-// ~> math:pow 1/2 3
-// ▶ (num 1/8)
-// ~> math:pow 1/2 -3
-// ▶ (num 8)
-// ~> math:pow 9 1/2
-// ▶ (num 3.0)
-// ~> math:pow 12 1.1
-// ▶ (num 15.38506624784179)
-// ```
-
 func pow(base, exp vals.Num) vals.Num {
 	if isExact(base) && isExactInt(exp) {
 		// Produce exact result
@@ -626,39 +254,6 @@ func isExactInt(n vals.Num) bool {
 	}
 }
 
-//elvdoc:fn round
-//
-// ```elvish
-// math:round $number
-// ```
-//
-// Outputs the nearest integer, rounding half away from zero. This function is
-// exactness-preserving.
-//
-// The results for the special floating-point values -0.0, +0.0, -Inf, +Inf and
-// NaN are themselves.
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> math:round 2
-// ▶ (num 2)
-// ~> math:round 1/3
-// ▶ (num 0)
-// ~> math:round 1/2
-// ▶ (num 1)
-// ~> math:round 2/3
-// ▶ (num 1)
-// ~> math:round -1/3
-// ▶ (num 0)
-// ~> math:round -1/2
-// ▶ (num -1)
-// ~> math:round -2/3
-// ▶ (num -1)
-// ~> math:round 2.5
-// ▶ (num 3.0)
-// ```
-
 func round(n vals.Num) vals.Num {
 	return integerize(n,
 		math.Round,
@@ -675,37 +270,6 @@ func round(n vals.Num) vals.Num {
 		})
 }
 
-//elvdoc:fn round-to-even
-//
-// ```elvish
-// math:round-to-even $number
-// ```
-//
-// Outputs the nearest integer, rounding ties to even. This function is
-// exactness-preserving.
-//
-// The results for the special floating-point values -0.0, +0.0, -Inf, +Inf and
-// NaN are themselves.
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> math:round-to-even 2
-// ▶ (num 2)
-// ~> math:round-to-even 1/2
-// ▶ (num 0)
-// ~> math:round-to-even 3/2
-// ▶ (num 2)
-// ~> math:round-to-even 5/2
-// ▶ (num 2)
-// ~> math:round-to-even -5/2
-// ▶ (num -2)
-// ~> math:round-to-even 2.5
-// ▶ (num 2.0)
-// ~> math:round-to-even 1.5
-// ▶ (num 2.0)
-// ```
-
 func roundToEven(n vals.Num) vals.Num {
 	return integerize(n,
 		math.RoundToEven,
@@ -722,109 +286,6 @@ func roundToEven(n vals.Num) vals.Num {
 		})
 }
 
-//elvdoc:fn sin
-//
-// ```elvish
-// math:sin $number
-// ```
-//
-// Computes the sine of `$number` in units of radians (not degrees). Examples:
-//
-// ```elvish-transcript
-// ~> math:sin 0
-// ▶ (num 0)
-// ~> math:sin 3.14159265
-// ▶ (num 3.5897930298416118e-09)
-// ```
-
-//elvdoc:fn sinh
-//
-// ```elvish
-// math:sinh $number
-// ```
-//
-// Computes the hyperbolic sine of `$number`. Example:
-//
-// ```elvish-transcript
-// ~> math:sinh 0
-// ▶ (num 0)
-// ```
-
-//elvdoc:fn sqrt
-//
-// ```elvish
-// math:sqrt $number
-// ```
-//
-// Computes the square-root of `$number`. Examples:
-//
-// ```elvish-transcript
-// ~> math:sqrt 0
-// ▶ (num 0)
-// ~> math:sqrt 4
-// ▶ (num 2)
-// ~> math:sqrt -4
-// ▶ (num NaN)
-// ```
-
-//elvdoc:fn tan
-//
-// ```elvish
-// math:tan $number
-// ```
-//
-// Computes the tangent of `$number` in units of radians (not degrees). Examples:
-//
-// ```elvish-transcript
-// ~> math:tan 0
-// ▶ (num 0)
-// ~> math:tan 3.14159265
-// ▶ (num -0.0000000035897930298416118)
-// ```
-
-//elvdoc:fn tanh
-//
-// ```elvish
-// math:tanh $number
-// ```
-//
-// Computes the hyperbolic tangent of `$number`. Example:
-//
-// ```elvish-transcript
-// ~> math:tanh 0
-// ▶ (num 0)
-// ```
-
-//elvdoc:fn trunc
-//
-// ```elvish
-// math:trunc $number
-// ```
-//
-// Outputs the integer portion of `$number`. This function is exactness-preserving.
-//
-// The results for the special floating-point values -0.0, +0.0, -Inf, +Inf and
-// NaN are themselves.
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> math:trunc 1
-// ▶ (num 1)
-// ~> math:trunc 3/2
-// ▶ (num 1)
-// ~> math:trunc 5/3
-// ▶ (num 1)
-// ~> math:trunc -3/2
-// ▶ (num -1)
-// ~> math:trunc -5/3
-// ▶ (num -1)
-// ~> math:trunc 1.7
-// ▶ (num 1.0)
-// ~> math:trunc -1.7
-// ▶ (num -1.0)
-// ```
-
 func trunc(n vals.Num) vals.Num {
 	return integerize(n,
 		math.Trunc,

+ 253 - 0
pkg/mods/path/path.d.elv

@@ -0,0 +1,253 @@
+#elvdoc:var list-separator
+#
+# OS-specific path list separator. Colon (`:`) on UNIX and semicolon (`;`) on
+# Windows. This variable is read-only.
+
+#elvdoc:var separator
+#
+# OS-specific path separator. Forward slash (`/`) on UNIX and backslash (`\`)
+# on Windows. This variable is read-only.
+
+#elvdoc:fn abs
+#
+# ```elvish
+# path:abs $path
+# ```
+#
+# Outputs `$path` converted to an absolute path.
+#
+# ```elvish-transcript
+# ~> cd ~
+# ~> path:abs bin
+# ▶ /home/user/bin
+# ```
+
+#elvdoc:fn base
+#
+# ```elvish
+# path:base $path
+# ```
+#
+# Outputs the last element of `$path`. This is analogous to the POSIX `basename` command. See the
+# [Go documentation](https://pkg.go.dev/path/filepath#Base) for more details.
+#
+# ```elvish-transcript
+# ~> path:base ~/bin
+# ▶ bin
+# ```
+
+#elvdoc:fn clean
+#
+# ```elvish
+# path:clean $path
+# ```
+#
+# Outputs the shortest version of `$path` equivalent to `$path` by purely lexical processing. This
+# is most useful for eliminating unnecessary relative path elements such as `.` and `..` without
+# asking the OS to evaluate the path name. See the [Go
+# documentation](https://pkg.go.dev/path/filepath#Clean) for more details.
+#
+# ```elvish-transcript
+# ~> path:clean ./../bin
+# ▶ ../bin
+# ```
+
+#elvdoc:fn dir
+#
+# ```elvish
+# path:dir $path
+# ```
+#
+# Outputs all but the last element of `$path`, typically the path's enclosing directory. See the
+# [Go documentation](https://pkg.go.dev/path/filepath#Dir) for more details. This is analogous to
+# the POSIX `dirname` command.
+#
+# ```elvish-transcript
+# ~> path:dir /a/b/c/something
+# ▶ /a/b/c
+# ```
+
+#elvdoc:fn ext
+#
+# ```elvish
+# ext $path
+# ```
+#
+# Outputs the file name extension used by `$path` (including the separating period). If there is no
+# extension the empty string is output. See the [Go
+# documentation](https://pkg.go.dev/path/filepath#Ext) for more details.
+#
+# ```elvish-transcript
+# ~> path:ext hello.elv
+# ▶ .elv
+# ```
+
+#elvdoc:fn is-abs
+#
+# ```elvish
+# is-abs $path
+# ```
+#
+# Outputs `$true` if the path is an absolute path. Note that platforms like Windows have different
+# rules than UNIX like platforms for what constitutes an absolute path. See the [Go
+# documentation](https://pkg.go.dev/path/filepath#IsAbs) for more details.
+#
+# ```elvish-transcript
+# ~> path:is-abs hello.elv
+# ▶ false
+# ~> path:is-abs /hello.elv
+# ▶ true
+# ```
+
+#elvdoc:fn eval-symlinks
+#
+# ```elvish
+# eval-symlinks $path
+# ```
+#
+# Outputs `$path` after resolving any symbolic links. If `$path` is relative the result will be
+# relative to the current directory, unless one of the components is an absolute symbolic link.
+# This function calls `path:clean` on the result before outputting it. This is analogous to the
+# external `realpath` or `readlink` command found on many systems. See the [Go
+# documentation](https://pkg.go.dev/path/filepath#EvalSymlinks) for more details.
+#
+# ```elvish-transcript
+# ~> mkdir bin
+# ~> ln -s bin sbin
+# ~> path:eval-symlinks ./sbin/a_command
+# ▶ bin/a_command
+# ```
+
+#elvdoc:fn join
+#
+# ```elvish
+# path:join $path-component...
+# ```
+#
+# Joins any number of path elements into a single path, separating them with an
+# [OS specific separator](#path:separator). Empty elements are ignored. The
+# result is [cleaned](#path:clean). However, if the argument list is empty or
+# all its elements are empty, Join returns an empty string. On Windows, the
+# result will only be a UNC path if the first non-empty element is a UNC path.
+#
+# ```elvish-transcript
+# ~> path:join home user bin
+# ▶ home/user/bin
+# ~> path:join $path:separator home user bin
+# ▶ /home/user/bin
+# ```
+
+#elvdoc:fn is-dir
+#
+# ```elvish
+# is-dir &follow-symlink=$false $path
+# ```
+#
+# Outputs `$true` if the path resolves to a directory. If the final element of the path is a
+# symlink, even if it points to a directory, it still outputs `$false` since a symlink is not a
+# directory. Setting option `&follow-symlink` to true will cause the last element of the path, if
+# it is a symlink, to be resolved before doing the test.
+#
+# ```elvish-transcript
+# ~> touch not-a-dir
+# ~> path:is-dir not-a-dir
+# ▶ false
+# ~> path:is-dir /tmp
+# ▶ true
+# ```
+#
+# @cf path:is-regular
+
+#elvdoc:fn is-regular
+#
+# ```elvish
+# is-regular &follow-symlink=$false $path
+# ```
+#
+# Outputs `$true` if the path resolves to a regular file. If the final element of the path is a
+# symlink, even if it points to a regular file, it still outputs `$false` since a symlink is not a
+# regular file. Setting option `&follow-symlink` to true will cause the last element of the path,
+# if it is a symlink, to be resolved before doing the test.
+#
+# **Note:** This isn't named `is-file` because a UNIX file may be a "bag of bytes" or may be a
+# named pipe, device special file (e.g. `/dev/tty`), etc.
+#
+# ```elvish-transcript
+# ~> touch not-a-dir
+# ~> path:is-regular not-a-dir
+# ▶ true
+# ~> path:is-regular /tmp
+# ▶ false
+# ```
+#
+# @cf path:is-dir
+
+#elvdoc:fn temp-dir
+#
+# ```elvish
+# temp-dir &dir='' $pattern?
+# ```
+#
+# Creates a new directory and outputs its name.
+#
+# The &dir option determines where the directory will be created; if it is an
+# empty string (the default), a system-dependent directory suitable for storing
+# temporary files will be used. The `$pattern` argument determines the name of
+# the directory, where the last star will be replaced by a random string; it
+# defaults to `elvish-*`.
+#
+# It is the caller's responsibility to remove the directory if it is intended
+# to be temporary.
+#
+# ```elvish-transcript
+# ~> path:temp-dir
+# ▶ /tmp/elvish-RANDOMSTR
+# ~> path:temp-dir x-
+# ▶ /tmp/x-RANDOMSTR
+# ~> path:temp-dir 'x-*.y'
+# ▶ /tmp/x-RANDOMSTR.y
+# ~> path:temp-dir &dir=.
+# ▶ elvish-RANDOMSTR
+# ~> path:temp-dir &dir=/some/dir
+# ▶ /some/dir/elvish-RANDOMSTR
+# ```
+
+#elvdoc:fn temp-file
+#
+# ```elvish
+# temp-file &dir='' $pattern?
+# ```
+#
+# Creates a new file and outputs a [file](language.html#file) object opened
+# for reading and writing.
+#
+# The &dir option determines where the file will be created; if it is an
+# empty string (the default), a system-dependent directory suitable for storing
+# temporary files will be used. The `$pattern` argument determines the name of
+# the file, where the last star will be replaced by a random string; it
+# defaults to `elvish-*`.
+#
+# It is the caller's responsibility to close the file with
+# [`file:close`](file.html#file:close). The caller should also remove the file
+# if it is intended to be temporary (with `rm $f[name]`).
+#
+# ```elvish-transcript
+# ~> var f = (path:temp-file)
+# ~> put $f[name]
+# ▶ /tmp/elvish-RANDOMSTR
+# ~> echo hello > $f
+# ~> cat $f[name]
+# hello
+# ~> var f = (path:temp-file x-)
+# ~> put $f[name]
+# ▶ /tmp/x-RANDOMSTR
+# ~> var f = (path:temp-file 'x-*.y')
+# ~> put $f[name]
+# ▶ /tmp/x-RANDOMSTR.y
+# ~> var f = (path:temp-file &dir=.)
+# ~> put $f[name]
+# ▶ elvish-RANDOMSTR
+# ~> var f = (path:temp-file &dir=/some/dir)
+# ~> put $f[name]
+# ▶ /some/dir/elvish-RANDOMSTR
+# ```

+ 0 - 254
pkg/mods/path/path.go

@@ -31,166 +31,6 @@ var Ns = eval.BuildNsNamed("path").
 		"temp-file":     tempFile,
 	}).Ns()
 
-//elvdoc:var list-separator
-//
-// OS-specific path list separator. Colon (`:`) on UNIX and semicolon (`;`) on
-// Windows. This variable is read-only.
-
-//elvdoc:var separator
-//
-// OS-specific path separator. Forward slash (`/`) on UNIX and backslash (`\`)
-// on Windows. This variable is read-only.
-
-//elvdoc:fn abs
-//
-// ```elvish
-// path:abs $path
-// ```
-//
-// Outputs `$path` converted to an absolute path.
-//
-// ```elvish-transcript
-// ~> cd ~
-// ~> path:abs bin
-// ▶ /home/user/bin
-// ```
-
-//elvdoc:fn base
-//
-// ```elvish
-// path:base $path
-// ```
-//
-// Outputs the last element of `$path`. This is analogous to the POSIX `basename` command. See the
-// [Go documentation](https://pkg.go.dev/path/filepath#Base) for more details.
-//
-// ```elvish-transcript
-// ~> path:base ~/bin
-// ▶ bin
-// ```
-
-//elvdoc:fn clean
-//
-// ```elvish
-// path:clean $path
-// ```
-//
-// Outputs the shortest version of `$path` equivalent to `$path` by purely lexical processing. This
-// is most useful for eliminating unnecessary relative path elements such as `.` and `..` without
-// asking the OS to evaluate the path name. See the [Go
-// documentation](https://pkg.go.dev/path/filepath#Clean) for more details.
-//
-// ```elvish-transcript
-// ~> path:clean ./../bin
-// ▶ ../bin
-// ```
-
-//elvdoc:fn dir
-//
-// ```elvish
-// path:dir $path
-// ```
-//
-// Outputs all but the last element of `$path`, typically the path's enclosing directory. See the
-// [Go documentation](https://pkg.go.dev/path/filepath#Dir) for more details. This is analogous to
-// the POSIX `dirname` command.
-//
-// ```elvish-transcript
-// ~> path:dir /a/b/c/something
-// ▶ /a/b/c
-// ```
-
-//elvdoc:fn ext
-//
-// ```elvish
-// ext $path
-// ```
-//
-// Outputs the file name extension used by `$path` (including the separating period). If there is no
-// extension the empty string is output. See the [Go
-// documentation](https://pkg.go.dev/path/filepath#Ext) for more details.
-//
-// ```elvish-transcript
-// ~> path:ext hello.elv
-// ▶ .elv
-// ```
-
-//elvdoc:fn is-abs
-//
-// ```elvish
-// is-abs $path
-// ```
-//
-// Outputs `$true` if the path is an absolute path. Note that platforms like Windows have different
-// rules than UNIX like platforms for what constitutes an absolute path. See the [Go
-// documentation](https://pkg.go.dev/path/filepath#IsAbs) for more details.
-//
-// ```elvish-transcript
-// ~> path:is-abs hello.elv
-// ▶ false
-// ~> path:is-abs /hello.elv
-// ▶ true
-// ```
-
-//elvdoc:fn eval-symlinks
-//
-// ```elvish
-// eval-symlinks $path
-// ```
-//
-// Outputs `$path` after resolving any symbolic links. If `$path` is relative the result will be
-// relative to the current directory, unless one of the components is an absolute symbolic link.
-// This function calls `path:clean` on the result before outputting it. This is analogous to the
-// external `realpath` or `readlink` command found on many systems. See the [Go
-// documentation](https://pkg.go.dev/path/filepath#EvalSymlinks) for more details.
-//
-// ```elvish-transcript
-// ~> mkdir bin
-// ~> ln -s bin sbin
-// ~> path:eval-symlinks ./sbin/a_command
-// ▶ bin/a_command
-// ```
-
-//elvdoc:fn join
-//
-// ```elvish
-// path:join $path-component...
-// ```
-//
-// Joins any number of path elements into a single path, separating them with an
-// [OS specific separator](#path:separator). Empty elements are ignored. The
-// result is [cleaned](#path:clean). However, if the argument list is empty or
-// all its elements are empty, Join returns an empty string. On Windows, the
-// result will only be a UNC path if the first non-empty element is a UNC path.
-//
-// ```elvish-transcript
-// ~> path:join home user bin
-// ▶ home/user/bin
-// ~> path:join $path:separator home user bin
-// ▶ /home/user/bin
-// ```
-
-//elvdoc:fn is-dir
-//
-// ```elvish
-// is-dir &follow-symlink=$false $path
-// ```
-//
-// Outputs `$true` if the path resolves to a directory. If the final element of the path is a
-// symlink, even if it points to a directory, it still outputs `$false` since a symlink is not a
-// directory. Setting option `&follow-symlink` to true will cause the last element of the path, if
-// it is a symlink, to be resolved before doing the test.
-//
-// ```elvish-transcript
-// ~> touch not-a-dir
-// ~> path:is-dir not-a-dir
-// ▶ false
-// ~> path:is-dir /tmp
-// ▶ true
-// ```
-//
-// @cf path:is-regular
-
 type isOpts struct{ FollowSymlink bool }
 
 func (opts *isOpts) SetDefaultOptions() {}
@@ -206,30 +46,6 @@ func isDir(opts isOpts, path string) bool {
 	return err == nil && fi.Mode().IsDir()
 }
 
-//elvdoc:fn is-regular
-//
-// ```elvish
-// is-regular &follow-symlink=$false $path
-// ```
-//
-// Outputs `$true` if the path resolves to a regular file. If the final element of the path is a
-// symlink, even if it points to a regular file, it still outputs `$false` since a symlink is not a
-// regular file. Setting option `&follow-symlink` to true will cause the last element of the path,
-// if it is a symlink, to be resolved before doing the test.
-//
-// **Note:** This isn't named `is-file` because a UNIX file may be a "bag of bytes" or may be a
-// named pipe, device special file (e.g. `/dev/tty`), etc.
-//
-// ```elvish-transcript
-// ~> touch not-a-dir
-// ~> path:is-regular not-a-dir
-// ▶ true
-// ~> path:is-regular /tmp
-// ▶ false
-// ```
-//
-// @cf path:is-dir
-
 func isRegular(opts isOpts, path string) bool {
 	var fi os.FileInfo
 	var err error
@@ -241,36 +57,6 @@ func isRegular(opts isOpts, path string) bool {
 	return err == nil && fi.Mode().IsRegular()
 }
 
-//elvdoc:fn temp-dir
-//
-// ```elvish
-// temp-dir &dir='' $pattern?
-// ```
-//
-// Creates a new directory and outputs its name.
-//
-// The &dir option determines where the directory will be created; if it is an
-// empty string (the default), a system-dependent directory suitable for storing
-// temporary files will be used. The `$pattern` argument determines the name of
-// the directory, where the last star will be replaced by a random string; it
-// defaults to `elvish-*`.
-//
-// It is the caller's responsibility to remove the directory if it is intended
-// to be temporary.
-//
-// ```elvish-transcript
-// ~> path:temp-dir
-// ▶ /tmp/elvish-RANDOMSTR
-// ~> path:temp-dir x-
-// ▶ /tmp/x-RANDOMSTR
-// ~> path:temp-dir 'x-*.y'
-// ▶ /tmp/x-RANDOMSTR.y
-// ~> path:temp-dir &dir=.
-// ▶ elvish-RANDOMSTR
-// ~> path:temp-dir &dir=/some/dir
-// ▶ /some/dir/elvish-RANDOMSTR
-// ```
-
 type mktempOpt struct{ Dir string }
 
 func (o *mktempOpt) SetDefaultOptions() {}
@@ -290,46 +76,6 @@ func tempDir(opts mktempOpt, args ...string) (string, error) {
 	return os.MkdirTemp(opts.Dir, pattern)
 }
 
-//elvdoc:fn temp-file
-//
-// ```elvish
-// temp-file &dir='' $pattern?
-// ```
-//
-// Creates a new file and outputs a [file](language.html#file) object opened
-// for reading and writing.
-//
-// The &dir option determines where the file will be created; if it is an
-// empty string (the default), a system-dependent directory suitable for storing
-// temporary files will be used. The `$pattern` argument determines the name of
-// the file, where the last star will be replaced by a random string; it
-// defaults to `elvish-*`.
-//
-// It is the caller's responsibility to close the file with
-// [`file:close`](file.html#file:close). The caller should also remove the file
-// if it is intended to be temporary (with `rm $f[name]`).
-//
-// ```elvish-transcript
-// ~> var f = (path:temp-file)
-// ~> put $f[name]
-// ▶ /tmp/elvish-RANDOMSTR
-// ~> echo hello > $f
-// ~> cat $f[name]
-// hello
-// ~> var f = (path:temp-file x-)
-// ~> put $f[name]
-// ▶ /tmp/x-RANDOMSTR
-// ~> var f = (path:temp-file 'x-*.y')
-// ~> put $f[name]
-// ▶ /tmp/x-RANDOMSTR.y
-// ~> var f = (path:temp-file &dir=.)
-// ~> put $f[name]
-// ▶ elvish-RANDOMSTR
-// ~> var f = (path:temp-file &dir=/some/dir)
-// ~> put $f[name]
-// ▶ /some/dir/elvish-RANDOMSTR
-// ```
-
 func tempFile(opts mktempOpt, args ...string) (*os.File, error) {
 	var pattern string
 	switch len(args) {

+ 46 - 0
pkg/mods/platform/platform.d.elv

@@ -0,0 +1,46 @@
+#elvdoc:var arch
+#
+# The architecture of the platform; e.g. amd64, arm, ppc.
+# This corresponds to Go's
+# [`GOARCH`](https://pkg.go.dev/runtime?tab=doc#pkg-constants) constant.
+# This is read-only.
+
+#elvdoc:var os
+#
+# The name of the operating system; e.g. darwin (macOS), linux, etc.
+# This corresponds to Go's
+# [`GOOS`](https://pkg.go.dev/runtime?tab=doc#pkg-constants) constant.
+# This is read-only.
+
+#elvdoc:var is-unix
+#
+# Whether or not the platform is UNIX-like. This includes Linux, macOS
+# (Darwin), FreeBSD, NetBSD, and OpenBSD. This can be used to decide, for
+# example, if the `unix` module is usable.
+# This is read-only.
+
+#elvdoc:var is-windows
+#
+# Whether or not the platform is Microsoft Windows.
+# This is read-only.
+
+#elvdoc:fn hostname
+#
+# ```elvish
+# platform:hostname &strip-domain=$false
+# ```
+#
+# Outputs the hostname of the system. If the option `&strip-domain` is `$true`,
+# strips the part after the first dot.
+#
+# This function throws an exception if it cannot determine the hostname. It is
+# implemented using Go's [`os.Hostname`](https://golang.org/pkg/os/#Hostname).
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> platform:hostname
+# ▶ lothlorien.elv.sh
+# ~> platform:hostname &strip-domain=$true
+# ▶ lothlorien
+# ```

+ 0 - 47
pkg/mods/platform/platform.go

@@ -11,53 +11,6 @@ import (
 	"src.elv.sh/pkg/eval/vars"
 )
 
-//elvdoc:var arch
-//
-// The architecture of the platform; e.g. amd64, arm, ppc.
-// This corresponds to Go's
-// [`GOARCH`](https://pkg.go.dev/runtime?tab=doc#pkg-constants) constant.
-// This is read-only.
-
-//elvdoc:var os
-//
-// The name of the operating system; e.g. darwin (macOS), linux, etc.
-// This corresponds to Go's
-// [`GOOS`](https://pkg.go.dev/runtime?tab=doc#pkg-constants) constant.
-// This is read-only.
-
-//elvdoc:var is-unix
-//
-// Whether or not the platform is UNIX-like. This includes Linux, macOS
-// (Darwin), FreeBSD, NetBSD, and OpenBSD. This can be used to decide, for
-// example, if the `unix` module is usable.
-// This is read-only.
-
-//elvdoc:var is-windows
-//
-// Whether or not the platform is Microsoft Windows.
-// This is read-only.
-
-//elvdoc:fn hostname
-//
-// ```elvish
-// platform:hostname &strip-domain=$false
-// ```
-//
-// Outputs the hostname of the system. If the option `&strip-domain` is `$true`,
-// strips the part after the first dot.
-//
-// This function throws an exception if it cannot determine the hostname. It is
-// implemented using Go's [`os.Hostname`](https://golang.org/pkg/os/#Hostname).
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> platform:hostname
-// ▶ lothlorien.elv.sh
-// ~> platform:hostname &strip-domain=$true
-// ▶ lothlorien
-// ```
-
 var osHostname = os.Hostname // to allow mocking in unit tests
 
 type hostnameOpt struct{ StripDomain bool }

+ 115 - 0
pkg/mods/re/re.d.elv

@@ -0,0 +1,115 @@
+#elvdoc:fn quote
+#
+# ```elvish
+# re:quote $string
+# ```
+#
+# Quote `$string` for use in a pattern. Examples:
+#
+# ```elvish-transcript
+# ~> re:quote a.txt
+# ▶ a\.txt
+# ~> re:quote '(*)'
+# ▶ '\(\*\)'
+# ```
+
+#elvdoc:fn match
+#
+# ```elvish
+# re:match &posix=$false $pattern $source
+# ```
+#
+# Determine whether `$pattern` matches `$source`. The pattern is not anchored.
+# Examples:
+#
+# ```elvish-transcript
+# ~> re:match . xyz
+# ▶ $true
+# ~> re:match . ''
+# ▶ $false
+# ~> re:match '[a-z]' A
+# ▶ $false
+# ```
+
+#elvdoc:fn find
+#
+# ```elvish
+# re:find &posix=$false &longest=$false &max=-1 $pattern $source
+# ```
+#
+# Find all matches of `$pattern` in `$source`.
+#
+# Each match is represented by a map-like value `$m`; `$m[text]`, `$m[start]` and
+# `$m[end]` are the text, start and end positions (as byte indices into `$source`)
+# of the match; `$m[groups]` is a list of submatches for capture groups in the
+# pattern. A submatch has a similar structure to a match, except that it does not
+# have a `group` key. The entire pattern is an implicit capture group, and it
+# always appears first.
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> re:find . ab
+# ▶ [&text=a &start=0 &end=1 &groups=[[&text=a &start=0 &end=1]]]
+# ▶ [&text=b &start=1 &end=2 &groups=[[&text=b &start=1 &end=2]]]
+# ~> re:find '[A-Z]([0-9])' 'A1 B2'
+# ▶ [&text=A1 &start=0 &end=2 &groups=[[&text=A1 &start=0 &end=2] [&text=1 &start=1 &end=2]]]
+# ▶ [&text=B2 &start=3 &end=5 &groups=[[&text=B2 &start=3 &end=5] [&text=2 &start=4 &end=5]]]
+# ```
+
+#elvdoc:fn replace
+#
+# ```elvish
+# re:replace &posix=$false &longest=$false &literal=$false $pattern $repl $source
+# ```
+#
+# Replace all occurrences of `$pattern` in `$source` with `$repl`.
+#
+# The replacement `$repl` can be any of the following:
+#
+# -   A string-typed replacement template. The template can use `$name` or
+#     `${name}` patterns to refer to capture groups, where `name` consists of
+#     letters, digits and underscores. A purely numeric patterns like `$1`
+#     refers to the capture group with the corresponding index; other names
+#     refer to capture groups named with the `(?P<name>...)`) syntax.
+#
+#     In the `$name` form, the name is taken to be as long as possible; `$1` is
+#     equivalent to `${1x}`, not `${1}x`; `$10` is equivalent to `${10}`, not `${1}0`.
+#
+#     To insert a literal `$`, use `$$`.
+#
+# -   A function that takes a string argument and outputs a string. For each
+#     match, the function is called with the content of the match, and its output
+#     is used as the replacement.
+#
+# If `$literal` is true, `$repl` must be a string and is treated literally instead
+# of as a pattern.
+#
+# Example:
+#
+# ```elvish-transcript
+# ~> re:replace '(ba|z)sh' '${1}SH' 'bash and zsh'
+# ▶ 'baSH and zSH'
+# ~> re:replace '(ba|z)sh' elvish 'bash and zsh rock'
+# ▶ 'elvish and elvish rock'
+# ~> re:replace '(ba|z)sh' {|x| put [&bash=BaSh &zsh=ZsH][$x] } 'bash and zsh'
+# ▶ 'BaSh and ZsH'
+# ```
+
+#elvdoc:fn split
+#
+# ```elvish
+# re:split &posix=$false &longest=$false &max=-1 $pattern $source
+# ```
+#
+# Split `$source`, using `$pattern` as separators. Examples:
+#
+# ```elvish-transcript
+# ~> re:split : /usr/sbin:/usr/bin:/bin
+# ▶ /usr/sbin
+# ▶ /usr/bin
+# ▶ /bin
+# ~> re:split &max=2 : /usr/sbin:/usr/bin:/bin
+# ▶ /usr/sbin
+# ▶ /usr/bin:/bin
+# ```

+ 0 - 116
pkg/mods/re/re.go

@@ -19,39 +19,6 @@ var Ns = eval.BuildNsNamed("re").
 		"split":   split,
 	}).Ns()
 
-//elvdoc:fn quote
-//
-// ```elvish
-// re:quote $string
-// ```
-//
-// Quote `$string` for use in a pattern. Examples:
-//
-// ```elvish-transcript
-// ~> re:quote a.txt
-// ▶ a\.txt
-// ~> re:quote '(*)'
-// ▶ '\(\*\)'
-// ```
-
-//elvdoc:fn match
-//
-// ```elvish
-// re:match &posix=$false $pattern $source
-// ```
-//
-// Determine whether `$pattern` matches `$source`. The pattern is not anchored.
-// Examples:
-//
-// ```elvish-transcript
-// ~> re:match . xyz
-// ▶ $true
-// ~> re:match . ''
-// ▶ $false
-// ~> re:match '[a-z]' A
-// ▶ $false
-// ```
-
 type matchOpts struct{ Posix bool }
 
 func (*matchOpts) SetDefaultOptions() {}
@@ -64,32 +31,6 @@ func match(opts matchOpts, argPattern, source string) (bool, error) {
 	return pattern.MatchString(source), nil
 }
 
-//elvdoc:fn find
-//
-// ```elvish
-// re:find &posix=$false &longest=$false &max=-1 $pattern $source
-// ```
-//
-// Find all matches of `$pattern` in `$source`.
-//
-// Each match is represented by a map-like value `$m`; `$m[text]`, `$m[start]` and
-// `$m[end]` are the text, start and end positions (as byte indices into `$source`)
-// of the match; `$m[groups]` is a list of submatches for capture groups in the
-// pattern. A submatch has a similar structure to a match, except that it does not
-// have a `group` key. The entire pattern is an implicit capture group, and it
-// always appears first.
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> re:find . ab
-// ▶ [&text=a &start=0 &end=1 &groups=[[&text=a &start=0 &end=1]]]
-// ▶ [&text=b &start=1 &end=2 &groups=[[&text=b &start=1 &end=2]]]
-// ~> re:find '[A-Z]([0-9])' 'A1 B2'
-// ▶ [&text=A1 &start=0 &end=2 &groups=[[&text=A1 &start=0 &end=2] [&text=1 &start=1 &end=2]]]
-// ▶ [&text=B2 &start=3 &end=5 &groups=[[&text=B2 &start=3 &end=5] [&text=2 &start=4 &end=5]]]
-// ```
-
 // Struct for holding options to find. Also used by split.
 type findOpts struct {
 	Posix   bool
@@ -129,45 +70,6 @@ func find(fm *eval.Frame, opts findOpts, argPattern, source string) error {
 	return nil
 }
 
-//elvdoc:fn replace
-//
-// ```elvish
-// re:replace &posix=$false &longest=$false &literal=$false $pattern $repl $source
-// ```
-//
-// Replace all occurrences of `$pattern` in `$source` with `$repl`.
-//
-// The replacement `$repl` can be any of the following:
-//
-// -   A string-typed replacement template. The template can use `$name` or
-//     `${name}` patterns to refer to capture groups, where `name` consists of
-//     letters, digits and underscores. A purely numeric patterns like `$1`
-//     refers to the capture group with the corresponding index; other names
-//     refer to capture groups named with the `(?P<name>...)`) syntax.
-//
-//     In the `$name` form, the name is taken to be as long as possible; `$1` is
-//     equivalent to `${1x}`, not `${1}x`; `$10` is equivalent to `${10}`, not `${1}0`.
-//
-//     To insert a literal `$`, use `$$`.
-//
-// -   A function that takes a string argument and outputs a string. For each
-//     match, the function is called with the content of the match, and its output
-//     is used as the replacement.
-//
-// If `$literal` is true, `$repl` must be a string and is treated literally instead
-// of as a pattern.
-//
-// Example:
-//
-// ```elvish-transcript
-// ~> re:replace '(ba|z)sh' '${1}SH' 'bash and zsh'
-// ▶ 'baSH and zSH'
-// ~> re:replace '(ba|z)sh' elvish 'bash and zsh rock'
-// ▶ 'elvish and elvish rock'
-// ~> re:replace '(ba|z)sh' {|x| put [&bash=BaSh &zsh=ZsH][$x] } 'bash and zsh'
-// ▶ 'BaSh and ZsH'
-// ```
-
 type replaceOpts struct {
 	Posix   bool
 	Longest bool
@@ -227,24 +129,6 @@ func replace(fm *eval.Frame, opts replaceOpts, argPattern string, argRepl any, s
 	}
 }
 
-//elvdoc:fn split
-//
-// ```elvish
-// re:split &posix=$false &longest=$false &max=-1 $pattern $source
-// ```
-//
-// Split `$source`, using `$pattern` as separators. Examples:
-//
-// ```elvish-transcript
-// ~> re:split : /usr/sbin:/usr/bin:/bin
-// ▶ /usr/sbin
-// ▶ /usr/bin
-// ▶ /bin
-// ~> re:split &max=2 : /usr/sbin:/usr/bin:/bin
-// ▶ /usr/sbin
-// ▶ /usr/bin:/bin
-// ```
-
 func split(fm *eval.Frame, opts findOpts, argPattern, source string) error {
 	out := fm.ValueOutput()
 

+ 44 - 0
pkg/mods/runtime/runtime.d.elv

@@ -0,0 +1,44 @@
+#elvdoc:var lib-dirs
+#
+# A list containing
+# [module search directories](command.html#module-search-directories).
+#
+# This variable is read-only.
+
+#elvdoc:var rc-path
+#
+# Path to the [RC file](command.html#rc-file), ignoring any possible overrides
+# by command-line flags and available in non-interactive mode.
+#
+# If there was an error in determining the path of the RC file, this variable
+# is `$nil`.
+#
+# This variable is read-only.
+#
+# @cf $runtime:effective-rc-path
+
+#elvdoc:var effective-rc-path
+#
+# Path to the [RC path](command.html#rc-file) that is actually used for this
+# Elvish session:
+#
+# - If Elvish is running non-interactively or invoked with the `-norc` flag,
+#   this variable is `$nil`.
+#
+# - If Elvish is invoked with the `-rc` flag, this variable contains the
+#   absolute path of the argument.
+#
+# - Otherwise (when Elvish is running interactively and invoked without
+#   `-norc` or `-rc`), this variable has the same value as `$rc-path`.
+#
+# This variable is read-only.
+#
+# @cf $runtime:rc-path
+
+#elvdoc:var elvish-path
+#
+# The path to the Elvish binary.
+#
+# If there was an error in determining the path, this variable is `$nil`.
+#
+# This variable is read-only.

+ 0 - 45
pkg/mods/runtime/runtime.go

@@ -9,51 +9,6 @@ import (
 	"src.elv.sh/pkg/eval/vars"
 )
 
-//elvdoc:var lib-dirs
-//
-// A list containing
-// [module search directories](command.html#module-search-directories).
-//
-// This variable is read-only.
-
-//elvdoc:var rc-path
-//
-// Path to the [RC file](command.html#rc-file), ignoring any possible overrides
-// by command-line flags and available in non-interactive mode.
-//
-// If there was an error in determining the path of the RC file, this variable
-// is `$nil`.
-//
-// This variable is read-only.
-//
-// @cf $runtime:effective-rc-path
-
-//elvdoc:var effective-rc-path
-//
-// Path to the [RC path](command.html#rc-file) that is actually used for this
-// Elvish session:
-//
-// - If Elvish is running non-interactively or invoked with the `-norc` flag,
-//   this variable is `$nil`.
-//
-// - If Elvish is invoked with the `-rc` flag, this variable contains the
-//   absolute path of the argument.
-//
-// - Otherwise (when Elvish is running interactively and invoked without
-//   `-norc` or `-rc`), this variable has the same value as `$rc-path`.
-//
-// This variable is read-only.
-//
-// @cf $runtime:rc-path
-
-//elvdoc:var elvish-path
-//
-// The path to the Elvish binary.
-//
-// If there was an error in determining the path, this variable is `$nil`.
-//
-// This variable is read-only.
-
 var osExecutable = os.Executable
 
 // Ns returns the namespace for the runtime: module.

+ 79 - 0
pkg/mods/store/store.d.elv

@@ -0,0 +1,79 @@
+#elvdoc:fn next-cmd-seq
+#
+# ```elvish
+# store:next-cmd-seq
+# ```
+#
+# Outputs the sequence number that will be used for the next entry of the
+# command history.
+
+#elvdoc:fn add-cmd
+#
+# ```elvish
+# store:add-cmd $text
+# ```
+#
+# Adds an entry to the command history with the given content. Outputs its
+# sequence number.
+
+#elvdoc:fn del-cmd
+#
+# ```elvish
+# store:del-cmd $seq
+# ```
+#
+# Deletes the command history entry with the given sequence number.
+#
+# **NOTE**: This command only deletes the entry from the persistent store. When
+# deleting an entry that was added in the current session, the deletion will
+# not take effect for the current session, since the entry still exists in the
+# in-memory per-session history.
+
+#elvdoc:fn cmd
+#
+# ```elvish
+# store:cmd $seq
+# ```
+#
+# Outputs the content of the command history entry with the given sequence
+# number.
+
+#elvdoc:fn cmds
+#
+# ```elvish
+# store:cmds $from $upto
+# ```
+#
+# Outputs all command history entries with sequence numbers between `$from`
+# (inclusive) and `$upto` (exclusive). Use -1 for `$upto` to not set an upper
+# bound.
+#
+# Each entry is represented by a pseudo-map with fields `text` and `seq`.
+
+#elvdoc:fn add-dir
+#
+# ```elvish
+# store:add-dir $path
+# ```
+#
+# Adds a path to the directory history. This will also cause the scores of all
+# other directories to decrease.
+
+#elvdoc:fn del-dir
+#
+# ```elvish
+# store:del-dir $path
+# ```
+#
+# Deletes a path from the directory history. This has no impact on the scores
+# of other directories.
+
+#elvdoc:fn dirs
+#
+# ```elvish
+# store:dirs
+# ```
+#
+# Outputs all directory history entries, in decreasing order of score.
+#
+# Each entry is represented by a pseudo-map with fields `path` and `score`.

+ 0 - 80
pkg/mods/store/store.go

@@ -5,86 +5,6 @@ import (
 	"src.elv.sh/pkg/store/storedefs"
 )
 
-//elvdoc:fn next-cmd-seq
-//
-// ```elvish
-// store:next-cmd-seq
-// ```
-//
-// Outputs the sequence number that will be used for the next entry of the
-// command history.
-
-//elvdoc:fn add-cmd
-//
-// ```elvish
-// store:add-cmd $text
-// ```
-//
-// Adds an entry to the command history with the given content. Outputs its
-// sequence number.
-
-//elvdoc:fn del-cmd
-//
-// ```elvish
-// store:del-cmd $seq
-// ```
-//
-// Deletes the command history entry with the given sequence number.
-//
-// **NOTE**: This command only deletes the entry from the persistent store. When
-// deleting an entry that was added in the current session, the deletion will
-// not take effect for the current session, since the entry still exists in the
-// in-memory per-session history.
-
-//elvdoc:fn cmd
-//
-// ```elvish
-// store:cmd $seq
-// ```
-//
-// Outputs the content of the command history entry with the given sequence
-// number.
-
-//elvdoc:fn cmds
-//
-// ```elvish
-// store:cmds $from $upto
-// ```
-//
-// Outputs all command history entries with sequence numbers between `$from`
-// (inclusive) and `$upto` (exclusive). Use -1 for `$upto` to not set an upper
-// bound.
-//
-// Each entry is represented by a pseudo-map with fields `text` and `seq`.
-
-//elvdoc:fn add-dir
-//
-// ```elvish
-// store:add-dir $path
-// ```
-//
-// Adds a path to the directory history. This will also cause the scores of all
-// other directories to decrease.
-
-//elvdoc:fn del-dir
-//
-// ```elvish
-// store:del-dir $path
-// ```
-//
-// Deletes a path from the directory history. This has no impact on the scores
-// of other directories.
-
-//elvdoc:fn dirs
-//
-// ```elvish
-// store:dirs
-// ```
-//
-// Outputs all directory history entries, in decreasing order of score.
-//
-// Each entry is represented by a pseudo-map with fields `path` and `score`.
-
 func Ns(s storedefs.Store) *eval.Ns {
 	return eval.BuildNsNamed("store").
 		AddGoFns(map[string]any{

+ 452 - 0
pkg/mods/str/str.d.elv

@@ -0,0 +1,452 @@
+#elvdoc:fn compare
+#
+# ```elvish
+# str:compare $a $b
+# ```
+#
+# Compares two strings and output an integer that will be 0 if a == b,
+# -1 if a < b, and +1 if a > b.
+#
+# ```elvish-transcript
+# ~> str:compare a a
+# ▶ 0
+# ~> str:compare a b
+# ▶ -1
+# ~> str:compare b a
+# ▶ 1
+# ```
+
+#elvdoc:fn contains
+#
+# ```elvish
+# str:contains $str $substr
+# ```
+#
+# Outputs whether `$str` contains `$substr` as a substring.
+#
+# ```elvish-transcript
+# ~> str:contains abcd x
+# ▶ $false
+# ~> str:contains abcd bc
+# ▶ $true
+# ```
+
+#elvdoc:fn contains-any
+#
+# ```elvish
+# str:contains-any $str $chars
+# ```
+#
+# Outputs whether `$str` contains any Unicode code points in `$chars`.
+#
+# ```elvish-transcript
+# ~> str:contains-any abcd x
+# ▶ $false
+# ~> str:contains-any abcd xby
+# ▶ $true
+# ```
+
+#elvdoc:fn count
+#
+# ```elvish
+# str:count $str $substr
+# ```
+#
+# Outputs the number of non-overlapping instances of `$substr` in `$s`.
+# If `$substr` is an empty string, output 1 + the number of Unicode code
+# points in `$s`.
+#
+# ```elvish-transcript
+# ~> str:count abcdefabcdef bc
+# ▶ 2
+# ~> str:count abcdef ''
+# ▶ 7
+# ```
+
+#elvdoc:fn equal-fold
+#
+# ```elvish
+# str:equal-fold $str1 $str2
+# ```
+#
+# Outputs if `$str1` and `$str2`, interpreted as UTF-8 strings, are equal
+# under Unicode case-folding.
+#
+# ```elvish-transcript
+# ~> str:equal-fold ABC abc
+# ▶ $true
+# ~> str:equal-fold abc ab
+# ▶ $false
+# ```
+
+#elvdoc:fn from-codepoints
+#
+# ```elvish
+# str:from-codepoints $number...
+# ```
+#
+# Outputs a string consisting of the given Unicode codepoints. Example:
+#
+# ```elvish-transcript
+# ~> str:from-codepoints 0x61
+# ▶ a
+# ~> str:from-codepoints 0x4f60 0x597d
+# ▶ 你好
+# ```
+#
+# @cf str:to-codepoints
+
+#elvdoc:fn from-utf8-bytes
+#
+# ```elvish
+# str:from-utf8-bytes $number...
+# ```
+#
+# Outputs a string consisting of the given Unicode bytes. Example:
+#
+# ```elvish-transcript
+# ~> str:from-utf8-bytes 0x61
+# ▶ a
+# ~> str:from-utf8-bytes 0xe4 0xbd 0xa0 0xe5 0xa5 0xbd
+# ▶ 你好
+# ```
+#
+# @cf str:to-utf8-bytes
+
+#elvdoc:fn has-prefix
+#
+# ```elvish
+# str:has-prefix $str $prefix
+# ```
+#
+# Outputs if `$str` begins with `$prefix`.
+#
+# ```elvish-transcript
+# ~> str:has-prefix abc ab
+# ▶ $true
+# ~> str:has-prefix abc bc
+# ▶ $false
+# ```
+
+#elvdoc:fn has-suffix
+#
+# ```elvish
+# str:has-suffix $str $suffix
+# ```
+#
+# Outputs if `$str` ends with `$suffix`.
+#
+# ```elvish-transcript
+# ~> str:has-suffix abc ab
+# ▶ $false
+# ~> str:has-suffix abc bc
+# ▶ $true
+# ```
+
+#elvdoc:fn index
+#
+# ```elvish
+# str:index $str $substr
+# ```
+#
+# Outputs the index of the first instance of `$substr` in `$str`, or -1
+# if `$substr` is not present in `$str`.
+#
+# ```elvish-transcript
+# ~> str:index abcd cd
+# ▶ 2
+# ~> str:index abcd xyz
+# ▶ -1
+# ```
+
+#elvdoc:fn index-any
+#
+# ```elvish
+# str:index-any $str $chars
+# ```
+#
+# Outputs the index of the first instance of any Unicode code point
+# from `$chars` in `$str`, or -1 if no Unicode code point from `$chars` is
+# present in `$str`.
+#
+# ```elvish-transcript
+# ~> str:index-any "chicken" "aeiouy"
+# ▶ 2
+# ~> str:index-any l33t aeiouy
+# ▶ -1
+# ```
+
+#elvdoc:fn join
+#
+# ```elvish
+# str:join $sep $input-list?
+# ```
+#
+# Joins inputs with `$sep`. Examples:
+#
+# ```elvish-transcript
+# ~> put lorem ipsum | str:join ,
+# ▶ lorem,ipsum
+# ~> str:join , [lorem ipsum]
+# ▶ lorem,ipsum
+# ~> str:join '' [lorem ipsum]
+# ▶ loremipsum
+# ~> str:join '...' [lorem ipsum]
+# ▶ lorem...ipsum
+# ```
+#
+# Etymology: Various languages,
+# [Python](https://docs.python.org/3.6/library/stdtypes.html#str.join).
+#
+# @cf str:split
+
+#elvdoc:fn last-index
+#
+# ```elvish
+# str:last-index $str $substr
+# ```
+#
+# Outputs the index of the last instance of `$substr` in `$str`,
+# or -1 if `$substr` is not present in `$str`.
+#
+# ```elvish-transcript
+# ~> str:last-index "elven speak elvish" elv
+# ▶ 12
+# ~> str:last-index "elven speak elvish" romulan
+# ▶ -1
+# ```
+
+#elvdoc:fn replace
+#
+# ```elvish
+# str:replace &max=-1 $old $repl $source
+# ```
+#
+# Replaces all occurrences of `$old` with `$repl` in `$source`. If `$max` is
+# non-negative, it determines the max number of substitutions.
+#
+# **Note**: This command does not support searching by regular expressions, `$old`
+# is always interpreted as a plain string. Use [re:replace](re.html#re:replace) if
+# you need to search by regex.
+
+#elvdoc:fn split
+#
+# ```elvish
+# str:split &max=-1 $sep $string
+# ```
+#
+# Splits `$string` by `$sep`. If `$sep` is an empty string, split it into
+# codepoints.
+#
+# If the `&max` option is non-negative, stops after producing the maximum
+# number of results.
+#
+# ```elvish-transcript
+# ~> str:split , lorem,ipsum
+# ▶ lorem
+# ▶ ipsum
+# ~> str:split '' 你好
+# ▶ 你
+# ▶ 好
+# ~> str:split &max=2 ' ' 'a b c d'
+# ▶ a
+# ▶ 'b c d'
+# ```
+#
+# **Note**: This command does not support splitting by regular expressions,
+# `$sep` is always interpreted as a plain string. Use [re:split](re.html#re:split)
+# if you need to split by regex.
+#
+# Etymology: Various languages, in particular
+# [Python](https://docs.python.org/3.6/library/stdtypes.html#str.split).
+#
+# @cf str:join
+
+#elvdoc:fn title
+#
+# ```elvish
+# str:title $str
+# ```
+#
+# Outputs `$str` with all Unicode letters that begin words mapped to their
+# Unicode title case.
+#
+# ```elvish-transcript
+# ~> str:title "her royal highness"
+# ▶ Her Royal Highness
+# ```
+
+#elvdoc:fn to-codepoints
+#
+# ```elvish
+# str:to-codepoints $string
+# ```
+#
+# Outputs value of each codepoint in `$string`, in hexadecimal. Examples:
+#
+# ```elvish-transcript
+# ~> str:to-codepoints a
+# ▶ 0x61
+# ~> str:to-codepoints 你好
+# ▶ 0x4f60
+# ▶ 0x597d
+# ```
+#
+# The output format is subject to change.
+#
+# @cf str:from-codepoints
+
+#elvdoc:fn to-lower
+#
+# ```elvish
+# str:to-lower $str
+# ```
+#
+# Outputs `$str` with all Unicode letters mapped to their lower-case
+# equivalent.
+#
+# ```elvish-transcript
+# ~> str:to-lower 'ABC!123'
+# ▶ abc!123
+# ```
+
+#elvdoc:fn to-utf8-bytes
+#
+# ```elvish
+# str:to-utf8-bytes $string
+# ```
+#
+# Outputs value of each byte in `$string`, in hexadecimal. Examples:
+#
+# ```elvish-transcript
+# ~> str:to-utf8-bytes a
+# ▶ 0x61
+# ~> str:to-utf8-bytes 你好
+# ▶ 0xe4
+# ▶ 0xbd
+# ▶ 0xa0
+# ▶ 0xe5
+# ▶ 0xa5
+# ▶ 0xbd
+# ```
+#
+# The output format is subject to change.
+#
+# @cf str:from-utf8-bytes
+
+#elvdoc:fn to-title
+#
+# ```elvish
+# str:to-title $str
+# ```
+#
+# Outputs `$str` with all Unicode letters mapped to their Unicode title case.
+#
+# ```elvish-transcript
+# ~> str:to-title "her royal highness"
+# ▶ HER ROYAL HIGHNESS
+# ~> str:to-title "хлеб"
+# ▶ ХЛЕБ
+# ```
+
+#elvdoc:fn to-upper
+#
+# ```elvish
+# str:to-upper
+# ```
+#
+# Outputs `$str` with all Unicode letters mapped to their upper-case
+# equivalent.
+#
+# ```elvish-transcript
+# ~> str:to-upper 'abc!123'
+# ▶ ABC!123
+# ```
+
+#elvdoc:fn trim
+#
+# ```elvish
+# str:trim $str $cutset
+# ```
+#
+# Outputs `$str` with all leading and trailing Unicode code points contained
+# in `$cutset` removed.
+#
+# ```elvish-transcript
+# ~> str:trim "¡¡¡Hello, Elven!!!" "!¡"
+# ▶ 'Hello, Elven'
+# ```
+
+#elvdoc:fn trim-left
+#
+# ```elvish
+# str:trim-left $str $cutset
+# ```
+#
+# Outputs `$str` with all leading Unicode code points contained in `$cutset`
+# removed. To remove a prefix string use [`str:trim-prefix`](#str:trim-prefix).
+#
+# ```elvish-transcript
+# ~> str:trim-left "¡¡¡Hello, Elven!!!" "!¡"
+# ▶ 'Hello, Elven!!!'
+# ```
+
+#elvdoc:fn trim-prefix
+#
+# ```elvish
+# str:trim-prefix $str $prefix
+# ```
+#
+# Outputs `$str` minus the leading `$prefix` string. If `$str` doesn't begin
+# with `$prefix`, `$str` is output unchanged.
+#
+# ```elvish-transcript
+# ~> str:trim-prefix "¡¡¡Hello, Elven!!!" "¡¡¡Hello, "
+# ▶ Elven!!!
+# ~> str:trim-prefix "¡¡¡Hello, Elven!!!" "¡¡¡Hola, "
+# ▶ '¡¡¡Hello, Elven!!!'
+# ```
+
+#elvdoc:fn trim-right
+#
+# ```elvish
+# str:trim-right $str $cutset
+# ```
+#
+# Outputs `$str` with all leading Unicode code points contained in `$cutset`
+# removed. To remove a suffix string use [`str:trim-suffix`](#str:trim-suffix).
+#
+# ```elvish-transcript
+# ~> str:trim-right "¡¡¡Hello, Elven!!!" "!¡"
+# ▶ '¡¡¡Hello, Elven'
+# ```
+
+#elvdoc:fn trim-space
+#
+# ```elvish
+# str:trim-space $str
+# ```
+#
+# Outputs `$str` with all leading and trailing white space removed as defined
+# by Unicode.
+#
+# ```elvish-transcript
+# ~> str:trim-space " \t\n Hello, Elven \n\t\r\n"
+# ▶ 'Hello, Elven'
+# ```
+
+#elvdoc:fn trim-suffix
+#
+# ```elvish
+# str:trim-suffix $str $suffix
+# ```
+#
+# Outputs `$str` minus the trailing `$suffix` string. If `$str` doesn't end
+# with `$suffix`, `$str` is output unchanged.
+#
+# ```elvish-transcript
+# ~> str:trim-suffix "¡¡¡Hello, Elven!!!" ", Elven!!!"
+# ▶ ¡¡¡Hello
+# ~> str:trim-suffix "¡¡¡Hello, Elven!!!" ", Klingons!!!"
+# ▶ '¡¡¡Hello, Elven!!!'
+# ```

+ 0 - 453
pkg/mods/str/str.go

@@ -54,104 +54,6 @@ var Ns = eval.BuildNsNamed("str").
 		"trim-suffix": strings.TrimSuffix,
 	}).Ns()
 
-//elvdoc:fn compare
-//
-// ```elvish
-// str:compare $a $b
-// ```
-//
-// Compares two strings and output an integer that will be 0 if a == b,
-// -1 if a < b, and +1 if a > b.
-//
-// ```elvish-transcript
-// ~> str:compare a a
-// ▶ 0
-// ~> str:compare a b
-// ▶ -1
-// ~> str:compare b a
-// ▶ 1
-// ```
-
-//elvdoc:fn contains
-//
-// ```elvish
-// str:contains $str $substr
-// ```
-//
-// Outputs whether `$str` contains `$substr` as a substring.
-//
-// ```elvish-transcript
-// ~> str:contains abcd x
-// ▶ $false
-// ~> str:contains abcd bc
-// ▶ $true
-// ```
-
-//elvdoc:fn contains-any
-//
-// ```elvish
-// str:contains-any $str $chars
-// ```
-//
-// Outputs whether `$str` contains any Unicode code points in `$chars`.
-//
-// ```elvish-transcript
-// ~> str:contains-any abcd x
-// ▶ $false
-// ~> str:contains-any abcd xby
-// ▶ $true
-// ```
-
-//elvdoc:fn count
-//
-// ```elvish
-// str:count $str $substr
-// ```
-//
-// Outputs the number of non-overlapping instances of `$substr` in `$s`.
-// If `$substr` is an empty string, output 1 + the number of Unicode code
-// points in `$s`.
-//
-// ```elvish-transcript
-// ~> str:count abcdefabcdef bc
-// ▶ 2
-// ~> str:count abcdef ''
-// ▶ 7
-// ```
-
-//elvdoc:fn equal-fold
-//
-// ```elvish
-// str:equal-fold $str1 $str2
-// ```
-//
-// Outputs if `$str1` and `$str2`, interpreted as UTF-8 strings, are equal
-// under Unicode case-folding.
-//
-// ```elvish-transcript
-// ~> str:equal-fold ABC abc
-// ▶ $true
-// ~> str:equal-fold abc ab
-// ▶ $false
-// ```
-
-//elvdoc:fn from-codepoints
-//
-// ```elvish
-// str:from-codepoints $number...
-// ```
-//
-// Outputs a string consisting of the given Unicode codepoints. Example:
-//
-// ```elvish-transcript
-// ~> str:from-codepoints 0x61
-// ▶ a
-// ~> str:from-codepoints 0x4f60 0x597d
-// ▶ 你好
-// ```
-//
-// @cf str:to-codepoints
-
 func fromCodepoints(nums ...int) (string, error) {
 	var b bytes.Buffer
 	for _, num := range nums {
@@ -181,23 +83,6 @@ func hex(i int) string {
 	return "0x" + strconv.FormatInt(int64(i), 16)
 }
 
-//elvdoc:fn from-utf8-bytes
-//
-// ```elvish
-// str:from-utf8-bytes $number...
-// ```
-//
-// Outputs a string consisting of the given Unicode bytes. Example:
-//
-// ```elvish-transcript
-// ~> str:from-utf8-bytes 0x61
-// ▶ a
-// ~> str:from-utf8-bytes 0xe4 0xbd 0xa0 0xe5 0xa5 0xbd
-// ▶ 你好
-// ```
-//
-// @cf str:to-utf8-bytes
-
 func fromUtf8Bytes(nums ...int) (string, error) {
 	var b bytes.Buffer
 	for _, num := range nums {
@@ -218,93 +103,6 @@ func fromUtf8Bytes(nums ...int) (string, error) {
 	return b.String(), nil
 }
 
-//elvdoc:fn has-prefix
-//
-// ```elvish
-// str:has-prefix $str $prefix
-// ```
-//
-// Outputs if `$str` begins with `$prefix`.
-//
-// ```elvish-transcript
-// ~> str:has-prefix abc ab
-// ▶ $true
-// ~> str:has-prefix abc bc
-// ▶ $false
-// ```
-
-//elvdoc:fn has-suffix
-//
-// ```elvish
-// str:has-suffix $str $suffix
-// ```
-//
-// Outputs if `$str` ends with `$suffix`.
-//
-// ```elvish-transcript
-// ~> str:has-suffix abc ab
-// ▶ $false
-// ~> str:has-suffix abc bc
-// ▶ $true
-// ```
-
-//elvdoc:fn index
-//
-// ```elvish
-// str:index $str $substr
-// ```
-//
-// Outputs the index of the first instance of `$substr` in `$str`, or -1
-// if `$substr` is not present in `$str`.
-//
-// ```elvish-transcript
-// ~> str:index abcd cd
-// ▶ 2
-// ~> str:index abcd xyz
-// ▶ -1
-// ```
-
-//elvdoc:fn index-any
-//
-// ```elvish
-// str:index-any $str $chars
-// ```
-//
-// Outputs the index of the first instance of any Unicode code point
-// from `$chars` in `$str`, or -1 if no Unicode code point from `$chars` is
-// present in `$str`.
-//
-// ```elvish-transcript
-// ~> str:index-any "chicken" "aeiouy"
-// ▶ 2
-// ~> str:index-any l33t aeiouy
-// ▶ -1
-// ```
-
-//elvdoc:fn join
-//
-// ```elvish
-// str:join $sep $input-list?
-// ```
-//
-// Joins inputs with `$sep`. Examples:
-//
-// ```elvish-transcript
-// ~> put lorem ipsum | str:join ,
-// ▶ lorem,ipsum
-// ~> str:join , [lorem ipsum]
-// ▶ lorem,ipsum
-// ~> str:join '' [lorem ipsum]
-// ▶ loremipsum
-// ~> str:join '...' [lorem ipsum]
-// ▶ lorem...ipsum
-// ```
-//
-// Etymology: Various languages,
-// [Python](https://docs.python.org/3.6/library/stdtypes.html#str.join).
-//
-// @cf str:split
-
 func join(sep string, inputs eval.Inputs) (string, error) {
 	var buf bytes.Buffer
 	var errJoin error
@@ -328,35 +126,6 @@ func join(sep string, inputs eval.Inputs) (string, error) {
 	return buf.String(), errJoin
 }
 
-//elvdoc:fn last-index
-//
-// ```elvish
-// str:last-index $str $substr
-// ```
-//
-// Outputs the index of the last instance of `$substr` in `$str`,
-// or -1 if `$substr` is not present in `$str`.
-//
-// ```elvish-transcript
-// ~> str:last-index "elven speak elvish" elv
-// ▶ 12
-// ~> str:last-index "elven speak elvish" romulan
-// ▶ -1
-// ```
-
-//elvdoc:fn replace
-//
-// ```elvish
-// str:replace &max=-1 $old $repl $source
-// ```
-//
-// Replaces all occurrences of `$old` with `$repl` in `$source`. If `$max` is
-// non-negative, it determines the max number of substitutions.
-//
-// **Note**: This command does not support searching by regular expressions, `$old`
-// is always interpreted as a plain string. Use [re:replace](re.html#re:replace) if
-// you need to search by regex.
-
 type maxOpt struct{ Max int }
 
 func (o *maxOpt) SetDefaultOptions() { o.Max = -1 }
@@ -365,39 +134,6 @@ func replace(opts maxOpt, old, repl, s string) string {
 	return strings.Replace(s, old, repl, opts.Max)
 }
 
-//elvdoc:fn split
-//
-// ```elvish
-// str:split &max=-1 $sep $string
-// ```
-//
-// Splits `$string` by `$sep`. If `$sep` is an empty string, split it into
-// codepoints.
-//
-// If the `&max` option is non-negative, stops after producing the maximum
-// number of results.
-//
-// ```elvish-transcript
-// ~> str:split , lorem,ipsum
-// ▶ lorem
-// ▶ ipsum
-// ~> str:split '' 你好
-// ▶ 你
-// ▶ 好
-// ~> str:split &max=2 ' ' 'a b c d'
-// ▶ a
-// ▶ 'b c d'
-// ```
-//
-// **Note**: This command does not support splitting by regular expressions,
-// `$sep` is always interpreted as a plain string. Use [re:split](re.html#re:split)
-// if you need to split by regex.
-//
-// Etymology: Various languages, in particular
-// [Python](https://docs.python.org/3.6/library/stdtypes.html#str.split).
-//
-// @cf str:join
-
 func split(fm *eval.Frame, opts maxOpt, sep, s string) error {
 	out := fm.ValueOutput()
 	parts := strings.SplitN(s, sep, opts.Max)
@@ -410,40 +146,6 @@ func split(fm *eval.Frame, opts maxOpt, sep, s string) error {
 	return nil
 }
 
-//elvdoc:fn title
-//
-// ```elvish
-// str:title $str
-// ```
-//
-// Outputs `$str` with all Unicode letters that begin words mapped to their
-// Unicode title case.
-//
-// ```elvish-transcript
-// ~> str:title "her royal highness"
-// ▶ Her Royal Highness
-// ```
-
-//elvdoc:fn to-codepoints
-//
-// ```elvish
-// str:to-codepoints $string
-// ```
-//
-// Outputs value of each codepoint in `$string`, in hexadecimal. Examples:
-//
-// ```elvish-transcript
-// ~> str:to-codepoints a
-// ▶ 0x61
-// ~> str:to-codepoints 你好
-// ▶ 0x4f60
-// ▶ 0x597d
-// ```
-//
-// The output format is subject to change.
-//
-// @cf str:from-codepoints
-
 func toCodepoints(fm *eval.Frame, s string) error {
 	out := fm.ValueOutput()
 	for _, r := range s {
@@ -455,44 +157,6 @@ func toCodepoints(fm *eval.Frame, s string) error {
 	return nil
 }
 
-//elvdoc:fn to-lower
-//
-// ```elvish
-// str:to-lower $str
-// ```
-//
-// Outputs `$str` with all Unicode letters mapped to their lower-case
-// equivalent.
-//
-// ```elvish-transcript
-// ~> str:to-lower 'ABC!123'
-// ▶ abc!123
-// ```
-
-//elvdoc:fn to-utf8-bytes
-//
-// ```elvish
-// str:to-utf8-bytes $string
-// ```
-//
-// Outputs value of each byte in `$string`, in hexadecimal. Examples:
-//
-// ```elvish-transcript
-// ~> str:to-utf8-bytes a
-// ▶ 0x61
-// ~> str:to-utf8-bytes 你好
-// ▶ 0xe4
-// ▶ 0xbd
-// ▶ 0xa0
-// ▶ 0xe5
-// ▶ 0xa5
-// ▶ 0xbd
-// ```
-//
-// The output format is subject to change.
-//
-// @cf str:from-utf8-bytes
-
 func toUtf8Bytes(fm *eval.Frame, s string) error {
 	out := fm.ValueOutput()
 	for _, r := range []byte(s) {
@@ -503,120 +167,3 @@ func toUtf8Bytes(fm *eval.Frame, s string) error {
 	}
 	return nil
 }
-
-//elvdoc:fn to-title
-//
-// ```elvish
-// str:to-title $str
-// ```
-//
-// Outputs `$str` with all Unicode letters mapped to their Unicode title case.
-//
-// ```elvish-transcript
-// ~> str:to-title "her royal highness"
-// ▶ HER ROYAL HIGHNESS
-// ~> str:to-title "хлеб"
-// ▶ ХЛЕБ
-// ```
-
-//elvdoc:fn to-upper
-//
-// ```elvish
-// str:to-upper
-// ```
-//
-// Outputs `$str` with all Unicode letters mapped to their upper-case
-// equivalent.
-//
-// ```elvish-transcript
-// ~> str:to-upper 'abc!123'
-// ▶ ABC!123
-// ```
-
-//elvdoc:fn trim
-//
-// ```elvish
-// str:trim $str $cutset
-// ```
-//
-// Outputs `$str` with all leading and trailing Unicode code points contained
-// in `$cutset` removed.
-//
-// ```elvish-transcript
-// ~> str:trim "¡¡¡Hello, Elven!!!" "!¡"
-// ▶ 'Hello, Elven'
-// ```
-
-//elvdoc:fn trim-left
-//
-// ```elvish
-// str:trim-left $str $cutset
-// ```
-//
-// Outputs `$str` with all leading Unicode code points contained in `$cutset`
-// removed. To remove a prefix string use [`str:trim-prefix`](#str:trim-prefix).
-//
-// ```elvish-transcript
-// ~> str:trim-left "¡¡¡Hello, Elven!!!" "!¡"
-// ▶ 'Hello, Elven!!!'
-// ```
-
-//elvdoc:fn trim-prefix
-//
-// ```elvish
-// str:trim-prefix $str $prefix
-// ```
-//
-// Outputs `$str` minus the leading `$prefix` string. If `$str` doesn't begin
-// with `$prefix`, `$str` is output unchanged.
-//
-// ```elvish-transcript
-// ~> str:trim-prefix "¡¡¡Hello, Elven!!!" "¡¡¡Hello, "
-// ▶ Elven!!!
-// ~> str:trim-prefix "¡¡¡Hello, Elven!!!" "¡¡¡Hola, "
-// ▶ '¡¡¡Hello, Elven!!!'
-// ```
-
-//elvdoc:fn trim-right
-//
-// ```elvish
-// str:trim-right $str $cutset
-// ```
-//
-// Outputs `$str` with all leading Unicode code points contained in `$cutset`
-// removed. To remove a suffix string use [`str:trim-suffix`](#str:trim-suffix).
-//
-// ```elvish-transcript
-// ~> str:trim-right "¡¡¡Hello, Elven!!!" "!¡"
-// ▶ '¡¡¡Hello, Elven'
-// ```
-
-//elvdoc:fn trim-space
-//
-// ```elvish
-// str:trim-space $str
-// ```
-//
-// Outputs `$str` with all leading and trailing white space removed as defined
-// by Unicode.
-//
-// ```elvish-transcript
-// ~> str:trim-space " \t\n Hello, Elven \n\t\r\n"
-// ▶ 'Hello, Elven'
-// ```
-
-//elvdoc:fn trim-suffix
-//
-// ```elvish
-// str:trim-suffix $str $suffix
-// ```
-//
-// Outputs `$str` minus the trailing `$suffix` string. If `$str` doesn't end
-// with `$suffix`, `$str` is output unchanged.
-//
-// ```elvish-transcript
-// ~> str:trim-suffix "¡¡¡Hello, Elven!!!" ", Elven!!!"
-// ▶ ¡¡¡Hello
-// ~> str:trim-suffix "¡¡¡Hello, Elven!!!" ", Klingons!!!"
-// ▶ '¡¡¡Hello, Elven!!!'
-// ```

+ 69 - 0
pkg/mods/unix/rlimit.d.elv

@@ -0,0 +1,69 @@
+#elvdoc:var rlimits
+#
+# A map describing resource limits of the current process.
+#
+# Each key is a string corresponds to a resource, and each value is a map with
+# keys `&cur` and `&max`, describing the soft and hard limits of that resource.
+# A missing `&cur` key means that there is no soft limit; a missing `&max` key
+# means that there is no hard limit.
+#
+# The following resources are supported, some only present on certain OSes:
+#
+# | Key          | Resource           | Unit    | OS                 |
+# | ------------ | ------------------ | ------- | ------------------ |
+# | `core`       | Core file          | bytes   | all                |
+# | `cpu`        | CPU time           | seconds | all                |
+# | `data`       | Data segment       | bytes   | all                |
+# | `fsize`      | File size          | bytes   | all                |
+# | `memlock`    | Locked memory      | bytes   | all                |
+# | `nofile`     | File descriptors   | number  | all                |
+# | `nproc`      | Processes          | number  | all                |
+# | `rss`        | Resident set size  | bytes   | all                |
+# | `stack`      | Stack segment      | bytes   | all                |
+# | `as`         | Address space      | bytes   | Linux, Free/NetBSD |
+# | `nthr`       | Threads            | number  | NetBSD             |
+# | `sbsize`     | Socket buffers     | bytes   | NetBSD             |
+# | `locks`      | File locks         | number  | Linux              |
+# | `msgqueue`   | Message queues     | bytes   | Linux              |
+# | `nice`       | 20 - nice value    |         | Linux              |
+# | `rtprio`     | Real-time priority |         | Linux              |
+# | `rttime`     | Real-time CPU time | seconds | Linux              |
+# | `sigpending` | Signals queued     | number  | Linux              |
+#
+# For the exact semantics of each resource, see the man page of `getrlimit`:
+# [Linux](https://man7.org/linux/man-pages/man2/setrlimit.2.html),
+# [macOS](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/getrlimit.2.html),
+# [FreeBSD](https://www.freebsd.org/cgi/man.cgi?query=getrlimit),
+# [NetBSD](https://man.netbsd.org/getrlimit.2),
+# [OpenBSD](https://man.openbsd.org/getrlimit.2). A key `foo` in the Elvish API
+# corresponds to `RLIMIT_FOO` in the C API.
+#
+# Examples:
+#
+# ```elvish-transcript
+# ~> put $unix:rlimits
+# ▶ [&nofile=[&cur=(num 256)] &fsize=[&] &nproc=[&max=(num 2666) &cur=(num 2666)] &memlock=[&] &cpu=[&] &core=[&cur=(num 0)] &stack=[&max=(num 67092480) &cur=(num 8372224)] &rss=[&] &data=[&]]
+# ~> # mimic Bash's "ulimit -a"
+# ~> keys $unix:rlimits | order | each {|key|
+#      var m = $unix:rlimits[$key]
+#      fn get {|k| if (has-key $m $k) { put $m[$k] } else { put unlimited } }
+#      printf "%-7v %-9v %-9v\n" $key (get cur) (get max)
+#    }
+# core    0         unlimited
+# cpu     unlimited unlimited
+# data    unlimited unlimited
+# fsize   unlimited unlimited
+# memlock unlimited unlimited
+# nofile  256       unlimited
+# nproc   2666      2666
+# rss     unlimited unlimited
+# stack   8372224   67092480
+# ~> # Decrease the soft limit on file descriptors
+# ~> set unix:rlimits[nofile][cur] = 100
+# ~> put $unix:rlimits[nofile]
+# ▶ [&cur=(num 100)]
+# ~> # Remove the soft limit on file descriptors
+# ~> del unix:rlimits[nofile][cur]
+# ~> put $unix:rlimits[nofile]
+# ▶ [&]
+# ```

+ 0 - 70
pkg/mods/unix/rlimit.go

@@ -11,76 +11,6 @@ import (
 	"src.elv.sh/pkg/eval/vals"
 )
 
-//elvdoc:var rlimits
-//
-// A map describing resource limits of the current process.
-//
-// Each key is a string corresponds to a resource, and each value is a map with
-// keys `&cur` and `&max`, describing the soft and hard limits of that resource.
-// A missing `&cur` key means that there is no soft limit; a missing `&max` key
-// means that there is no hard limit.
-//
-// The following resources are supported, some only present on certain OSes:
-//
-// | Key          | Resource           | Unit    | OS                 |
-// | ------------ | ------------------ | ------- | ------------------ |
-// | `core`       | Core file          | bytes   | all                |
-// | `cpu`        | CPU time           | seconds | all                |
-// | `data`       | Data segment       | bytes   | all                |
-// | `fsize`      | File size          | bytes   | all                |
-// | `memlock`    | Locked memory      | bytes   | all                |
-// | `nofile`     | File descriptors   | number  | all                |
-// | `nproc`      | Processes          | number  | all                |
-// | `rss`        | Resident set size  | bytes   | all                |
-// | `stack`      | Stack segment      | bytes   | all                |
-// | `as`         | Address space      | bytes   | Linux, Free/NetBSD |
-// | `nthr`       | Threads            | number  | NetBSD             |
-// | `sbsize`     | Socket buffers     | bytes   | NetBSD             |
-// | `locks`      | File locks         | number  | Linux              |
-// | `msgqueue`   | Message queues     | bytes   | Linux              |
-// | `nice`       | 20 - nice value    |         | Linux              |
-// | `rtprio`     | Real-time priority |         | Linux              |
-// | `rttime`     | Real-time CPU time | seconds | Linux              |
-// | `sigpending` | Signals queued     | number  | Linux              |
-//
-// For the exact semantics of each resource, see the man page of `getrlimit`:
-// [Linux](https://man7.org/linux/man-pages/man2/setrlimit.2.html),
-// [macOS](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/getrlimit.2.html),
-// [FreeBSD](https://www.freebsd.org/cgi/man.cgi?query=getrlimit),
-// [NetBSD](https://man.netbsd.org/getrlimit.2),
-// [OpenBSD](https://man.openbsd.org/getrlimit.2). A key `foo` in the Elvish API
-// corresponds to `RLIMIT_FOO` in the C API.
-//
-// Examples:
-//
-// ```elvish-transcript
-// ~> put $unix:rlimits
-// ▶ [&nofile=[&cur=(num 256)] &fsize=[&] &nproc=[&max=(num 2666) &cur=(num 2666)] &memlock=[&] &cpu=[&] &core=[&cur=(num 0)] &stack=[&max=(num 67092480) &cur=(num 8372224)] &rss=[&] &data=[&]]
-// ~> # mimic Bash's "ulimit -a"
-// ~> keys $unix:rlimits | order | each {|key|
-//      var m = $unix:rlimits[$key]
-//      fn get {|k| if (has-key $m $k) { put $m[$k] } else { put unlimited } }
-//      printf "%-7v %-9v %-9v\n" $key (get cur) (get max)
-//    }
-// core    0         unlimited
-// cpu     unlimited unlimited
-// data    unlimited unlimited
-// fsize   unlimited unlimited
-// memlock unlimited unlimited
-// nofile  256       unlimited
-// nproc   2666      2666
-// rss     unlimited unlimited
-// stack   8372224   67092480
-// ~> # Decrease the soft limit on file descriptors
-// ~> set unix:rlimits[nofile][cur] = 100
-// ~> put $unix:rlimits[nofile]
-// ▶ [&cur=(num 100)]
-// ~> # Remove the soft limit on file descriptors
-// ~> del unix:rlimits[nofile][cur]
-// ~> put $unix:rlimits[nofile]
-// ▶ [&]
-// ```
-
 type rlimitsVar struct{}
 
 var (

+ 19 - 0
pkg/mods/unix/umask.d.elv

@@ -0,0 +1,19 @@
+#elvdoc:var umask
+#
+# The file mode creation mask. Its value is a string in Elvish octal
+# representation; e.g. 0o027. This makes it possible to use it in any context
+# that expects a `$number`.
+#
+# When assigning a new value a string is implicitly treated as an
+# octal number. If that fails the usual rules for interpreting
+# [numbers](./language.html#number) are used. The following are equivalent:
+# `set unix:umask = 027` and `set unix:umask = 0o27`. You can also assign to it
+# a `float64` data type that has no fractional component. The assigned value
+# must be within the range [0 ... 0o777], otherwise the assignment will throw
+# an exception.
+#
+# You can do a temporary assignment to affect a single command; e.g. `umask=077
+# touch a_file`. After the command completes the old umask will be restored.
+# **Warning**: Since the umask applies to the entire process, not individual
+# threads, changing it temporarily in this manner is dangerous if you are doing
+# anything in parallel, such as via the [`peach`](builtin.html#peach) command.

+ 0 - 20
pkg/mods/unix/umask.go

@@ -19,26 +19,6 @@ const (
 	validUmaskMsg = "integer in the range [0..0o777]"
 )
 
-//elvdoc:var umask
-//
-// The file mode creation mask. Its value is a string in Elvish octal
-// representation; e.g. 0o027. This makes it possible to use it in any context
-// that expects a `$number`.
-//
-// When assigning a new value a string is implicitly treated as an
-// octal number. If that fails the usual rules for interpreting
-// [numbers](./language.html#number) are used. The following are equivalent:
-// `set unix:umask = 027` and `set unix:umask = 0o27`. You can also assign to it
-// a `float64` data type that has no fractional component. The assigned value
-// must be within the range [0 ... 0o777], otherwise the assignment will throw
-// an exception.
-//
-// You can do a temporary assignment to affect a single command; e.g. `umask=077
-// touch a_file`. After the command completes the old umask will be restored.
-// **Warning**: Since the umask applies to the entire process, not individual
-// threads, changing it temporarily in this manner is dangerous if you are doing
-// anything in parallel, such as via the [`peach`](builtin.html#peach) command.
-
 // UmaskVariable is a variable whose value always reflects the current file
 // creation permission mask. Setting it changes the current file creation
 // permission mask for the process (not an individual thread).