├── .cirrus.yml ├── .codecov.yml ├── .codespellrc ├── .dockerignore ├── .gitattributes ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ └── feature_request.yml └── workflows │ ├── check_cirrus.yml │ ├── check_website.yml │ ├── ci.yml │ └── docker.yml ├── .gitignore ├── .vscode └── launch.json ├── 0.22.0-release-notes.md ├── CODE_OF_CONDUCT.md ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── branding ├── forum-banner-dark.svg ├── forum-banner-light.svg ├── forum-logo.svg ├── logo-full-bleed.svg ├── logo.svg ├── youtube-channel-logo.svg └── youtube-channel-watermark.svg ├── cmd ├── elvish │ └── main.go ├── elvmdfmt │ └── main.go ├── nodaemon │ └── elvish │ │ └── main.go └── withpprof │ └── elvish │ └── main.go ├── docs ├── README.md ├── architecture │ └── doc.go ├── building.md ├── contributing.md ├── documenting.md ├── elvish-as-library.md ├── packaging.md ├── security.md ├── testing.md └── workflows.md ├── e2e ├── e2e_test.go └── script_test.elvts ├── flake.lock ├── flake.nix ├── go.mod ├── go.sum ├── gomod2nix.toml ├── pkg ├── buildinfo │ ├── buildinfo.go │ ├── buildinfo_test.elvts │ ├── buildinfo_test.go │ └── transcripts_test.go ├── cli │ ├── app.go │ ├── app_spec.go │ ├── app_test.go │ ├── clitest │ │ ├── apptest.go │ │ ├── apptest_test.go │ │ ├── fake_tty.go │ │ └── fake_tty_test.go │ ├── examples │ │ ├── e3bc │ │ │ ├── bc │ │ │ │ └── bc.go │ │ │ ├── completion.go │ │ │ └── main.go │ │ ├── nav │ │ │ └── main.go │ │ ├── widget │ │ │ └── main.go │ │ └── win_tty │ │ │ └── main_windows.go │ ├── histutil │ │ ├── db.go │ │ ├── db_store.go │ │ ├── db_store_test.go │ │ ├── dedup_cursor.go │ │ ├── dedup_cursor_test.go │ │ ├── doc.go │ │ ├── hybrid_store.go │ │ ├── hybrid_store_test.go │ │ ├── mem_store.go │ │ ├── mem_store_test.go │ │ ├── store.go │ │ └── test_db.go │ ├── loop.go │ ├── loop_test.go │ ├── lscolors │ │ ├── feature.go │ │ ├── feature_nonunix_test.go │ │ ├── feature_test.go │ │ ├── feature_unix_test.go │ │ ├── lscolors.go │ │ ├── lscolors_test.go │ │ ├── stat_notsolaris.go │ │ ├── stat_solaris.go │ │ ├── stat_unix.go │ │ └── stat_windows.go │ ├── modes │ │ ├── completion.go │ │ ├── completion_test.go │ │ ├── filter_spec.go │ │ ├── histlist.go │ │ ├── histlist_test.go │ │ ├── histwalk.go │ │ ├── histwalk_test.go │ │ ├── instant.go │ │ ├── instant_test.go │ │ ├── lastcmd.go │ │ ├── lastcmd_test.go │ │ ├── listing.go │ │ ├── listing_test.go │ │ ├── location.go │ │ ├── location_test.go │ │ ├── mode.go │ │ ├── mode_test.go │ │ ├── navigation.go │ │ ├── navigation_fs.go │ │ ├── navigation_fs_test.go │ │ ├── navigation_test.go │ │ ├── navigation_unix_test.go │ │ ├── stub.go │ │ └── stub_test.go │ ├── prompt │ │ ├── prompt.go │ │ └── prompt_test.go │ ├── term │ │ ├── buffer.go │ │ ├── buffer_builder.go │ │ ├── buffer_builder_test.go │ │ ├── buffer_test.go │ │ ├── event.go │ │ ├── file_reader_unix.go │ │ ├── file_reader_unix_test.go │ │ ├── read_rune.go │ │ ├── read_rune_test.go │ │ ├── reader.go │ │ ├── reader_test.go │ │ ├── reader_unix.go │ │ ├── reader_unix_test.go │ │ ├── reader_windows.go │ │ ├── reader_windows_test.go │ │ ├── setup.go │ │ ├── setup_unix.go │ │ ├── setup_unix_test.go │ │ ├── setup_windows.go │ │ ├── setup_windows_test.go │ │ ├── term.go │ │ ├── writer.go │ │ └── writer_test.go │ ├── tk │ │ ├── codearea.go │ │ ├── codearea_render.go │ │ ├── codearea_test.go │ │ ├── colview.go │ │ ├── colview_test.go │ │ ├── combobox.go │ │ ├── combobox_test.go │ │ ├── empty.go │ │ ├── label.go │ │ ├── layout_test.go │ │ ├── listbox.go │ │ ├── listbox_state.go │ │ ├── listbox_test.go │ │ ├── listbox_window.go │ │ ├── listbox_window_test.go │ │ ├── scrollbar.go │ │ ├── textview.go │ │ ├── textview_test.go │ │ ├── utils_test.go │ │ ├── widget.go │ │ └── widget_test.go │ ├── tty.go │ └── tty_unix_test.go ├── daemon │ ├── activate.go │ ├── activate_test.go │ ├── activate_unix_test.go │ ├── client.go │ ├── daemondefs │ │ └── daemondefs.go │ ├── internal │ │ └── api │ │ │ └── api.go │ ├── server.go │ ├── server_test.elvts │ ├── server_test.go │ ├── server_unix_test.go │ ├── service.go │ ├── sys_unix.go │ ├── sys_windows.go │ └── transcripts_test.go ├── diag │ ├── context.go │ ├── context_test.go │ ├── doc.go │ ├── error.go │ ├── error_test.go │ ├── range.go │ ├── range_test.go │ ├── show_error.go │ ├── show_error_test.go │ ├── shower.go │ └── testutil_test.go ├── diff │ ├── LICENSE │ └── diff.go ├── edit │ ├── binding_map.go │ ├── binding_map_test.elvts │ ├── binding_map_test.go │ ├── buffer_builtins.d.elv │ ├── buffer_builtins.go │ ├── builtins.d.elv │ ├── builtins.go │ ├── builtins_test.elvts │ ├── builtins_test.go │ ├── command_api.d.elv │ ├── command_api.go │ ├── command_api_test.go │ ├── complete │ │ ├── complete.go │ │ ├── complete_test.go │ │ ├── completers.go │ │ ├── filterers.go │ │ ├── generators.go │ │ ├── ns_helper.go │ │ └── raw_item.go │ ├── complete_getopt.d.elv │ ├── complete_getopt.go │ ├── complete_getopt_test.elvts │ ├── completion.d.elv │ ├── completion.go │ ├── completion_test.elvts │ ├── completion_test.go │ ├── config_api.d.elv │ ├── config_api.go │ ├── config_api_test.go │ ├── editor.d.elv │ ├── editor.go │ ├── editor_test.go │ ├── filter │ │ ├── compile.go │ │ ├── compile_test.go │ │ ├── filter.go │ │ ├── highlight.go │ │ └── highlight_test.go │ ├── highlight.d.elv │ ├── highlight.go │ ├── highlight │ │ ├── highlight.go │ │ ├── highlighter.go │ │ ├── highlighter_test.elvts │ │ ├── highlighter_test.go │ │ ├── regions.go │ │ ├── testexport_test.go │ │ ├── theme.go │ │ └── transcripts_test.go │ ├── highlight_test.go │ ├── hist_store.go │ ├── histwalk.d.elv │ ├── histwalk.go │ ├── histwalk_test.go │ ├── init.elv │ ├── insert_api.d.elv │ ├── insert_api.go │ ├── insert_api_test.go │ ├── instant.d.elv │ ├── instant.go │ ├── instant_test.go │ ├── key_binding.go │ ├── listing.d.elv │ ├── listing.go │ ├── listing_custom.d.elv │ ├── listing_custom.go │ ├── listing_nonwindows_test.go │ ├── listing_test.go │ ├── listing_windows_test.go │ ├── minibuf.go │ ├── minibuf_test.go │ ├── navigation.d.elv │ ├── navigation.go │ ├── navigation_test.go │ ├── prompt.d.elv │ ├── prompt.go │ ├── prompt_test.go │ ├── repl.d.elv │ ├── repl.go │ ├── state_api.d.elv │ ├── state_api.go │ ├── state_api_test.go │ ├── store_api.d.elv │ ├── store_api.go │ ├── store_api_test.go │ ├── testutils_test.go │ ├── transcripts_test.go │ ├── vars.d.elv │ ├── vars.go │ └── vars_test.elvts ├── elvdoc │ ├── elvdoc.go │ ├── elvdoc_test.go │ ├── highlight.go │ └── highlight_test.go ├── env │ └── env.go ├── errutil │ ├── errutil.go │ ├── multi_error.go │ └── multi_error_test.go ├── eval │ ├── benchmarks_test.go │ ├── builtin_fn_cmd.d.elv │ ├── builtin_fn_cmd.go │ ├── builtin_fn_cmd_test.elvts │ ├── builtin_fn_cmd_unix.go │ ├── builtin_fn_cmd_windows.go │ ├── builtin_fn_container.d.elv │ ├── builtin_fn_container.go │ ├── builtin_fn_container_test.elvts │ ├── builtin_fn_debug.d.elv │ ├── builtin_fn_debug.go │ ├── builtin_fn_env.d.elv │ ├── builtin_fn_env.go │ ├── builtin_fn_env_test.elvts │ ├── builtin_fn_flow.d.elv │ ├── builtin_fn_flow.go │ ├── builtin_fn_flow_test.elvts │ ├── builtin_fn_fs.d.elv │ ├── builtin_fn_fs.go │ ├── builtin_fn_fs_test.elvts │ ├── builtin_fn_io.d.elv │ ├── builtin_fn_io.go │ ├── builtin_fn_io_test.elvts │ ├── builtin_fn_misc.d.elv │ ├── builtin_fn_misc.go │ ├── builtin_fn_misc_test.elvts │ ├── builtin_fn_num.d.elv │ ├── builtin_fn_num.go │ ├── builtin_fn_num_test.elvts │ ├── builtin_fn_pred.d.elv │ ├── builtin_fn_pred.go │ ├── builtin_fn_pred_test.elvts │ ├── builtin_fn_str.d.elv │ ├── builtin_fn_str.go │ ├── builtin_fn_str_test.elvts │ ├── builtin_fn_stream.d.elv │ ├── builtin_fn_stream.go │ ├── builtin_fn_stream_test.elvts │ ├── builtin_fn_styled.d.elv │ ├── builtin_fn_styled.go │ ├── builtin_fn_styled_test.elvts │ ├── builtin_fn_time.d.elv │ ├── builtin_fn_time.go │ ├── builtin_fn_time_test.elvts │ ├── builtin_ns.d.elv │ ├── builtin_ns.go │ ├── builtin_ns_test.elvts │ ├── builtin_special.go │ ├── builtin_special_test.elvts │ ├── callable.go │ ├── chdir_test.go │ ├── closure.go │ ├── closure_test.elvts │ ├── compile_effect.go │ ├── compile_effect_test.elvts │ ├── compile_lvalue.go │ ├── compile_value.go │ ├── compile_value_test.elvts │ ├── compiler.go │ ├── compiler_test.elvts │ ├── compiler_test.go │ ├── deprecation.go │ ├── errs │ │ ├── errs.go │ │ └── errs_test.go │ ├── eval.d.elv │ ├── eval.go │ ├── eval_examples_test.go │ ├── eval_test.elvts │ ├── eval_test.go │ ├── evaltest │ │ ├── non_unix.go │ │ ├── test_transcript.go │ │ └── unix.go │ ├── exception.go │ ├── exception_test.elvts │ ├── exception_test.go │ ├── exception_unix_test.go │ ├── external_cmd.go │ ├── external_cmd_unix.go │ ├── external_cmd_unix_internal_test.go │ ├── external_cmd_windows.go │ ├── frame.go │ ├── fuzz_test.go │ ├── generic_utils.go │ ├── glob.go │ ├── glob_test.elvts │ ├── go_fn.go │ ├── go_fn_internal_test.go │ ├── go_fn_test.elvts │ ├── go_fn_test.go │ ├── hook.go │ ├── hook_test.elvts │ ├── interrupts.go │ ├── node_utils.go │ ├── ns.go │ ├── ns_test.elvts │ ├── options.go │ ├── options_test.go │ ├── plugin.go │ ├── plugin_gccgo.go │ ├── port.go │ ├── port_helper_test.go │ ├── port_unix.go │ ├── port_windows.go │ ├── process_unix.go │ ├── process_windows.go │ ├── purely_eval.go │ ├── purely_eval_test.go │ ├── pwd.go │ ├── testdata │ │ └── fuzz │ │ │ └── FuzzCheck │ │ │ └── 30e5cf5b35c294c05ffc72c31859e85bd4c663bbac718db7b941824b8d74af82 │ ├── testexport_test.go │ ├── testutil_test.go │ ├── transcripts_test.go │ ├── transcripts_unix_test.go │ ├── transcripts_windows_test.go │ ├── vals │ │ ├── aliased_types.go │ │ ├── aliased_types_test.go │ │ ├── assoc.go │ │ ├── assoc_test.go │ │ ├── bool.go │ │ ├── bool_test.go │ │ ├── cmp.go │ │ ├── cmp_test.go │ │ ├── concat.go │ │ ├── concat_test.go │ │ ├── conversion.go │ │ ├── conversion_test.go │ │ ├── dissoc.go │ │ ├── dissoc_test.go │ │ ├── doc.go │ │ ├── equal.go │ │ ├── equal_test.go │ │ ├── errors_test.go │ │ ├── feed.go │ │ ├── feed_test.go │ │ ├── field_map.go │ │ ├── field_map_test.go │ │ ├── has_key.go │ │ ├── has_key_test.go │ │ ├── hash.go │ │ ├── hash_test.go │ │ ├── index.go │ │ ├── index_list.go │ │ ├── index_string.go │ │ ├── index_test.go │ │ ├── iterate.go │ │ ├── iterate_keys.go │ │ ├── iterate_keys_test.go │ │ ├── iterate_test.go │ │ ├── kind.go │ │ ├── kind_test.go │ │ ├── len.go │ │ ├── len_test.go │ │ ├── num.go │ │ ├── num_test.go │ │ ├── pipe.go │ │ ├── pseudo_map.go │ │ ├── pseudo_map_test.go │ │ ├── reflect_wrappers.go │ │ ├── repr.go │ │ ├── repr_helpers.go │ │ ├── repr_test.go │ │ ├── string.go │ │ ├── string_test.go │ │ ├── struct_map_test.go │ │ ├── tester.go │ │ └── testutils_test.go │ ├── value_test.go │ ├── var_parse.go │ ├── var_parse_test.go │ ├── var_ref.go │ └── vars │ │ ├── blackhole.go │ │ ├── blackhole_test.go │ │ ├── callback.go │ │ ├── callback_test.go │ │ ├── element.go │ │ ├── element_test.go │ │ ├── env.go │ │ ├── env_list.go │ │ ├── env_test.go │ │ ├── ptr.go │ │ ├── ptr_test.go │ │ ├── read_only.go │ │ ├── read_only_test.go │ │ └── vars.go ├── fsutil │ ├── claim.go │ ├── claim_test.go │ ├── fsutil.go │ ├── gethome.go │ ├── getwd.go │ ├── getwd_test.go │ ├── search.go │ ├── search_unix.go │ ├── search_unix_test.go │ ├── search_windows.go │ └── search_windows_test.go ├── getopt │ ├── getopt.go │ ├── getopt_test.go │ └── zstring.go ├── glob │ ├── glob.go │ ├── glob_test.go │ ├── parse.go │ ├── parse_test.go │ └── pattern.go ├── logutil │ ├── logutil.go │ └── logutil_test.go ├── lsp │ ├── lsp.go │ ├── lsp_test.elvts │ ├── lsp_test.go │ ├── server.go │ └── transcripts_test.go ├── md │ ├── fmt.go │ ├── fmt_test.go │ ├── html.go │ ├── html_test.go │ ├── inline.go │ ├── md.go │ ├── md_test.go │ ├── mdrun │ │ └── main.go │ ├── smart_puncts.go │ ├── smart_puncts_test.go │ ├── spec │ │ ├── LICENSE │ │ └── spec.json │ ├── stack.go │ ├── testdata │ │ └── fuzz │ │ │ ├── FuzzFmtPreservesHTMLRender │ │ │ ├── 09165d96e6eede6b6057e300935fb1aa5c98243ed4f94b75a3ae31fb129e696d │ │ │ ├── 0d768707e28d09f6ef49b5e8c7e8f44fbea157f6296a05abb302ee5b77e3a168 │ │ │ ├── 17c530b620d9fbcf8870fe975ed11dd7062eb582d7ca3fc5ddbc293d5344e2f9 │ │ │ ├── 2b69704609fc2e3745fba9a435b29b3858c597efe349cf8cc39c193e6f02173c │ │ │ ├── 2ced69587e727c3c37b87201e0e44e450090e6a195425a8fe0dc66bbc5163fd6 │ │ │ ├── 2ec9c489ff1c9ed8d8a24c2eb9e4033303149dcd9875ea82bf4764d0df144af5 │ │ │ ├── 334ff8e8eaece3f84601f65db41d9cffa5634be1d23c4f97ea44edf4669f712f │ │ │ ├── 3463752fb4670a5a72d0033d9f2caee37023353a019825bb48223e7a01d9b760 │ │ │ ├── 4310d034c2ad018a0649d15c7d6748bfad6779fb067b28b09debb146eb77bd31 │ │ │ ├── 43d96cdf434f4f13cd32c391724d7c1cbc55d63d92195ea9da67c397abb722fa │ │ │ ├── 45f9492476d79ae796da2ffeb3476370586553a5071b32bd1cc7a07fbd80356f │ │ │ ├── 46d002372d39c68359423d57dbe63c3c83003ecbd5e601286a5f80bc691624ef │ │ │ ├── 4951856280eb053ef3a943dcf6fb1e27cd595e4ab8aa00934517a702e71ca940 │ │ │ ├── 49ef19e3514a8f95ff404e89d70d308b76d947b505a9593e2e72712048f2ae42 │ │ │ ├── 51e230c835df97b3aec428fe7a0be1704811421c5c28a78c9c3b74983157b1e4 │ │ │ ├── 594ea6c06082c3fc565a1304ce5a809d2a37dac682163c23c73be7748bc5b464 │ │ │ ├── 5e0c9718d6e600b573a921052b15c44fb57a68a9d70af7a7c8899b33adefcc40 │ │ │ ├── 5fb0a2c38bf290e32115dd5c2de37cf22994dfaa3c2c74200060e8f6d0d0d2be │ │ │ ├── 6a6981e2afe37e15899b27af919cd50085520fdeab41051147b037418805bfd6 │ │ │ ├── 6ed9c798d44c6e3a7c68257ace3ece80a752882b1a06d06a101f375abc30fa32 │ │ │ ├── 714ffe658ccfa9609ff79b93aa1462f9a1b4c1a4709c0256ae7bc4244578601c │ │ │ ├── 722391ce85af71be128f8d7aa7b3620e9d4810458b8148c119256a93647fea3e │ │ │ ├── 7425c53ab767b535434fe5e744491bc978146e002c5f6c701ea0ed3a7032bf89 │ │ │ ├── 763096c0f188a6f5a2cad16641570cfe484af56c6a406c3d89948406e4f11d62 │ │ │ ├── 77f51992726987c5b66121aa85a2b57152ae80152ad2831a7b7d4c761a09ed2f │ │ │ ├── 7ef0c0744eba12ae7f987c61107a4180b0772e25dd796045922da521e9a1b4cc │ │ │ ├── 7fd9444d106647162a0fbfff2f7c2f9da240565a759c0371cd5af930da19186c │ │ │ ├── 85278dc7594f426d6b7b4412586b262b25229d360f6c5fd07cc57839c048abee │ │ │ ├── 89d1293838e9db8b9ad5b72565dcf93f284492177b143bff251c8c5a35e96c48 │ │ │ ├── 8becac1ca7e4e7c1456c91a25697a367a6b471b0c4a05e331456cd966003a108 │ │ │ ├── 8ec2e1d50f68c77c2a1e2707f9afa4794d33566315c26ae5c039390f55985a29 │ │ │ ├── 95f31a02dbd47cd18ea3483ba9d91074fa80f371e144f9958c93a9f6e2a2e48a │ │ │ ├── 9e5f2825cc97261ce08d95ce1a30e32ea78945cd5946af20a92307506bf4fa53 │ │ │ ├── a1b7773f70c10e0032514d1d0068a9b1d51b82e0a70adcd591858046509bb653 │ │ │ ├── aa47fa137928697726bba1b40b01f4e2a1c6980652433dd47b20d2517616c685 │ │ │ ├── acbe5b22b3e9beb52a963220e2fe8e7e3f0f4bdcd7d0af4ab3815f69bbb6077b │ │ │ ├── adb64ce0b0270d2fb724e07aa3dc7f0b7cb343708c008e021575b529d1e8395e │ │ │ ├── c3eb680407bb9b0d9a2fc29b862115d798bccc3309fddb86395824cadba8f63d │ │ │ ├── c760e1f86fcf5e26acf8f17b1c9da06b8503d320f3642e82a4a8ea8a37a55910 │ │ │ ├── ca797b6dad7e8007aa973269e07ab8e08e078ef5c738b7bea8d136ca05cd7a4d │ │ │ ├── cd37db7c4899c0be1226816e12693c93970d1a5292f752293695b2490d98fea2 │ │ │ ├── d66d86f848073d5c90685009906e1fb0840748fd8d3a452ee8cb3e6d3f1b29e9 │ │ │ ├── d845768a00eefd7adde472aca51dbfb198601986e0f1c2ad6257b4afd4540176 │ │ │ ├── d853024ac25e2be9f90784cfc58f28c496c9455b3c988b0964c02b45ac451b74 │ │ │ ├── d8e4495c47b5e918fc92795426735bcc9ee5beaea5058a84bbccba4401c6386d │ │ │ ├── d9dbd4e786917123c3126420bb75315b0affdca4ef3fc5b521be051566b28479 │ │ │ ├── de531027ab47da7f412a44d596530c35c32101ea60128c2328cdfd2a12cf20d3 │ │ │ ├── e609714031cd68fb7e402746b4c842229d77388ac34e6fbcad3ce52431a6e2a6 │ │ │ ├── eba78016235f789ac52ed7a9b12064cb034f410c7ecd324dce7afba390b409a2 │ │ │ ├── f56cebbc6539e13372fc5723238d6ff42177d27e683121e0d287d85ecbbde400 │ │ │ ├── f5a2058125b0d6bc9bd21817b8bedb3ed3fd6ee77b8dbcd12e3f6533e17aa335 │ │ │ └── fdb49d060e4e8905df6ecffcc3044a27cd7dc348ecc3c7e6049064e0e2b96ca1 │ │ │ ├── FuzzReflowFmtPreservesHTMLRenderModuleWhitespaces │ │ │ ├── 179da62c2709fec804728d69facdd60c840f5cf2082ef74822ff6f7bc12a02e8 │ │ │ ├── 467f234f387a81f6e97d86814ca835b444ea00a1a9470f100ce449d6b2beb3d8 │ │ │ ├── 586bc16e05e19d40cc33415c9a08aef70f05c221fc3dbe850c1869c65d062b50 │ │ │ ├── 80832a884668db006bd77bf19e3758fe3413e528fd38966ed8086f956d5bc201 │ │ │ ├── ac5a3d974210f5d5bcbd47eb5a3ea1bb1e3c0091077981bedae9f9c7ab2449c1 │ │ │ ├── d0138fd295a0fa831075f449614b0a8b8cf421e8754463f1907d68ef05ef0475 │ │ │ ├── e20e64463c3adce77239a55290f960552bc2bf611ef237800b77d0b25bc06cc6 │ │ │ └── e56bf66cc0560c5e7905445712d6fd4ef85fc0f0f55e304904fcba58645beacd │ │ │ ├── FuzzReflowFmtResultFitsInWidth │ │ │ ├── 4addab28ca79b43124a432b0980b1504d78dc5ea7b8c722d4a3f960729240178 │ │ │ └── c5523be82830755cd690a04810119e39290f0dc446c38e612dc0cc46ccc4cdaf │ │ │ └── FuzzReflowFmtResultIsUnchangedUnderFmt │ │ │ └── 1f26a323e0bc46cde6ab0cdbf3d947e58eab9df8bbd189bda681902c307a0797 │ ├── testexport_test.go │ ├── testutils_test.go │ ├── text.go │ ├── text_test.go │ ├── trace.go │ ├── tty.go │ ├── tty_test.go │ └── zstring.go ├── mods │ ├── daemon │ │ ├── daemon.go │ │ └── daemon_test.go │ ├── doc │ │ ├── doc.d.elv │ │ ├── doc.go │ │ ├── doc_test.elvts │ │ ├── doc_test.go │ │ ├── fakepkg │ │ │ ├── eval │ │ │ │ ├── break.md │ │ │ │ ├── builtin.d.elv │ │ │ │ └── num.md │ │ │ └── mods │ │ │ │ └── foo │ │ │ │ ├── foo.d.elv │ │ │ │ ├── function.md │ │ │ │ └── variable.md │ │ ├── match.go │ │ ├── match_test.go │ │ └── testexport_test.go │ ├── epm │ │ ├── epm.elv │ │ ├── epm.go │ │ ├── epm_test.elvts │ │ └── epm_test.go │ ├── file │ │ ├── file.d.elv │ │ ├── file.go │ │ ├── file_test.elvts │ │ └── file_test.go │ ├── flag │ │ ├── flag.d.elv │ │ ├── flag.go │ │ ├── flag_test.elvts │ │ └── flag_test.go │ ├── math │ │ ├── math.d.elv │ │ ├── math.go │ │ ├── math_test.elvts │ │ └── math_test.go │ ├── md │ │ ├── md.d.elv │ │ ├── md.go │ │ ├── md_test.elvts │ │ └── md_test.go │ ├── mods.go │ ├── os │ │ ├── os.d.elv │ │ ├── os.go │ │ ├── os_test.elvts │ │ ├── os_test.go │ │ ├── os_unix.go │ │ ├── os_unix_test.go │ │ ├── os_windows.go │ │ ├── os_windows_test.go │ │ ├── special_modes.go │ │ ├── stat.go │ │ ├── stat_bsd.go │ │ ├── stat_unix.go │ │ └── stat_windows.go │ ├── path │ │ ├── path.d.elv │ │ ├── path.go │ │ ├── path_test.elvts │ │ └── path_test.go │ ├── platform │ │ ├── platform.d.elv │ │ ├── platform.go │ │ ├── platform_test.elvts │ │ ├── platform_test.go │ │ ├── testexport_test.go │ │ ├── unix.go │ │ └── windows.go │ ├── re │ │ ├── match.go │ │ ├── re.d.elv │ │ ├── re.go │ │ ├── re_test.elvts │ │ └── re_test.go │ ├── readline-binding │ │ ├── readline-binding.elv │ │ ├── readline-binding_test.elvts │ │ ├── readlinebinding.go │ │ └── readlinebinding_test.go │ ├── runtime │ │ ├── runtime.d.elv │ │ ├── runtime.go │ │ ├── runtime_test.elvts │ │ ├── runtime_test.go │ │ └── testexport_test.go │ ├── store │ │ ├── store.d.elv │ │ ├── store.go │ │ ├── store_test.elvts │ │ └── store_test.go │ ├── str │ │ ├── str.d.elv │ │ ├── str.go │ │ ├── str_test.elvts │ │ └── str_test.go │ └── unix │ │ ├── non_unix.go │ │ ├── rlim_t_int64.go │ │ ├── rlim_t_uint64.go │ │ ├── rlimit.d.elv │ │ ├── rlimit.go │ │ ├── rlimit_keys.go │ │ ├── rlimit_keys_as.go │ │ ├── rlimit_keys_linux.go │ │ ├── rlimit_keys_netbsd.go │ │ ├── rlimit_test.elvts │ │ ├── testexport_test.go │ │ ├── umask.d.elv │ │ ├── umask.go │ │ ├── umask_test.elvts │ │ ├── umask_test.go │ │ ├── unix.go │ │ └── unix_test.go ├── must │ └── must.go ├── parse │ ├── check_ast_test.go │ ├── check_parse_tree_test.go │ ├── cmpd │ │ ├── cmpd.go │ │ └── cmpd_test.go │ ├── fuzz_test.go │ ├── node.go │ ├── np │ │ ├── np.go │ │ └── np_test.go │ ├── parse.go │ ├── parse_test.go │ ├── parser.go │ ├── parseutil │ │ ├── parseutil.go │ │ └── parseutil_test.go │ ├── pprint.go │ ├── pprint_test.go │ ├── quote.go │ ├── quote_test.go │ ├── source.go │ ├── source_test.go │ ├── testdata │ │ └── fuzz │ │ │ └── FuzzParse │ │ │ ├── 20ffbed79ec93b4b3f5872e6a329fee708da4b901c1b9c9ac10912ea7e862c9c │ │ │ └── cfd8d7d9847d47d7fa94af306484de70f8952db8250aa6ebbf7b1c84afb13a91 │ ├── testutil_test.go │ └── zstring.go ├── persistent │ ├── .gitignore │ ├── .travis.yml │ ├── LICENSE │ ├── README.md │ ├── add-slowdown │ ├── hash │ │ └── hash.go │ ├── hashmap │ │ ├── hashmap.go │ │ ├── hashmap_test.go │ │ └── map.go │ ├── list │ │ └── list.go │ ├── persistent.go │ └── vector │ │ ├── vector.go │ │ └── vector_test.go ├── pkg.go ├── pprof │ ├── pprof.go │ ├── pprof_test.elvts │ └── transcripts_test.go ├── prog │ ├── flags.go │ ├── prog.go │ ├── prog_test.elvts │ ├── progtest │ │ └── progtest.go │ └── transcripts_test.go ├── rpc │ ├── LICENSE │ ├── client.go │ ├── debug.go │ └── server.go ├── shell │ ├── interact.go │ ├── interact_test.elvts │ ├── paths.go │ ├── paths_test.elvts │ ├── paths_unix.go │ ├── paths_windows.go │ ├── script.go │ ├── script_test.elvts │ ├── shell.go │ ├── shell_test.elvts │ ├── signal_unix.go │ ├── signal_windows.go │ ├── testexports_test.go │ ├── transcripts_test.go │ └── transcripts_unix_test.go ├── store │ ├── buckets.go │ ├── cmd.go │ ├── cmd_test.go │ ├── db_store.go │ ├── dir.go │ ├── dir_test.go │ ├── staticcheck.conf │ ├── store.go │ ├── storedefs │ │ └── storedefs.go │ ├── storetest │ │ ├── cmd.go │ │ ├── dir.go │ │ └── storetest.go │ └── temp_store.go ├── strutil │ ├── camel_to_dashed.go │ ├── camel_to_dashed_test.go │ ├── chop.go │ ├── chop_test.go │ ├── eol_sol.go │ ├── eol_sol_test.go │ ├── join_lines.go │ ├── join_lines_test.go │ ├── strutil.go │ ├── subseq.go │ ├── subseq_test.go │ ├── title.go │ └── title_test.go ├── sys │ ├── dumpstack.go │ ├── eunix │ │ ├── eunix.go │ │ ├── tc.go │ │ ├── termios.go │ │ ├── termios_bsd.go │ │ ├── termios_notbsd.go │ │ ├── termiosflag_darwin.go │ │ ├── termiosflag_notdarwin.go │ │ ├── waitforread.go │ │ └── waitforread_test.go │ ├── ewindows │ │ ├── console.go │ │ ├── ewindows.go │ │ ├── types.go │ │ ├── wait.go │ │ └── ztypes_windows.go │ ├── signal_nonunix.go │ ├── signal_unix.go │ ├── sys.go │ ├── winsize_unix.go │ └── winsize_windows.go ├── testutil │ ├── chmod.go │ ├── dedent.go │ ├── dedent_test.go │ ├── fs.go │ ├── recover.go │ ├── recover_test.go │ ├── scaled.go │ ├── scaled_test.go │ ├── set.go │ ├── set_test.go │ ├── temp_env.go │ ├── temp_env_test.go │ ├── testdir.go │ ├── testdir_nonwindows_test.go │ ├── testdir_test.go │ ├── testutil.go │ ├── testutil_test.go │ ├── umask.go │ ├── umask_unix.go │ └── umask_windows.go ├── transcript │ ├── transcript.go │ └── transcript_test.go ├── tt │ ├── cmpopt.go │ ├── tt.go │ └── tt_test.go ├── ui │ ├── color.go │ ├── color_test.go │ ├── key.go │ ├── key_test.go │ ├── mark_lines.go │ ├── mark_lines_test.go │ ├── parse_sgr.go │ ├── parse_sgr_test.go │ ├── style.go │ ├── style_regions.go │ ├── style_regions_test.go │ ├── style_test.go │ ├── styledown │ │ ├── fuzz_test.go │ │ ├── styledown.go │ │ ├── styledown_test.elvts │ │ ├── testdata │ │ │ └── fuzz │ │ │ │ └── FuzzDerenderIsInverseOfRender │ │ │ │ ├── 6977bdc7b3d73e31 │ │ │ │ ├── 75d8aa7f136bd12c │ │ │ │ └── f8a03ebd1a65bf42 │ │ └── transcripts_test.go │ ├── styling.go │ ├── styling_test.go │ ├── text.go │ ├── text_builder.go │ ├── text_segment.go │ ├── text_segment_test.go │ ├── text_test.go │ └── ui.go └── wcwidth │ ├── wcwidth.go │ └── wcwidth_test.go ├── tools ├── buildall.elv ├── check-disallowed.sh ├── check-fmt-go.sh ├── check-fmt-md.sh ├── check-gen.sh ├── imports-graph.elv ├── pre-push ├── prune-cover.sh └── run-race.elv ├── vscode ├── .eslint.js ├── .gitignore ├── .vscodeignore ├── HACKING.md ├── LICENSE ├── README.md ├── icon.png ├── language-configuration.json ├── package-lock.json ├── package.json ├── sample.elv ├── sample.elvts ├── sample.styledown ├── snippets │ └── elvish.json ├── src │ ├── extension.ts │ ├── lsp.ts │ ├── styledown.ts │ ├── transcript.ts │ └── utils │ │ ├── logging.ts │ │ └── styling.ts ├── syntaxes │ ├── elvish-in-markdown.tmLanguage.json │ ├── elvish-transcript.tmLanguage.json │ └── elvish.tmLanguage.json ├── transcript-language-configuration.json └── tsconfig.json └── website ├── .gitignore ├── Makefile ├── README.md ├── blog ├── 0.10-release-notes.md ├── 0.11-release-notes.md ├── 0.12-release-notes.md ├── 0.13-release-notes.md ├── 0.13.1-release-notes.md ├── 0.14.0-release-notes.md ├── 0.14.1-release-notes.md ├── 0.15.0-release-notes.md ├── 0.16.0-release-notes.md ├── 0.17.0-release-notes.md ├── 0.18.0-release-notes.md ├── 0.19.1-release-notes.md ├── 0.20.0-release-notes.md ├── 0.20.1-release-notes.md ├── 0.21.0-release-notes.md ├── 0.9-release-notes.md ├── index.toml ├── live.md ├── newsletter-july-2017.md ├── newsletter-sep-2017.md └── prelude.md ├── cmd ├── gensite │ ├── .gitignore │ ├── feed_tmpl.go │ ├── main.go │ ├── model.go │ └── render.go ├── md2html │ ├── elvdoc.go │ ├── elvdoc_targets.go │ ├── elvdoc_test.elvts │ ├── highlight.go │ ├── html_codec.go │ ├── macros.go │ ├── main.go │ └── transcripts_test.go ├── runefreq │ └── main.go └── ttyshot │ ├── apply_dir.go │ ├── interp.go │ ├── main.go │ ├── parse.go │ └── rc.elv ├── favicons ├── android-chrome-192x192.png ├── android-chrome-512x512.png ├── apple-touch-icon.png ├── browserconfig.xml ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon.ico ├── mstile-144x144.png ├── mstile-150x150.png ├── mstile-310x150.png ├── mstile-310x310.png ├── mstile-70x70.png └── site.webmanifest ├── fonts ├── FiraMono-Bold.woff2 ├── FiraMono-Regular.woff2 ├── SourceSerif4-It.woff2 ├── SourceSerif4-Regular.woff2 ├── SourceSerif4-Semibold.woff2 └── SourceSerif4-SemiboldIt.woff2 ├── gen-fonts.elv ├── get ├── all-binaries.md ├── default-shell.md ├── index.toml ├── package-manager.md ├── prelude.css ├── prelude.js └── prelude.md ├── go.mod ├── go.sum ├── home.md ├── home ├── cmd-history-ttyshot.elvts ├── cmd-history-ttyshot.html ├── dir-history-ttyshot.elvts ├── dir-history-ttyshot.html ├── file-manager-ttyshot.elvts ├── file-manager-ttyshot.html ├── home.css ├── pipelines-ttyshot.elvts └── pipelines-ttyshot.html ├── icon-font.css ├── index.toml ├── learn ├── arguments-and-outputs.md ├── arguments-and-outputs │ ├── command-history-listing-narrowed-ttyshot.elvts │ ├── command-history-listing-narrowed-ttyshot.html │ ├── command-history-listing-ttyshot.elvts │ ├── command-history-listing-ttyshot.html │ ├── command-history-up-ttyshot.elvts │ └── command-history-up-ttyshot.html ├── effective-elvish.md ├── faq.md ├── first-commands.md ├── first-commands │ ├── location-mode-elv-ttyshot.elvts │ ├── location-mode-elv-ttyshot.html │ ├── location-mode-ttyshot.elvts │ └── location-mode-ttyshot.html ├── index.toml ├── organizing-and-reusing-code.md ├── pipelines-and-io.md ├── scripting-case-studies.md ├── scripting-case-studies │ ├── misspelt-variable-ttyshot.elvts │ └── misspelt-variable-ttyshot.html ├── tour.css ├── tour.md ├── tour │ ├── completion-filter-ttyshot.elvts │ ├── completion-filter-ttyshot.html │ ├── completion-ttyshot.elvts │ ├── completion-ttyshot.html │ ├── history-list-ttyshot.elvts │ ├── history-list-ttyshot.html │ ├── history-walk-prefix-ttyshot.elvts │ ├── history-walk-prefix-ttyshot.html │ ├── history-walk-ttyshot.elvts │ ├── history-walk-ttyshot.html │ ├── lastcmd-ttyshot.elvts │ ├── lastcmd-ttyshot.html │ ├── location-filter-ttyshot.elvts │ ├── location-filter-ttyshot.html │ ├── location-ttyshot.elvts │ ├── location-ttyshot.html │ ├── navigation-ttyshot.elvts │ ├── navigation-ttyshot.html │ ├── unicode-prompts-ttyshot.elvts │ └── unicode-prompts-ttyshot.html ├── unique-semantics.md ├── value-types.md └── variables-and-loops.md ├── ref ├── builtin.md ├── command.md ├── doc.md ├── edit.md ├── edit │ ├── autofix-ttyshot.elvts │ ├── autofix-ttyshot.html │ ├── completion-mode-ttyshot.elvts │ └── completion-mode-ttyshot.html ├── epm.md ├── file.md ├── flag.md ├── index.toml ├── language.md ├── math.md ├── md.md ├── os.md ├── path.md ├── platform.md ├── prelude.md ├── re.md ├── readline-binding.md ├── runtime.md ├── store.md ├── str.md └── unix.md ├── reset.css ├── sgr.css ├── slides ├── 2023-03-london-gophers.md ├── 2024-08-gophercon-uk.md ├── 2024-08-rc-design.md ├── 2024-08-rc-implementation.md ├── 2024-08-rc-implementation │ ├── cd.svg │ ├── op-tree.svg │ ├── parser-state.svg │ └── syntax-tree.svg ├── 2024-09-london-gophers.md ├── 2024-10-golang-oxford.md ├── 2025-02-fosdem.md ├── Makefile ├── gen.elv └── template.html ├── sponsor ├── index.toml ├── prelude.css └── prelude.md ├── style.css ├── template.html └── tools ├── check-rellinks.py ├── cmd-deps ├── docset-data └── Info.plist ├── md-deps ├── mkdocset └── mkdsidx.py /.codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: 4 | default: 5 | threshold: 0.1% 6 | patch: off 7 | comment: false 8 | # The following patterns are also consumed by a hacky sed script in 9 | # tools/prune-cover.sh, which does not support globs. 10 | ignore: 11 | # Exclude test helpers. 12 | - "pkg/cli/clitest" 13 | - "pkg/cli/histutil/test_db.go" 14 | - "pkg/eval/evaltest" 15 | - "pkg/eval/vals/tester.go" 16 | - "pkg/prog/progtest" 17 | - "pkg/store/storetest" 18 | - "pkg/must" 19 | # Exclude commands for manual testing. 20 | - "pkg/cli/examples" 21 | - "pkg/md/mdrun" 22 | # Exclude files generated by stringer. 23 | - "pkg/getopt/zstring.go" 24 | - "pkg/md/zstring.go" 25 | - "pkg/parse/zstring.go" 26 | # Exclude the copied diff and rpc packages. 27 | - "pkg/diff" 28 | - "pkg/rpc" 29 | -------------------------------------------------------------------------------- /.codespellrc: -------------------------------------------------------------------------------- 1 | [codespell] 2 | ignore-words-list = ro,upto,nd,doas,fo,shouldbe,iterm,lates,testof 3 | skip = ./.git,./vscode/node_modules,./vscode/dist,./website/_dst,./website/*.html 4 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | /_* 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.go filter=goimports 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: xiaq 2 | patreon: xiaq 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | 24 | # Nix 25 | /result 26 | # Project specific 27 | cover 28 | /_* 29 | /elvish 30 | /branding/*.png 31 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Run Extension", 6 | "type": "extensionHost", 7 | "request": "launch", 8 | "args": [ 9 | "--extensionDevelopmentPath=${workspaceFolder}/vscode" 10 | ], 11 | "preLaunchTask": "npm: build - vscode", 12 | "outFiles": [ 13 | "${workspaceFolder}/vscode/dist/*.js" 14 | ], 15 | "sourceMaps": true 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | The Elvish community follows the 2 | [Go community code of conduct](https://go.dev/conduct). 3 | 4 | The short version: 5 | 6 | - Treat everyone with respect and kindness. 7 | 8 | - Be thoughtful in how you communicate. 9 | 10 | - Don't be destructive or inflammatory. 11 | 12 | - If you encounter an issue, please contact xiaq via Telegram, Matrix or 13 | Discord DM or email (xiaqqaix@gmail.com). (We don't have a team of stewards 14 | or a committee of representatives (yet).) 15 | 16 | Consistent with the Go community code of conduct, the following specific points 17 | apply: 18 | 19 | - Respect how other people choose to use Elvish and their system in general. 20 | 21 | Elvish is opinionated software, but that doesn't mean the use cases Elvish 22 | chooses not to support are inherently bad. 23 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.22-alpine3.19 as builder 2 | RUN apk add --no-cache --virtual build-deps make git 3 | # Build Elvish 4 | COPY . /go/src/src.elv.sh 5 | RUN make -C /go/src/src.elv.sh get 6 | 7 | FROM alpine:3.19 8 | COPY --from=builder /go/bin/elvish /bin/elvish 9 | CMD ["/bin/elvish"] 10 | -------------------------------------------------------------------------------- /cmd/elvish/main.go: -------------------------------------------------------------------------------- 1 | // Elvish is a cross-platform shell, supporting Linux, BSDs and Windows. It 2 | // features an expressive programming language, with features like namespacing 3 | // and anonymous functions, and a fully programmable user interface with 4 | // friendly defaults. It is suitable for both interactive use and scripting. 5 | package main 6 | 7 | import ( 8 | "os" 9 | 10 | "src.elv.sh/pkg/buildinfo" 11 | "src.elv.sh/pkg/daemon" 12 | "src.elv.sh/pkg/lsp" 13 | "src.elv.sh/pkg/prog" 14 | "src.elv.sh/pkg/shell" 15 | ) 16 | 17 | func main() { 18 | os.Exit(prog.Run( 19 | [3]*os.File{os.Stdin, os.Stdout, os.Stderr}, os.Args, 20 | prog.Composite( 21 | &buildinfo.Program{}, &daemon.Program{}, &lsp.Program{}, 22 | &shell.Program{ActivateDaemon: daemon.Activate}))) 23 | } 24 | -------------------------------------------------------------------------------- /cmd/nodaemon/elvish/main.go: -------------------------------------------------------------------------------- 1 | // Command elvish is an alternative main program of Elvish that does not include 2 | // the daemon subprogram. 3 | package main 4 | 5 | import ( 6 | "os" 7 | 8 | "src.elv.sh/pkg/buildinfo" 9 | "src.elv.sh/pkg/lsp" 10 | "src.elv.sh/pkg/prog" 11 | "src.elv.sh/pkg/shell" 12 | ) 13 | 14 | func main() { 15 | os.Exit(prog.Run( 16 | [3]*os.File{os.Stdin, os.Stdout, os.Stderr}, os.Args, 17 | prog.Composite(&buildinfo.Program{}, &lsp.Program{}, &shell.Program{}))) 18 | } 19 | -------------------------------------------------------------------------------- /cmd/withpprof/elvish/main.go: -------------------------------------------------------------------------------- 1 | // Command elvish is an alternative main program of Elvish that supports writing 2 | // pprof profiles. 3 | package main 4 | 5 | import ( 6 | "os" 7 | 8 | "src.elv.sh/pkg/buildinfo" 9 | "src.elv.sh/pkg/daemon" 10 | "src.elv.sh/pkg/lsp" 11 | "src.elv.sh/pkg/pprof" 12 | "src.elv.sh/pkg/prog" 13 | "src.elv.sh/pkg/shell" 14 | ) 15 | 16 | func main() { 17 | os.Exit(prog.Run( 18 | [3]*os.File{os.Stdin, os.Stdout, os.Stderr}, os.Args, 19 | prog.Composite( 20 | &pprof.Program{}, &buildinfo.Program{}, &daemon.Program{}, &lsp.Program{}, 21 | &shell.Program{ActivateDaemon: daemon.Activate}))) 22 | } 23 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | 💡 Tip: If you are looking for docs for Elvish users, like tutorials and 2 | reference pages, refer to Elvish's website [elv.sh](https://elv.sh) instead. 3 | 4 | This directory contains developer documentation: 5 | 6 | - 🏗️ [Building Elvish from source](building.md) 7 | 8 | - 📦 [Packaging Elvish](packaging.md) 9 | 10 | - 🔑 [Security policy](security.md) 11 | 12 | - 🧩 [Using Elvish as a library](elvish-as-library.md) 13 | 14 | If you'd like to contribute to Elvish: 15 | 16 | - 🏢 17 | [Architecture overview](https://pkg.go.dev/src.elv.sh@master/docs/architecture) 18 | 19 | This document is written as a godoc comment. You can also read the Go source 20 | [architecture/doc.go](architecture/doc.go). 21 | 22 | - 👋 [Process for contributing to Elvish](contributing.md) 23 | 24 | - 🧪 [Testing changes](testing.md) 25 | 26 | - 📚 [Documenting changes](documenting.md) 27 | 28 | - 🔧 [Common development workflows](workflows.md) 29 | -------------------------------------------------------------------------------- /docs/contributing.md: -------------------------------------------------------------------------------- 1 | # Process for contributing to Elvish 2 | 3 | The only person with direct commit access is the project's founder @xiaq. If you 4 | intend to make user-visible changes to Elvish's behavior (as opposed to fixing 5 | typos and obvious bugs), it is good idea to talk to him first; this will make it 6 | easier to review your changes. He should be reachable in the user group most of 7 | the time. 8 | 9 | On the other hand, if you find it easier to express your thoughts directly in 10 | code, it is also completely fine to directly send a pull request, as long as you 11 | don't mind the risk of the PR being rejected due to lack of prior discussion. 12 | 13 | ## Licensing 14 | 15 | By contributing, you agree to license your code under the same license as 16 | existing source code of Elvish. See the [README](../README.md) at the project 17 | root for the license. 18 | -------------------------------------------------------------------------------- /docs/security.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Only the HEAD and the last release is supported by the developers of Elvish. 6 | 7 | However, since some operating systems contain outdated Elvish packages, please 8 | also feel free to get in touch for security issues in unsupported versions. You 9 | can check the versions of Elvish packages on 10 | [Repology](https://repology.org/project/elvish/versions). 11 | 12 | ## Reporting a Vulnerability 13 | 14 | Please contact Qi Xiao at xiaqqaix@gmail.com. 15 | -------------------------------------------------------------------------------- /e2e/script_test.elvts: -------------------------------------------------------------------------------- 1 | ////////////// 2 | # run script # 3 | ////////////// 4 | 5 | # with -c # 6 | ~> ./elvish -c 'echo hello from -c' 7 | hello from -c 8 | 9 | # with file # 10 | ~> echo 'echo hello from file' > script.elv 11 | ./elvish script.elv 12 | hello from file 13 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module src.elv.sh 2 | 3 | require ( 4 | github.com/creack/pty v1.1.21 5 | github.com/google/go-cmp v0.6.0 6 | github.com/mattn/go-isatty v0.0.20 7 | github.com/sourcegraph/jsonrpc2 v0.2.0 8 | go.etcd.io/bbolt v1.3.10 9 | golang.org/x/sync v0.8.0 10 | golang.org/x/sys v0.24.0 11 | pkg.nimblebun.works/go-lsp v1.1.0 12 | ) 13 | 14 | go 1.22 15 | -------------------------------------------------------------------------------- /pkg/buildinfo/buildinfo_test.elvts: -------------------------------------------------------------------------------- 1 | //each:elvish-in-global 2 | 3 | //////////////////// 4 | # program behavior # 5 | //////////////////// 6 | 7 | // Tests in this section are necessarily tautological, since we can't hardcode 8 | // the actual versions in the tests. Instead, all we do is verifying that the 9 | // output from the flags are consistent with the information in $buildinfo. 10 | 11 | ## -version ## 12 | ~> elvish -version | eq (one) $buildinfo[version] 13 | ▶ $true 14 | ~> elvish -version -json | eq (one) (to-json [$buildinfo[version]]) 15 | ▶ $true 16 | 17 | ## -buildinfo ## 18 | ~> elvish -buildinfo | eq (slurp) "Version: "$buildinfo[version]"\nGo version: "$buildinfo[go-version]"\n" 19 | ▶ $true 20 | ~> elvish -buildinfo -json | eq (one) (to-json [$buildinfo]) 21 | ▶ $true 22 | 23 | ## exits with NextProgram if neither flag is given ## 24 | ~> elvish 25 | [stderr] internal error: no suitable subprogram 26 | [exit] 2 27 | -------------------------------------------------------------------------------- /pkg/buildinfo/transcripts_test.go: -------------------------------------------------------------------------------- 1 | package buildinfo_test 2 | 3 | import ( 4 | "embed" 5 | "testing" 6 | 7 | "src.elv.sh/pkg/buildinfo" 8 | "src.elv.sh/pkg/eval/evaltest" 9 | "src.elv.sh/pkg/prog/progtest" 10 | ) 11 | 12 | //go:embed *.elvts 13 | var transcripts embed.FS 14 | 15 | func TestTranscripts(t *testing.T) { 16 | evaltest.TestTranscriptsInFS(t, transcripts, 17 | "elvish-in-global", progtest.ElvishInGlobal(&buildinfo.Program{}), 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /pkg/cli/examples/e3bc/completion.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "src.elv.sh/pkg/cli/modes" 5 | "src.elv.sh/pkg/ui" 6 | ) 7 | 8 | var items = []string{ 9 | // Functions 10 | "length(", "read(", "scale(", "sqrt(", 11 | // Functions in math library 12 | "s(", "c(", "a(", "l(", "e(", "j(", 13 | // Statements 14 | "print ", "if ", "while (", "for (", 15 | "break", "continue", "halt", "return", "return (", 16 | // Pseudo statements 17 | "limits", "quit", "warranty", 18 | } 19 | 20 | func candidates() []modes.CompletionItem { 21 | candidates := make([]modes.CompletionItem, len(items)) 22 | for i, item := range items { 23 | candidates[i] = modes.CompletionItem{ToShow: ui.T(item), ToInsert: item} 24 | } 25 | return candidates 26 | } 27 | -------------------------------------------------------------------------------- /pkg/cli/examples/nav/main.go: -------------------------------------------------------------------------------- 1 | // Command nav runs the navigation mode of the line editor. 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | 7 | "src.elv.sh/pkg/cli" 8 | "src.elv.sh/pkg/cli/modes" 9 | "src.elv.sh/pkg/cli/term" 10 | "src.elv.sh/pkg/cli/tk" 11 | ) 12 | 13 | func main() { 14 | app := cli.NewApp(cli.AppSpec{}) 15 | w, _ := modes.NewNavigation(app, modes.NavigationSpec{ 16 | Bindings: tk.MapBindings{ 17 | term.K('x'): func(tk.Widget) { app.CommitCode() }, 18 | }, 19 | }) 20 | app.PushAddon(w) 21 | 22 | code, err := app.ReadCode() 23 | fmt.Println("code:", code) 24 | if err != nil { 25 | fmt.Println("err", err) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /pkg/cli/histutil/db.go: -------------------------------------------------------------------------------- 1 | package histutil 2 | 3 | import ( 4 | "src.elv.sh/pkg/store/storedefs" 5 | ) 6 | 7 | // DB is the interface of the storage database. 8 | type DB interface { 9 | NextCmdSeq() (int, error) 10 | AddCmd(cmd string) (int, error) 11 | CmdsWithSeq(from, upto int) ([]storedefs.Cmd, error) 12 | PrevCmd(upto int, prefix string) (storedefs.Cmd, error) 13 | NextCmd(from int, prefix string) (storedefs.Cmd, error) 14 | } 15 | -------------------------------------------------------------------------------- /pkg/cli/histutil/dedup_cursor_test.go: -------------------------------------------------------------------------------- 1 | package histutil 2 | 3 | import ( 4 | "testing" 5 | 6 | "src.elv.sh/pkg/store/storedefs" 7 | ) 8 | 9 | func TestDedupCursor(t *testing.T) { 10 | s := NewMemStore("0", "1", "2") 11 | c := NewDedupCursor(s.Cursor("")) 12 | 13 | wantCmds := []storedefs.Cmd{ 14 | {Text: "0", Seq: 0}, 15 | {Text: "1", Seq: 1}, 16 | {Text: "2", Seq: 2}} 17 | 18 | testCursorIteration(t, c, wantCmds) 19 | // Go back again, this time with a full stack 20 | testCursorIteration(t, c, wantCmds) 21 | 22 | c = NewDedupCursor(s.Cursor("")) 23 | // Should be a no-op 24 | c.Next() 25 | testCursorIteration(t, c, wantCmds) 26 | 27 | c = NewDedupCursor(s.Cursor("")) 28 | c.Prev() 29 | c.Next() 30 | _, err := c.Get() 31 | if err != ErrEndOfHistory { 32 | t.Errorf("Get -> error %v, want ErrEndOfHistory", err) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /pkg/cli/histutil/doc.go: -------------------------------------------------------------------------------- 1 | // Package histutil provides utilities for working with command history. 2 | package histutil 3 | -------------------------------------------------------------------------------- /pkg/cli/histutil/mem_store_test.go: -------------------------------------------------------------------------------- 1 | package histutil 2 | 3 | import ( 4 | "testing" 5 | 6 | "src.elv.sh/pkg/store/storedefs" 7 | ) 8 | 9 | func TestMemStore_Cursor(t *testing.T) { 10 | s := NewMemStore("+ 0", "- 1", "+ 2") 11 | testCursorIteration(t, s.Cursor("+"), []storedefs.Cmd{ 12 | {Text: "+ 0", Seq: 0}, 13 | {Text: "+ 2", Seq: 2}, 14 | }) 15 | } 16 | 17 | // Remaining methods tested along with HybridStore 18 | -------------------------------------------------------------------------------- /pkg/cli/lscolors/feature_nonunix_test.go: -------------------------------------------------------------------------------- 1 | //go:build windows || plan9 || js 2 | 3 | package lscolors 4 | 5 | import ( 6 | "errors" 7 | ) 8 | 9 | var errNotSupportedOnNonUnix = errors.New("not supported on non-Unix OS") 10 | 11 | func createNamedPipe(fname string) error { 12 | return errNotSupportedOnNonUnix 13 | } 14 | -------------------------------------------------------------------------------- /pkg/cli/lscolors/feature_unix_test.go: -------------------------------------------------------------------------------- 1 | //go:build unix 2 | 3 | package lscolors 4 | 5 | import ( 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | func createNamedPipe(fname string) error { 10 | return unix.Mkfifo(fname, 0600) 11 | } 12 | -------------------------------------------------------------------------------- /pkg/cli/lscolors/stat_notsolaris.go: -------------------------------------------------------------------------------- 1 | //go:build !solaris 2 | 3 | package lscolors 4 | 5 | import "os" 6 | 7 | func isDoor(info os.FileInfo) bool { 8 | // Doors are only supported on Solaris. 9 | return false 10 | } 11 | -------------------------------------------------------------------------------- /pkg/cli/lscolors/stat_solaris.go: -------------------------------------------------------------------------------- 1 | package lscolors 2 | 3 | import ( 4 | "os" 5 | "syscall" 6 | ) 7 | 8 | // Taken from Illumos header file. 9 | const sIFDOOR = 0xD000 10 | 11 | func isDoor(info os.FileInfo) bool { 12 | return info.Sys().(*syscall.Stat_t).Mode&sIFDOOR == sIFDOOR 13 | } 14 | -------------------------------------------------------------------------------- /pkg/cli/lscolors/stat_unix.go: -------------------------------------------------------------------------------- 1 | //go:build unix 2 | 3 | package lscolors 4 | 5 | import ( 6 | "os" 7 | "syscall" 8 | ) 9 | 10 | func isMultiHardlink(info os.FileInfo) bool { 11 | // The nlink field from stat considers all the "." and ".." references to 12 | // directories to be hard links, making all directories technically 13 | // multi-hardlink (one link from parent, one "." from itself, and one ".." 14 | // for every subdirectories). However, for the purpose of filename 15 | // highlighting, only regular files should ever be considered 16 | // multi-hardlink. 17 | return !info.IsDir() && info.Sys().(*syscall.Stat_t).Nlink > 1 18 | } 19 | -------------------------------------------------------------------------------- /pkg/cli/lscolors/stat_windows.go: -------------------------------------------------------------------------------- 1 | package lscolors 2 | 3 | import "os" 4 | 5 | func isMultiHardlink(info os.FileInfo) bool { 6 | // Windows supports hardlinks, but it is not exposed directly. We omit the 7 | // implementation for now. 8 | // TODO: Maybe implement it? 9 | return false 10 | } 11 | -------------------------------------------------------------------------------- /pkg/cli/modes/filter_spec.go: -------------------------------------------------------------------------------- 1 | package modes 2 | 3 | import ( 4 | "strings" 5 | 6 | "src.elv.sh/pkg/ui" 7 | ) 8 | 9 | // FilterSpec specifies the configuration for the filter in listing modes. 10 | type FilterSpec struct { 11 | // Called with the filter text to get the filter predicate. If nil, the 12 | // predicate performs substring match. 13 | Maker func(string) func(string) bool 14 | // Highlighter for the filter. If nil, the filter will not be highlighted. 15 | Highlighter func(string) (ui.Text, []ui.Text) 16 | } 17 | 18 | func (f FilterSpec) makePredicate(p string) func(string) bool { 19 | if f.Maker == nil { 20 | return func(s string) bool { return strings.Contains(s, p) } 21 | } 22 | return f.Maker(p) 23 | } 24 | -------------------------------------------------------------------------------- /pkg/cli/term/reader_test.go: -------------------------------------------------------------------------------- 1 | package term 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | 7 | "src.elv.sh/pkg/tt" 8 | ) 9 | 10 | var Args = tt.Args 11 | 12 | func TestIsReadErrorRecoverable(t *testing.T) { 13 | tt.Test(t, IsReadErrorRecoverable, 14 | Args(seqError{}).Rets(true), 15 | Args(ErrStopped).Rets(true), 16 | Args(errTimeout).Rets(true), 17 | 18 | Args(errors.New("other error")).Rets(false), 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /pkg/cli/term/term.go: -------------------------------------------------------------------------------- 1 | // Package term provides functionality for working with terminals. 2 | package term 3 | 4 | import "src.elv.sh/pkg/logutil" 5 | 6 | var logger = logutil.GetLogger("[cli/term] ") 7 | -------------------------------------------------------------------------------- /pkg/cli/term/writer_test.go: -------------------------------------------------------------------------------- 1 | package term 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | 7 | "src.elv.sh/pkg/ui" 8 | ) 9 | 10 | func TestWriter(t *testing.T) { 11 | sb := &strings.Builder{} 12 | testOutput := func(want string) { 13 | t.Helper() 14 | if sb.String() != want { 15 | t.Errorf("got %q, want %q", sb.String(), want) 16 | } 17 | sb.Reset() 18 | } 19 | 20 | w := NewWriter(sb) 21 | w.UpdateBuffer( 22 | ui.T("note 1"), 23 | NewBufferBuilder(10).Write("line 1").SetDotHere().Buffer(), 24 | false) 25 | testOutput(hideCursor + "\r \033[J\r\033[?7h\033[mnote 1\n\033[?7lline 1\r\033[6C" + showCursor) 26 | } 27 | -------------------------------------------------------------------------------- /pkg/cli/tk/empty.go: -------------------------------------------------------------------------------- 1 | package tk 2 | 3 | import ( 4 | "src.elv.sh/pkg/cli/term" 5 | ) 6 | 7 | // Empty is an empty widget. 8 | type Empty struct{} 9 | 10 | // Render shows nothing, although the resulting Buffer still occupies one line. 11 | func (Empty) Render(width, height int) *term.Buffer { 12 | return term.NewBufferBuilder(width).Buffer() 13 | } 14 | 15 | // MaxHeight returns 1, since this widget always occupies one line. 16 | func (Empty) MaxHeight(width, height int) int { 17 | return 1 18 | } 19 | 20 | // Handle always returns false. 21 | func (Empty) Handle(event term.Event) bool { 22 | return false 23 | } 24 | -------------------------------------------------------------------------------- /pkg/cli/tk/label.go: -------------------------------------------------------------------------------- 1 | package tk 2 | 3 | import ( 4 | "src.elv.sh/pkg/cli/term" 5 | "src.elv.sh/pkg/ui" 6 | ) 7 | 8 | // Label is a Renderer that writes out a text. 9 | type Label struct { 10 | Content ui.Text 11 | } 12 | 13 | // Render shows the content. If the given box is too small, the text is cropped. 14 | func (l Label) Render(width, height int) *term.Buffer { 15 | // TODO: Optimize by stopping as soon as $height rows are written. 16 | b := l.render(width) 17 | b.TrimToLines(0, height) 18 | return b 19 | } 20 | 21 | // MaxHeight returns the maximum height the Label can take when rendering within 22 | // a bound box. 23 | func (l Label) MaxHeight(width, height int) int { 24 | return len(l.render(width).Lines) 25 | } 26 | 27 | func (l Label) render(width int) *term.Buffer { 28 | return term.NewBufferBuilder(width).WriteStyled(l.Content).Buffer() 29 | } 30 | 31 | // Handle always returns false. 32 | func (l Label) Handle(event term.Event) bool { 33 | return false 34 | } 35 | -------------------------------------------------------------------------------- /pkg/daemon/server_test.elvts: -------------------------------------------------------------------------------- 1 | //each:elvish-in-global 2 | 3 | //////////////////// 4 | # error conditions # 5 | //////////////////// 6 | 7 | ## no -daemon flag ## 8 | ~> elvish 9 | [stderr] internal error: no suitable subprogram 10 | [exit] 2 11 | 12 | ## superfluous arguments ## 13 | ~> elvish -daemon x &check-stderr-contains='arguments are not allowed with -daemon' 14 | [stderr contains "arguments are not allowed with -daemon"] true 15 | [exit] 2 16 | 17 | ## can't listen to socket ## 18 | //in-temp-dir 19 | ~> print > sock 20 | ~> elvish -daemon -sock sock -db db &check-stdout-contains='failed to listen on sock' 21 | [stdout contains "failed to listen on sock"] true 22 | [exit] 2 23 | -------------------------------------------------------------------------------- /pkg/daemon/server_unix_test.go: -------------------------------------------------------------------------------- 1 | //go:build unix 2 | 3 | package daemon 4 | 5 | import ( 6 | "os" 7 | "syscall" 8 | "testing" 9 | ) 10 | 11 | func TestProgram_QuitsOnSystemSignal_SIGINT(t *testing.T) { 12 | testProgram_QuitsOnSystemSignal(t, syscall.SIGINT) 13 | } 14 | 15 | func TestProgram_QuitsOnSystemSignal_SIGTERM(t *testing.T) { 16 | testProgram_QuitsOnSystemSignal(t, syscall.SIGTERM) 17 | } 18 | 19 | func testProgram_QuitsOnSystemSignal(t *testing.T, sig os.Signal) { 20 | t.Helper() 21 | setup(t) 22 | startServerOpts(t, cli("sock", "db"), ServeOpts{Signals: nil}) 23 | p, err := os.FindProcess(os.Getpid()) 24 | if err != nil { 25 | t.Fatalf("FindProcess: %v", err) 26 | } 27 | p.Signal(sig) 28 | // startServerOpts will wait for server to terminate at cleanup 29 | } 30 | -------------------------------------------------------------------------------- /pkg/daemon/sys_unix.go: -------------------------------------------------------------------------------- 1 | //go:build unix 2 | 3 | package daemon 4 | 5 | import ( 6 | "os" 7 | "syscall" 8 | 9 | "golang.org/x/sys/unix" 10 | ) 11 | 12 | var errConnRefused = syscall.ECONNREFUSED 13 | 14 | // Make sure that files created by the daemon is not accessible to other users. 15 | func setUmaskForDaemon() { unix.Umask(0077) } 16 | 17 | func procAttrForSpawn(files []*os.File) *os.ProcAttr { 18 | return &os.ProcAttr{ 19 | Dir: "/", 20 | Env: []string{}, 21 | Files: files, 22 | Sys: &syscall.SysProcAttr{ 23 | Setsid: true, // detach from current terminal 24 | }, 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /pkg/daemon/transcripts_test.go: -------------------------------------------------------------------------------- 1 | package daemon_test 2 | 3 | import ( 4 | "embed" 5 | "testing" 6 | 7 | "src.elv.sh/pkg/daemon" 8 | "src.elv.sh/pkg/eval/evaltest" 9 | "src.elv.sh/pkg/prog/progtest" 10 | ) 11 | 12 | //go:embed *.elvts 13 | var transcripts embed.FS 14 | 15 | func TestTranscripts(t *testing.T) { 16 | evaltest.TestTranscriptsInFS(t, transcripts, 17 | "elvish-in-global", progtest.ElvishInGlobal(&daemon.Program{}), 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /pkg/diag/doc.go: -------------------------------------------------------------------------------- 1 | // Package diag contains building blocks for formatting and processing 2 | // diagnostic information. 3 | package diag 4 | -------------------------------------------------------------------------------- /pkg/diag/range_test.go: -------------------------------------------------------------------------------- 1 | package diag 2 | 3 | import ( 4 | "testing" 5 | 6 | "src.elv.sh/pkg/tt" 7 | ) 8 | 9 | var Args = tt.Args 10 | 11 | type aRanger struct { 12 | Ranging 13 | } 14 | 15 | func TestEmbeddingRangingImplementsRanger(t *testing.T) { 16 | r := Ranging{1, 10} 17 | s := Ranger(aRanger{Ranging{1, 10}}) 18 | if s.Range() != r { 19 | t.Errorf("s.Range() = %v, want %v", s.Range(), r) 20 | } 21 | } 22 | 23 | func TestPointRanging(t *testing.T) { 24 | tt.Test(t, PointRanging, 25 | Args(1).Rets(Ranging{1, 1}), 26 | ) 27 | } 28 | 29 | func TestMixedRanging(t *testing.T) { 30 | tt.Test(t, MixedRanging, 31 | Args(Ranging{1, 2}, Ranging{0, 4}).Rets(Ranging{1, 4}), 32 | Args(Ranging{0, 4}, Ranging{1, 2}).Rets(Ranging{0, 2}), 33 | ) 34 | } 35 | -------------------------------------------------------------------------------- /pkg/diag/show_error.go: -------------------------------------------------------------------------------- 1 | package diag 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | ) 7 | 8 | // ShowError shows an error. It uses the Show method if the error 9 | // implements Shower. Otherwise, it prints the error in bold and red, with a 10 | // trailing newline. 11 | func ShowError(w io.Writer, err error) { 12 | if shower, ok := err.(Shower); ok { 13 | fmt.Fprintln(w, shower.Show("")) 14 | } else { 15 | fmt.Fprintf(w, "\033[31;1m%s\033[m\n", err.Error()) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /pkg/diag/show_error_test.go: -------------------------------------------------------------------------------- 1 | package diag 2 | 3 | import ( 4 | "errors" 5 | "strings" 6 | "testing" 7 | ) 8 | 9 | type showerError struct{} 10 | 11 | func (showerError) Error() string { return "error" } 12 | 13 | func (showerError) Show(_ string) string { return "show" } 14 | 15 | var showErrorTests = []struct { 16 | name string 17 | err error 18 | wantBuf string 19 | }{ 20 | {"A Shower error", showerError{}, "show\n"}, 21 | {"A errors.New error", errors.New("ERROR"), "\033[31;1mERROR\033[m\n"}, 22 | } 23 | 24 | func TestShowError(t *testing.T) { 25 | for _, test := range showErrorTests { 26 | t.Run(test.name, func(t *testing.T) { 27 | sb := &strings.Builder{} 28 | ShowError(sb, test.err) 29 | if sb.String() != test.wantBuf { 30 | t.Errorf("Wrote %q, want %q", sb.String(), test.wantBuf) 31 | } 32 | }) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /pkg/diag/shower.go: -------------------------------------------------------------------------------- 1 | package diag 2 | 3 | // Shower wraps the Show function. 4 | type Shower interface { 5 | // Show takes an indentation string and shows. 6 | Show(indent string) string 7 | } 8 | -------------------------------------------------------------------------------- /pkg/diag/testutil_test.go: -------------------------------------------------------------------------------- 1 | package diag 2 | 3 | import ( 4 | "testing" 5 | 6 | "src.elv.sh/pkg/testutil" 7 | ) 8 | 9 | var dedent = testutil.Dedent 10 | 11 | func setContextBodyMarkers(t *testing.T, start, end string) { 12 | testutil.Set(t, &ContextBodyStartMarker, start) 13 | testutil.Set(t, &ContextBodyEndMarker, end) 14 | } 15 | 16 | func setMessageMarkers(t *testing.T, start, end string) { 17 | testutil.Set(t, &messageStart, start) 18 | testutil.Set(t, &messageEnd, end) 19 | } 20 | -------------------------------------------------------------------------------- /pkg/edit/binding_map_test.go: -------------------------------------------------------------------------------- 1 | package edit 2 | 3 | import ( 4 | "testing" 5 | 6 | "src.elv.sh/pkg/eval" 7 | "src.elv.sh/pkg/eval/vals" 8 | "src.elv.sh/pkg/eval/vars" 9 | ) 10 | 11 | // The happy path of bindingHelp is tested in modes that use bindingHelp. 12 | 13 | func TestBindingHelp_NoBinding(t *testing.T) { 14 | ns := eval.BuildNs(). 15 | AddGoFn("a", func() {}). 16 | AddVar("binding", vars.FromInit(bindingsMap{vals.EmptyMap})). 17 | Ns() 18 | 19 | // A bindings map with no relevant binding 20 | if got := bindingTips(ns, "binding", bindingTip("do a", "a")); len(got) > 0 { 21 | t.Errorf("got %v, want empty text", got) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /pkg/edit/builtins_test.elvts: -------------------------------------------------------------------------------- 1 | //each:wordify-in-global 2 | 3 | /////////// 4 | # wordify # 5 | /////////// 6 | 7 | ~> wordify 'ls str [list]' 8 | ▶ ls 9 | ▶ str 10 | ▶ '[list]' 11 | // propagates output errors 12 | ~> wordify foo >&- 13 | Exception: port does not support value output 14 | [tty]:1:1-15: wordify foo >&- 15 | -------------------------------------------------------------------------------- /pkg/edit/command_api.d.elv: -------------------------------------------------------------------------------- 1 | # Key bindings for command mode. This is currently a very small subset of Vi 2 | # command mode bindings. 3 | # 4 | # See also [`edit:command:start`](). 5 | var command:binding 6 | 7 | # Enter command mode. This mode is intended to emulate Vi's command mode, but 8 | # it is very incomplete right now. 9 | # 10 | # See also [`$edit:command:binding`](). 11 | fn command:start { } 12 | -------------------------------------------------------------------------------- /pkg/edit/command_api.go: -------------------------------------------------------------------------------- 1 | package edit 2 | 3 | // Implementation of the editor "command" mode. 4 | 5 | import ( 6 | "src.elv.sh/pkg/cli/modes" 7 | "src.elv.sh/pkg/eval" 8 | ) 9 | 10 | func initCommandAPI(ed *Editor, ev *eval.Evaler, nb eval.NsBuilder) { 11 | bindingVar := newBindingVar(emptyBindingsMap) 12 | bindings := newMapBindings(ed, ev, bindingVar) 13 | nb.AddNs("command", 14 | eval.BuildNsNamed("edit:command"). 15 | AddVar("binding", bindingVar). 16 | AddGoFns(map[string]any{ 17 | "start": func() { 18 | w := modes.NewStub(modes.StubSpec{ 19 | Bindings: bindings, 20 | Name: " COMMAND ", 21 | }) 22 | ed.app.PushAddon(w) 23 | }, 24 | })) 25 | } 26 | -------------------------------------------------------------------------------- /pkg/edit/command_api_test.go: -------------------------------------------------------------------------------- 1 | package edit 2 | 3 | import ( 4 | "testing" 5 | 6 | "src.elv.sh/pkg/cli/term" 7 | "src.elv.sh/pkg/ui" 8 | ) 9 | 10 | func TestCommandMode(t *testing.T) { 11 | f := setup(t) 12 | 13 | evals(f.Evaler, `set edit:insert:binding[Ctrl-'['] = $edit:command:start~`) 14 | feedInput(f.TTYCtrl, "echo") 15 | f.TTYCtrl.Inject(term.K('[', ui.Ctrl)) 16 | f.TestTTY(t, 17 | "~> echo", Styles, 18 | " vvvv", term.DotHere, "\n", 19 | " COMMAND ", Styles, 20 | "*********", 21 | ) 22 | 23 | f.TTYCtrl.Inject(term.K('b')) 24 | f.TestTTY(t, 25 | "~> ", term.DotHere, 26 | "echo\n", Styles, 27 | "vvvv", 28 | " COMMAND ", Styles, 29 | "*********", 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /pkg/edit/complete/filterers.go: -------------------------------------------------------------------------------- 1 | package complete 2 | 3 | import "strings" 4 | 5 | // FilterPrefix filters raw items by prefix. It can be used as a Filterer in 6 | // Config. 7 | func FilterPrefix(ctxName, seed string, items []RawItem) []RawItem { 8 | var filtered []RawItem 9 | for _, cand := range items { 10 | if strings.HasPrefix(cand.String(), seed) { 11 | filtered = append(filtered, cand) 12 | } 13 | } 14 | return filtered 15 | } 16 | -------------------------------------------------------------------------------- /pkg/edit/editor.d.elv: -------------------------------------------------------------------------------- 1 | # A list of exceptions thrown from callbacks such as prompts. Useful for 2 | # examining tracebacks and other metadata. 3 | var exceptions 4 | -------------------------------------------------------------------------------- /pkg/edit/highlight.d.elv: -------------------------------------------------------------------------------- 1 | # Executes the currently suggested [autofix](#autofix). 2 | fn apply-autofix { } 3 | -------------------------------------------------------------------------------- /pkg/edit/highlight/testexport_test.go: -------------------------------------------------------------------------------- 1 | package highlight 2 | 3 | var MaxBlockForLate = &maxBlockForLate 4 | -------------------------------------------------------------------------------- /pkg/edit/highlight/theme.go: -------------------------------------------------------------------------------- 1 | package highlight 2 | 3 | import ( 4 | "src.elv.sh/pkg/ui" 5 | ) 6 | 7 | var stylingFor = map[string]ui.Styling{ 8 | barewordRegion: nil, 9 | singleQuotedRegion: ui.FgYellow, 10 | doubleQuotedRegion: ui.FgYellow, 11 | variableRegion: ui.FgMagenta, 12 | wildcardRegion: nil, 13 | tildeRegion: nil, 14 | 15 | commentRegion: ui.FgCyan, 16 | 17 | ">": ui.FgGreen, 18 | ">>": ui.FgGreen, 19 | "<": ui.FgGreen, 20 | "?>": ui.FgGreen, 21 | "|": ui.FgGreen, 22 | "?(": ui.Bold, 23 | "(": ui.Bold, 24 | ")": ui.Bold, 25 | "[": ui.Bold, 26 | "]": ui.Bold, 27 | "{": ui.Bold, 28 | "}": ui.Bold, 29 | "&": ui.Bold, 30 | 31 | commandRegion: ui.FgGreen, 32 | keywordRegion: ui.FgYellow, 33 | errorRegion: ui.Stylings(ui.FgBrightWhite, ui.BgRed), 34 | } 35 | 36 | var ( 37 | stylingForGoodCommand = ui.FgGreen 38 | stylingForBadCommand = ui.FgRed 39 | ) 40 | -------------------------------------------------------------------------------- /pkg/edit/histwalk.d.elv: -------------------------------------------------------------------------------- 1 | # ```elvish 2 | # edit:history:binding 3 | # ``` 4 | # 5 | # Binding table for the history mode. 6 | var history:binding 7 | 8 | # Starts the history mode. 9 | fn history:start { } 10 | 11 | # Walks to the previous entry in history mode. 12 | fn history:up { } 13 | 14 | # Walks to the next entry in history mode. 15 | fn history:down { } 16 | 17 | # Walks to the next entry in history mode, or quit the history mode if already 18 | # at the newest entry. 19 | fn history:down-or-quit { } 20 | 21 | # Import command history entries that happened after the current session 22 | # started. 23 | fn history:fast-forward { } 24 | 25 | # Replaces the content of the buffer with the current history mode entry, and 26 | # closes history mode. 27 | fn history:accept { } 28 | -------------------------------------------------------------------------------- /pkg/edit/instant.d.elv: -------------------------------------------------------------------------------- 1 | #doc:show-unstable 2 | # Binding for the instant mode. 3 | var -instant:binding 4 | 5 | #doc:show-unstable 6 | # Starts the instant mode. In instant mode, any text entered at the command 7 | # line is evaluated immediately, with the output displayed. 8 | # 9 | # **WARNING**: Beware of unintended consequences when using destructive 10 | # commands. For example, if you type `sudo rm -rf /tmp/*` in the instant mode, 11 | # Elvish will attempt to evaluate `sudo rm -rf /` when you typed that far. 12 | fn -instant:start { } 13 | -------------------------------------------------------------------------------- /pkg/edit/minibuf_test.go: -------------------------------------------------------------------------------- 1 | package edit 2 | 3 | import ( 4 | "testing" 5 | 6 | "src.elv.sh/pkg/cli/term" 7 | ) 8 | 9 | func TestMinibuf(t *testing.T) { 10 | f := setup(t) 11 | 12 | evals(f.Evaler, `edit:minibuf:start`) 13 | f.TestTTY(t, 14 | "~> \n", 15 | " MINIBUF ", Styles, 16 | "********* ", term.DotHere, 17 | ) 18 | feedInput(f.TTYCtrl, "edit:insert-at-dot put\n") 19 | f.TestTTY(t, 20 | "~> put", Styles, 21 | " vvv", term.DotHere, 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /pkg/edit/prompt.d.elv: -------------------------------------------------------------------------------- 1 | # See [Prompts](#prompts). 2 | var prompt 3 | 4 | #doc:show-unstable 5 | # See [Prompt Eagerness](#prompt-eagerness). 6 | var -prompt-eagerness 7 | 8 | # See [Stale Prompt](#stale-prompt). 9 | var prompt-stale-threshold 10 | 11 | # See [Stale Prompt](#stale-prompt). 12 | var prompt-stale-transformer. 13 | 14 | # See [Prompts](#prompts). 15 | var rprompt 16 | 17 | #doc:show-unstable 18 | # See [Prompt Eagerness](#prompt-eagerness). 19 | var -rprompt-eagerness 20 | 21 | # See [Stale Prompt](#stale-prompt). 22 | var rprompt-stale-threshold 23 | 24 | # See [Stale Prompt](#stale-prompt). 25 | var rprompt-stale-transformer. 26 | 27 | # See [RPrompt Persistency](#rprompt-persistency). 28 | var rprompt-persistent 29 | -------------------------------------------------------------------------------- /pkg/edit/state_api.d.elv: -------------------------------------------------------------------------------- 1 | # Inserts the given text at the dot, moving the dot after the newly 2 | # inserted text. 3 | fn insert-at-dot {|text| } 4 | 5 | # Equivalent to assigning `$text` to `$edit:current-command`. 6 | fn replace-input {|text| } 7 | 8 | #doc:show-unstable 9 | # Contains the current position of the cursor, as a byte position within 10 | # `$edit:current-command`. 11 | var -dot 12 | 13 | # Contains the content of the current input. Setting the variable will 14 | # cause the cursor to move to the very end, as if `edit-dot = (count 15 | # $edit:current-command)` has been invoked. 16 | # 17 | # This API is subject to change. 18 | var current-command 19 | -------------------------------------------------------------------------------- /pkg/env/env.go: -------------------------------------------------------------------------------- 1 | // Package env keeps names of environment variables with special significance to 2 | // Elvish. 3 | package env 4 | 5 | // Environment variables with special significance to Elvish. 6 | const ( 7 | HOME = "HOME" 8 | LS_COLORS = "LS_COLORS" 9 | NO_COLOR = "NO_COLOR" 10 | PATH = "PATH" 11 | PWD = "PWD" 12 | SHLVL = "SHLVL" 13 | USERNAME = "USERNAME" 14 | 15 | // Only used on Unix 16 | XDG_CONFIG_HOME = "XDG_CONFIG_HOME" 17 | XDG_DATA_DIRS = "XDG_DATA_DIRS" 18 | XDG_DATA_HOME = "XDG_DATA_HOME" 19 | XDG_RUNTIME_DIR = "XDG_RUNTIME_DIR" 20 | XDG_STATE_HOME = "XDG_STATE_HOME" 21 | 22 | // Only used on Windows 23 | PATHEXT = "PATHEXT" 24 | 25 | // Only used in tests 26 | ELVISH_TEST_TIME_SCALE = "ELVISH_TEST_TIME_SCALE" 27 | ) 28 | -------------------------------------------------------------------------------- /pkg/errutil/errutil.go: -------------------------------------------------------------------------------- 1 | // Package errutil contains common error-related utilities. 2 | package errutil 3 | -------------------------------------------------------------------------------- /pkg/eval/builtin_fn_cmd_windows.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import "errors" 4 | 5 | var errNotSupportedOnWindows = errors.New("not supported on Windows") 6 | 7 | func execFn(...any) error { 8 | return errNotSupportedOnWindows 9 | } 10 | 11 | func fg(...int) error { 12 | return errNotSupportedOnWindows 13 | } 14 | -------------------------------------------------------------------------------- /pkg/eval/builtin_fn_debug.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "runtime" 5 | 6 | "src.elv.sh/pkg/logutil" 7 | "src.elv.sh/pkg/parse" 8 | ) 9 | 10 | func init() { 11 | addBuiltinFns(map[string]any{ 12 | "src": src, 13 | "-gc": _gc, 14 | "-stack": _stack, 15 | "-log": _log, 16 | }) 17 | } 18 | 19 | func src(fm *Frame) parse.Source { 20 | return fm.src 21 | } 22 | 23 | func _gc() { 24 | runtime.GC() 25 | } 26 | 27 | func _stack(fm *Frame) error { 28 | // TODO(xiaq): Dup with main.go. 29 | buf := make([]byte, 1024) 30 | for runtime.Stack(buf, true) == cap(buf) { 31 | buf = make([]byte, cap(buf)*2) 32 | } 33 | _, err := fm.ByteOutput().Write(buf) 34 | return err 35 | } 36 | 37 | func _log(fname string) error { 38 | return logutil.SetOutputFile(fname) 39 | } 40 | -------------------------------------------------------------------------------- /pkg/eval/builtin_fn_env.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | ) 7 | 8 | // ErrNonExistentEnvVar is raised by the get-env command when the environment 9 | // variable does not exist. 10 | var ErrNonExistentEnvVar = errors.New("non-existent environment variable") 11 | 12 | func init() { 13 | addBuiltinFns(map[string]any{ 14 | "has-env": hasEnv, 15 | "get-env": getEnv, 16 | "set-env": os.Setenv, 17 | "unset-env": os.Unsetenv, 18 | }) 19 | } 20 | 21 | func hasEnv(key string) bool { 22 | _, ok := os.LookupEnv(key) 23 | return ok 24 | } 25 | 26 | func getEnv(key string) (string, error) { 27 | value, ok := os.LookupEnv(key) 28 | if !ok { 29 | return "", ErrNonExistentEnvVar 30 | } 31 | return value, nil 32 | } 33 | -------------------------------------------------------------------------------- /pkg/eval/builtin_fn_env_test.elvts: -------------------------------------------------------------------------------- 1 | /////////// 2 | # get-env # 3 | /////////// 4 | 5 | ## outputs value of existing env variable ## 6 | //set-env var test-val 7 | ~> get-env var 8 | ▶ test-val 9 | ~> put $E:var 10 | ▶ test-val 11 | 12 | ## throws if env variable doesn't exist ## 13 | //unset-env var 14 | ~> get-env var 15 | Exception: non-existent environment variable 16 | [tty]:1:1-11: get-env var 17 | 18 | /////////// 19 | # has-env # 20 | /////////// 21 | 22 | ## exists ## 23 | //set-env var test-val 24 | ~> has-env var 25 | ▶ $true 26 | 27 | ## doesn't exist ## 28 | //unset-env var 29 | ~> has-env var 30 | ▶ $false 31 | 32 | /////////// 33 | # set-env # 34 | /////////// 35 | ~> set-env var test-val 36 | ~> echo $E:var 37 | test-val 38 | -------------------------------------------------------------------------------- /pkg/eval/builtin_fn_fs.d.elv: -------------------------------------------------------------------------------- 1 | #//skip-test 2 | 3 | # Changes directory. 4 | # 5 | # This affects the entire process, including parallel tasks that are started 6 | # implicitly (such as prompt functions) or explicitly (such as one started by 7 | # [`peach`]()). 8 | # 9 | # Note that Elvish's `cd` does not support `cd -`. 10 | # 11 | # In interactive shells, [location mode](../learn/tour.html#directory-history) 12 | # provides an alternative to quickly change to past directories. 13 | # 14 | # See also [`$pwd`](). 15 | fn cd {|dirname| } 16 | 17 | # If `$path` represents a path under the home directory, replace the home 18 | # directory with `~`. Examples: 19 | # 20 | # ```elvish-transcript 21 | # ~> echo $E:HOME 22 | # /Users/foo 23 | # ~> tilde-abbr /Users/foo 24 | # ▶ '~' 25 | # ~> tilde-abbr /Users/foobar 26 | # ▶ /Users/foobar 27 | # ~> tilde-abbr /Users/foo/a/b 28 | # ▶ '~/a/b' 29 | # ``` 30 | fn tilde-abbr {|path| } 31 | -------------------------------------------------------------------------------- /pkg/eval/builtin_fn_fs.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "src.elv.sh/pkg/eval/errs" 5 | "src.elv.sh/pkg/fsutil" 6 | ) 7 | 8 | // Filesystem commands. 9 | 10 | func init() { 11 | addBuiltinFns(map[string]any{ 12 | // Directory 13 | "cd": cd, 14 | 15 | // Path 16 | "tilde-abbr": tildeAbbr, 17 | }) 18 | } 19 | 20 | func cd(fm *Frame, args ...string) error { 21 | var dir string 22 | switch len(args) { 23 | case 0: 24 | var err error 25 | dir, err = getHome("") 26 | if err != nil { 27 | return err 28 | } 29 | case 1: 30 | dir = args[0] 31 | default: 32 | return errs.ArityMismatch{What: "arguments", ValidLow: 0, ValidHigh: 1, Actual: len(args)} 33 | } 34 | 35 | return fm.Evaler.Chdir(dir) 36 | } 37 | 38 | func tildeAbbr(path string) string { 39 | return fsutil.TildeAbbr(path) 40 | } 41 | -------------------------------------------------------------------------------- /pkg/eval/builtin_fn_fs_test.elvts: -------------------------------------------------------------------------------- 1 | ////////////// 2 | # tilde-abbr # 3 | ////////////// 4 | 5 | //with-temp-home 6 | ~> tilde-abbr ~/foobar 7 | ▶ '~/foobar' 8 | 9 | ////// 10 | # cd # 11 | ////// 12 | 13 | //each:with-temp-home 14 | //each:in-temp-dir 15 | 16 | ## explicit argument ## 17 | ~> use os 18 | use path 19 | os:mkdir ~/d1 20 | ~> cd ~/d1 21 | eq $pwd (path:join ~ d1) 22 | ▶ $true 23 | 24 | ## changes to home with no argument ## 25 | ~> cd 26 | ~> eq $pwd ~ 27 | ▶ $true 28 | 29 | ## arity check ## 30 | ~> cd dir1 dir2 31 | Exception: arity mismatch: arguments must be 0 to 1 values, but is 2 values 32 | [tty]:1:1-12: cd dir1 dir2 33 | 34 | ## GetHome error ## 35 | //mock-get-home-error can't get home 36 | ~> cd 37 | Exception: can't get home 38 | [tty]:1:1-2: cd 39 | -------------------------------------------------------------------------------- /pkg/eval/builtin_ns.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "strconv" 5 | "syscall" 6 | 7 | "src.elv.sh/pkg/buildinfo" 8 | "src.elv.sh/pkg/eval/vars" 9 | ) 10 | 11 | var builtinNs = BuildNsNamed("").AddVars(map[string]vars.Var{ 12 | "_": vars.NewBlackhole(), 13 | "pid": vars.NewReadOnly(strconv.Itoa(syscall.Getpid())), 14 | "ok": vars.NewReadOnly(OK), 15 | "nil": vars.NewReadOnly(nil), 16 | "true": vars.NewReadOnly(true), 17 | "false": vars.NewReadOnly(false), 18 | "buildinfo": vars.NewReadOnly(buildinfo.Value), 19 | "version": vars.NewReadOnly(buildinfo.Value.Version), 20 | "paths": vars.NewEnvListVar("PATH"), 21 | "nop" + FnSuffix: vars.NewReadOnly(nopGoFn), 22 | }) 23 | 24 | func addBuiltinFns(fns map[string]any) { 25 | builtinNs.AddGoFns(fns) 26 | } 27 | -------------------------------------------------------------------------------- /pkg/eval/callable.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | // Callable wraps the Call method. 4 | type Callable interface { 5 | // Call calls the receiver in a Frame with arguments and options. 6 | Call(fm *Frame, args []any, opts map[string]any) error 7 | } 8 | 9 | var ( 10 | // NoArgs is an empty argument list. It can be used as an argument to Call. 11 | NoArgs = []any{} 12 | // NoOpts is an empty option map. It can be used as an argument to Call. 13 | NoOpts = map[string]any{} 14 | ) 15 | -------------------------------------------------------------------------------- /pkg/eval/compiler_test.elvts: -------------------------------------------------------------------------------- 1 | //////////////////////////// 2 | # compile-time deprecation # 3 | //////////////////////////// 4 | 5 | //deprecation-level 21 6 | //in-temp-dir 7 | // This test will need to be frequently updated as deprecated commands get 8 | // removed. 9 | // 10 | // Deprecations of other builtins are implemented in the same way, so we 11 | // don't test them repeatedly 12 | 13 | ~> use os 14 | os:mkdir foo 15 | ~> ./foo 16 | Deprecation: implicit cd is deprecated; use cd or location mode instead 17 | [tty]:1:1-5: ./foo 18 | 19 | /////////////////////////////// 20 | # multiple compilation errors # 21 | /////////////////////////////// 22 | 23 | ~> echo $x; echo $y 24 | Multiple compilation errors: 25 | variable $x not found 26 | [tty]:1:6-7: echo $x; echo $y 27 | variable $y not found 28 | [tty]:1:15-16: echo $x; echo $y 29 | -------------------------------------------------------------------------------- /pkg/eval/deprecation.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "src.elv.sh/pkg/diag" 5 | ) 6 | 7 | type deprecationRegistry struct { 8 | registered map[deprecation]struct{} 9 | } 10 | 11 | func newDeprecationRegistry() deprecationRegistry { 12 | return deprecationRegistry{registered: make(map[deprecation]struct{})} 13 | } 14 | 15 | type deprecation struct { 16 | srcName string 17 | location diag.Ranging 18 | message string 19 | } 20 | 21 | // Registers a deprecation, and returns whether it was registered for the first 22 | // time. 23 | func (r *deprecationRegistry) register(dep deprecation) bool { 24 | if _, ok := r.registered[dep]; ok { 25 | return false 26 | } 27 | r.registered[dep] = struct{}{} 28 | return true 29 | } 30 | -------------------------------------------------------------------------------- /pkg/eval/evaltest/non_unix.go: -------------------------------------------------------------------------------- 1 | //go:build !unix 2 | 3 | package evaltest 4 | 5 | const isUNIX = false 6 | -------------------------------------------------------------------------------- /pkg/eval/evaltest/unix.go: -------------------------------------------------------------------------------- 1 | //go:build unix 2 | 3 | package evaltest 4 | 5 | const isUNIX = true 6 | -------------------------------------------------------------------------------- /pkg/eval/exception_test.elvts: -------------------------------------------------------------------------------- 1 | ////////////////////// 2 | # Flow introspection # 3 | ////////////////////// 4 | ~> put ?(return)[reason][type name] 5 | ▶ flow 6 | ▶ return 7 | 8 | ///////////////////////////////// 9 | # ExternalCmdExit introspection # 10 | ///////////////////////////////// 11 | 12 | ## Unix ## 13 | //only-on unix 14 | ~> put ?(false)[reason][type exit-status] 15 | ▶ external-cmd/exited 16 | ▶ 1 17 | 18 | ## Windows ## 19 | //only-on windows 20 | ~> put ?(cmd /c exit 1)[reason][type exit-status] 21 | ▶ external-cmd/exited 22 | ▶ 1 23 | 24 | // TODO: Test killed and stopped commands 25 | 26 | /////////////////////////////// 27 | # PipelineError introspection # 28 | /////////////////////////////// 29 | 30 | ~> put ?(fail 1 | fail 2)[reason][type] 31 | ▶ pipeline 32 | ~> count ?(fail 1 | fail 2)[reason][exceptions] 33 | ▶ (num 2) 34 | ~> put ?(fail 1 | fail 2)[reason][exceptions][0][reason][type] 35 | ▶ fail 36 | -------------------------------------------------------------------------------- /pkg/eval/external_cmd_unix.go: -------------------------------------------------------------------------------- 1 | //go:build unix 2 | 3 | package eval 4 | 5 | import "syscall" 6 | 7 | func isSIGPIPE(s syscall.Signal) bool { 8 | return s == syscall.SIGPIPE 9 | } 10 | -------------------------------------------------------------------------------- /pkg/eval/external_cmd_windows.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import "syscall" 4 | 5 | func isSIGPIPE(s syscall.Signal) bool { 6 | // Windows doesn't have SIGPIPE. 7 | return false 8 | } 9 | -------------------------------------------------------------------------------- /pkg/eval/fuzz_test.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "testing" 5 | 6 | "src.elv.sh/pkg/parse" 7 | ) 8 | 9 | func FuzzCheck(f *testing.F) { 10 | f.Add("echo") 11 | f.Add("put $x") 12 | f.Add("put foo bar | each {|x| echo $x }") 13 | f.Fuzz(func(t *testing.T, code string) { 14 | NewEvaler().Check(parse.Source{Name: "[fuzz]", Code: code}, nil) 15 | }) 16 | } 17 | -------------------------------------------------------------------------------- /pkg/eval/generic_utils.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | // Some generic utils that should appear in the standard library soon. 4 | 5 | func mapKeys[K comparable, V any](m map[K]V) []K { 6 | ks := make([]K, 0, len(m)) 7 | for k := range m { 8 | ks = append(ks, k) 9 | } 10 | return ks 11 | } 12 | 13 | func sliceContains[T comparable](xs []T, y T) bool { 14 | for _, x := range xs { 15 | if x == y { 16 | return true 17 | } 18 | } 19 | return false 20 | } 21 | -------------------------------------------------------------------------------- /pkg/eval/go_fn_internal_test.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "testing" 5 | "unsafe" 6 | 7 | "src.elv.sh/pkg/eval/vals" 8 | "src.elv.sh/pkg/persistent/hash" 9 | ) 10 | 11 | func TestGoFnAsValue(t *testing.T) { 12 | fn1 := NewGoFn("fn1", func() {}) 13 | fn2 := NewGoFn("fn2", func() {}) 14 | vals.TestValue(t, fn1). 15 | Kind("fn"). 16 | Hash(hash.Pointer(unsafe.Pointer(fn1.(*goFn)))). 17 | Equal(fn1). 18 | NotEqual(fn2). 19 | Repr("") 20 | } 21 | -------------------------------------------------------------------------------- /pkg/eval/hook_test.elvts: -------------------------------------------------------------------------------- 1 | //each:call-hook-in-global 2 | 3 | ~> call-hook test-hook [{ echo hook1 } { echo hook2 }] 4 | hook1 5 | hook2 6 | // Arguments 7 | ~> call-hook test-hook [{|x| echo hook$x }] foo 8 | hookfoo 9 | // Invalid hook list 10 | ~> call-hook test-hook [not-a-fn] 11 | hook test-hook[0] must be callable 12 | // Exception thrown from hook prints the exception to port 2, rather than being 13 | // propagated 14 | ~> call-hook test-hook [{ fail bad }] 15 | echo after call-hook >&2 16 | Exception: bad 17 | [tty]:1:24-32: call-hook test-hook [{ fail bad }] 18 | after call-hook 19 | -------------------------------------------------------------------------------- /pkg/eval/interrupts.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "os" 7 | "os/signal" 8 | "syscall" 9 | ) 10 | 11 | // ErrInterrupted is thrown when the execution is interrupted by a signal. 12 | var ErrInterrupted = errors.New("interrupted") 13 | 14 | // ListenInterrupts returns a Context that is canceled when SIGINT or SIGQUIT 15 | // has been received by the process. It also returns a function to cancel the 16 | // Context, which should be called when it is no longer needed. 17 | func ListenInterrupts() (context.Context, func()) { 18 | ctx, cancel := context.WithCancel(context.Background()) 19 | 20 | sigCh := make(chan os.Signal, 1) 21 | signal.Notify(sigCh, syscall.SIGINT, syscall.SIGQUIT) 22 | 23 | go func() { 24 | select { 25 | case <-sigCh: 26 | cancel() 27 | case <-ctx.Done(): 28 | } 29 | signal.Stop(sigCh) 30 | }() 31 | 32 | return ctx, func() { 33 | cancel() 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /pkg/eval/ns_test.elvts: -------------------------------------------------------------------------------- 1 | ////// 2 | # ns # 3 | ////// 4 | 5 | ~> kind-of (ns [&]) 6 | ▶ ns 7 | 8 | ## equality ## 9 | // an Ns is only equal to itself ## 10 | ~> var ns = (ns [&]) 11 | eq $ns $ns 12 | ▶ $true 13 | ~> eq (ns [&]) (ns [&]) 14 | ▶ $false 15 | ~> eq (ns [&]) [&] 16 | ▶ $false 17 | 18 | ## access ## 19 | ~> var ns: = (ns [&a=b &x=y]) 20 | put $ns:a 21 | ▶ b 22 | ~> var ns: = (ns [&a=b &x=y]) 23 | put $ns:[a] 24 | ▶ b 25 | 26 | ## keys ## 27 | ~> keys (ns [&a=b &x=y]) | order 28 | ▶ a 29 | ▶ x 30 | 31 | ## has-key ## 32 | ~> has-key (ns [&a=b &x=y]) a 33 | ▶ $true 34 | ~> has-key (ns [&a=b &x=y]) b 35 | ▶ $false 36 | -------------------------------------------------------------------------------- /pkg/eval/options_test.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | 7 | "src.elv.sh/pkg/tt" 8 | ) 9 | 10 | var Args = tt.Args 11 | 12 | type opts struct { 13 | Foo string 14 | } 15 | 16 | // Equal is required by cmp.Diff, since opts contains unexported fields. 17 | func (o opts) Equal(p opts) bool { return o == p } 18 | 19 | func TestScanOptions(t *testing.T) { 20 | // A wrapper of ScanOptions, to make it easier to test 21 | wrapper := func(src RawOptions, dstInit any) (any, error) { 22 | ptr := reflect.New(reflect.TypeOf(dstInit)) 23 | ptr.Elem().Set(reflect.ValueOf(dstInit)) 24 | err := scanOptions(src, ptr.Interface()) 25 | return ptr.Elem().Interface(), err 26 | } 27 | 28 | tt.Test(t, tt.Fn(wrapper).Named("scanOptions"), 29 | Args(RawOptions{"foo": "lorem ipsum"}, opts{}). 30 | Rets(opts{Foo: "lorem ipsum"}, nil), 31 | Args(RawOptions{"bar": 20}, opts{}). 32 | Rets(opts{}, UnknownOption{"bar"}), 33 | ) 34 | } 35 | -------------------------------------------------------------------------------- /pkg/eval/plugin.go: -------------------------------------------------------------------------------- 1 | //go:build !gccgo 2 | 3 | package eval 4 | 5 | import "plugin" 6 | 7 | var pluginOpen = plugin.Open 8 | -------------------------------------------------------------------------------- /pkg/eval/plugin_gccgo.go: -------------------------------------------------------------------------------- 1 | //go:build gccgo 2 | 3 | package eval 4 | 5 | import "errors" 6 | 7 | var errPluginNotImplemented = errors.New("plugin not implemented") 8 | 9 | type pluginStub struct{} 10 | 11 | func pluginOpen(name string) (pluginStub, error) { 12 | return pluginStub{}, errPluginNotImplemented 13 | } 14 | 15 | func (pluginStub) Lookup(symName string) (any, error) { 16 | return nil, errPluginNotImplemented 17 | } 18 | -------------------------------------------------------------------------------- /pkg/eval/port_unix.go: -------------------------------------------------------------------------------- 1 | //go:build unix 2 | 3 | package eval 4 | 5 | import "syscall" 6 | 7 | var epipe = syscall.EPIPE 8 | -------------------------------------------------------------------------------- /pkg/eval/port_windows.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import "syscall" 4 | 5 | // Error number 232 is what Windows returns when trying to write on a pipe who 6 | // reader has gone. The syscall package defines an EPIPE on Windows, but that's 7 | // not what Windows API actually returns. 8 | // 9 | // https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499- 10 | var epipe = syscall.Errno(232) 11 | -------------------------------------------------------------------------------- /pkg/eval/process_unix.go: -------------------------------------------------------------------------------- 1 | //go:build unix 2 | 3 | package eval 4 | 5 | import ( 6 | "os" 7 | "os/signal" 8 | "syscall" 9 | 10 | "src.elv.sh/pkg/sys" 11 | "src.elv.sh/pkg/sys/eunix" 12 | ) 13 | 14 | // Process control functions in Unix. 15 | 16 | func putSelfInFg() error { 17 | if !sys.IsATTY(os.Stdin.Fd()) { 18 | return nil 19 | } 20 | // If Elvish is in the background, the tcsetpgrp call below will either fail 21 | // (if the process is in an orphaned process group) or stop the process. 22 | // Ignoring TTOU fixes that. 23 | signal.Ignore(syscall.SIGTTOU) 24 | defer signal.Reset(syscall.SIGTTOU) 25 | return eunix.Tcsetpgrp(0, syscall.Getpgrp()) 26 | } 27 | 28 | func makeSysProcAttr(bg bool) *syscall.SysProcAttr { 29 | return &syscall.SysProcAttr{Setpgid: bg} 30 | } 31 | -------------------------------------------------------------------------------- /pkg/eval/process_windows.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import "syscall" 4 | 5 | // Nop on Windows. 6 | func putSelfInFg() error { return nil } 7 | 8 | // The bitmask for CreationFlags in SysProcAttr to start a process in background. 9 | const detachedProcess = 0x00000008 10 | 11 | func makeSysProcAttr(bg bool) *syscall.SysProcAttr { 12 | flags := uint32(0) 13 | if bg { 14 | flags |= detachedProcess 15 | } 16 | return &syscall.SysProcAttr{CreationFlags: flags} 17 | } 18 | -------------------------------------------------------------------------------- /pkg/eval/testdata/fuzz/FuzzCheck/30e5cf5b35c294c05ffc72c31859e85bd4c663bbac718db7b941824b8d74af82: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("{}= 0") 3 | -------------------------------------------------------------------------------- /pkg/eval/testexport_test.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | // Pointers to variables that can be mutated for testing. 4 | var ( 5 | GetHome = &getHome 6 | Getwd = &getwd 7 | OSExit = &osExit 8 | TimeAfter = &timeAfter 9 | TimeNow = &timeNow 10 | NextEvalCount = &nextEvalCount 11 | 12 | ExceptionCauseStartMarker = &exceptionCauseStartMarker 13 | ExceptionCauseEndMarker = &exceptionCauseEndMarker 14 | ) 15 | -------------------------------------------------------------------------------- /pkg/eval/testutil_test.go: -------------------------------------------------------------------------------- 1 | package eval_test 2 | 3 | import ( 4 | "src.elv.sh/pkg/testutil" 5 | "src.elv.sh/pkg/tt" 6 | ) 7 | 8 | var ( 9 | It = tt.It 10 | Dedent = testutil.Dedent 11 | ) 12 | -------------------------------------------------------------------------------- /pkg/eval/transcripts_unix_test.go: -------------------------------------------------------------------------------- 1 | //go:build unix 2 | 3 | package eval_test 4 | 5 | import ( 6 | "os" 7 | "testing" 8 | "time" 9 | 10 | "golang.org/x/sys/unix" 11 | "src.elv.sh/pkg/eval" 12 | "src.elv.sh/pkg/testutil" 13 | ) 14 | 15 | func injectTimeAfterWithSIGINTOrSkip(t *testing.T) { 16 | testutil.Set(t, eval.TimeAfter, 17 | func(_ *eval.Frame, d time.Duration) <-chan time.Time { 18 | go unix.Kill(os.Getpid(), unix.SIGINT) 19 | return time.After(d) 20 | }) 21 | } 22 | -------------------------------------------------------------------------------- /pkg/eval/transcripts_windows_test.go: -------------------------------------------------------------------------------- 1 | package eval_test 2 | 3 | import "testing" 4 | 5 | func injectTimeAfterWithSIGINTOrSkip(t *testing.T) { t.Skip() } 6 | -------------------------------------------------------------------------------- /pkg/eval/vals/aliased_types_test.go: -------------------------------------------------------------------------------- 1 | package vals 2 | 3 | import ( 4 | "testing" 5 | 6 | "src.elv.sh/pkg/testutil" 7 | "src.elv.sh/pkg/tt" 8 | ) 9 | 10 | var Args = tt.Args 11 | 12 | func TestMakeMap_PanicsWithOddNumberOfArguments(t *testing.T) { 13 | tt.Test(t, testutil.Recover, 14 | //lint:ignore SA5012 testing panic 15 | Args(func() { MakeMap("foo") }).Rets("odd number of arguments to MakeMap"), 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /pkg/eval/vals/bool.go: -------------------------------------------------------------------------------- 1 | package vals 2 | 3 | // Booler wraps the Bool method. 4 | type Booler interface { 5 | // Bool computes the truth value of the receiver. 6 | Bool() bool 7 | } 8 | 9 | // Bool converts a value to bool. It is implemented for nil, the builtin bool 10 | // type, and types implementing the Booler interface. For all other values, it 11 | // returns true. 12 | func Bool(v any) bool { 13 | switch v := v.(type) { 14 | case nil: 15 | return false 16 | case bool: 17 | return v 18 | case Booler: 19 | return v.Bool() 20 | } 21 | return true 22 | } 23 | -------------------------------------------------------------------------------- /pkg/eval/vals/bool_test.go: -------------------------------------------------------------------------------- 1 | package vals 2 | 3 | import ( 4 | "testing" 5 | 6 | "src.elv.sh/pkg/tt" 7 | ) 8 | 9 | type customBooler struct{ b bool } 10 | 11 | func (b customBooler) Bool() bool { return b.b } 12 | 13 | type customNonBooler struct{} 14 | 15 | func TestBool(t *testing.T) { 16 | tt.Test(t, Bool, 17 | Args(nil).Rets(false), 18 | 19 | Args(true).Rets(true), 20 | Args(false).Rets(false), 21 | 22 | Args(customBooler{true}).Rets(true), 23 | Args(customBooler{false}).Rets(false), 24 | 25 | Args(customNonBooler{}).Rets(true), 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /pkg/eval/vals/cmp_test.go: -------------------------------------------------------------------------------- 1 | package vals 2 | 3 | import ( 4 | "testing" 5 | 6 | "src.elv.sh/pkg/tt" 7 | ) 8 | 9 | func TestCmp(t *testing.T) { 10 | // Cmp is tested by tests of the Elvish compare command. 11 | } 12 | 13 | func TestCmpTotal_FieldMap(t *testing.T) { 14 | // CmpTotal should pretend that field maps are maps too. Since maps don't 15 | // have an internal ordering, comparing a field map to another field map or 16 | // to a map should always return CmpEqual, like comparing two maps. 17 | // 18 | // This is not covered by tests of the Elvish compare command because Elvish 19 | // code are not supposed to know which values are actually field maps. 20 | x := fieldMap{} 21 | y := fieldMap{} 22 | z := EmptyMap 23 | tt.Test(t, CmpTotal, 24 | tt.Args(x, y).Rets(CmpEqual), 25 | tt.Args(x, z).Rets(CmpEqual), 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /pkg/eval/vals/dissoc.go: -------------------------------------------------------------------------------- 1 | package vals 2 | 3 | // Dissocer wraps the Dissoc method. 4 | type Dissocer interface { 5 | // Dissoc returns a slightly modified version of the receiver with key k 6 | // dissociated with any value. 7 | Dissoc(k any) any 8 | } 9 | 10 | // Dissoc takes a container and a key, and returns a modified version of the 11 | // container, with the given key dissociated with any value. It is implemented 12 | // for the Map type and types satisfying the Dissocer interface. For other 13 | // types, it returns nil. 14 | func Dissoc(a, k any) any { 15 | switch a := a.(type) { 16 | case Map: 17 | return a.Dissoc(k) 18 | case Dissocer: 19 | return a.Dissoc(k) 20 | default: 21 | if keys := GetFieldMapKeys(a); keys != nil { 22 | return promoteFieldMapToMap(a, keys).Dissoc(k) 23 | } 24 | return nil 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /pkg/eval/vals/dissoc_test.go: -------------------------------------------------------------------------------- 1 | package vals 2 | 3 | import ( 4 | "testing" 5 | 6 | "src.elv.sh/pkg/tt" 7 | ) 8 | 9 | type dissocer struct{} 10 | 11 | func (dissocer) Dissoc(any) any { return "custom ret" } 12 | 13 | func TestDissoc(t *testing.T) { 14 | tt.Test(t, Dissoc, 15 | Args(MakeMap("k1", "v1", "k2", "v2"), "k1"). 16 | Rets(eq(MakeMap("k2", "v2"))), 17 | Args(fieldMap{"lorem", "ipsum", 23}, "foo-bar"). 18 | Rets(eq(MakeMap("foo", "lorem", "bar", "ipsum"))), 19 | Args(dissocer{}, "x").Rets("custom ret"), 20 | Args("", "x").Rets(nil), 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /pkg/eval/vals/doc.go: -------------------------------------------------------------------------------- 1 | // Package vals contains basic facilities for manipulating values used in the 2 | // Elvish runtime. 3 | package vals 4 | -------------------------------------------------------------------------------- /pkg/eval/vals/errors_test.go: -------------------------------------------------------------------------------- 1 | package vals 2 | 3 | import ( 4 | "testing" 5 | 6 | "src.elv.sh/pkg/tt" 7 | ) 8 | 9 | func TestErrors(t *testing.T) { 10 | tt.Test(t, error.Error, 11 | Args(cannotIterate{"num"}).Rets("cannot iterate num"), 12 | Args(cannotIterateKeysOf{"num"}).Rets("cannot iterate keys of num"), 13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /pkg/eval/vals/feed.go: -------------------------------------------------------------------------------- 1 | package vals 2 | 3 | // Feed calls the function with given values, breaking earlier if the function 4 | // returns false. 5 | func Feed(f func(any) bool, values ...any) { 6 | for _, value := range values { 7 | if !f(value) { 8 | break 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /pkg/eval/vals/feed_test.go: -------------------------------------------------------------------------------- 1 | package vals 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestFeed(t *testing.T) { 9 | var fed []any 10 | 11 | Feed(func(x any) bool { 12 | fed = append(fed, x) 13 | return x != 10 14 | }, 1, 2, 3, 10, 11, 12, 13) 15 | 16 | wantFed := []any{1, 2, 3, 10} 17 | if !reflect.DeepEqual(fed, wantFed) { 18 | t.Errorf("Fed %v, want %v", fed, wantFed) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /pkg/eval/vals/kind_test.go: -------------------------------------------------------------------------------- 1 | package vals 2 | 3 | import ( 4 | "math/big" 5 | "os" 6 | "testing" 7 | 8 | "src.elv.sh/pkg/tt" 9 | ) 10 | 11 | type xtype int 12 | 13 | func TestKind(t *testing.T) { 14 | tt.Test(t, Kind, 15 | Args(nil).Rets("nil"), 16 | Args(true).Rets("bool"), 17 | Args("").Rets("string"), 18 | Args(1).Rets("number"), 19 | Args(bigInt(z)).Rets("number"), 20 | Args(big.NewRat(1, 2)).Rets("number"), 21 | Args(1.0).Rets("number"), 22 | Args(os.Stdin).Rets("file"), 23 | Args(EmptyList).Rets("list"), 24 | Args(EmptyMap).Rets("map"), 25 | Args(xtype(0)).Rets("!!vals.xtype"), 26 | Args(os.Stdin).Rets("file"), 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /pkg/eval/vals/len.go: -------------------------------------------------------------------------------- 1 | package vals 2 | 3 | import ( 4 | "src.elv.sh/pkg/persistent/vector" 5 | ) 6 | 7 | // Lener wraps the Len method. 8 | type Lener interface { 9 | // Len computes the length of the receiver. 10 | Len() int 11 | } 12 | 13 | var _ Lener = vector.Vector(nil) 14 | 15 | // Len returns the length of the value, or -1 if the value does not have a 16 | // well-defined length. It is implemented for the builtin type string, field map 17 | // types, and types satisfying the Lener interface. For other types, it returns 18 | // -1. 19 | func Len(v any) int { 20 | switch v := v.(type) { 21 | case string: 22 | return len(v) 23 | case Lener: 24 | return v.Len() 25 | default: 26 | if keys := GetFieldMapKeys(v); keys != nil { 27 | return len(keys) 28 | } 29 | return -1 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /pkg/eval/vals/len_test.go: -------------------------------------------------------------------------------- 1 | package vals 2 | 3 | import ( 4 | "testing" 5 | 6 | "src.elv.sh/pkg/tt" 7 | ) 8 | 9 | func TestLen(t *testing.T) { 10 | tt.Test(t, Len, 11 | Args("foobar").Rets(6), 12 | Args(10).Rets(-1), 13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /pkg/eval/vals/pipe.go: -------------------------------------------------------------------------------- 1 | package vals 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | // Pipe wraps a pair of [*os.File] that are the two ends of a pipe. 8 | type Pipe struct{ R, W *os.File } 9 | -------------------------------------------------------------------------------- /pkg/eval/vals/reflect_wrappers.go: -------------------------------------------------------------------------------- 1 | package vals 2 | 3 | import "reflect" 4 | 5 | var ( 6 | dummy any 7 | nilValue = reflect.ValueOf(&dummy).Elem() 8 | emptyInterfaceType = reflect.TypeOf(&dummy).Elem() 9 | ) 10 | 11 | // ValueOf is like reflect.ValueOf, except that when given an argument of nil, 12 | // it does not return a zero Value, but the Value for the zero value of the 13 | // empty interface. 14 | func ValueOf(i any) reflect.Value { 15 | if i == nil { 16 | return nilValue 17 | } 18 | return reflect.ValueOf(i) 19 | } 20 | 21 | // TypeOf is like reflect.TypeOf, except that when given an argument of nil, it 22 | // does not return nil, but the Type for the empty interface. 23 | func TypeOf(i any) reflect.Type { 24 | if i == nil { 25 | return emptyInterfaceType 26 | } 27 | return reflect.TypeOf(i) 28 | } 29 | -------------------------------------------------------------------------------- /pkg/eval/vals/string_test.go: -------------------------------------------------------------------------------- 1 | package vals 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "src.elv.sh/pkg/tt" 8 | ) 9 | 10 | func TestToString(t *testing.T) { 11 | tt.Test(t, ToString, 12 | // string 13 | Args("a").Rets("a"), 14 | 15 | Args(1).Rets("1"), 16 | 17 | // float64 18 | Args(0.1).Rets("0.1"), 19 | Args(42.0).Rets("42.0"), 20 | // Whole numbers with more than 14 digits and trailing 0 are printed in 21 | // scientific notation. 22 | Args(1e13).Rets("10000000000000.0"), 23 | Args(1e14).Rets("1e+14"), 24 | Args(1e14+1).Rets("100000000000001.0"), 25 | // Numbers smaller than 0.0001 are printed in scientific notation. 26 | Args(0.0001).Rets("0.0001"), 27 | Args(0.00001).Rets("1e-05"), 28 | Args(0.00009).Rets("9e-05"), 29 | 30 | // Stringer 31 | Args(bytes.NewBufferString("buffer")).Rets("buffer"), 32 | // None of the above: delegate to Repr 33 | Args(true).Rets("$true"), 34 | ) 35 | } 36 | -------------------------------------------------------------------------------- /pkg/eval/vals/struct_map_test.go: -------------------------------------------------------------------------------- 1 | package vals 2 | -------------------------------------------------------------------------------- /pkg/eval/vals/testutils_test.go: -------------------------------------------------------------------------------- 1 | package vals 2 | 3 | import ( 4 | "src.elv.sh/pkg/tt" 5 | ) 6 | 7 | // Returns a tt.Matcher that matches using the Equal function. 8 | func eq(r any) tt.Matcher { return equalMatcher{r} } 9 | 10 | type equalMatcher struct{ want any } 11 | 12 | func (em equalMatcher) Match(got tt.RetValue) bool { return Equal(got, em.want) } 13 | -------------------------------------------------------------------------------- /pkg/eval/vars/blackhole.go: -------------------------------------------------------------------------------- 1 | package vars 2 | 3 | type blackhole struct{} 4 | 5 | func (blackhole) Set(any) error { 6 | return nil 7 | } 8 | 9 | func (blackhole) Get() any { 10 | return nil 11 | } 12 | 13 | // NewBlackhole returns a blackhole variable. Assignments to a blackhole 14 | // variable will be discarded, and getting a blackhole variable always returns 15 | // nil. 16 | func NewBlackhole() Var { 17 | return blackhole{} 18 | } 19 | 20 | // IsBlackhole returns whether the variable is a blackhole variable. 21 | func IsBlackhole(v Var) bool { 22 | _, ok := v.(blackhole) 23 | return ok 24 | } 25 | -------------------------------------------------------------------------------- /pkg/eval/vars/blackhole_test.go: -------------------------------------------------------------------------------- 1 | package vars 2 | 3 | import ( 4 | "testing" 5 | 6 | "src.elv.sh/pkg/tt" 7 | ) 8 | 9 | var Args = tt.Args 10 | 11 | func TestBlackhole(t *testing.T) { 12 | v := NewBlackhole() 13 | err := v.Set("foo") 14 | if err != nil { 15 | t.Errorf("v.Set(%q) -> %v, want nil", "foo", err) 16 | } 17 | val := v.Get() 18 | if val != nil { 19 | t.Errorf("v.Get() -> %v, want nil", val) 20 | } 21 | } 22 | 23 | func TestIsBlackhole(t *testing.T) { 24 | tt.Test(t, IsBlackhole, 25 | Args(NewBlackhole()).Rets(true), 26 | Args(FromInit("")).Rets(false), 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /pkg/eval/vars/callback.go: -------------------------------------------------------------------------------- 1 | package vars 2 | 3 | import ( 4 | "src.elv.sh/pkg/eval/errs" 5 | ) 6 | 7 | type callback struct { 8 | set func(any) error 9 | get func() any 10 | } 11 | 12 | // FromSetGet makes a variable from a set callback and a get callback. 13 | func FromSetGet(set func(any) error, get func() any) Var { 14 | return &callback{set, get} 15 | } 16 | 17 | func (cv *callback) Set(val any) error { 18 | return cv.set(val) 19 | } 20 | 21 | func (cv *callback) Get() any { 22 | return cv.get() 23 | } 24 | 25 | type roCallback func() any 26 | 27 | // FromGet makes a variable from a get callback. The variable is read-only. 28 | func FromGet(get func() any) Var { 29 | return roCallback(get) 30 | } 31 | 32 | func (cv roCallback) Set(any) error { 33 | return errs.SetReadOnlyVar{} 34 | } 35 | 36 | func (cv roCallback) Get() any { 37 | return cv() 38 | } 39 | -------------------------------------------------------------------------------- /pkg/eval/vars/env.go: -------------------------------------------------------------------------------- 1 | package vars 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | ) 7 | 8 | var errEnvMustBeString = errors.New("environment variable can only be set string values") 9 | 10 | type envVariable struct { 11 | name string 12 | } 13 | 14 | func (ev envVariable) Set(val any) error { 15 | if s, ok := val.(string); ok { 16 | os.Setenv(ev.name, s) 17 | return nil 18 | } 19 | return errEnvMustBeString 20 | } 21 | 22 | func (ev envVariable) Get() any { 23 | return os.Getenv(ev.name) 24 | } 25 | 26 | func (ev envVariable) Unset() error { 27 | return os.Unsetenv(ev.name) 28 | } 29 | 30 | func (ev envVariable) IsSet() bool { 31 | _, ok := os.LookupEnv(ev.name) 32 | return ok 33 | } 34 | 35 | // FromEnv returns a Var corresponding to the named environment variable. 36 | func FromEnv(name string) UnsettableVar { 37 | return envVariable{name} 38 | } 39 | -------------------------------------------------------------------------------- /pkg/eval/vars/env_test.go: -------------------------------------------------------------------------------- 1 | package vars 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "src.elv.sh/pkg/testutil" 8 | ) 9 | 10 | func TestEnvVariable(t *testing.T) { 11 | name := "elvish_test" 12 | testutil.Unsetenv(t, name) 13 | 14 | v := FromEnv(name).(envVariable) 15 | 16 | if set := v.IsSet(); set != false { 17 | t.Errorf("EnvVariable.Set returns true for unset env variable") 18 | } 19 | 20 | err := v.Set("foo") 21 | if err != nil || os.Getenv(name) != "foo" { 22 | t.Errorf("EnvVariable.Set doesn't alter env value") 23 | } 24 | 25 | if set := v.IsSet(); set != true { 26 | t.Errorf("EnvVariable.Set returns false for set env variable") 27 | } 28 | 29 | err = v.Set(true) 30 | if err != errEnvMustBeString { 31 | t.Errorf("envVariable.Set to a non-string value didn't return an error") 32 | } 33 | 34 | os.Setenv(name, "bar") 35 | if v.Get() != "bar" { 36 | t.Errorf("EnvVariable.Get doesn't return value set elsewhere") 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /pkg/eval/vars/ptr_test.go: -------------------------------------------------------------------------------- 1 | package vars 2 | 3 | import "testing" 4 | 5 | func TestFromPtr(t *testing.T) { 6 | i := 10 7 | variable := FromPtr(&i) 8 | if g := variable.Get(); g != 10 { 9 | t.Errorf(`Get -> %v, want 10`, g) 10 | } 11 | err := variable.Set("20") 12 | if err != nil { 13 | t.Errorf(`Setting ptrVariable with "20" returns error %v`, err) 14 | } 15 | if i != 20 { 16 | t.Errorf(`Setting ptrVariable didn't change underlying value`) 17 | } 18 | err = variable.Set("x") 19 | if err == nil { 20 | t.Errorf("Setting ptrVariable with incompatible value returns no error") 21 | } 22 | } 23 | 24 | func TestFromInit(t *testing.T) { 25 | v := FromInit(true) 26 | if val := v.Get(); val != true { 27 | t.Errorf("Get returned %v, want true", val) 28 | } 29 | if err := v.Set("233"); err != nil { 30 | t.Errorf("Set errors: %v", err) 31 | } 32 | if val := v.Get(); val != "233" { 33 | t.Errorf(`Get returns %v, want "233"`, val) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /pkg/eval/vars/read_only.go: -------------------------------------------------------------------------------- 1 | package vars 2 | 3 | import ( 4 | "src.elv.sh/pkg/eval/errs" 5 | ) 6 | 7 | type readOnly struct { 8 | value any 9 | } 10 | 11 | // NewReadOnly creates a variable that is read-only and always returns an error 12 | // on Set. 13 | func NewReadOnly(v any) Var { 14 | return readOnly{v} 15 | } 16 | 17 | func (rv readOnly) Set(val any) error { 18 | return errs.SetReadOnlyVar{} 19 | } 20 | 21 | func (rv readOnly) Get() any { 22 | return rv.value 23 | } 24 | 25 | // IsReadOnly returns whether v is a read-only variable. 26 | func IsReadOnly(v Var) bool { 27 | switch v.(type) { 28 | case readOnly: 29 | return true 30 | case roCallback: 31 | return true 32 | default: 33 | return false 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /pkg/eval/vars/read_only_test.go: -------------------------------------------------------------------------------- 1 | package vars 2 | 3 | import ( 4 | "testing" 5 | 6 | "src.elv.sh/pkg/eval/errs" 7 | "src.elv.sh/pkg/tt" 8 | ) 9 | 10 | func TestNewReadOnly(t *testing.T) { 11 | v := NewReadOnly("haha") 12 | if v.Get() != "haha" { 13 | t.Errorf("Get doesn't return initial value") 14 | } 15 | 16 | err := v.Set("lala") 17 | if _, ok := err.(errs.SetReadOnlyVar); !ok { 18 | t.Errorf("Set a readonly var doesn't error as expected: %#v", err) 19 | } 20 | } 21 | 22 | func TestIsReadOnly(t *testing.T) { 23 | tt.Test(t, IsReadOnly, 24 | Args(NewReadOnly("foo")).Rets(true), 25 | Args(FromGet(func() any { return "foo" })).Rets(true), 26 | Args(FromInit("foo")).Rets(false), 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /pkg/eval/vars/vars.go: -------------------------------------------------------------------------------- 1 | // Package vars contains basic types for manipulating Elvish variables. 2 | package vars 3 | 4 | // Var represents an Elvish variable. 5 | type Var interface { 6 | Set(v any) error 7 | Get() any 8 | } 9 | 10 | // UnsettableVar represents an Elvish variable that can be in an unset state. 11 | type UnsettableVar interface { 12 | Var 13 | Unset() error 14 | IsSet() bool 15 | } 16 | -------------------------------------------------------------------------------- /pkg/fsutil/fsutil.go: -------------------------------------------------------------------------------- 1 | // Package fsutil provides filesystem utilities. 2 | package fsutil 3 | -------------------------------------------------------------------------------- /pkg/fsutil/getwd.go: -------------------------------------------------------------------------------- 1 | package fsutil 2 | 3 | import ( 4 | "os" 5 | "runtime" 6 | "strings" 7 | ) 8 | 9 | // Getwd returns path of the working directory in a format suitable as the 10 | // prompt. 11 | func Getwd() string { 12 | pwd, err := os.Getwd() 13 | if err != nil { 14 | return "?" 15 | } 16 | return TildeAbbr(pwd) 17 | } 18 | 19 | // TildeAbbr abbreviates the user's home directory to ~. 20 | func TildeAbbr(path string) string { 21 | home, err := GetHome("") 22 | if home == "" || home == "/" { 23 | // If home is "" or "/", do not abbreviate because (1) it is likely a 24 | // problem with the environment and (2) it will make the path actually 25 | // longer. 26 | return path 27 | } 28 | if err == nil { 29 | if path == home { 30 | return "~" 31 | } else if strings.HasPrefix(path, home+"/") || (runtime.GOOS == "windows" && strings.HasPrefix(path, home+"\\")) { 32 | return "~" + path[len(home):] 33 | } 34 | } 35 | return path 36 | } 37 | -------------------------------------------------------------------------------- /pkg/fsutil/search_unix.go: -------------------------------------------------------------------------------- 1 | //go:build unix 2 | 3 | package fsutil 4 | 5 | import "os" 6 | 7 | func isExecutable(stat os.FileInfo) bool { 8 | return !stat.IsDir() && stat.Mode()&0o111 != 0 9 | } 10 | -------------------------------------------------------------------------------- /pkg/fsutil/search_unix_test.go: -------------------------------------------------------------------------------- 1 | //go:build unix 2 | 3 | package fsutil 4 | 5 | import ( 6 | "reflect" 7 | "sort" 8 | "testing" 9 | 10 | "src.elv.sh/pkg/testutil" 11 | ) 12 | 13 | func TestEachExternal(t *testing.T) { 14 | binPath := testutil.InTempDir(t) 15 | 16 | testutil.Setenv(t, "PATH", "/foo:"+binPath+":/bar") 17 | 18 | testutil.ApplyDir(testutil.Dir{ 19 | "dir": testutil.Dir{}, 20 | "file": "", 21 | "cmdx": "#!/bin/sh", 22 | "cmd1": testutil.File{Perm: 0755, Content: "#!/bin/sh"}, 23 | "cmd2": testutil.File{Perm: 0755, Content: "#!/bin/sh"}, 24 | "cmd3": testutil.File{Perm: 0755, Content: ""}, 25 | }) 26 | 27 | wantCmds := []string{"cmd1", "cmd2", "cmd3"} 28 | gotCmds := []string{} 29 | EachExternal(func(cmd string) { gotCmds = append(gotCmds, cmd) }) 30 | 31 | sort.Strings(gotCmds) 32 | if !reflect.DeepEqual(wantCmds, gotCmds) { 33 | t.Errorf("EachExternal want %q got %q", wantCmds, gotCmds) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /pkg/lsp/lsp_test.elvts: -------------------------------------------------------------------------------- 1 | //each:elvish-in-global 2 | 3 | /////////////////////////////////// 4 | # return NextProgram without -lsp # 5 | /////////////////////////////////// 6 | 7 | ~> elvish 8 | [stderr] internal error: no suitable subprogram 9 | [exit] 2 10 | -------------------------------------------------------------------------------- /pkg/lsp/transcripts_test.go: -------------------------------------------------------------------------------- 1 | package lsp_test 2 | 3 | import ( 4 | "embed" 5 | "testing" 6 | 7 | "src.elv.sh/pkg/eval/evaltest" 8 | "src.elv.sh/pkg/lsp" 9 | "src.elv.sh/pkg/prog/progtest" 10 | ) 11 | 12 | //go:embed *.elvts 13 | var transcripts embed.FS 14 | 15 | func TestTranscripts(t *testing.T) { 16 | evaltest.TestTranscriptsInFS(t, transcripts, 17 | "elvish-in-global", progtest.ElvishInGlobal(&lsp.Program{}), 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /pkg/md/spec/LICENSE: -------------------------------------------------------------------------------- 1 | The spec tests (spec.json) are derived from the CommonMark spec (spec.txt), 2 | which are 3 | 4 | Copyright (C) 2014-16 John MacFarlane 5 | 6 | Released under the Creative Commons CC-BY-SA 4.0 license: 7 | . 8 | -------------------------------------------------------------------------------- /pkg/md/stack.go: -------------------------------------------------------------------------------- 1 | package md 2 | 3 | type stack[T any] []T 4 | 5 | func (s *stack[T]) push(v T) { 6 | *s = append(*s, v) 7 | } 8 | 9 | func (s stack[T]) peek() T { 10 | return s[len(s)-1] 11 | } 12 | 13 | func (s *stack[T]) pop() T { 14 | last := s.peek() 15 | *s = (*s)[:len(*s)-1] 16 | return last 17 | } 18 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/09165d96e6eede6b6057e300935fb1aa5c98243ed4f94b75a3ae31fb129e696d: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("[](<>(0))") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/0d768707e28d09f6ef49b5e8c7e8f44fbea157f6296a05abb302ee5b77e3a168: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("<&@0>") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/17c530b620d9fbcf8870fe975ed11dd7062eb582d7ca3fc5ddbc293d5344e2f9: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("999999990)\n0)\n0)\n0)\n0)\n0)\n0)\n0)\n0)\n0)\n0)") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/2b69704609fc2e3745fba9a435b29b3858c597efe349cf8cc39c193e6f02173c: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("\\ \n0") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/2ced69587e727c3c37b87201e0e44e450090e6a195425a8fe0dc66bbc5163fd6: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("[]( <0)") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/2ec9c489ff1c9ed8d8a24c2eb9e4033303149dcd9875ea82bf4764d0df144af5: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("* ```\n0") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/334ff8e8eaece3f84601f65db41d9cffa5634be1d23c4f97ea44edf4669f712f: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string(" ***") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/3463752fb4670a5a72d0033d9f2caee37023353a019825bb48223e7a01d9b760: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("_0 _") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/4310d034c2ad018a0649d15c7d6748bfad6779fb067b28b09debb146eb77bd31: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("*\n+ ***") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/43d96cdf434f4f13cd32c391724d7c1cbc55d63d92195ea9da67c397abb722fa: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string(">\\---") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/45f9492476d79ae796da2ffeb3476370586553a5071b32bd1cc7a07fbd80356f: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("* --") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/46d002372d39c68359423d57dbe63c3c83003ecbd5e601286a5f80bc691624ef: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("_00*0*_") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/4951856280eb053ef3a943dcf6fb1e27cd595e4ab8aa00934517a702e71ca940: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("- - - *") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/49ef19e3514a8f95ff404e89d70d308b76d947b505a9593e2e72712048f2ae42: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("*\n ") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/51e230c835df97b3aec428fe7a0be1704811421c5c28a78c9c3b74983157b1e4: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("�*`0`*") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/594ea6c06082c3fc565a1304ce5a809d2a37dac682163c23c73be7748bc5b464: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("*0\n ***\n0*") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/5e0c9718d6e600b573a921052b15c44fb57a68a9d70af7a7c8899b33adefcc40: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("0\n ") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/5fb0a2c38bf290e32115dd5c2de37cf22994dfaa3c2c74200060e8f6d0d0d2be: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("_______0__!!!!!!!!!!!!!__!___") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/6a6981e2afe37e15899b27af919cd50085520fdeab41051147b037418805bfd6: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("[](\\<)") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/6ed9c798d44c6e3a7c68257ace3ece80a752882b1a06d06a101f375abc30fa32: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("*0!*�") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/714ffe658ccfa9609ff79b93aa1462f9a1b4c1a4709c0256ae7bc4244578601c: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("*!00*\xb1") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/722391ce85af71be128f8d7aa7b3620e9d4810458b8148c119256a93647fea3e: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("00000000000000000000000000000000 _0000_*0*") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/7425c53ab767b535434fe5e744491bc978146e002c5f6c701ea0ed3a7032bf89: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("<0\\@0>") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/763096c0f188a6f5a2cad16641570cfe484af56c6a406c3d89948406e4f11d62: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("[](0 (\n>))") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/77f51992726987c5b66121aa85a2b57152ae80152ad2831a7b7d4c761a09ed2f: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("\t~~~") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/7ef0c0744eba12ae7f987c61107a4180b0772e25dd796045922da521e9a1b4cc: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("*![*]()") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/7fd9444d106647162a0fbfff2f7c2f9da240565a759c0371cd5af930da19186c: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/85278dc7594f426d6b7b4412586b262b25229d360f6c5fd07cc57839c048abee: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("0\n* 0) 00") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/89d1293838e9db8b9ad5b72565dcf93f284492177b143bff251c8c5a35e96c48: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("\t") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/8becac1ca7e4e7c1456c91a25697a367a6b471b0c4a05e331456cd966003a108: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("~~~ ") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/8ec2e1d50f68c77c2a1e2707f9afa4794d33566315c26ae5c039390f55985a29: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("0000*`*") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/95f31a02dbd47cd18ea3483ba9d91074fa80f371e144f9958c93a9f6e2a2e48a: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/9e5f2825cc97261ce08d95ce1a30e32ea78945cd5946af20a92307506bf4fa53: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("___ ___") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/a1b7773f70c10e0032514d1d0068a9b1d51b82e0a70adcd591858046509bb653: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("[](<\x00>)") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/aa47fa137928697726bba1b40b01f4e2a1c6980652433dd47b20d2517616c685: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string(" 0") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/acbe5b22b3e9beb52a963220e2fe8e7e3f0f4bdcd7d0af4ab3815f69bbb6077b: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("~~~ ") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/adb64ce0b0270d2fb724e07aa3dc7f0b7cb343708c008e021575b529d1e8395e: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("# 0 0") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/c3eb680407bb9b0d9a2fc29b862115d798bccc3309fddb86395824cadba8f63d: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string(">```\n\n>") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/c760e1f86fcf5e26acf8f17b1c9da06b8503d320f3642e82a4a8ea8a37a55910: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("*[0***0]()") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/ca797b6dad7e8007aa973269e07ab8e08e078ef5c738b7bea8d136ca05cd7a4d: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("![ \\\n]()") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/cd37db7c4899c0be1226816e12693c93970d1a5292f752293695b2490d98fea2: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/d66d86f848073d5c90685009906e1fb0840748fd8d3a452ee8cb3e6d3f1b29e9: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("~~~\\\\!") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/d845768a00eefd7adde472aca51dbfb198601986e0f1c2ad6257b4afd4540176: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("_0*0_ 0*00") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/d853024ac25e2be9f90784cfc58f28c496c9455b3c988b0964c02b45ac451b74: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("___ 0") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/d8e4495c47b5e918fc92795426735bcc9ee5beaea5058a84bbccba4401c6386d: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("* 0) * --") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/d9dbd4e786917123c3126420bb75315b0affdca4ef3fc5b521be051566b28479: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("0\n* * * +") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/de531027ab47da7f412a44d596530c35c32101ea60128c2328cdfd2a12cf20d3: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("* *") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/e609714031cd68fb7e402746b4c842229d77388ac34e6fbcad3ce52431a6e2a6: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("[]()") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/eba78016235f789ac52ed7a9b12064cb034f410c7ecd324dce7afba390b409a2: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("0\n ") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/f56cebbc6539e13372fc5723238d6ff42177d27e683121e0d287d85ecbbde400: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("* * * 0\n *") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/f5a2058125b0d6bc9bd21817b8bedb3ed3fd6ee77b8dbcd12e3f6533e17aa335: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("# \\#") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzFmtPreservesHTMLRender/fdb49d060e4e8905df6ecffcc3044a27cd7dc348ecc3c7e6049064e0e2b96ca1: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("0 ") 3 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzReflowFmtPreservesHTMLRenderModuleWhitespaces/179da62c2709fec804728d69facdd60c840f5cf2082ef74822ff6f7bc12a02e8: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("--\n-") 3 | int(157) 4 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzReflowFmtPreservesHTMLRenderModuleWhitespaces/467f234f387a81f6e97d86814ca835b444ea00a1a9470f100ce449d6b2beb3d8: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("") 3 | int(103) 4 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzReflowFmtPreservesHTMLRenderModuleWhitespaces/586bc16e05e19d40cc33415c9a08aef70f05c221fc3dbe850c1869c65d062b50: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("*\\\n0*") 3 | int(75) 4 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzReflowFmtPreservesHTMLRenderModuleWhitespaces/80832a884668db006bd77bf19e3758fe3413e528fd38966ed8086f956d5bc201: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("\\\n0") 3 | int(4) 4 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzReflowFmtPreservesHTMLRenderModuleWhitespaces/ac5a3d974210f5d5bcbd47eb5a3ea1bb1e3c0091077981bedae9f9c7ab2449c1: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("[](< >)") 3 | int(20) 4 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzReflowFmtPreservesHTMLRenderModuleWhitespaces/d0138fd295a0fa831075f449614b0a8b8cf421e8754463f1907d68ef05ef0475: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("0

\n0") 3 | int(80) 4 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzReflowFmtPreservesHTMLRenderModuleWhitespaces/e20e64463c3adce77239a55290f960552bc2bf611ef237800b77d0b25bc06cc6: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("000000000 \\\n0") 3 | int(20) 4 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzReflowFmtPreservesHTMLRenderModuleWhitespaces/e56bf66cc0560c5e7905445712d6fd4ef85fc0f0f55e304904fcba58645beacd: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string(" ") 3 | int(28) 4 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzReflowFmtResultFitsInWidth/4addab28ca79b43124a432b0980b1504d78dc5ea7b8c722d4a3f960729240178: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("\x02 00") 3 | int(-118) 4 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzReflowFmtResultFitsInWidth/c5523be82830755cd690a04810119e39290f0dc446c38e612dc0cc46ccc4cdaf: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("\\# 000") 3 | int(5) 4 | -------------------------------------------------------------------------------- /pkg/md/testdata/fuzz/FuzzReflowFmtResultIsUnchangedUnderFmt/1f26a323e0bc46cde6ab0cdbf3d947e58eab9df8bbd189bda681902c307a0797: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("\\\n ") 3 | int(17) 4 | -------------------------------------------------------------------------------- /pkg/md/testexport_test.go: -------------------------------------------------------------------------------- 1 | package md 2 | 3 | var EscapeURL = &escapeURL 4 | -------------------------------------------------------------------------------- /pkg/mods/daemon/daemon.go: -------------------------------------------------------------------------------- 1 | // Package daemon implements the builtin daemon: module. 2 | package daemon 3 | 4 | import ( 5 | "strconv" 6 | 7 | "src.elv.sh/pkg/daemon/daemondefs" 8 | "src.elv.sh/pkg/eval" 9 | "src.elv.sh/pkg/eval/vars" 10 | ) 11 | 12 | // Ns makes the daemon: namespace. 13 | func Ns(d daemondefs.Client) *eval.Ns { 14 | getPid := func() (string, error) { 15 | pid, err := d.Pid() 16 | return string(strconv.Itoa(pid)), err 17 | } 18 | 19 | // TODO: Deprecate the variable in favor of the function. 20 | getPidVar := func() any { 21 | pid, err := getPid() 22 | if err != nil { 23 | return "-1" 24 | } 25 | return pid 26 | } 27 | 28 | return eval.BuildNsNamed("daemon"). 29 | AddVars(map[string]vars.Var{ 30 | "pid": vars.FromGet(getPidVar), 31 | "sock": vars.NewReadOnly(string(d.SockPath())), 32 | }). 33 | AddGoFns(map[string]any{ 34 | "pid": getPid, 35 | }).Ns() 36 | } 37 | -------------------------------------------------------------------------------- /pkg/mods/daemon/daemon_test.go: -------------------------------------------------------------------------------- 1 | package daemon 2 | 3 | import "testing" 4 | 5 | func TestDaemon(t *testing.T) { 6 | // TODO 7 | } 8 | -------------------------------------------------------------------------------- /pkg/mods/doc/fakepkg/eval/break.md: -------------------------------------------------------------------------------- 1 | Usage: 2 | 3 | ```elvish 4 | break 5 | ``` 6 | 7 | Terminates a loop. 8 | -------------------------------------------------------------------------------- /pkg/mods/doc/fakepkg/eval/builtin.d.elv: -------------------------------------------------------------------------------- 1 | # Terminates a loop. 2 | fn break { } 3 | 4 | # Constructs a [typed number](language.html#number). Another 5 | # [link](#foo). 6 | fn num {|x| } 7 | -------------------------------------------------------------------------------- /pkg/mods/doc/fakepkg/eval/num.md: -------------------------------------------------------------------------------- 1 | Usage: 2 | 3 | ```elvish 4 | num $x 5 | ``` 6 | 7 | 9 | 10 | Constructs a [typed number](https://elv.sh/ref/language.html#number). Another 11 | [link](#foo). 12 | -------------------------------------------------------------------------------- /pkg/mods/doc/fakepkg/mods/foo/foo.d.elv: -------------------------------------------------------------------------------- 1 | # A variable. Lorem ipsum. 2 | var variable 3 | 4 | # A function with long documentation. Lorem ipsum dolor sit amet. 5 | # Consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut 6 | # labore et dolore magna aliqua. 7 | fn function {|x| } 8 | -------------------------------------------------------------------------------- /pkg/mods/doc/fakepkg/mods/foo/function.md: -------------------------------------------------------------------------------- 1 | Usage: 2 | 3 | ```elvish 4 | foo:function $x 5 | ``` 6 | 7 | A function with long documentation. Lorem ipsum dolor sit amet. Consectetur 8 | adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna 9 | aliqua. 10 | -------------------------------------------------------------------------------- /pkg/mods/doc/fakepkg/mods/foo/variable.md: -------------------------------------------------------------------------------- 1 | A variable. Lorem ipsum. 2 | -------------------------------------------------------------------------------- /pkg/mods/doc/testexport_test.go: -------------------------------------------------------------------------------- 1 | package doc 2 | 3 | var ( 4 | DocsMapWithError = &docsMapWithError 5 | Match = match 6 | ) 7 | -------------------------------------------------------------------------------- /pkg/mods/epm/epm.go: -------------------------------------------------------------------------------- 1 | package epm 2 | 3 | import _ "embed" 4 | 5 | // Code contains the source code of the epm module. 6 | // 7 | //go:embed epm.elv 8 | var Code string 9 | -------------------------------------------------------------------------------- /pkg/mods/epm/epm_test.elvts: -------------------------------------------------------------------------------- 1 | //prepare-deps 2 | 3 | // A smoke test to ensure that the epm module has no errors. 4 | ~> use epm 5 | -------------------------------------------------------------------------------- /pkg/mods/epm/epm_test.go: -------------------------------------------------------------------------------- 1 | package epm_test 2 | 3 | import ( 4 | "embed" 5 | "testing" 6 | 7 | "src.elv.sh/pkg/eval/evaltest" 8 | "src.elv.sh/pkg/mods" 9 | ) 10 | 11 | //go:embed *.elvts 12 | var transcripts embed.FS 13 | 14 | func TestTranscripts(t *testing.T) { 15 | evaltest.TestTranscriptsInFS(t, transcripts, "prepare-deps", mods.AddTo) 16 | } 17 | -------------------------------------------------------------------------------- /pkg/mods/file/file_test.go: -------------------------------------------------------------------------------- 1 | package file_test 2 | 3 | import ( 4 | "embed" 5 | "os" 6 | "testing" 7 | 8 | "src.elv.sh/pkg/eval/evaltest" 9 | ) 10 | 11 | //go:embed *.elvts 12 | var transcripts embed.FS 13 | 14 | func TestTranscripts(t *testing.T) { 15 | evaltest.TestTranscriptsInFS(t, transcripts, 16 | "skip-unless-can-open", func(t *testing.T, name string) { 17 | if !canOpen(name) { 18 | t.SkipNow() 19 | } 20 | }, 21 | ) 22 | } 23 | 24 | func canOpen(name string) bool { 25 | f, err := os.Open(name) 26 | f.Close() 27 | return err == nil 28 | } 29 | -------------------------------------------------------------------------------- /pkg/mods/flag/flag_test.go: -------------------------------------------------------------------------------- 1 | package flag_test 2 | 3 | import ( 4 | "embed" 5 | "testing" 6 | 7 | "src.elv.sh/pkg/eval/evaltest" 8 | ) 9 | 10 | //go:embed *.elvts *.elv 11 | var transcripts embed.FS 12 | 13 | func TestTranscripts(t *testing.T) { 14 | evaltest.TestTranscriptsInFS(t, transcripts) 15 | } 16 | -------------------------------------------------------------------------------- /pkg/mods/math/math_test.go: -------------------------------------------------------------------------------- 1 | package math_test 2 | 3 | import ( 4 | "embed" 5 | "testing" 6 | 7 | "src.elv.sh/pkg/eval/evaltest" 8 | ) 9 | 10 | //go:embed *.elvts *.elv 11 | var transcripts embed.FS 12 | 13 | func TestTranscripts(t *testing.T) { 14 | evaltest.TestTranscriptsInFS(t, transcripts) 15 | } 16 | -------------------------------------------------------------------------------- /pkg/mods/md/md.d.elv: -------------------------------------------------------------------------------- 1 | #//each:eval use md 2 | 3 | #doc:added-in 0.21 4 | # Renders `$markdown` in the terminal. 5 | # 6 | # The `&width` option specifies the width to wrap the output to. If it is 0 (the 7 | # default) or negative, `show` queries the terminal width of the standard output 8 | # and use it as the width, falling back to 80 if the query fails (for example 9 | # when the standard output is not a terminal). 10 | # 11 | # Examples: 12 | # 13 | # ```elvish-transcript 14 | # ~> md:show "#h1 heading\n- List\n- Item" 15 | # #h1 heading 16 | # 17 | # • List 18 | # 19 | # • Item 20 | # ``` 21 | # 22 | # See also [`doc:show`](). 23 | fn show {|&width=0| markdown} 24 | -------------------------------------------------------------------------------- /pkg/mods/md/md.go: -------------------------------------------------------------------------------- 1 | // Package md exposes functionality from src.elv.sh/pkg/md. 2 | package md 3 | 4 | import ( 5 | "src.elv.sh/pkg/elvdoc" 6 | "src.elv.sh/pkg/eval" 7 | "src.elv.sh/pkg/md" 8 | "src.elv.sh/pkg/sys" 9 | ) 10 | 11 | // Ns is the namespace for the md: module. 12 | var Ns = eval.BuildNsNamed("md"). 13 | AddGoFns(map[string]any{ 14 | "show": show, 15 | }).Ns() 16 | 17 | type showOpts struct { 18 | Width int 19 | } 20 | 21 | func (*showOpts) SetDefaultOptions() {} 22 | 23 | func show(fm *eval.Frame, opts showOpts, markdown string) error { 24 | width := opts.Width 25 | if width <= 0 { 26 | _, width = sys.WinSize(fm.Port(1).File) 27 | if width <= 0 { 28 | width = 80 29 | } 30 | } 31 | codec := &md.TTYCodec{ 32 | Width: width, 33 | HighlightCodeBlock: elvdoc.HighlightCodeBlock, 34 | } 35 | _, err := fm.ByteOutput().WriteString(md.RenderString(markdown, codec)) 36 | return err 37 | } 38 | -------------------------------------------------------------------------------- /pkg/mods/md/md_test.elvts: -------------------------------------------------------------------------------- 1 | //each:eval use md 2 | 3 | /////////// 4 | # md:show # 5 | /////////// 6 | 7 | // Transcript tests are not run with a real terminal connected to the output, so 8 | // the width will fall back to 80. 9 | ~> md:show 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.' 10 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor 11 | incididunt ut labore et dolore magna aliqua. 12 | ~> md:show "#h1 heading\n- List\n- Item" 13 | #h1 heading 14 | 15 | • List 16 | 17 | • Item 18 | 19 | ## explicit &width ## 20 | ~> md:show &width=40 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.' 21 | Lorem ipsum dolor sit amet, consectetur 22 | adipiscing elit. Sed do eiusmod tempor 23 | incididunt ut labore et dolore magna 24 | aliqua. 25 | -------------------------------------------------------------------------------- /pkg/mods/md/md_test.go: -------------------------------------------------------------------------------- 1 | package md_test 2 | 3 | import ( 4 | "embed" 5 | "testing" 6 | 7 | "src.elv.sh/pkg/eval/evaltest" 8 | ) 9 | 10 | //go:embed *.elvts *.elv 11 | var transcripts embed.FS 12 | 13 | func TestTranscripts(t *testing.T) { 14 | evaltest.TestTranscriptsInFS(t, transcripts) 15 | } 16 | -------------------------------------------------------------------------------- /pkg/mods/os/os_unix.go: -------------------------------------------------------------------------------- 1 | //go:build unix 2 | 3 | package os 4 | 5 | const DevTTY = "/dev/tty" 6 | -------------------------------------------------------------------------------- /pkg/mods/os/os_unix_test.go: -------------------------------------------------------------------------------- 1 | //go:build unix 2 | 3 | package os_test 4 | 5 | import ( 6 | "testing" 7 | 8 | "golang.org/x/sys/unix" 9 | "src.elv.sh/pkg/must" 10 | ) 11 | 12 | func mkFifoOrSkip(name string) { must.OK(unix.Mkfifo(name, 0o600)) } 13 | 14 | func createWindowsSpecialFileOrSkip(t *testing.T) { t.Skip("not on Windows") } 15 | -------------------------------------------------------------------------------- /pkg/mods/os/os_windows.go: -------------------------------------------------------------------------------- 1 | package os 2 | 3 | const DevTTY = "CON" 4 | -------------------------------------------------------------------------------- /pkg/mods/os/os_windows_test.go: -------------------------------------------------------------------------------- 1 | package os_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "golang.org/x/sys/windows" 7 | "src.elv.sh/pkg/must" 8 | "src.elv.sh/pkg/testutil" 9 | ) 10 | 11 | func mkFifoOrSkip(t *testing.T, _ string) { 12 | t.Skip("can't make FIFO on Windows") 13 | } 14 | 15 | func createWindowsSpecialFileOrSkip(t *testing.T) { 16 | testutil.ApplyDir(testutil.Dir{ 17 | "directory": testutil.Dir{}, 18 | "readonly": "", 19 | "hidden": "", 20 | }) 21 | mustSetFileAttributes("readonly", windows.FILE_ATTRIBUTE_READONLY) 22 | mustSetFileAttributes("hidden", windows.FILE_ATTRIBUTE_HIDDEN) 23 | } 24 | 25 | func mustSetFileAttributes(name string, attr uint32) { 26 | must.OK(windows.SetFileAttributes(must.OK1(windows.UTF16PtrFromString(name)), attr)) 27 | } 28 | -------------------------------------------------------------------------------- /pkg/mods/os/stat_bsd.go: -------------------------------------------------------------------------------- 1 | //go:build darwin || freebsd || netbsd || openbsd 2 | 3 | package os 4 | 5 | import "syscall" 6 | 7 | func init() { 8 | extraStatFields["gen"] = func(st *syscall.Stat_t) uint64 { return uint64(st.Gen) } 9 | extraStatFields["flags"] = func(st *syscall.Stat_t) uint64 { return uint64(st.Flags) } 10 | } 11 | -------------------------------------------------------------------------------- /pkg/mods/path/path_test.go: -------------------------------------------------------------------------------- 1 | package path_test 2 | 3 | import ( 4 | "embed" 5 | "testing" 6 | 7 | "src.elv.sh/pkg/eval/evaltest" 8 | "src.elv.sh/pkg/testutil" 9 | ) 10 | 11 | //go:embed *.elvts 12 | var transcripts embed.FS 13 | 14 | func TestTranscripts(t *testing.T) { 15 | evaltest.TestTranscriptsInFS(t, transcripts, 16 | "in-temp-dir-with-d-f", func(t *testing.T) { 17 | testutil.InTempDir(t) 18 | testutil.ApplyDir(testutil.Dir{ 19 | "d": testutil.Dir{ 20 | "f": "", 21 | }, 22 | }) 23 | }, 24 | ) 25 | } 26 | -------------------------------------------------------------------------------- /pkg/mods/platform/platform_test.go: -------------------------------------------------------------------------------- 1 | package platform_test 2 | 3 | import ( 4 | "embed" 5 | "errors" 6 | "testing" 7 | 8 | "src.elv.sh/pkg/eval/evaltest" 9 | "src.elv.sh/pkg/mods/platform" 10 | "src.elv.sh/pkg/testutil" 11 | ) 12 | 13 | //go:embed *.elvts 14 | var transcripts embed.FS 15 | 16 | func TestTranscripts(t *testing.T) { 17 | evaltest.TestTranscriptsInFS(t, transcripts, 18 | "mock-hostname", func(t *testing.T, hostname string) { 19 | testutil.Set(t, platform.OSHostname, func() (string, error) { return hostname, nil }) 20 | }, 21 | "mock-hostname-error", func(t *testing.T, msg string) { 22 | err := errors.New(msg) 23 | testutil.Set(t, platform.OSHostname, func() (string, error) { return "", err }) 24 | }, 25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /pkg/mods/platform/testexport_test.go: -------------------------------------------------------------------------------- 1 | package platform 2 | 3 | var OSHostname = &osHostname 4 | -------------------------------------------------------------------------------- /pkg/mods/platform/unix.go: -------------------------------------------------------------------------------- 1 | //go:build unix 2 | 3 | package platform 4 | 5 | const ( 6 | isUnix = true 7 | isWindows = false 8 | ) 9 | -------------------------------------------------------------------------------- /pkg/mods/platform/windows.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package platform 4 | 5 | const ( 6 | isUnix = false 7 | isWindows = true 8 | ) 9 | -------------------------------------------------------------------------------- /pkg/mods/re/match.go: -------------------------------------------------------------------------------- 1 | package re 2 | 3 | import ( 4 | "src.elv.sh/pkg/eval/vals" 5 | ) 6 | 7 | type matchStruct struct { 8 | Text string 9 | Start int 10 | End int 11 | Groups vals.List 12 | } 13 | 14 | type submatchStruct struct { 15 | Text string 16 | Start int 17 | End int 18 | } 19 | -------------------------------------------------------------------------------- /pkg/mods/re/re_test.go: -------------------------------------------------------------------------------- 1 | package re_test 2 | 3 | import ( 4 | "embed" 5 | "testing" 6 | 7 | "src.elv.sh/pkg/eval/evaltest" 8 | ) 9 | 10 | //go:embed *.elvts *.elv 11 | var transcripts embed.FS 12 | 13 | func TestTranscripts(t *testing.T) { 14 | evaltest.TestTranscriptsInFS(t, transcripts) 15 | } 16 | -------------------------------------------------------------------------------- /pkg/mods/readline-binding/readline-binding_test.elvts: -------------------------------------------------------------------------------- 1 | //prepare-deps 2 | 3 | // A smoke test to ensure that the readline-binding module has no errors. 4 | ~> use readline-binding 5 | -------------------------------------------------------------------------------- /pkg/mods/readline-binding/readlinebinding.go: -------------------------------------------------------------------------------- 1 | package readline_binding 2 | 3 | import _ "embed" 4 | 5 | // Code contains the source code of the readline-binding module. 6 | // 7 | //go:embed readline-binding.elv 8 | var Code string 9 | -------------------------------------------------------------------------------- /pkg/mods/readline-binding/readlinebinding_test.go: -------------------------------------------------------------------------------- 1 | package readline_binding_test 2 | 3 | import ( 4 | "embed" 5 | "os" 6 | "testing" 7 | 8 | "src.elv.sh/pkg/cli" 9 | "src.elv.sh/pkg/edit" 10 | "src.elv.sh/pkg/eval" 11 | "src.elv.sh/pkg/eval/evaltest" 12 | "src.elv.sh/pkg/mods" 13 | ) 14 | 15 | //go:embed *.elvts 16 | var transcripts embed.FS 17 | 18 | func TestTranscripts(t *testing.T) { 19 | evaltest.TestTranscriptsInFS(t, transcripts, 20 | "prepare-deps", 21 | func(ev *eval.Evaler) { 22 | mods.AddTo(ev) 23 | ed := edit.NewEditor(cli.NewTTY(os.Stdin, os.Stderr), ev, nil) 24 | ev.ExtendBuiltin(eval.BuildNs().AddNs("edit", ed)) 25 | }) 26 | } 27 | -------------------------------------------------------------------------------- /pkg/mods/runtime/runtime_test.elvts: -------------------------------------------------------------------------------- 1 | # runtime module with good paths # 2 | 3 | //use-runtime-good-paths 4 | 5 | ~> put $runtime:lib-dirs 6 | ▶ [/lib/1 /lib/2] 7 | ~> put $runtime:rc-path 8 | ▶ /path/to/rc.elv 9 | ~> put $runtime:effective-rc-path 10 | ▶ /path/to/effective/rc.elv 11 | ~> put $runtime:elvish-path 12 | ▶ /path/to/elvish 13 | 14 | # runtime module with bad paths # 15 | 16 | //use-runtime-bad-paths 17 | 18 | ~> put $runtime:lib-dirs 19 | ▶ [] 20 | ~> put $runtime:elvish-path 21 | ▶ $nil 22 | ~> put $runtime:rc-path 23 | ▶ $nil 24 | ~> put $runtime:effective-rc-path 25 | ▶ $nil 26 | -------------------------------------------------------------------------------- /pkg/mods/runtime/testexport_test.go: -------------------------------------------------------------------------------- 1 | package runtime 2 | 3 | var OSExecutable = &osExecutable 4 | -------------------------------------------------------------------------------- /pkg/mods/store/store.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import ( 4 | "src.elv.sh/pkg/eval" 5 | "src.elv.sh/pkg/store/storedefs" 6 | ) 7 | 8 | func Ns(s storedefs.Store) *eval.Ns { 9 | return eval.BuildNsNamed("store"). 10 | AddGoFns(map[string]any{ 11 | "next-cmd-seq": s.NextCmdSeq, 12 | "add-cmd": s.AddCmd, 13 | "del-cmd": s.DelCmd, 14 | "cmd": s.Cmd, 15 | "cmds": s.CmdsWithSeq, 16 | "next-cmd": s.NextCmd, 17 | "prev-cmd": s.PrevCmd, 18 | 19 | "add-dir": func(dir string) error { return s.AddDir(dir, 1) }, 20 | "del-dir": s.DelDir, 21 | "dirs": func() ([]storedefs.Dir, error) { return s.Dirs(storedefs.NoBlacklist) }, 22 | }).Ns() 23 | } 24 | -------------------------------------------------------------------------------- /pkg/mods/store/store_test.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import ( 4 | "embed" 5 | "testing" 6 | 7 | "src.elv.sh/pkg/eval" 8 | "src.elv.sh/pkg/eval/evaltest" 9 | "src.elv.sh/pkg/must" 10 | "src.elv.sh/pkg/store" 11 | "src.elv.sh/pkg/testutil" 12 | ) 13 | 14 | //go:embed *.elvts 15 | var transcripts embed.FS 16 | 17 | func TestTranscripts(t *testing.T) { 18 | evaltest.TestTranscriptsInFS(t, transcripts, 19 | "use-store-brand-new", func(t *testing.T, ev *eval.Evaler) { 20 | testutil.InTempDir(t) 21 | s := must.OK1(store.NewStore("db")) 22 | ev.ExtendGlobal(eval.BuildNs().AddNs("store", Ns(s))) 23 | }, 24 | ) 25 | } 26 | -------------------------------------------------------------------------------- /pkg/mods/str/str_test.go: -------------------------------------------------------------------------------- 1 | package str_test 2 | 3 | import ( 4 | "embed" 5 | "testing" 6 | 7 | "src.elv.sh/pkg/eval/evaltest" 8 | ) 9 | 10 | //go:embed *.elvts *.elv 11 | var transcripts embed.FS 12 | 13 | func TestTranscripts(t *testing.T) { 14 | evaltest.TestTranscriptsInFS(t, transcripts) 15 | } 16 | -------------------------------------------------------------------------------- /pkg/mods/unix/non_unix.go: -------------------------------------------------------------------------------- 1 | //go:build !unix 2 | 3 | package unix 4 | 5 | import ( 6 | "src.elv.sh/pkg/eval" 7 | ) 8 | 9 | // ExposeUnixNs indicate whether this module should be exposed as a usable 10 | // elvish namespace. 11 | const ExposeUnixNs = false 12 | 13 | // Ns is an Elvish namespace that contains variables and functions that deal 14 | // with features unique to Unix-like operating systems. 15 | var Ns = &eval.Ns{} 16 | -------------------------------------------------------------------------------- /pkg/mods/unix/rlim_t_int64.go: -------------------------------------------------------------------------------- 1 | //go:build freebsd 2 | 3 | package unix 4 | 5 | import ( 6 | "math" 7 | "math/big" 8 | "strconv" 9 | ) 10 | 11 | type rlimT = int64 12 | 13 | var rlimTValid = "number between 0 and " + strconv.FormatInt(math.MaxInt64, 10) 14 | 15 | const maxInt = int64(^uint(0) >> 1) 16 | 17 | func convertRlimT(x int64) any { 18 | if x <= maxInt { 19 | return int(x) 20 | } 21 | return big.NewInt(int64(x)) 22 | 23 | } 24 | 25 | func parseRlimT(val any) (int64, bool) { 26 | switch val := val.(type) { 27 | case int: 28 | return int64(val), true 29 | case *big.Int: 30 | if val.IsInt64() { 31 | return val.Int64(), true 32 | } 33 | case string: 34 | num, err := strconv.ParseInt(val, 0, 64) 35 | if err == nil { 36 | return num, true 37 | } 38 | } 39 | return 0, false 40 | } 41 | -------------------------------------------------------------------------------- /pkg/mods/unix/rlimit_keys_as.go: -------------------------------------------------------------------------------- 1 | //go:build linux || freebsd || netbsd 2 | 3 | package unix 4 | 5 | import "golang.org/x/sys/unix" 6 | 7 | func init() { 8 | addRlimitKeys(map[int]string{ 9 | unix.RLIMIT_AS: "as", 10 | }) 11 | } 12 | -------------------------------------------------------------------------------- /pkg/mods/unix/rlimit_keys_linux.go: -------------------------------------------------------------------------------- 1 | package unix 2 | 3 | import "golang.org/x/sys/unix" 4 | 5 | func init() { 6 | // https://man7.org/linux/man-pages/man2/setrlimit.2.html 7 | addRlimitKeys(map[int]string{ 8 | unix.RLIMIT_LOCKS: "locks", 9 | unix.RLIMIT_MSGQUEUE: "msgqueue", 10 | unix.RLIMIT_NICE: "nice", 11 | unix.RLIMIT_RTPRIO: "rtprio", 12 | unix.RLIMIT_RTTIME: "rttime", 13 | unix.RLIMIT_SIGPENDING: "sigpending", 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /pkg/mods/unix/rlimit_keys_netbsd.go: -------------------------------------------------------------------------------- 1 | package unix 2 | 3 | func init() { 4 | // https://man.netbsd.org/getrlimit.2 5 | // 6 | // RLIMIT_NTHR and RLIMIT_SBSIZE are missing from x/sys/unix; the values are 7 | // taken from https://github.com/NetBSD/src/blob/trunk/sys/sys/resource.h. 8 | addRlimitKeys(map[int]string{ 9 | 11: "nthr", 10 | 9: "sbsize", 11 | }) 12 | } 13 | -------------------------------------------------------------------------------- /pkg/mods/unix/testexport_test.go: -------------------------------------------------------------------------------- 1 | //go:build unix 2 | 3 | package unix 4 | 5 | type RlimT = rlimT 6 | 7 | var ( 8 | GetRlimit = &getRlimit 9 | SetRlimit = &setRlimit 10 | ) 11 | -------------------------------------------------------------------------------- /pkg/mods/unix/unix.go: -------------------------------------------------------------------------------- 1 | //go:build unix 2 | 3 | // Package unix exports an Elvish namespace that contains variables and 4 | // functions that deal with features unique to Unix-like operating systems. On 5 | // non-Unix operating systems it exports an empty namespace. 6 | package unix 7 | 8 | import ( 9 | "src.elv.sh/pkg/eval" 10 | "src.elv.sh/pkg/eval/vars" 11 | "src.elv.sh/pkg/logutil" 12 | ) 13 | 14 | // ExposeUnixNs indicate whether this module should be exposed as a usable 15 | // elvish namespace. 16 | const ExposeUnixNs = true 17 | 18 | // Ns is an Elvish namespace that contains variables and functions that deal 19 | // with features unique to Unix-like operating systems. 20 | var Ns = eval.BuildNs(). 21 | AddVars(map[string]vars.Var{ 22 | "umask": UmaskVariable{}, 23 | "rlimits": rlimitsVar{}, 24 | }).Ns() 25 | 26 | var logger = logutil.GetLogger("[mods/unix] ") 27 | -------------------------------------------------------------------------------- /pkg/parse/cmpd/cmpd_test.go: -------------------------------------------------------------------------------- 1 | package cmpd 2 | 3 | import "testing" 4 | 5 | func Test(t *testing.T) { 6 | // Test coverage of this package is provided by tests of its users. 7 | } 8 | -------------------------------------------------------------------------------- /pkg/parse/np/np_test.go: -------------------------------------------------------------------------------- 1 | package np 2 | 3 | import "testing" 4 | 5 | func Test(t *testing.T) { 6 | // Test coverage of this package is provided by tests of its users. 7 | } 8 | -------------------------------------------------------------------------------- /pkg/parse/parseutil/parseutil.go: -------------------------------------------------------------------------------- 1 | // Package parseutil contains utilities built on top of the parse package. 2 | package parseutil 3 | 4 | import ( 5 | "strings" 6 | 7 | "src.elv.sh/pkg/parse" 8 | ) 9 | 10 | // Wordify turns a piece of source code into words. 11 | func Wordify(src string) []string { 12 | tree, _ := parse.Parse(parse.Source{Name: "[unknown]", Code: src}, parse.Config{}) 13 | return wordifyInner(tree.Root, nil) 14 | } 15 | 16 | func wordifyInner(n parse.Node, words []string) []string { 17 | if len(parse.Children(n)) == 0 || isCompound(n) { 18 | text := parse.SourceText(n) 19 | if strings.TrimFunc(text, parse.IsWhitespace) != "" { 20 | return append(words, text) 21 | } 22 | return words 23 | } 24 | for _, ch := range parse.Children(n) { 25 | words = wordifyInner(ch, words) 26 | } 27 | return words 28 | } 29 | 30 | func isCompound(n parse.Node) bool { 31 | _, ok := n.(*parse.Compound) 32 | return ok 33 | } 34 | -------------------------------------------------------------------------------- /pkg/parse/parseutil/parseutil_test.go: -------------------------------------------------------------------------------- 1 | package parseutil 2 | 3 | import "testing" 4 | 5 | func Test(t *testing.T) { 6 | // Required to get accurate test coverage report. 7 | } 8 | -------------------------------------------------------------------------------- /pkg/parse/source.go: -------------------------------------------------------------------------------- 1 | package parse 2 | 3 | // TODO(xiaq): Move this into the diag package after implementing phantom types. 4 | 5 | // Source describes a piece of source code. 6 | type Source struct { 7 | Name string 8 | Code string 9 | IsFile bool 10 | } 11 | 12 | // SourceForTest returns a Source used for testing. 13 | func SourceForTest(code string) Source { 14 | return Source{Name: "[test]", Code: code} 15 | } 16 | -------------------------------------------------------------------------------- /pkg/parse/source_test.go: -------------------------------------------------------------------------------- 1 | package parse_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "src.elv.sh/pkg/eval/vals" 7 | "src.elv.sh/pkg/parse" 8 | ) 9 | 10 | func TestSourceAsMap(t *testing.T) { 11 | vals.TestValue(t, parse.Source{Name: "[tty]", Code: "echo"}). 12 | Kind("map"). 13 | Repr("[&code=echo &is-file=$false &name='[tty]']"). 14 | AllKeys("name", "code", "is-file") 15 | 16 | vals.TestValue(t, parse.Source{Name: "/etc/rc.elv", Code: "echo", IsFile: true}). 17 | Index("is-file", true) 18 | } 19 | -------------------------------------------------------------------------------- /pkg/parse/testdata/fuzz/FuzzParse/20ffbed79ec93b4b3f5872e6a329fee708da4b901c1b9c9ac10912ea7e862c9c: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("(((((((((((((((((((((((((((((((($'") 3 | -------------------------------------------------------------------------------- /pkg/parse/testdata/fuzz/FuzzParse/cfd8d7d9847d47d7fa94af306484de70f8952db8250aa6ebbf7b1c84afb13a91: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("((((((((((((((((((===(((((((((((((((\xbd\xf3v\xd4X\x9e\xfd\x89(((((((((0") 3 | -------------------------------------------------------------------------------- /pkg/parse/testutil_test.go: -------------------------------------------------------------------------------- 1 | package parse 2 | 3 | import ( 4 | "reflect" 5 | 6 | "src.elv.sh/pkg/tt" 7 | ) 8 | 9 | var Args = tt.Args 10 | 11 | var nodeType = reflect.TypeOf((*Node)(nil)).Elem() 12 | -------------------------------------------------------------------------------- /pkg/persistent/.gitignore: -------------------------------------------------------------------------------- 1 | /cover 2 | 3 | [._]*.s[a-w][a-z] 4 | [._]s[a-w][a-z] 5 | *.un~ 6 | Session.vim 7 | .netrwhist 8 | *~ 9 | 10 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 11 | *.o 12 | *.a 13 | *.so 14 | 15 | # Folders 16 | _obj 17 | _test 18 | 19 | # Architecture specific extensions/prefixes 20 | *.[568vq] 21 | [568vq].out 22 | 23 | *.cgo1.go 24 | *.cgo2.c 25 | _cgo_defun.c 26 | _cgo_gotypes.go 27 | _cgo_export.* 28 | 29 | _testmain.go 30 | 31 | *.exe 32 | *.test 33 | *.prof 34 | -------------------------------------------------------------------------------- /pkg/persistent/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.14 4 | - 1.15 5 | sudo: false 6 | os: 7 | - linux 8 | - osx 9 | script: 10 | make travis 11 | -------------------------------------------------------------------------------- /pkg/persistent/list/list.go: -------------------------------------------------------------------------------- 1 | // Package list implements persistent list. 2 | package list 3 | 4 | // List is a persistent list. 5 | type List interface { 6 | // Len returns the number of values in the list. 7 | Len() int 8 | // Conj returns a new list with an additional value in the front. 9 | Conj(any) List 10 | // First returns the first value in the list. 11 | First() any 12 | // Rest returns the list after the first value. 13 | Rest() List 14 | } 15 | 16 | // Empty is an empty list. 17 | var Empty List = &list{} 18 | 19 | type list struct { 20 | first any 21 | rest *list 22 | count int 23 | } 24 | 25 | func (l *list) Len() int { 26 | return l.count 27 | } 28 | 29 | func (l *list) Conj(val any) List { 30 | return &list{val, l, l.count + 1} 31 | } 32 | 33 | func (l *list) First() any { 34 | return l.first 35 | } 36 | 37 | func (l *list) Rest() List { 38 | return l.rest 39 | } 40 | -------------------------------------------------------------------------------- /pkg/persistent/persistent.go: -------------------------------------------------------------------------------- 1 | // Package persistent contains subpackages for persistent data structures, 2 | // similar to those of Clojure. 3 | package persistent 4 | -------------------------------------------------------------------------------- /pkg/pkg.go: -------------------------------------------------------------------------------- 1 | // Package pkg is the root of packages that implement Elvish. 2 | package pkg 3 | 4 | import "embed" 5 | 6 | // ElvFiles contains the Elvish sources found inside pkg - the .d.elv files for 7 | // modules implemented in Go and the actual sources of bundled modules. It is 8 | // defined to support reading documentation of builtin modules from Elvish 9 | // itself. 10 | // 11 | // Some of these files may be embedded as a string variable elsewhere. This is 12 | // fine since the compiler is smart enough to only include one copy of the same 13 | // file in the binary (as least as of Go 1.21). 14 | // 15 | // This is only used by [src.elv.sh/pkg/mods/doc], but has to live in this 16 | // package because go:embed only supports embedding files found in the current 17 | // directory and its descendents, and this directory is the lowest common 18 | // ancestor of these files. 19 | // 20 | //go:embed eval/*.elv edit/*.elv mods/*/*.elv 21 | var ElvFiles embed.FS 22 | -------------------------------------------------------------------------------- /pkg/pprof/pprof_test.elvts: -------------------------------------------------------------------------------- 1 | //each:elvish-in-global 2 | //each:in-temp-dir 3 | 4 | /////////////// 5 | # -cpuprofile # 6 | /////////////// 7 | 8 | ~> elvish -cpuprofile cpu 9 | ~> use os 10 | > (os:stat cpu)[size] 0 11 | ▶ $true 12 | 13 | ## bad path ## 14 | ~> elvish -cpuprofile bad/cpu &check-stderr-contains='Warning: cannot create CPU profile:' 15 | [stderr contains "Warning: cannot create CPU profile:"] true 16 | 17 | 18 | ////////////////// 19 | # -allocsprofile # 20 | ////////////////// 21 | 22 | ~> elvish -allocsprofile allocs 23 | ~> use os 24 | > (os:stat allocs)[size] 0 25 | ▶ $true 26 | 27 | ## bad path ## 28 | ~> elvish -allocsprofile bad/allocs &check-stderr-contains='Warning: cannot create memory allocation profile:' 29 | [stderr contains "Warning: cannot create memory allocation profile:"] true 30 | 31 | -------------------------------------------------------------------------------- /pkg/pprof/transcripts_test.go: -------------------------------------------------------------------------------- 1 | package pprof_test 2 | 3 | import ( 4 | "embed" 5 | "os" 6 | "testing" 7 | 8 | "src.elv.sh/pkg/eval/evaltest" 9 | "src.elv.sh/pkg/pprof" 10 | "src.elv.sh/pkg/prog" 11 | "src.elv.sh/pkg/prog/progtest" 12 | ) 13 | 14 | //go:embed *.elvts 15 | var transcripts embed.FS 16 | 17 | func TestTranscripts(t *testing.T) { 18 | evaltest.TestTranscriptsInFS(t, transcripts, 19 | "elvish-in-global", progtest.ElvishInGlobal( 20 | prog.Composite(&pprof.Program{}, noopProgram{})), 21 | ) 22 | } 23 | 24 | type noopProgram struct{} 25 | 26 | func (noopProgram) RegisterFlags(*prog.FlagSet) {} 27 | func (noopProgram) Run([3]*os.File, []string) error { return nil } 28 | -------------------------------------------------------------------------------- /pkg/rpc/debug.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package rpc 6 | 7 | /* 8 | Some HTML presented at http://machine:port/debug/rpc 9 | Lists services, their methods, and some statistics, still rudimentary. 10 | */ 11 | 12 | // If set, print log statements for internal and I/O errors. 13 | var debugLog = false 14 | -------------------------------------------------------------------------------- /pkg/shell/signal_unix.go: -------------------------------------------------------------------------------- 1 | //go:build unix 2 | 3 | package shell 4 | 5 | import ( 6 | "fmt" 7 | "io" 8 | "os" 9 | "syscall" 10 | 11 | "src.elv.sh/pkg/sys" 12 | ) 13 | 14 | func handleSignal(sig os.Signal, stderr io.Writer) { 15 | switch sig { 16 | case syscall.SIGHUP: 17 | syscall.Kill(0, syscall.SIGHUP) 18 | os.Exit(0) 19 | case syscall.SIGUSR1: 20 | fmt.Fprint(stderr, sys.DumpStack()) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /pkg/shell/signal_windows.go: -------------------------------------------------------------------------------- 1 | package shell 2 | 3 | import ( 4 | "io" 5 | "os" 6 | "syscall" 7 | ) 8 | 9 | func handleSignal(sig os.Signal, stderr io.Writer) { 10 | switch sig { 11 | // See https://pkg.go.dev/os/signal#hdr-Windows for the semantics of SIGTERM 12 | // on Windows. 13 | case syscall.SIGTERM: 14 | os.Exit(0) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /pkg/shell/testexports_test.go: -------------------------------------------------------------------------------- 1 | package shell 2 | 3 | var SecureRunDir = secureRunDir 4 | -------------------------------------------------------------------------------- /pkg/shell/transcripts_unix_test.go: -------------------------------------------------------------------------------- 1 | //go:build unix 2 | 3 | package shell_test 4 | 5 | import "syscall" 6 | 7 | func init() { 8 | sigCHLDName = syscall.SIGCHLD.String() 9 | } 10 | -------------------------------------------------------------------------------- /pkg/store/buckets.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | const ( 4 | bucketCmd = "cmd" 5 | bucketDir = "dir" 6 | ) 7 | 8 | // The following buckets were used before and are thus reserved: 9 | // "schema" 10 | -------------------------------------------------------------------------------- /pkg/store/cmd_test.go: -------------------------------------------------------------------------------- 1 | package store_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "src.elv.sh/pkg/store" 7 | "src.elv.sh/pkg/store/storetest" 8 | ) 9 | 10 | func TestCmd(t *testing.T) { 11 | storetest.TestCmd(t, store.MustTempStore(t)) 12 | } 13 | -------------------------------------------------------------------------------- /pkg/store/dir_test.go: -------------------------------------------------------------------------------- 1 | package store_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "src.elv.sh/pkg/store" 7 | "src.elv.sh/pkg/store/storetest" 8 | ) 9 | 10 | func TestDir(t *testing.T) { 11 | storetest.TestDir(t, store.MustTempStore(t)) 12 | } 13 | -------------------------------------------------------------------------------- /pkg/store/staticcheck.conf: -------------------------------------------------------------------------------- 1 | dot_import_whitelist = ["src.elv.sh/pkg/store/storedefs"] 2 | -------------------------------------------------------------------------------- /pkg/store/store.go: -------------------------------------------------------------------------------- 1 | // Package store defines the permanent storage service. 2 | package store 3 | -------------------------------------------------------------------------------- /pkg/store/storetest/storetest.go: -------------------------------------------------------------------------------- 1 | // Package storetest keeps test suites against storedefs.Store. 2 | package storetest 3 | 4 | func matchErr(e1, e2 error) bool { 5 | return (e1 == nil && e2 == nil) || (e1 != nil && e2 != nil && e1.Error() == e2.Error()) 6 | } 7 | -------------------------------------------------------------------------------- /pkg/strutil/camel_to_dashed.go: -------------------------------------------------------------------------------- 1 | package strutil 2 | 3 | import ( 4 | "strings" 5 | "unicode" 6 | ) 7 | 8 | // CamelToDashed converts a CamelCaseIdentifier to a dash-separated-identifier, 9 | // or a camelCaseIdentifier to a -dash-separated-identifier. All-cap words 10 | // are converted to lower case; HTTP becomes http and HTTPRequest becomes 11 | // http-request. 12 | func CamelToDashed(camel string) string { 13 | var sb strings.Builder 14 | runes := []rune(camel) 15 | for i, r := range runes { 16 | if (i == 0 && unicode.IsLower(r)) || 17 | (0 < i && i < len(runes)-1 && 18 | unicode.IsUpper(r) && unicode.IsLower(runes[i+1])) { 19 | sb.WriteRune('-') 20 | } 21 | sb.WriteRune(unicode.ToLower(r)) 22 | } 23 | return sb.String() 24 | } 25 | -------------------------------------------------------------------------------- /pkg/strutil/camel_to_dashed_test.go: -------------------------------------------------------------------------------- 1 | package strutil 2 | 3 | import ( 4 | "testing" 5 | 6 | "src.elv.sh/pkg/tt" 7 | ) 8 | 9 | var Args = tt.Args 10 | 11 | func TestCamelToDashed(t *testing.T) { 12 | tt.Test(t, CamelToDashed, 13 | Args("CamelCase").Rets("camel-case"), 14 | Args("camelCase").Rets("-camel-case"), 15 | Args("HTTP").Rets("http"), 16 | Args("HTTPRequest").Rets("http-request"), 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /pkg/strutil/chop.go: -------------------------------------------------------------------------------- 1 | package strutil 2 | 3 | // ChopLineEnding removes a line ending ("\r\n" or "\n") from the end of s. It 4 | // returns s if it doesn't end with a line ending. 5 | func ChopLineEnding(s string) string { 6 | if len(s) >= 2 && s[len(s)-2:] == "\r\n" { // Windows line ending 7 | return s[:len(s)-2] 8 | } else if len(s) >= 1 && s[len(s)-1] == '\n' { // Unix line ending 9 | return s[:len(s)-1] 10 | } 11 | return s 12 | } 13 | 14 | // ChopTerminator removes a specific terminator byte from the end of s. It 15 | // returns s if it doesn't end with the specified terminator. 16 | func ChopTerminator(s string, terminator byte) string { 17 | if len(s) >= 1 && s[len(s)-1] == terminator { 18 | return s[:len(s)-1] 19 | } 20 | return s 21 | } 22 | -------------------------------------------------------------------------------- /pkg/strutil/chop_test.go: -------------------------------------------------------------------------------- 1 | package strutil 2 | 3 | import ( 4 | "testing" 5 | 6 | "src.elv.sh/pkg/tt" 7 | ) 8 | 9 | func TestChopLineEnding(t *testing.T) { 10 | tt.Test(t, ChopLineEnding, 11 | Args("").Rets(""), 12 | Args("text").Rets("text"), 13 | Args("text\n").Rets("text"), 14 | Args("text\r\n").Rets("text"), 15 | // Only chop off one line ending 16 | Args("text\n\n").Rets("text\n"), 17 | // Preserve internal line endings 18 | Args("text\ntext 2\n").Rets("text\ntext 2"), 19 | ) 20 | } 21 | 22 | func TestChopTerminator(t *testing.T) { 23 | tt.Test(t, ChopTerminator, 24 | Args("", byte('\x00')).Rets(""), 25 | Args("foo", byte('\x00')).Rets("foo"), 26 | Args("foo\x00", byte('\x00')).Rets("foo"), 27 | Args("foo\x00\x00", byte('\x00')).Rets("foo\x00"), 28 | Args("foo\x00bar\x00", byte('\x00')).Rets("foo\x00bar"), 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /pkg/strutil/eol_sol.go: -------------------------------------------------------------------------------- 1 | package strutil 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | // FindFirstEOL returns the index of the first '\n'. When there is no '\n', the 8 | // length of s is returned. 9 | func FindFirstEOL(s string) int { 10 | eol := strings.IndexRune(s, '\n') 11 | if eol == -1 { 12 | eol = len(s) 13 | } 14 | return eol 15 | } 16 | 17 | // FindLastSOL returns an index just after the last '\n'. 18 | func FindLastSOL(s string) int { 19 | return strings.LastIndex(s, "\n") + 1 20 | } 21 | -------------------------------------------------------------------------------- /pkg/strutil/eol_sol_test.go: -------------------------------------------------------------------------------- 1 | package strutil 2 | 3 | import "testing" 4 | 5 | var EOLSOLTests = []struct { 6 | s string 7 | wantFirstEOL, wantLastSOL int 8 | }{ 9 | {"0", 1, 0}, 10 | {"\n12", 0, 1}, 11 | {"01\n", 2, 3}, 12 | {"01\n34", 2, 3}, 13 | } 14 | 15 | func TestEOLSOL(t *testing.T) { 16 | for _, tc := range EOLSOLTests { 17 | eol := FindFirstEOL(tc.s) 18 | if eol != tc.wantFirstEOL { 19 | t.Errorf("FindFirstEOL(%q) => %d, want %d", tc.s, eol, tc.wantFirstEOL) 20 | } 21 | sol := FindLastSOL(tc.s) 22 | if sol != tc.wantLastSOL { 23 | t.Errorf("FindLastSOL(%q) => %d, want %d", tc.s, sol, tc.wantLastSOL) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /pkg/strutil/join_lines.go: -------------------------------------------------------------------------------- 1 | package strutil 2 | 3 | import "strings" 4 | 5 | // JoinLines appends each line with a "\n" and joins all of them. 6 | func JoinLines(lines []string) string { 7 | if len(lines) == 0 { 8 | return "" 9 | } 10 | return strings.Join(lines, "\n") + "\n" 11 | } 12 | -------------------------------------------------------------------------------- /pkg/strutil/join_lines_test.go: -------------------------------------------------------------------------------- 1 | package strutil_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "src.elv.sh/pkg/strutil" 7 | "src.elv.sh/pkg/tt" 8 | ) 9 | 10 | var Args = tt.Args 11 | 12 | func TestJoinLines(t *testing.T) { 13 | tt.Test(t, strutil.JoinLines, 14 | Args([]string(nil)).Rets(""), 15 | Args([]string{"foo"}).Rets("foo\n"), 16 | Args([]string{"foo", "bar"}).Rets("foo\nbar\n"), 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /pkg/strutil/strutil.go: -------------------------------------------------------------------------------- 1 | // Package strutil provides string utilities. 2 | package strutil 3 | -------------------------------------------------------------------------------- /pkg/strutil/subseq.go: -------------------------------------------------------------------------------- 1 | package strutil 2 | 3 | import "strings" 4 | 5 | // HasSubseq determines whether s has t as its subsequence. A string t is a 6 | // subsequence of a string s if and only if there is a possible sequence of 7 | // steps of deleting characters from s that result in t. 8 | func HasSubseq(s, t string) bool { 9 | for _, p := range t { 10 | i := strings.IndexRune(s, p) 11 | if i == -1 { 12 | return false 13 | } 14 | s = s[i+len(string(p)):] 15 | } 16 | return true 17 | } 18 | -------------------------------------------------------------------------------- /pkg/strutil/subseq_test.go: -------------------------------------------------------------------------------- 1 | package strutil 2 | 3 | import "testing" 4 | 5 | var hasSubseqTests = []struct { 6 | s, t string 7 | want bool 8 | }{ 9 | {"", "", true}, 10 | {"a", "", true}, 11 | {"a", "a", true}, 12 | {"ab", "a", true}, 13 | {"ab", "b", true}, 14 | {"abc", "ac", true}, 15 | {"abcdefg", "bg", true}, 16 | {"abcdefg", "ga", false}, 17 | {"foo lorem ipsum", "f l i", true}, 18 | {"foo lorem ipsum", "oo o pm", true}, 19 | {"你好世界", "好", true}, 20 | {"你好世界", "好界", true}, 21 | } 22 | 23 | func TestHasSubseq(t *testing.T) { 24 | for _, test := range hasSubseqTests { 25 | if b := HasSubseq(test.s, test.t); b != test.want { 26 | t.Errorf("HasSubseq(%q, %q) = %v, want %v", test.s, test.t, b, test.want) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /pkg/strutil/title.go: -------------------------------------------------------------------------------- 1 | package strutil 2 | 3 | import ( 4 | "unicode" 5 | "unicode/utf8" 6 | ) 7 | 8 | // Title returns s with the first codepoint changed to title case. 9 | func Title(s string) string { 10 | r, size := utf8.DecodeRuneInString(s) 11 | if r == utf8.RuneError || size == 0 { 12 | return s 13 | } 14 | return string(unicode.ToTitle(r)) + s[size:] 15 | } 16 | -------------------------------------------------------------------------------- /pkg/strutil/title_test.go: -------------------------------------------------------------------------------- 1 | package strutil 2 | 3 | import ( 4 | "testing" 5 | 6 | "src.elv.sh/pkg/tt" 7 | ) 8 | 9 | func TestTitle(t *testing.T) { 10 | tt.Test(t, Title, 11 | Args("").Rets(""), 12 | Args("foo").Rets("Foo"), 13 | Args("\xf0").Rets("\xf0"), 14 | Args("FOO").Rets("FOO"), 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /pkg/sys/dumpstack.go: -------------------------------------------------------------------------------- 1 | package sys 2 | 3 | import "runtime" 4 | 5 | const dumpStackBufSizeInit = 8192 6 | 7 | func DumpStack() string { 8 | buf := make([]byte, dumpStackBufSizeInit) 9 | for { 10 | n := runtime.Stack(buf, true) 11 | if n < cap(buf) { 12 | return string(buf[:n]) 13 | } 14 | buf = make([]byte, cap(buf)*2) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /pkg/sys/eunix/eunix.go: -------------------------------------------------------------------------------- 1 | //go:build unix 2 | 3 | // Package eunix provides extra Unix-specific system utilities. 4 | package eunix 5 | -------------------------------------------------------------------------------- /pkg/sys/eunix/tc.go: -------------------------------------------------------------------------------- 1 | //go:build unix 2 | 3 | package eunix 4 | 5 | import ( 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | // Tcsetpgrp sets the terminal foreground process group. 10 | func Tcsetpgrp(fd int, pid int) error { 11 | return unix.IoctlSetPointerInt(fd, unix.TIOCSPGRP, pid) 12 | } 13 | -------------------------------------------------------------------------------- /pkg/sys/eunix/termios_bsd.go: -------------------------------------------------------------------------------- 1 | //go:build darwin || dragonfly || freebsd || netbsd || openbsd 2 | 3 | // Copyright 2015 go-termios Author. All Rights Reserved. 4 | // https://github.com/go-termios/termios 5 | // Author: John Lenton 6 | 7 | package eunix 8 | 9 | import "golang.org/x/sys/unix" 10 | 11 | const ( 12 | getAttrIOCTL = unix.TIOCGETA 13 | setAttrNowIOCTL = unix.TIOCSETA 14 | setAttrDrainIOCTL = unix.TIOCSETAW 15 | setAttrFlushIOCTL = unix.TIOCSETAF 16 | flushIOCTL = unix.TIOCFLUSH 17 | ) 18 | -------------------------------------------------------------------------------- /pkg/sys/eunix/termios_notbsd.go: -------------------------------------------------------------------------------- 1 | //go:build linux || solaris 2 | 3 | // Copyright 2015 go-termios Author. All Rights Reserved. 4 | // https://github.com/go-termios/termios 5 | // Author: John Lenton 6 | 7 | package eunix 8 | 9 | import "golang.org/x/sys/unix" 10 | 11 | const ( 12 | getAttrIOCTL = unix.TCGETS 13 | setAttrNowIOCTL = unix.TCSETS 14 | setAttrDrainIOCTL = unix.TCSETSW 15 | setAttrFlushIOCTL = unix.TCSETSF 16 | flushIOCTL = unix.TCFLSH 17 | ) 18 | -------------------------------------------------------------------------------- /pkg/sys/eunix/termiosflag_darwin.go: -------------------------------------------------------------------------------- 1 | package eunix 2 | 3 | // Only Darwin uses 64-bit flags in Termios on 64-bit architectures. See: 4 | // https://cs.opensource.google/search?q=%5BIOCL%5Dflag.*uint64&sq=&ss=go%2Fx%2Fsys 5 | // 6 | // Darwin uses 32-bit flags on 32-bit architectures, but Go no longer supports 7 | // them since Go 1.15. 8 | 9 | type termiosFlag = uint64 10 | -------------------------------------------------------------------------------- /pkg/sys/eunix/termiosflag_notdarwin.go: -------------------------------------------------------------------------------- 1 | //go:build unix && !darwin 2 | 3 | package eunix 4 | 5 | type termiosFlag = uint32 6 | -------------------------------------------------------------------------------- /pkg/sys/eunix/waitforread_test.go: -------------------------------------------------------------------------------- 1 | //go:build unix 2 | 3 | package eunix 4 | 5 | import ( 6 | "io" 7 | "testing" 8 | 9 | "src.elv.sh/pkg/must" 10 | ) 11 | 12 | func TestWaitForRead(t *testing.T) { 13 | r0, w0 := must.Pipe() 14 | r1, w1 := must.Pipe() 15 | defer closeAll(r0, w0, r1, w1) 16 | 17 | w0.WriteString("x") 18 | ready, err := WaitForRead(-1, r0, r1) 19 | if err != nil { 20 | t.Error("WaitForRead errors:", err) 21 | } 22 | if !ready[0] { 23 | t.Error("Want ready[0]") 24 | } 25 | if ready[1] { 26 | t.Error("Don't want ready[1]") 27 | } 28 | } 29 | 30 | func closeAll(files ...io.Closer) { 31 | for _, file := range files { 32 | file.Close() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /pkg/sys/ewindows/ewindows.go: -------------------------------------------------------------------------------- 1 | //go:generate cmd /c go tool cgo -godefs types.go > ztypes_windows.go && gofmt -w ztypes_windows.go 2 | //go:build windows 3 | 4 | // Package ewindows provides extra Windows-specific system utilities. 5 | package ewindows 6 | 7 | import "golang.org/x/sys/windows" 8 | 9 | var kernel32 = windows.NewLazySystemDLL("kernel32.dll") 10 | -------------------------------------------------------------------------------- /pkg/sys/ewindows/types.go: -------------------------------------------------------------------------------- 1 | //go:build ignore 2 | 3 | package ewindows 4 | 5 | /* 6 | #include 7 | */ 8 | import "C" 9 | 10 | type ( 11 | Coord C.COORD 12 | InputRecord C.INPUT_RECORD 13 | 14 | KeyEvent C.KEY_EVENT_RECORD 15 | MouseEvent C.MOUSE_EVENT_RECORD 16 | WindowBufferSizeEvent C.WINDOW_BUFFER_SIZE_RECORD 17 | MenuEvent C.MENU_EVENT_RECORD 18 | FocusEvent C.FOCUS_EVENT_RECORD 19 | ) 20 | 21 | const ( 22 | KEY_EVENT = C.KEY_EVENT 23 | MOUSE_EVENT = C.MOUSE_EVENT 24 | WINDOW_BUFFER_SIZE_EVENT = C.WINDOW_BUFFER_SIZE_EVENT 25 | MENU_EVENT = C.MENU_EVENT 26 | FOCUS_EVENT = C.FOCUS_EVENT 27 | ) 28 | -------------------------------------------------------------------------------- /pkg/sys/signal_nonunix.go: -------------------------------------------------------------------------------- 1 | //go:build windows || plan9 || js 2 | 3 | package sys 4 | 5 | import ( 6 | "os" 7 | "os/signal" 8 | ) 9 | 10 | func notifySignals() chan os.Signal { 11 | // This catches every signal regardless of whether it is ignored. 12 | sigCh := make(chan os.Signal, sigsChanBufferSize) 13 | signal.Notify(sigCh) 14 | return sigCh 15 | } 16 | -------------------------------------------------------------------------------- /pkg/sys/signal_unix.go: -------------------------------------------------------------------------------- 1 | //go:build unix 2 | 3 | package sys 4 | 5 | import ( 6 | "os" 7 | "os/signal" 8 | "syscall" 9 | ) 10 | 11 | func notifySignals() chan os.Signal { 12 | // This catches every signal regardless of whether it is ignored. 13 | sigCh := make(chan os.Signal, sigsChanBufferSize) 14 | signal.Notify(sigCh) 15 | // Calling signal.Notify will reset the signal ignore status, so we need to 16 | // call signal.Ignore every time we call signal.Notify. 17 | // 18 | // TODO: Remove this if, and when, job control is implemented. This 19 | // handles the case of running an external command from an interactive 20 | // prompt. 21 | // 22 | // See https://b.elv.sh/988. 23 | signal.Ignore(syscall.SIGTTIN, syscall.SIGTTOU, syscall.SIGTSTP) 24 | return sigCh 25 | } 26 | -------------------------------------------------------------------------------- /pkg/sys/sys.go: -------------------------------------------------------------------------------- 1 | // Package sys provide system utilities with the same API across OSes. 2 | // 3 | // The subpackages eunix and ewindows provide OS-specific utilities. 4 | package sys 5 | 6 | import ( 7 | "os" 8 | 9 | "github.com/mattn/go-isatty" 10 | ) 11 | 12 | const sigsChanBufferSize = 256 13 | 14 | // NotifySignals returns a channel on which all signals gets delivered. 15 | func NotifySignals() chan os.Signal { return notifySignals() } 16 | 17 | // SIGWINCH is the window size change signal. 18 | const SIGWINCH = sigWINCH 19 | 20 | // Winsize queries the size of the terminal referenced by the given file. 21 | func WinSize(file *os.File) (row, col int) { return winSize(file) } 22 | 23 | // IsATTY determines whether the given file is a terminal. 24 | func IsATTY(fd uintptr) bool { 25 | return isatty.IsTerminal(fd) || isatty.IsCygwinTerminal(fd) 26 | } 27 | -------------------------------------------------------------------------------- /pkg/sys/winsize_unix.go: -------------------------------------------------------------------------------- 1 | //go:build unix 2 | 3 | // Copyright 2015 go-termios Author. All Rights Reserved. 4 | // https://github.com/go-termios/termios 5 | // Author: John Lenton 6 | 7 | package sys 8 | 9 | import ( 10 | "os" 11 | 12 | "golang.org/x/sys/unix" 13 | ) 14 | 15 | const sigWINCH = unix.SIGWINCH 16 | 17 | func winSize(file *os.File) (row, col int) { 18 | fd := int(file.Fd()) 19 | ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ) 20 | if err != nil { 21 | return -1, -1 22 | } 23 | 24 | // Pick up a reasonable value for row and col 25 | // if they equal zero in special case, 26 | // e.g. serial console 27 | if ws.Col == 0 { 28 | ws.Col = 80 29 | } 30 | if ws.Row == 0 { 31 | ws.Row = 24 32 | } 33 | 34 | return int(ws.Row), int(ws.Col) 35 | } 36 | -------------------------------------------------------------------------------- /pkg/sys/winsize_windows.go: -------------------------------------------------------------------------------- 1 | package sys 2 | 3 | import ( 4 | "os" 5 | "syscall" 6 | 7 | "golang.org/x/sys/windows" 8 | ) 9 | 10 | // Windows doesn't have SIGCH, so use an impossible value. 11 | const sigWINCH = syscall.Signal(-1) 12 | 13 | func winSize(file *os.File) (row, col int) { 14 | var info windows.ConsoleScreenBufferInfo 15 | err := windows.GetConsoleScreenBufferInfo(windows.Handle(file.Fd()), &info) 16 | if err != nil { 17 | return -1, -1 18 | } 19 | window := info.Window 20 | return int(window.Bottom - window.Top), int(window.Right - window.Left) 21 | } 22 | -------------------------------------------------------------------------------- /pkg/testutil/chmod.go: -------------------------------------------------------------------------------- 1 | package testutil 2 | 3 | import ( 4 | "io/fs" 5 | "os" 6 | ) 7 | 8 | // ChmodOrSkip runs [os.Chmod], but skips the test if file's mode is not exactly 9 | // mode or if there is any error. 10 | func ChmodOrSkip(s Skipper, name string, mode fs.FileMode) { 11 | err := os.Chmod(name, mode) 12 | if err != nil { 13 | s.Skipf("chmod: %v", err) 14 | } 15 | fi, err := os.Stat(name) 16 | if err != nil { 17 | s.Skipf("stat: %v", err) 18 | } 19 | if fi.Mode() != mode { 20 | s.Skipf("file mode %O is not %O", fi.Mode(), mode) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /pkg/testutil/dedent.go: -------------------------------------------------------------------------------- 1 | package testutil 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | // Dedent removes an optional leading newline, and removes the indentation 9 | // present in the first line from all subsequent non-empty lines. 10 | // 11 | // Dedent panics if any non-empty line does not start with the same indentation 12 | // as the first line. 13 | func Dedent(text string) string { 14 | lines := strings.Split(strings.TrimPrefix(text, "\n"), "\n") 15 | line0 := lines[0] 16 | indent := line0[:len(line0)-len(strings.TrimLeft(lines[0], " \t"))] 17 | for i, line := range lines { 18 | if !strings.HasPrefix(line, indent) && line != "" { 19 | panic(fmt.Sprintf("line %d is not empty but doesn't start with %q", i, indent)) 20 | } 21 | lines[i] = strings.TrimPrefix(line, indent) 22 | } 23 | return strings.Join(lines, "\n") 24 | } 25 | -------------------------------------------------------------------------------- /pkg/testutil/fs.go: -------------------------------------------------------------------------------- 1 | package testutil 2 | 3 | type FS map[string]File 4 | -------------------------------------------------------------------------------- /pkg/testutil/recover.go: -------------------------------------------------------------------------------- 1 | package testutil 2 | 3 | func Recover(f func()) (r any) { 4 | defer func() { r = recover() }() 5 | f() 6 | return 7 | } 8 | -------------------------------------------------------------------------------- /pkg/testutil/recover_test.go: -------------------------------------------------------------------------------- 1 | package testutil 2 | 3 | import ( 4 | "testing" 5 | 6 | "src.elv.sh/pkg/tt" 7 | ) 8 | 9 | var Args = tt.Args 10 | 11 | func TestRecover(t *testing.T) { 12 | tt.Test(t, Recover, 13 | Args(func() {}).Rets(nil), 14 | Args(func() { 15 | panic("unreachable") 16 | }).Rets("unreachable"), 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /pkg/testutil/scaled.go: -------------------------------------------------------------------------------- 1 | package testutil 2 | 3 | import ( 4 | "os" 5 | "strconv" 6 | "time" 7 | 8 | "src.elv.sh/pkg/env" 9 | ) 10 | 11 | // Scaled returns d scaled by $E:ELVISH_TEST_TIME_SCALE. If the environment 12 | // variable does not exist or contains an invalid value, the scale defaults to 13 | // 1. 14 | func Scaled(d time.Duration) time.Duration { 15 | return time.Duration(float64(d) * TestTimeScale()) 16 | } 17 | 18 | // TestTimeScale parses $E:ELVISH_TEST_TIME_SCALE, defaulting to 1 it it's not 19 | // set or can't be parsed as a float64. 20 | func TestTimeScale() float64 { 21 | env := os.Getenv(env.ELVISH_TEST_TIME_SCALE) 22 | if env == "" { 23 | return 1 24 | } 25 | scale, err := strconv.ParseFloat(env, 64) 26 | if err != nil || scale <= 0 { 27 | return 1 28 | } 29 | return scale 30 | } 31 | -------------------------------------------------------------------------------- /pkg/testutil/set.go: -------------------------------------------------------------------------------- 1 | package testutil 2 | 3 | func Set[T any](c Cleanuper, p *T, v T) { 4 | old := *p 5 | *p = v 6 | c.Cleanup(func() { *p = old }) 7 | } 8 | -------------------------------------------------------------------------------- /pkg/testutil/set_test.go: -------------------------------------------------------------------------------- 1 | package testutil 2 | 3 | import "testing" 4 | 5 | func TestSet(t *testing.T) { 6 | c := &cleanuper{} 7 | s := "old" 8 | Set(c, &s, "new") 9 | if s != "new" { 10 | t.Errorf("After Set, s = %q, want %q", s, "new") 11 | } 12 | 13 | c.runCleanups() 14 | if s != "old" { 15 | t.Errorf("After Set, s = %q, want %q", s, "old") 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /pkg/testutil/temp_env.go: -------------------------------------------------------------------------------- 1 | package testutil 2 | 3 | import "os" 4 | 5 | // Setenv sets the value of an environment variable for the duration of a test. 6 | // It returns value. 7 | func Setenv(c Cleanuper, name, value string) string { 8 | SaveEnv(c, name) 9 | os.Setenv(name, value) 10 | return value 11 | } 12 | 13 | // Setenv unsets an environment variable for the duration of a test. 14 | func Unsetenv(c Cleanuper, name string) { 15 | SaveEnv(c, name) 16 | os.Unsetenv(name) 17 | } 18 | 19 | // SaveEnv saves the current value of an environment variable so that it will be 20 | // restored after a test has finished. 21 | func SaveEnv(c Cleanuper, name string) { 22 | oldValue, existed := os.LookupEnv(name) 23 | if existed { 24 | c.Cleanup(func() { os.Setenv(name, oldValue) }) 25 | } else { 26 | c.Cleanup(func() { os.Unsetenv(name) }) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /pkg/testutil/testdir_nonwindows_test.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | 3 | package testutil 4 | 5 | import ( 6 | "os" 7 | "testing" 8 | ) 9 | 10 | func TestApplyDir_CreatesFileWithPerm(t *testing.T) { 11 | InTempDir(t) 12 | 13 | ApplyDir(Dir{ 14 | // For some unknown reason, termux on Android does not set the 15 | // group and other permission bits correctly, so we use 700 here. 16 | "a": File{0700, "a content"}, 17 | }) 18 | 19 | testFileContent(t, "a", "a content") 20 | testFilePerm(t, "a", 0700) 21 | } 22 | 23 | func testFilePerm(t *testing.T, filename string, wantPerm os.FileMode) { 24 | t.Helper() 25 | info, err := os.Stat(filename) 26 | if err != nil { 27 | t.Errorf("Could not stat %v: %v", filename, err) 28 | return 29 | } 30 | if perm := info.Mode().Perm(); perm != wantPerm { 31 | t.Errorf("File %v has perm %o, want %o", filename, perm, wantPerm) 32 | wd, err := os.Getwd() 33 | if err == nil { 34 | t.Logf("pwd is %v", wd) 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /pkg/testutil/testutil.go: -------------------------------------------------------------------------------- 1 | // Package testutil contains common test utilities. 2 | package testutil 3 | 4 | // Cleanuper wraps the Cleanup method. It is a subset of [testing.TB], thus 5 | // satisfied by [*testing.T] and [*testing.B]. 6 | type Cleanuper interface { 7 | Cleanup(func()) 8 | } 9 | 10 | // Skipper wraps the Skipf method. It is a subset of [testing.TB], thus 11 | // satisfied by [*testing.T] and [*testing.B]. 12 | type Skipper interface { 13 | Skipf(format string, args ...any) 14 | } 15 | -------------------------------------------------------------------------------- /pkg/testutil/testutil_test.go: -------------------------------------------------------------------------------- 1 | package testutil 2 | 3 | type cleanuper struct{ fns []func() } 4 | 5 | func (c *cleanuper) Cleanup(fn func()) { c.fns = append(c.fns, fn) } 6 | 7 | func (c *cleanuper) runCleanups() { 8 | for i := len(c.fns) - 1; i >= 0; i-- { 9 | c.fns[i]() 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /pkg/testutil/umask.go: -------------------------------------------------------------------------------- 1 | package testutil 2 | 3 | // Umask sets the umask for the duration of the test, and restores it afterwards. 4 | func Umask(c Cleanuper, m int) { 5 | save := umask(m) 6 | c.Cleanup(func() { _ = umask(save) }) 7 | } 8 | -------------------------------------------------------------------------------- /pkg/testutil/umask_unix.go: -------------------------------------------------------------------------------- 1 | //go:build unix 2 | 3 | package testutil 4 | 5 | import "golang.org/x/sys/unix" 6 | 7 | var umask = unix.Umask 8 | -------------------------------------------------------------------------------- /pkg/testutil/umask_windows.go: -------------------------------------------------------------------------------- 1 | package testutil 2 | 3 | func umask(int) int { return 0 } 4 | -------------------------------------------------------------------------------- /pkg/ui/styledown/testdata/fuzz/FuzzDerenderIsInverseOfRender/6977bdc7b3d73e31: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("00000000000\n *\n\n0 red\n1 green") 3 | string("* green") 4 | -------------------------------------------------------------------------------- /pkg/ui/styledown/testdata/fuzz/FuzzDerenderIsInverseOfRender/75d8aa7f136bd12c: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("\x02\n") 3 | string("0") 4 | -------------------------------------------------------------------------------- /pkg/ui/styledown/testdata/fuzz/FuzzDerenderIsInverseOfRender/f8a03ebd1a65bf42: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("00000000000\n R G G\n\nR red\nG green") 3 | string("0 red\n0 green") 4 | -------------------------------------------------------------------------------- /pkg/ui/styledown/transcripts_test.go: -------------------------------------------------------------------------------- 1 | package styledown_test 2 | 3 | import ( 4 | "embed" 5 | "testing" 6 | 7 | "src.elv.sh/pkg/eval/evaltest" 8 | ) 9 | 10 | //go:embed *.elvts 11 | var transcripts embed.FS 12 | 13 | func TestTranscripts(t *testing.T) { 14 | // Both Render and Derender are available as Elvish builtins, so no 15 | // additional setup is needed. 16 | evaltest.TestTranscriptsInFS(t, transcripts) 17 | } 18 | -------------------------------------------------------------------------------- /pkg/ui/ui.go: -------------------------------------------------------------------------------- 1 | // Package ui contains types that may be used by different editor frontends. 2 | package ui 3 | -------------------------------------------------------------------------------- /tools/check-fmt-go.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # Check if Go files are properly formatted without modifying them. 3 | 4 | echo 'Go files need these changes:' 5 | # The grep is needed because `goimports -d` and `gofmt -d` always exits with 0. 6 | if find . -name '*.go' | xargs goimports -d | grep .; then 7 | exit 1 8 | fi 9 | if find . -name '*.go' | xargs gofmt -s -d | grep .; then 10 | exit 1 11 | fi 12 | echo ' None!' 13 | -------------------------------------------------------------------------------- /tools/check-fmt-md.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # Check if Markdown files are properly formatted without modifying them. 3 | 4 | echo 'Markdown files that need changes:' 5 | if find . -name '*.md' | grep -v '/node_modules/' | xargs go run src.elv.sh/cmd/elvmdfmt -width 80 -d | grep .; then 6 | exit 1 7 | fi 8 | echo ' None!' 9 | -------------------------------------------------------------------------------- /tools/prune-cover.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | # Prune the same objects from the "make cover" report that we tell Codecov 4 | # (https://codecov.io/gh/elves/elvish/) to ignore. 5 | 6 | if test $# != 1 7 | then 8 | echo 'Usage: cover_prune.sh ${codecov.yml}' >&2 9 | exit 1 10 | fi 11 | yaml="$1" 12 | data="$2" 13 | 14 | sed -En '/^ignore:/,/^[^ ]/s/^ *- "(.*)"/src.elv.sh\/\1/p' $yaml > $yaml.ignore 15 | grep -F -v -f $yaml.ignore 16 | rm $yaml.ignore 17 | -------------------------------------------------------------------------------- /tools/run-race.elv: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env elvish 2 | # Prints "-race" if running on a platform that supports the race detector. 3 | use re 4 | 5 | # Keep in sync with the official list here: 6 | # https://golang.org/doc/articles/race_detector#Requirements 7 | var supported-os-arch = [ 8 | linux/amd64 9 | linux/ppc64le 10 | linux/arm64 11 | linux/s390x 12 | freebsd/amd64 13 | netbsd/amd64 14 | darwin/amd64 15 | darwin/arm64 16 | ] 17 | 18 | if (eq 1 (go env CGO_ENABLED)) { 19 | var os arch = (go env GOOS GOARCH) 20 | var os-arch = $os/$arch 21 | if (has-value $supported-os-arch $os-arch) { 22 | echo -race 23 | } elif (eq windows/amd64 $os-arch) { 24 | # Race detector on windows/amd64 requires gcc: 25 | # https://github.com/golang/go/issues/27089 26 | if (has-external gcc) { 27 | echo -race 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /vscode/.eslint.js: -------------------------------------------------------------------------------- 1 | /**@type {import('eslint').Linter.Config} */ 2 | // eslint-disable-next-line no-undef 3 | module.exports = { 4 | root: true, 5 | parser: '@typescript-eslint/parser', 6 | plugins: [ 7 | '@typescript-eslint', 8 | ], 9 | extends: [ 10 | 'eslint:recommended', 11 | 'plugin:@typescript-eslint/recommended', 12 | ], 13 | rules: { 14 | 'semi': [2, "always"], 15 | '@typescript-eslint/no-unused-vars': 0, 16 | '@typescript-eslint/no-explicit-any': 0, 17 | '@typescript-eslint/explicit-module-boundary-types': 0, 18 | '@typescript-eslint/no-non-null-assertion': 0, 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /vscode/.gitignore: -------------------------------------------------------------------------------- 1 | /dist 2 | /node_modules 3 | /*.vsix 4 | -------------------------------------------------------------------------------- /vscode/.vscodeignore: -------------------------------------------------------------------------------- 1 | node_modules/** 2 | -------------------------------------------------------------------------------- /vscode/HACKING.md: -------------------------------------------------------------------------------- 1 | # Developing the Visual Studio Code extension 2 | 3 | Follow these steps to run the extension from source: 4 | 5 | 1. Install NPM dependencies: `npm install`. 6 | 7 | 2. Open the repository root (**not** this directory) in VS Code, and choose 8 | "Run Extension" from the 9 | [debug side bar](https://code.visualstudio.com/docs/editor/debugging). 10 | 11 | See Visual Studio Code's documentation on 12 | [developing language extensions](https://code.visualstudio.com/api/language-extensions/overview). 13 | -------------------------------------------------------------------------------- /vscode/README.md: -------------------------------------------------------------------------------- 1 | # Elvish for Visual Studio Code 2 | 3 | This extension provides support for [Elvish](https://elv.sh) in 4 | [Visual Studio Code](https://code.visualstudio.com). 5 | 6 | Current functionalities: 7 | 8 | - Syntax highlighting and configuration (e.g. bracket pairs) 9 | 10 | - Error checking and basic autocompletion, using Elvish's builtin language 11 | server 12 | -------------------------------------------------------------------------------- /vscode/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elves/elvish/49937be09a7e615a244df724fe0678af017ebe51/vscode/icon.png -------------------------------------------------------------------------------- /vscode/language-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | "lineComment": "#", 4 | }, 5 | // Barewords and variables have different patterns. This is the pattern for 6 | // variables because it's more important for VS Code to recognize variable 7 | // names properly. 8 | "wordPattern": "[\\w\\d_:~-]+", 9 | "brackets": [ 10 | ["{", "}"], 11 | ["[", "]"], 12 | ["(", ")"] 13 | ], 14 | "autoClosingPairs": [ 15 | ["{", "}"], 16 | ["[", "]"], 17 | ["(", ")"], 18 | ["\"", "\""], 19 | ["'", "'"] 20 | ], 21 | "surroundingPairs": [ 22 | ["{", "}"], 23 | ["[", "]"], 24 | ["(", ")"], 25 | ["\"", "\""], 26 | ["'", "'"] 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /vscode/sample.elv: -------------------------------------------------------------------------------- 1 | # A sample file to test syntax highlighting. 2 | 3 | nop "double \n quoted" and 'single '' quoted' # comment 4 | 5 | # Various variable contexts 6 | nop $pid 7 | var var-name = { var fn-name~ = {var not-var-name} } 8 | nop (set var-name = foo | tmp var-name = bar); del var-name 9 | with var-name = foo { } 10 | # This one doesn't work, we need a real parser or some very messy heuristics 11 | with [var-name1 = foo] [var-name2 = bar] { } 12 | for var-name [] { } 13 | try { } catch var-name { } 14 | 15 | # Builtin functions 16 | != a (nop b) | echo c 17 | 18 | # Builtin special command 19 | and a b # "operator" 20 | use re # "other" 21 | if a { } elif b { } else { } 22 | try { } except err { } else { } finally { } 23 | 24 | # Metacharacters 25 | echo ** () [] 26 | 27 | # Regression tests 28 | set-env # should highlight entire set-env 29 | set-foo # should highlight nothing 30 | -------------------------------------------------------------------------------- /vscode/sample.elvts: -------------------------------------------------------------------------------- 1 | # A sample file for elvish transcript files. # 2 | 3 | // The line above should be highlighted as a heading, and this line should be 4 | // highlighted as a comment. 5 | 6 | // H2s are also supported: 7 | 8 | ## This is an H2 ## 9 | 10 | // The code after the prompt should be highlighted as Elvish code. 11 | ~> if $true { echo ok } 12 | ok 13 | // The output should be unhighlighted. 14 | -------------------------------------------------------------------------------- /vscode/sample.styledown: -------------------------------------------------------------------------------- 1 | ~> echo $foo 2 | ** GGGG MMMM 3 | 4 | G fg-green bold 5 | M fg-magenta 6 | -------------------------------------------------------------------------------- /vscode/src/lsp.ts: -------------------------------------------------------------------------------- 1 | /** Hooks up LSP for Elvish sources. */ 2 | 3 | import * as vscode from 'vscode'; 4 | import { LanguageClient } from 'vscode-languageclient/node'; 5 | 6 | export function activateLsp(context: vscode.ExtensionContext) { 7 | const client = new LanguageClient( 8 | "elvish", 9 | "Elvish Language Server", 10 | { command: "elvish", args: ["-lsp"] }, 11 | { documentSelector: [{ scheme: "file", language: "elvish" }] } 12 | ); 13 | client.start(); 14 | 15 | return () => { client.stop() }; 16 | } 17 | -------------------------------------------------------------------------------- /vscode/src/utils/logging.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | export let debugChannel: vscode.OutputChannel; 4 | 5 | export function activateLogging(context: vscode.ExtensionContext) { 6 | debugChannel = vscode.window.createOutputChannel('Elvish Debug'); 7 | return () => { }; 8 | } 9 | -------------------------------------------------------------------------------- /vscode/syntaxes/elvish-transcript.tmLanguage.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", 3 | "name": "Elvish transcript", 4 | "scopeName": "source.elvish-transcript", 5 | "fileTypes": [ 6 | "elvts" 7 | ], 8 | "patterns": [ 9 | { 10 | "begin": "(^|\\G)[~/][^ ]*> ", 11 | "while": "(^|\\G) ", 12 | "contentName": "meta.embedded.block.elvish", 13 | "patterns": [ 14 | { 15 | "include": "source.elvish" 16 | } 17 | ] 18 | }, 19 | { 20 | "name": "markup.heading.1.elvish-transcript", 21 | "match": "(^|\\G)# .* #$" 22 | }, 23 | { 24 | "name": "markup.heading.2.elvish-transcript", 25 | "match": "(^|\\G)## .* ##$" 26 | }, 27 | { 28 | "name": "comment.line.double-slash.elvish-transcript", 29 | "match": "(^|\\G)//.*$" 30 | } 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /vscode/transcript-language-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | "lineComment": "//" 4 | }, 5 | // See comment in language-configuration.json. 6 | "wordPattern": "[\\w\\d_:~-]+", 7 | } 8 | -------------------------------------------------------------------------------- /vscode/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es2020", 5 | "lib": ["es2020"], 6 | "outDir": "tsc-out", 7 | "sourceMap": true, 8 | "strict": true, 9 | "rootDir": "src" 10 | }, 11 | "exclude": ["node_modules", ".vscode-test"] 12 | } 13 | -------------------------------------------------------------------------------- /website/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | !/template.html 3 | !*-ttyshot.html 4 | *.raw 5 | /tools/*.bin 6 | /_* 7 | /Elvish.docset 8 | /Elvish.tgz 9 | -------------------------------------------------------------------------------- /website/blog/0.13.1-release-notes.md: -------------------------------------------------------------------------------- 1 | Version 0.13.1 has been released on 2020-03-21, less than 3 months after 0.13. 2 | This is a bugfix release. If you upgraded from 0.12, please also read the 3 | [0.13 release notes](0.13-release-notes.html). 4 | 5 | As usual, [prebuilt binaries](https://elv.sh/get) are offered for most common 6 | platforms. 7 | 8 | The following bugs have been fixed: [#883](https://b.elv.sh/883), 9 | [#891](https://b.elv.sh/891), [#852](https://b.elv.sh/852), 10 | [#788](https://b.elv.sh/788). 11 | 12 | A single new feature has been introduced: the `edit:redraw` function now accepts 13 | a `full` option. When true, it forces Elvish to do a full redraw, as opposed to 14 | an incremental redraw. 15 | -------------------------------------------------------------------------------- /website/blog/0.14.1-release-notes.md: -------------------------------------------------------------------------------- 1 | Version 0.14.1 has been released on 2020-08-16, a month and a half after 0.14.0. 2 | This is a bugfix release. If you upgraded from 0.13.x, please also read the 3 | [0.14.0 release notes](0.14.0-release-notes.html). 4 | 5 | As usual, [prebuilt binaries](https://elv.sh/get) are offered for most common 6 | platforms. 7 | 8 | This release fixes the lack of deprecation warnings in 9 | [imported modules](https://b.elv.sh/1072) and when using the `explode` builtin. 10 | There are no new features. 11 | -------------------------------------------------------------------------------- /website/blog/0.20.1-release-notes.md: -------------------------------------------------------------------------------- 1 | Elvish 0.20.1 fixes a test that is failing on s370x. There are no user-visible 2 | changes. 3 | 4 | For changes since the 0.19.x series, see the 5 | [0.20.0 release notes](0.20.0-release-notes.html). 6 | -------------------------------------------------------------------------------- /website/blog/prelude.md: -------------------------------------------------------------------------------- 1 | The draft release notes for the next release can be found 2 | [here on GitHub](https://github.com/elves/elvish/blob/master/0.22.0-release-notes.md). 3 | -------------------------------------------------------------------------------- /website/cmd/gensite/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | [._]*.s[a-w][a-z] 26 | [._]s[a-w][a-z] 27 | *.un~ 28 | Session.vim 29 | .netrwhist 30 | *~ 31 | -------------------------------------------------------------------------------- /website/cmd/gensite/feed_tmpl.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | const feedTemplText = ` 4 | 5 | {{ .SiteTitle }} 6 | 7 | 8 | {{ .LastModified }} 9 | {{ .RootURL }}/ 10 | 11 | {{ $rootURL := .RootURL }} 12 | {{ $author := .Author }} 13 | {{ range $info := .Articles}} 14 | 15 | {{ $info.Title }} 16 | {{ $link := print $rootURL "/" $info.Category "/" $info.Name ".html" }} 17 | 18 | {{ $link }} 19 | {{ $info.LastModified }} 20 | {{ $author }} 21 | {{ $info.Content | html }} 22 | 23 | {{ end }} 24 | 25 | ` 26 | -------------------------------------------------------------------------------- /website/cmd/md2html/transcripts_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "embed" 5 | "strings" 6 | "testing" 7 | 8 | "src.elv.sh/pkg/elvdoc" 9 | "src.elv.sh/pkg/eval" 10 | "src.elv.sh/pkg/eval/evaltest" 11 | ) 12 | 13 | //go:embed *.elvts 14 | var transcripts embed.FS 15 | 16 | func TestTranscripts(t *testing.T) { 17 | evaltest.TestTranscriptsInFS(t, transcripts, 18 | "elvdoc-to-md-in-global", func(ev *eval.Evaler) { 19 | ev.ExtendGlobal(eval.BuildNs().AddGoFn("elvdoc-to-md", elvdocToMd)) 20 | }, 21 | ) 22 | } 23 | 24 | func elvdocToMd(fm *eval.Frame, src string) error { 25 | docs, err := elvdoc.Extract(strings.NewReader(src), "") 26 | if err != nil { 27 | return err 28 | } 29 | var sb strings.Builder 30 | writeElvdocSections(&sb, docs) 31 | _, err = fm.ByteOutput().WriteString(sb.String()) 32 | return err 33 | } 34 | -------------------------------------------------------------------------------- /website/cmd/runefreq/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "log" 8 | "os" 9 | "sort" 10 | ) 11 | 12 | func main() { 13 | freq := map[int]int{} 14 | rd := bufio.NewReader(os.Stdin) 15 | for { 16 | r, _, err := rd.ReadRune() 17 | if err == io.EOF { 18 | break 19 | } else if err != nil { 20 | log.Fatal(err) 21 | } 22 | if r > 0x7f { 23 | freq[int(r)]++ 24 | } 25 | } 26 | var keys []int 27 | for k := range freq { 28 | keys = append(keys, k) 29 | } 30 | sort.Ints(keys) 31 | for _, k := range keys { 32 | fmt.Printf("%d U+%04d %s\n", freq[k], k, string(rune(k))) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /website/favicons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elves/elvish/49937be09a7e615a244df724fe0678af017ebe51/website/favicons/android-chrome-192x192.png -------------------------------------------------------------------------------- /website/favicons/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elves/elvish/49937be09a7e615a244df724fe0678af017ebe51/website/favicons/android-chrome-512x512.png -------------------------------------------------------------------------------- /website/favicons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elves/elvish/49937be09a7e615a244df724fe0678af017ebe51/website/favicons/apple-touch-icon.png -------------------------------------------------------------------------------- /website/favicons/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #da532c 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /website/favicons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elves/elvish/49937be09a7e615a244df724fe0678af017ebe51/website/favicons/favicon-16x16.png -------------------------------------------------------------------------------- /website/favicons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elves/elvish/49937be09a7e615a244df724fe0678af017ebe51/website/favicons/favicon-32x32.png -------------------------------------------------------------------------------- /website/favicons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elves/elvish/49937be09a7e615a244df724fe0678af017ebe51/website/favicons/favicon.ico -------------------------------------------------------------------------------- /website/favicons/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elves/elvish/49937be09a7e615a244df724fe0678af017ebe51/website/favicons/mstile-144x144.png -------------------------------------------------------------------------------- /website/favicons/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elves/elvish/49937be09a7e615a244df724fe0678af017ebe51/website/favicons/mstile-150x150.png -------------------------------------------------------------------------------- /website/favicons/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elves/elvish/49937be09a7e615a244df724fe0678af017ebe51/website/favicons/mstile-310x150.png -------------------------------------------------------------------------------- /website/favicons/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elves/elvish/49937be09a7e615a244df724fe0678af017ebe51/website/favicons/mstile-310x310.png -------------------------------------------------------------------------------- /website/favicons/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elves/elvish/49937be09a7e615a244df724fe0678af017ebe51/website/favicons/mstile-70x70.png -------------------------------------------------------------------------------- /website/favicons/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "short_name": "", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /website/fonts/FiraMono-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elves/elvish/49937be09a7e615a244df724fe0678af017ebe51/website/fonts/FiraMono-Bold.woff2 -------------------------------------------------------------------------------- /website/fonts/FiraMono-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elves/elvish/49937be09a7e615a244df724fe0678af017ebe51/website/fonts/FiraMono-Regular.woff2 -------------------------------------------------------------------------------- /website/fonts/SourceSerif4-It.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elves/elvish/49937be09a7e615a244df724fe0678af017ebe51/website/fonts/SourceSerif4-It.woff2 -------------------------------------------------------------------------------- /website/fonts/SourceSerif4-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elves/elvish/49937be09a7e615a244df724fe0678af017ebe51/website/fonts/SourceSerif4-Regular.woff2 -------------------------------------------------------------------------------- /website/fonts/SourceSerif4-Semibold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elves/elvish/49937be09a7e615a244df724fe0678af017ebe51/website/fonts/SourceSerif4-Semibold.woff2 -------------------------------------------------------------------------------- /website/fonts/SourceSerif4-SemiboldIt.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elves/elvish/49937be09a7e615a244df724fe0678af017ebe51/website/fonts/SourceSerif4-SemiboldIt.woff2 -------------------------------------------------------------------------------- /website/get/index.toml: -------------------------------------------------------------------------------- 1 | prelude = "prelude" 2 | extraCSS = ["prelude.css"] 3 | extraJS = ["prelude.js"] 4 | autoIndex = true 5 | 6 | [[articles]] 7 | name = "default-shell" 8 | title = "Using Elvish as your default shell" 9 | 10 | [[articles]] 11 | name = "package-manager" 12 | title = "Installing Elvish with a package manager" 13 | 14 | [[articles]] 15 | name = "all-binaries" 16 | title = "All binaries available for download" 17 | -------------------------------------------------------------------------------- /website/go.mod: -------------------------------------------------------------------------------- 1 | module src.elv.sh/website 2 | 3 | go 1.22 4 | 5 | require ( 6 | github.com/BurntSushi/toml v1.4.0 7 | github.com/creack/pty v1.1.23 8 | src.elv.sh v0.21.0 9 | ) 10 | 11 | require ( 12 | github.com/mattn/go-isatty v0.0.20 // indirect 13 | golang.org/x/sync v0.8.0 // indirect 14 | golang.org/x/sys v0.24.0 // indirect 15 | ) 16 | 17 | replace src.elv.sh => ../ 18 | -------------------------------------------------------------------------------- /website/home/cmd-history-ttyshot.elvts: -------------------------------------------------------------------------------- 1 | ~> use store 2 | store:add-cmd 'cd ~/videos' 3 | store:add-cmd 'ffmpeg -i input.mp4 -c:v libx264 -c:a aac output.mp4' 4 | store:add-cmd 'ffmpeg -i input.mp4 -vf "transpose=1,scale=640:360,split [a][b];[a] palettegen [p];[b][p] paletteuse" -loop 0 output.gif' 5 | edit:history:fast-forward 6 | ~> echo '[CUT]' 7 | ~> #send-keys C-R ff 8 | -------------------------------------------------------------------------------- /website/home/cmd-history-ttyshot.html: -------------------------------------------------------------------------------- 1 | ~> elf@host 2 | HISTORY (dedup on) ff Ctrl-D dedup 3 | 34 ffmpeg -i input.mp4 -c:v libx264 -c:a aac outpu 4 | 35 ffmpeg -i input.mp4 -vf "transpose=1,scale=640: 5 | -------------------------------------------------------------------------------- /website/home/dir-history-ttyshot.elvts: -------------------------------------------------------------------------------- 1 | ~> use store 2 | store:add-dir /opt/java 3 | store:add-dir ~/java/com/acme/project 4 | store:add-dir ~/java/com/acme/project/utilities 5 | echo '[CUT]' 6 | ~> #send-keys C-L j 7 | -------------------------------------------------------------------------------- /website/home/dir-history-ttyshot.html: -------------------------------------------------------------------------------- 1 | ~> elf@host 2 | LOCATION j 3 | 10 ~/java/com/acme/project/utilities 4 | 10 ~/java/com/acme/project 5 | 10 /opt/java 6 | -------------------------------------------------------------------------------- /website/home/file-manager-ttyshot.elvts: -------------------------------------------------------------------------------- 1 | ~> cd elvish 2 | set edit:max-height = 10 3 | echo '[CUT]' 4 | ~> #send-keys C-N 5 | -------------------------------------------------------------------------------- /website/home/file-manager-ttyshot.html: -------------------------------------------------------------------------------- 1 | ~/elvish> elf@host 2 | NAVIGATING Ctrl-H hidden Ctrl-F filter 3 | bash 1.0-release.m 1.0 has not been released y 4 | elvis CONTRIBUTING. 5 | zsh Dockerfile 6 | LICENSE 7 | Makefile 8 | PACKAGING.md 9 | README.md 10 | SECURITY.md 11 | -------------------------------------------------------------------------------- /website/home/pipelines-ttyshot.elvts: -------------------------------------------------------------------------------- 1 | ~> range 1100 1111 | 2 | each {|x| curl -sL xkcd.com/$x/info.0.json } | 3 | from-json | 4 | each {|x| printf "%g: %s\n" $x[num] $x[title] } 5 | -------------------------------------------------------------------------------- /website/index.toml: -------------------------------------------------------------------------------- 1 | title = "Elvish Shell" 2 | author = "Qi Xiao" 3 | feedPosts = 10 4 | template = "template.html" 5 | baseCSS = ["reset.css", "style.css", "icon-font.css", "sgr.css"] 6 | rootURL = "https://elv.sh" 7 | 8 | [index] 9 | name = "home" 10 | extraCSS = ["home/home.css"] 11 | 12 | [[categories]] 13 | name = "get" 14 | title = "Get Elvish" 15 | 16 | [[categories]] 17 | name = "learn" 18 | title = "Learn Elvish" 19 | 20 | [[categories]] 21 | name = "ref" 22 | title = "Elvish Reference" 23 | 24 | [[categories]] 25 | name = "blog" 26 | title = "Elvish Blog" 27 | 28 | [[categories]] 29 | name = "sponsor" 30 | title = "Sponsoring Elvish's Development" 31 | navHTML = "" 32 | -------------------------------------------------------------------------------- /website/learn/arguments-and-outputs/command-history-listing-narrowed-ttyshot.elvts: -------------------------------------------------------------------------------- 1 | //rows 8 2 | ~> store:add-cmd 'randint 1 7' 3 | edit:history:fast-forward 4 | echo '[CUT]' 5 | ~> #send-keys C-R git 6 | -------------------------------------------------------------------------------- /website/learn/arguments-and-outputs/command-history-listing-narrowed-ttyshot.html: -------------------------------------------------------------------------------- 1 | ~> elf@host 2 | HISTORY (dedup on) git Ctrl-D dedup 3 | 7 git branch 4 | 8 git checkout . 5 | 9 git commit 6 | 19 git status 7 | -------------------------------------------------------------------------------- /website/learn/arguments-and-outputs/command-history-listing-ttyshot.elvts: -------------------------------------------------------------------------------- 1 | //rows 8 2 | ~> store:add-cmd 'randint 1 7' 3 | edit:history:fast-forward 4 | echo '[CUT]' 5 | ~> #send-keys C-R 6 | -------------------------------------------------------------------------------- /website/learn/arguments-and-outputs/command-history-listing-ttyshot.html: -------------------------------------------------------------------------------- 1 | ~> elf@host 2 | HISTORY (dedup on) Ctrl-D dedup 3 | 21 echo $pwd 4 | 22 * (+ 3 4) (- 100 94) 5 | 31 make 6 | 32 math:min 3 1 30 7 | 33 randint 1 7 8 | -------------------------------------------------------------------------------- /website/learn/arguments-and-outputs/command-history-up-ttyshot.elvts: -------------------------------------------------------------------------------- 1 | ~> store:add-cmd 'randint 1 7' 2 | edit:history:fast-forward 3 | echo '[CUT]' 4 | ~> #send-keys Up 5 | -------------------------------------------------------------------------------- /website/learn/arguments-and-outputs/command-history-up-ttyshot.html: -------------------------------------------------------------------------------- 1 | ~> randint 1 7 elf@host 2 | HISTORY #34 3 | -------------------------------------------------------------------------------- /website/learn/first-commands/location-mode-elv-ttyshot.elvts: -------------------------------------------------------------------------------- 1 | //rows 8 2 | ~> use store 3 | store:add-dir ~/tmp 4 | echo '[CUT]' 5 | ~> #send-keys C-L elv 6 | -------------------------------------------------------------------------------- /website/learn/first-commands/location-mode-elv-ttyshot.html: -------------------------------------------------------------------------------- 1 | ~> elf@host 2 | LOCATION elv 3 | 10 ~/elvish 4 | 10 ~/.local/share/elvish 5 | 10 ~/elvish/website 6 | 9 ~/.config/elvish 7 | 9 ~/elvish/pkg/edit 8 | -------------------------------------------------------------------------------- /website/learn/first-commands/location-mode-ttyshot.elvts: -------------------------------------------------------------------------------- 1 | //rows 8 2 | ~> use store 3 | store:add-dir ~/tmp 4 | echo '[CUT]' 5 | ~> #send-keys C-L 6 | -------------------------------------------------------------------------------- /website/learn/first-commands/location-mode-ttyshot.html: -------------------------------------------------------------------------------- 1 | ~> elf@host 2 | LOCATION 3 | 18 ~/tmp 4 | 10 ~/elvish 5 | 10 ~/.local/share/elvish 6 | 10 ~/elvish/website 7 | 9 ~/.config/elvish 8 | -------------------------------------------------------------------------------- /website/learn/scripting-case-studies/misspelt-variable-ttyshot.elvts: -------------------------------------------------------------------------------- 1 | //cols 69 2 | ~> var project = ~/project 3 | ~> #send-keys rm Space -rf Space $projetc/bin 4 | -------------------------------------------------------------------------------- /website/learn/scripting-case-studies/misspelt-variable-ttyshot.html: -------------------------------------------------------------------------------- 1 | ~> var project = ~/project 2 | ~> rm -rf $projetc/bin elf@host 3 | compilation error: [interactive]:1:8-15: variable $projetc not found 4 | -------------------------------------------------------------------------------- /website/learn/tour.css: -------------------------------------------------------------------------------- 1 | td[colspan] { 2 | text-align: center; 3 | } 4 | -------------------------------------------------------------------------------- /website/learn/tour/completion-filter-ttyshot.elvts: -------------------------------------------------------------------------------- 1 | ~> cd elvish 2 | ~> #send-keys echo Space Tab .md 3 | -------------------------------------------------------------------------------- /website/learn/tour/completion-filter-ttyshot.html: -------------------------------------------------------------------------------- 1 | ~> cd elvish 2 | ~/elvish> echo 1.0-release.md elf@host 3 | COMPLETING argument .md 4 | 1.0-release.md PACKAGING.md SECURITY.md 5 | CONTRIBUTING.md README.md 6 | -------------------------------------------------------------------------------- /website/learn/tour/completion-ttyshot.elvts: -------------------------------------------------------------------------------- 1 | ~> cd elvish 2 | ~> #send-keys echo Space Tab 3 | -------------------------------------------------------------------------------- /website/learn/tour/completion-ttyshot.html: -------------------------------------------------------------------------------- 1 | ~> cd elvish 2 | ~/elvish> echo 1.0-release.md elf@host 3 | COMPLETING argument 4 | 1.0-release.md README.md syntaxes/ 5 | CONTRIBUTING.md SECURITY.md tools/ 6 | Dockerfile cmd/ vscode/ 7 | LICENSE go.mod website/ 8 | Makefile go.sum 9 | PACKAGING.md pkg/ 10 | -------------------------------------------------------------------------------- /website/learn/tour/history-list-ttyshot.elvts: -------------------------------------------------------------------------------- 1 | ~> #send-keys C-R 2 | -------------------------------------------------------------------------------- /website/learn/tour/history-walk-prefix-ttyshot.elvts: -------------------------------------------------------------------------------- 1 | ~> #send-keys echo Up Up Up 2 | -------------------------------------------------------------------------------- /website/learn/tour/history-walk-prefix-ttyshot.html: -------------------------------------------------------------------------------- 1 | ~> echo (styled warning: red) bumpy road elf@host 2 | HISTORY #2 3 | -------------------------------------------------------------------------------- /website/learn/tour/history-walk-ttyshot.elvts: -------------------------------------------------------------------------------- 1 | ~> #send-keys Up 2 | -------------------------------------------------------------------------------- /website/learn/tour/history-walk-ttyshot.html: -------------------------------------------------------------------------------- 1 | ~> math:min 3 1 30 elf@host 2 | Ctrl-A autofix: use math Tab Enter autofix first 3 | HISTORY #32 4 | -------------------------------------------------------------------------------- /website/learn/tour/lastcmd-ttyshot.elvts: -------------------------------------------------------------------------------- 1 | ~> echo abc def 2 | ~> #send-keys vim Space M-, 3 | -------------------------------------------------------------------------------- /website/learn/tour/lastcmd-ttyshot.html: -------------------------------------------------------------------------------- 1 | ~> echo abc def 2 | abc def 3 | ~> vim elf@host 4 | LASTCMD 5 | echo abc def 6 | 0 echo 7 | 1 abc 8 | 2 def 9 | -------------------------------------------------------------------------------- /website/learn/tour/location-filter-ttyshot.elvts: -------------------------------------------------------------------------------- 1 | ~> #send-keys C-L local 2 | -------------------------------------------------------------------------------- /website/learn/tour/location-filter-ttyshot.html: -------------------------------------------------------------------------------- 1 | ~> elf@host 2 | LOCATION local 3 | 10 ~/.local/share/elvish 4 | 9 /usr/local 5 | 9 /usr/local/share 6 | 9 /usr/local/bin 7 | -------------------------------------------------------------------------------- /website/learn/tour/location-ttyshot.elvts: -------------------------------------------------------------------------------- 1 | ~> #send-keys C-L 2 | -------------------------------------------------------------------------------- /website/learn/tour/navigation-ttyshot.elvts: -------------------------------------------------------------------------------- 1 | ~> cd elvish; echo '[CUT]' 2 | ~> #send-keys C-N 3 | -------------------------------------------------------------------------------- /website/learn/tour/unicode-prompts-ttyshot.elvts: -------------------------------------------------------------------------------- 1 | ~> set edit:rprompt = (constantly ^ 2 | (styled (whoami)✸(hostname) inverse)) 3 | ~> set edit:prompt = { 4 | tilde-abbr $pwd 5 | styled '❱ ' bright-red 6 | } 7 | ~> #send-keys # Space Fancy Space unicode Space prompts! 8 | -------------------------------------------------------------------------------- /website/learn/tour/unicode-prompts-ttyshot.html: -------------------------------------------------------------------------------- 1 | ~> set edit:rprompt = (constantly ^ 2 | (styled (whoami)(hostname) inverse)) 3 | ~> set edit:prompt = { 4 | tilde-abbr $pwd 5 | styled '❱ ' bright-red 6 | } 7 | ~# Fancy unicode prompts! elf✸host.example.com 8 | -------------------------------------------------------------------------------- /website/ref/doc.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | @module doc 4 | 5 | # Introduction 6 | 7 | The `doc:` module provides access to the documentation of Elvish modules. 8 | 9 | This module only supports builtin modules now, but will expand in future to 10 | cover user-defined modules too. 11 | 12 | Function usages are given in the same format as in the reference doc for the 13 | [builtin module](builtin.html). 14 | -------------------------------------------------------------------------------- /website/ref/edit/autofix-ttyshot.elvts: -------------------------------------------------------------------------------- 1 | ~> #send-keys str: 2 | -------------------------------------------------------------------------------- /website/ref/edit/autofix-ttyshot.html: -------------------------------------------------------------------------------- 1 | ~> str: elf@host 2 | Ctrl-A autofix: use str Tab Enter autofix first 3 | -------------------------------------------------------------------------------- /website/ref/edit/completion-mode-ttyshot.elvts: -------------------------------------------------------------------------------- 1 | ~> cd elvish; echo '[CUT]' 2 | ~> #send-keys vim Space Tab 3 | -------------------------------------------------------------------------------- /website/ref/edit/completion-mode-ttyshot.html: -------------------------------------------------------------------------------- 1 | ~/elvish> vim 1.0-release.md elf@host 2 | COMPLETING argument 3 | 1.0-release.md README.md syntaxes/ 4 | CONTRIBUTING.md SECURITY.md tools/ 5 | Dockerfile cmd/ vscode/ 6 | LICENSE go.mod website/ 7 | Makefile go.sum 8 | PACKAGING.md pkg/ 9 | -------------------------------------------------------------------------------- /website/ref/file.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | @module file 4 | 5 | # Introduction 6 | 7 | The `file:` module provides utilities for manipulating file objects. 8 | 9 | Function usages are given in the same format as in the reference doc for the 10 | [builtin module](builtin.html). 11 | -------------------------------------------------------------------------------- /website/ref/math.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | @module math 4 | 5 | # Introduction 6 | 7 | The `math:` module provides mathematical functions and constants. 8 | 9 | Function usages are given in the same format as in the reference doc for the 10 | [builtin module](builtin.html). In particular, all the commands in this module 11 | conform to the convention of [numeric commands](builtin.html#numeric-commands). 12 | -------------------------------------------------------------------------------- /website/ref/md.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | @module md 4 | 5 | # Introduction 6 | 7 | The `md:` module provides utilities for working with Markdown text. 8 | 9 | Function usages are given in the same format as in the reference doc for the 10 | [builtin module](builtin.html). 11 | 12 | This module uses a custom implementation that supports 13 | [a large subset](https://pkg.go.dev/src.elv.sh/pkg/md#hdr-Which_Markdown_variant_does_this_package_implement_) 14 | of CommonMark. 15 | -------------------------------------------------------------------------------- /website/ref/path.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | @module path 4 | 5 | # Introduction 6 | 7 | The `path:` module provides functions for manipulating and testing filesystem 8 | paths. 9 | 10 | Function usages are given in the same format as in the reference doc for the 11 | [builtin module](builtin.html). 12 | -------------------------------------------------------------------------------- /website/ref/platform.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | @module platform 4 | 5 | # Introduction 6 | 7 | The `platform:` module provides access to the platform's identifying data. 8 | -------------------------------------------------------------------------------- /website/ref/prelude.md: -------------------------------------------------------------------------------- 1 | Reference documents also available as a docset. Download the 2 | [latest build](https://elv.sh/ref/docset/Elvish.tgz) or subscribe to the 3 | [feed](dash-feed://https%3A%2F%2Felv.sh%2Fref%2Fdocset%2FElvish.xml). 4 | -------------------------------------------------------------------------------- /website/ref/re.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | @module re 4 | 5 | # Introduction 6 | 7 | The `re:` module wraps Go's `regexp` package. See the Go's doc for supported 8 | [regular expression syntax](https://godoc.org/regexp/syntax). 9 | 10 | Function usages notations follow the same convention as the 11 | [builtin module doc](builtin.html). 12 | 13 | The following options are supported by multiple functions in this module: 14 | 15 | - `&posix=$false`: Use POSIX ERE syntax. See also 16 | [doc](http://godoc.org/regexp#CompilePOSIX) in Go package. 17 | 18 | - `&longest=$false`: Prefer leftmost-longest match. See also 19 | [doc](http://godoc.org/regexp#Regexp.Longest) in Go package. 20 | 21 | - `&max=-1`: If non-negative, limits the maximum number of results. 22 | -------------------------------------------------------------------------------- /website/ref/readline-binding.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | @module readline-binding 4 | 5 | # Introduction 6 | 7 | The `readline-binding` module provides GNU readline-like key bindings, such as 8 | binding Ctrl-A to move the cursor to the start of the line. GNU 9 | readline bindings are the default for shells such as Bash. So if you are 10 | migrating from Bash to Elvish you probably want to add the following to your 11 | [`rc.elv`](command.html#rc-file): 12 | 13 | ```elvish 14 | use readline-binding 15 | ``` 16 | 17 | Note that this will override some of the standard bindings. For example, 18 | Ctrl-L will be bound to a function that clears the terminal screen 19 | rather than start location mode. The standard bindings are usually relocated to 20 | use Alt as the modifier -- the location mode is bound to Alt-L for 21 | example. 22 | 23 | See the 24 | [source code](https://src.elv.sh/pkg/mods/readline-binding/readline-binding.elv) 25 | for details. 26 | -------------------------------------------------------------------------------- /website/ref/runtime.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | @module runtime 4 | 5 | # Introduction 6 | 7 | The `runtime:` module provides information about the Elvish runtime. 8 | -------------------------------------------------------------------------------- /website/ref/store.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | @module store 4 | 5 | # Introduction 6 | 7 | The `store:` module provides access to Elvish's persistent data store. It is 8 | only available in interactive mode now. 9 | -------------------------------------------------------------------------------- /website/ref/str.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | @module str 4 | 5 | # Introduction 6 | 7 | The `str:` module provides string manipulation functions. 8 | 9 | Function usages are given in the same format as in the reference doc for the 10 | [builtin module](builtin.html). 11 | 12 | The builtin module also contains some string utilities, such as string 13 | comparison commands like [` 2 | 3 | @module unix 4 | 5 | # Introduction 6 | 7 | The `unix:` module provides access to features that only make sense on UNIX-like 8 | operating systems, such as Linux, FreeBSD, and macOS. 9 | 10 | On non-UNIX operating systems, such as MS Windows, this namespace does not exist 11 | and `use unix` will fail. Use the 12 | [`$platform:is-unix`](platform.html#$platform:is-unix) variable to determine if 13 | this namespace is usable. 14 | -------------------------------------------------------------------------------- /website/slides/Makefile: -------------------------------------------------------------------------------- 1 | MDS := $(wildcard *.md) 2 | HTMLS := $(MDS:.md=.html) 3 | 4 | all: $(HTMLS) 5 | 6 | %.html: %.md gen.elv template.html 7 | ./gen.elv $< $@ 8 | -------------------------------------------------------------------------------- /website/slides/gen.elv: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env elvish 2 | use str 3 | use flag 4 | 5 | fn main { |&title=Presentation md html| 6 | var content = (go run src.elv.sh/website/cmd/md2html < $md | slurp) 7 | slurp < template.html | 8 | str:replace '$common-css' (cat ../reset.css ../sgr.css | slurp) (one) | 9 | str:replace '$title' $title (one) | 10 | str:replace '$content' $content (one) | 11 | print (one) > $html 12 | } 13 | 14 | flag:call $main~ $args 15 | -------------------------------------------------------------------------------- /website/sponsor/index.toml: -------------------------------------------------------------------------------- 1 | prelude = "prelude" 2 | extraCSS = ["prelude.css"] 3 | -------------------------------------------------------------------------------- /website/sponsor/prelude.css: -------------------------------------------------------------------------------- 1 | .action { 2 | display: flex; 3 | column-gap: 16px; 4 | } 5 | 6 | .action a { 7 | width: fit-content; 8 | height: 40px; 9 | padding: 0 16px; 10 | border: 1px solid; 11 | border-radius: 5px; 12 | font-family: var(--sans-font); 13 | color: white; 14 | display: flex; 15 | justify-content: center; 16 | align-items: center; 17 | } 18 | 19 | .action a.github { 20 | background: #333; 21 | } 22 | 23 | .action a.patreon { 24 | background: var(--deep-orange-700); 25 | } 26 | -------------------------------------------------------------------------------- /website/sponsor/prelude.md: -------------------------------------------------------------------------------- 1 | Elvish is free software under a 2 | [permissive BSD license](https://github.com/elves/elvish/blob/master/LICENSE). 3 | 4 | However, developing free software is not free. Elvish is a project exploring the 5 | boundary of what shells can do, and developing it takes considerable time, 6 | energy and creativity. 7 | 8 | To make the project more sustainable, please sponsor Elvish's creator and main 9 | developer, `xiaq`: 10 | 11 |
15 | -------------------------------------------------------------------------------- /website/tools/cmd-deps: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Outputs the dependencies of a command. 4 | # 5 | # Must be run from the website directory. 6 | 7 | go list -f '{{ join .Deps "\n" }}' $1 | 8 | sed -n '/^src\.elv\.sh/{ 9 | s/^src\.elv\.sh/../ 10 | p 11 | }' | 12 | while read dir; do 13 | echo $dir $dir/* 14 | done 15 | -------------------------------------------------------------------------------- /website/tools/docset-data/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIdentifier 6 | elvish 7 | CFBundleName 8 | Elvish 9 | DocSetPlatformFamily 10 | elvish 11 | isDashDocset 12 | 13 | DashDocSetFamily 14 | dashtoc 15 | 16 | 17 | -------------------------------------------------------------------------------- /website/tools/md-deps: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Outputs the extra dependencies of a Markdown file. 4 | # 5 | # Must be run from the website directory. 6 | 7 | cat ${1%.html}.md | 8 | awk '$1 == "```ttyshot" { getline; print $1 "-ttyshot.html" }' 9 | 10 | cat ${1%.html}.md | 11 | awk '$1 == "@module" { 12 | if ($2 == "builtin") { 13 | print "eval" 14 | } else if ($2 == "edit") { 15 | print "eval" 16 | } else { 17 | print "mods/" $2 18 | } 19 | }' | 20 | while read dir; do 21 | echo ../pkg/$dir ../pkg/$dir/*.elv 22 | done 23 | -------------------------------------------------------------------------------- /website/tools/mkdocset: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Generate docset from reference docs. 4 | # 5 | # Docset is a format for packaging docs for offline consumption: 6 | # https://kapeli.com/docsets 7 | # 8 | # External dependencies: 9 | # 10 | # - python3 11 | 12 | if test $# != 2; then 13 | echo "Usage: mkdocset.elv $website $docset" 14 | exit 1 15 | fi 16 | 17 | bindir=$(dirname "$0") 18 | website=$1 19 | docset=$2 20 | 21 | mkdir -p $docset/Contents/Resources/Documents 22 | cp $bindir/../favicons/favicon-16x16.png $docset/icon.png 23 | cp $bindir/../favicons/favicon-32x32.png $docset/icon@2x.png 24 | cp $bindir/docset-data/Info.plist $docset/Contents 25 | cp $website/ref/*.html $docset/Contents/Resources/Documents 26 | rm $docset/Contents/Resources/Documents/index.html 27 | python3 $bindir/mkdsidx.py $website/ref $docset/Contents/Resources/docSet.dsidx 28 | --------------------------------------------------------------------------------